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 will block callbacks until it is decoded.
diff --git a/src/cobalt/media/base/pipeline.h b/src/cobalt/media/base/pipeline.h
index 82863ce..1853ae5 100644
--- a/src/cobalt/media/base/pipeline.h
+++ b/src/cobalt/media/base/pipeline.h
@@ -15,6 +15,8 @@
#ifndef COBALT_MEDIA_BASE_PIPELINE_H_
#define COBALT_MEDIA_BASE_PIPELINE_H_
+#include <vector>
+
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop_proxy.h"
@@ -38,6 +40,8 @@
typedef void* PipelineWindow;
#endif // defined(COBALT_USE_SBPLAYER_PIPELINE)
+// #define COBALT_MEDIA_ENABLE_VIDEO_DUMPER 1
+
namespace cobalt {
namespace media {
@@ -46,9 +50,17 @@
// Callback to notify that a DRM system is ready.
typedef base::Callback<void(SbDrmSystem)> DrmSystemReadyCB;
-// Callback to set a DrmSystemReadyCB.
+// Callback to set an DrmSystemReadyCB.
typedef base::Callback<void(const DrmSystemReadyCB&)> SetDrmSystemReadyCB;
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+// Callback to notify that EME init data is ready.
+typedef base::Callback<void(const std::vector<uint8_t>&)> EMEInitDataReadyCB;
+
+// Callback to set an EMEInitDataReadyCB.
+typedef base::Callback<void(const EMEInitDataReadyCB&)> SetEMEInitDataReadyCB;
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+
// Pipeline contains the common interface for media pipelines. It provides
// functions to perform asynchronous initialization, pausing, seeking and
// playing.
@@ -104,11 +116,15 @@
// It is an error to call this method after the pipeline has already started.
virtual void Start(Demuxer* demuxer,
const SetDrmSystemReadyCB& set_drm_system_ready_cb,
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ const SetEMEInitDataReadyCB& set_eme_init_data_ready_cb,
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
const PipelineStatusCB& ended_cb,
const PipelineStatusCB& error_cb,
const PipelineStatusCB& seek_cb,
const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb) = 0;
+ const base::Closure& duration_change_cb,
+ const base::Closure& output_mode_change_cb) = 0;
// Asynchronously stops the pipeline, executing |stop_cb| when the pipeline
// teardown has completed.
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index b773589..13db021 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -39,6 +39,7 @@
#include "cobalt/media/base/sbplayer_set_bounds_helper.h"
#include "cobalt/media/base/starboard_player.h"
#include "cobalt/media/base/video_decoder_config.h"
+#include "cobalt/media/base/video_dumper.h"
#include "ui/gfx/size.h"
namespace cobalt {
@@ -51,6 +52,8 @@
namespace {
+const char kVideoDumpFileName[] = "video_content.dmp";
+
// Used to post parameters to SbPlayerPipeline::StartTask() as the number of
// parameters exceed what base::Bind() can support.
struct StartTaskParameters {
@@ -61,6 +64,7 @@
PipelineStatusCB seek_cb;
Pipeline::BufferingStateCB buffering_state_cb;
base::Closure duration_change_cb;
+ base::Closure output_mode_change_cb;
};
// SbPlayerPipeline is a PipelineBase implementation that uses the SbPlayer
@@ -79,10 +83,14 @@
void Resume() OVERRIDE;
void Start(Demuxer* demuxer,
const SetDrmSystemReadyCB& set_drm_system_ready_cb,
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ const SetEMEInitDataReadyCB& set_eme_init_data_ready_cb,
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
const PipelineStatusCB& ended_cb, const PipelineStatusCB& error_cb,
const PipelineStatusCB& seek_cb,
const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb) OVERRIDE;
+ const base::Closure& duration_change_cb,
+ const base::Closure& output_mode_change_cb) OVERRIDE;
void Stop(const base::Closure& stop_cb) OVERRIDE;
void Seek(TimeDelta time, const PipelineStatusCB& seek_cb);
@@ -136,6 +144,15 @@
void SuspendTask(base::WaitableEvent* done_event);
void ResumeTask(base::WaitableEvent* done_event);
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ void OnEMEInitDataReady(const std::vector<uint8_t>& eme_init_data) {
+ DCHECK(!eme_init_data.empty());
+ eme_init_datas_.push_back(eme_init_data);
+ }
+
+ std::vector<std::vector<uint8_t> > eme_init_datas_;
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+
// Message loop used to execute pipeline tasks. It is thread-safe.
scoped_refptr<base::MessageLoopProxy> message_loop_;
@@ -187,6 +204,7 @@
PipelineStatusCB error_cb_;
BufferingStateCB buffering_state_cb_;
base::Closure duration_change_cb_;
+ base::Closure output_mode_change_cb_;
base::optional<bool> decode_to_texture_output_mode_;
// Demuxer reference used for setting the preload value.
@@ -210,6 +228,8 @@
bool suspended_;
bool stopped_;
+ VideoDumper video_dumper_;
+
DISALLOW_COPY_AND_ASSIGN(SbPlayerPipeline);
};
@@ -229,7 +249,8 @@
video_read_in_progress_(false),
set_bounds_helper_(new SbPlayerSetBoundsHelper),
suspended_(false),
- stopped_(false) {}
+ stopped_(false),
+ video_dumper_(kVideoDumpFileName) {}
SbPlayerPipeline::~SbPlayerPipeline() { DCHECK(!player_); }
@@ -255,13 +276,15 @@
waitable_event.Wait();
}
-void SbPlayerPipeline::Start(Demuxer* demuxer,
- const SetDrmSystemReadyCB& set_drm_system_ready_cb,
- const PipelineStatusCB& ended_cb,
- const PipelineStatusCB& error_cb,
- const PipelineStatusCB& seek_cb,
- const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb) {
+void SbPlayerPipeline::Start(
+ Demuxer* demuxer, const SetDrmSystemReadyCB& set_drm_system_ready_cb,
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ const SetEMEInitDataReadyCB& set_eme_init_data_ready_cb,
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ const PipelineStatusCB& ended_cb, const PipelineStatusCB& error_cb,
+ const PipelineStatusCB& seek_cb, const BufferingStateCB& buffering_state_cb,
+ const base::Closure& duration_change_cb,
+ const base::Closure& output_mode_change_cb) {
TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::Start");
DCHECK(demuxer);
@@ -270,6 +293,7 @@
DCHECK(!seek_cb.is_null());
DCHECK(!buffering_state_cb.is_null());
DCHECK(!duration_change_cb.is_null());
+ DCHECK(!output_mode_change_cb.is_null());
StartTaskParameters parameters;
parameters.demuxer = demuxer;
@@ -279,6 +303,12 @@
parameters.seek_cb = seek_cb;
parameters.buffering_state_cb = buffering_state_cb;
parameters.duration_change_cb = duration_change_cb;
+ parameters.output_mode_change_cb = output_mode_change_cb;
+
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ set_eme_init_data_ready_cb.Run(base::Bind(
+ &SbPlayerPipeline::OnEMEInitDataReady, base::Unretained(this)));
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
message_loop_->PostTask(
FROM_HERE, base::Bind(&SbPlayerPipeline::StartTask, this, parameters));
@@ -489,6 +519,7 @@
}
buffering_state_cb_ = parameters.buffering_state_cb;
duration_change_cb_ = parameters.duration_change_cb;
+ output_mode_change_cb_ = parameters.output_mode_change_cb;
const bool kEnableTextTracks = false;
demuxer_->Initialize(this,
@@ -578,6 +609,10 @@
const VideoDecoderConfig& video_config =
video_stream_->video_decoder_config();
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ video_dumper_.DumpEmeInitData(eme_init_datas_);
+ video_dumper_.DumpConfigs(audio_config, video_config);
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
{
base::AutoLock auto_lock(lock_);
player_.reset(new StarboardPlayer(
@@ -588,6 +623,14 @@
}
if (player_->IsValid()) {
+ base::Closure output_mode_change_cb;
+ {
+ base::AutoLock auto_lock(lock_);
+ DCHECK(!output_mode_change_cb_.is_null());
+ output_mode_change_cb = base::ResetAndReturn(&output_mode_change_cb_);
+ }
+ output_mode_change_cb.Run();
+
return;
}
@@ -724,6 +767,7 @@
video_read_in_progress_ = false;
}
+ video_dumper_.DumpAccessUnit(buffer);
player_->WriteBuffer(type, buffer);
}
diff --git a/src/cobalt/media/base/shell_video_frame_provider.h b/src/cobalt/media/base/shell_video_frame_provider.h
index eb98efb..a5bd969 100644
--- a/src/cobalt/media/base/shell_video_frame_provider.h
+++ b/src/cobalt/media/base/shell_video_frame_provider.h
@@ -28,7 +28,7 @@
// We should consider remove VideoFrame as it is no longer useful.
class VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
public:
- int texture_id() const { return 0; }
+ uintptr_t texture_id() const { return 0; }
base::TimeDelta GetTimestamp() const { return base::TimeDelta(); }
};
diff --git a/src/cobalt/media/base/video_dmp_reader.h b/src/cobalt/media/base/video_dmp_reader.h
new file mode 100644
index 0000000..51c9f14
--- /dev/null
+++ b/src/cobalt/media/base/video_dmp_reader.h
@@ -0,0 +1,219 @@
+// 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_MEDIA_BASE_VIDEO_DMP_READER_H_
+#define COBALT_MEDIA_BASE_VIDEO_DMP_READER_H_
+
+// This file is deliberately not using any Cobalt/Starboard specific API so it
+// can be used in an independent application.
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+// File: <BOM> <Record>*
+// BOM: 0x76543210
+// Record: <4 bytes type> + <4 bytes size> + <|size| bytes binary data>
+// type: 0: audio config, 1: video config, 2: eme init data,
+// 3: audio access unit, 4: video access unit.
+// audio/video access unit;
+// <8 bytes time stamp in microseconds>
+// <4 bytes size of key_id> + |size| bytes of key id
+// <4 bytes size of iv> + |size| bytes of iv
+// <4 bytes count> (0 for non-encrypted AU/frame)
+// (subsample: 4 bytes clear size, 4 bytes encrypted size) * |count|
+// <4 bytes size>
+// |size| bytes encoded binary data
+class VideoDmpReader {
+ public:
+ enum {
+ kRecordTypeAudioConfig,
+ kRecordTypeVideoConfig,
+ kRecordTypeEmeInitData,
+ kRecordTypeAudioAccessUnit,
+ kRecordTypeVideoAccessUnit,
+ };
+
+ enum AccessUnitType { kAccessUnitTypeAudio, kAccessUnitTypeVideo };
+
+ struct Subsample {
+ uint32_t clear_bytes;
+ uint32_t encrypted_bytes;
+ };
+
+ typedef std::vector<Subsample> Subsamples;
+ typedef std::vector<uint8_t> EmeInitData;
+
+ class AccessUnit {
+ public:
+ AccessUnit(AccessUnitType access_unit_type, int64_t timestamp,
+ const std::vector<uint8_t>& key_id,
+ const std::vector<uint8_t>& iv, const Subsamples& subsamples,
+ std::vector<uint8_t> data)
+ : access_unit_type_(access_unit_type),
+ timestamp_(timestamp),
+ key_id_(key_id),
+ iv_(iv),
+ subsamples_(subsamples),
+ data_(std::move(data)) {}
+ AccessUnitType access_unit_type() const { return access_unit_type_; }
+ int64_t timestamp() const { return timestamp_; }
+ // Returns empty vector when the AU is not encrypted.
+ const std::vector<uint8_t>& key_id() const { return key_id_; }
+ const std::vector<uint8_t>& iv() const { return iv_; }
+ const Subsamples& subsamples() const { return subsamples_; }
+ const std::vector<uint8_t> data() const { return data_; }
+
+ private:
+ AccessUnitType access_unit_type_;
+ int64_t timestamp_;
+ std::vector<uint8_t> key_id_;
+ std::vector<uint8_t> iv_;
+ Subsamples subsamples_;
+ std::vector<uint8_t> data_;
+ };
+
+ static const uint32_t kBOM = 0x76543210;
+
+ VideoDmpReader() : reverse_byte_order_(false) {}
+
+ // Abstract function implemented by the derived class to read data. When
+ // the return value is less than |bytes_to_read|, the stream reaches the end.
+ virtual size_t Read(void* buffer, size_t bytes_to_read) = 0;
+ virtual void ReportFatalError() = 0;
+
+ const std::vector<EmeInitData> eme_init_datas() const {
+ return eme_init_datas_;
+ }
+ const std::vector<AccessUnit>& access_units() const { return access_units_; }
+
+ void Parse() {
+ uint32_t bom;
+ ReadChecked(&bom);
+ if (bom != kBOM) {
+ std::reverse(reinterpret_cast<uint8_t*>(&bom),
+ reinterpret_cast<uint8_t*>(&bom + 1));
+ if (bom != kBOM) {
+ ReportFatalError();
+ return;
+ }
+ reverse_byte_order_ = true;
+ }
+ uint32_t type;
+ EmeInitData eme_init_data;
+ while (ReadUnchecked(&type)) {
+ uint32_t size;
+ switch (type) {
+ case kRecordTypeAudioConfig:
+ ReadChecked(&size);
+ if (size != 0) {
+ ReportFatalError();
+ }
+ break;
+ case kRecordTypeVideoConfig:
+ ReadChecked(&size);
+ if (size != 0) {
+ ReportFatalError();
+ }
+ break;
+ case kRecordTypeEmeInitData:
+ ReadChecked(&eme_init_data);
+ eme_init_datas_.push_back(eme_init_data);
+ break;
+ case kRecordTypeAudioAccessUnit:
+ ReadChecked(&size);
+ ReadAndAppendAccessUnitChecked(kAccessUnitTypeAudio);
+ break;
+ case kRecordTypeVideoAccessUnit:
+ ReadChecked(&size);
+ ReadAndAppendAccessUnitChecked(kAccessUnitTypeVideo);
+ break;
+ }
+ }
+ }
+
+ private:
+ void ReadAndAppendAccessUnitChecked(AccessUnitType access_unit_type) {
+ int64_t timestamp;
+ ReadChecked(×tamp);
+
+ std::vector<uint8_t> key_id, iv;
+ ReadChecked(&key_id);
+ ReadChecked(&iv);
+
+ uint32_t subsample_count;
+ ReadChecked(&subsample_count);
+
+ Subsamples subsamples(subsample_count);
+ for (auto& subsample : subsamples) {
+ ReadChecked(&subsample.clear_bytes);
+ ReadChecked(&subsample.encrypted_bytes);
+ }
+
+ std::vector<uint8_t> data;
+ ReadChecked(&data);
+
+ access_units_.emplace_back(access_unit_type, timestamp, key_id, iv,
+ subsamples, std::move(data));
+ }
+
+ template <typename T>
+ bool ReadUnchecked(T* value) {
+ if (!value) {
+ ReportFatalError();
+ return false;
+ }
+
+ int bytes_to_read = static_cast<int>(sizeof(*value));
+ int bytes_read = Read(value, bytes_to_read);
+
+ if (reverse_byte_order_) {
+ std::reverse(reinterpret_cast<uint8_t*>(value),
+ reinterpret_cast<uint8_t*>(value + 1));
+ }
+
+ return bytes_to_read == bytes_read;
+ }
+ template <typename T>
+ void ReadChecked(T* value) {
+ if (!ReadUnchecked(value)) {
+ ReportFatalError();
+ }
+ }
+ void ReadChecked(std::vector<uint8_t>* value) {
+ if (!value) {
+ ReportFatalError();
+ }
+
+ uint32_t size;
+ ReadChecked(&size);
+
+ value->resize(size);
+
+ if (size == 0) {
+ return;
+ }
+
+ int bytes_read = Read(value->data(), size);
+ if (bytes_read != size) {
+ ReportFatalError();
+ }
+ }
+
+ bool reverse_byte_order_;
+ std::vector<EmeInitData> eme_init_datas_;
+ std::vector<AccessUnit> access_units_;
+};
+
+#endif // COBALT_MEDIA_BASE_VIDEO_DMP_READER_H_
diff --git a/src/cobalt/media/base/video_dumper.h b/src/cobalt/media/base/video_dumper.h
new file mode 100644
index 0000000..c547989
--- /dev/null
+++ b/src/cobalt/media/base/video_dumper.h
@@ -0,0 +1,169 @@
+// 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_MEDIA_BASE_VIDEO_DUMPER_H_
+#define COBALT_MEDIA_BASE_VIDEO_DUMPER_H_
+
+#include <string>
+#include <vector>
+
+#include "cobalt/media/base/audio_decoder_config.h"
+#include "cobalt/media/base/decoder_buffer.h"
+#include "cobalt/media/base/demuxer_stream.h"
+#include "cobalt/media/base/video_decoder_config.h"
+#include "cobalt/media/base/video_dmp_reader.h"
+#include "starboard/file.h"
+
+namespace cobalt {
+namespace media {
+
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+
+// This class saves video data according to the format specified inside
+// video_dmp_reader.h.
+class VideoDumper {
+ public:
+ typedef VideoDmpReader::EmeInitData EmeInitData;
+
+ explicit VideoDumper(const char* file_name) {
+ bool created = false;
+ file_ = SbFileOpen(file_name, kSbFileCreateAlways | kSbFileWrite, &created,
+ NULL);
+ DCHECK(created);
+ DCHECK(SbFileIsValid(file_));
+
+ // Using a local variable to avoid addressing a constant which may cause
+ // link error.
+ uint32 bom = VideoDmpReader::kBOM;
+ Write<uint32>(bom);
+ }
+
+ ~VideoDumper() { SbFileClose(file_); }
+
+ void DumpEmeInitData(const std::vector<EmeInitData>& eme_init_datas) {
+ for (auto& eme_init_data : eme_init_datas) {
+ if (eme_init_data.empty()) {
+ continue;
+ }
+ Write<uint32>(VideoDmpReader::kRecordTypeEmeInitData);
+ // EME init data size
+ Write<uint32>(static_cast<uint32>(eme_init_data.size()));
+ Write(eme_init_data.data(), eme_init_data.size());
+ }
+ }
+
+ void DumpConfigs(const AudioDecoderConfig& audio_config,
+ const VideoDecoderConfig& video_config) {
+ // Temporarily write empty audio/video configs
+ Write<uint32>(VideoDmpReader::kRecordTypeAudioConfig);
+ Write<uint32>(0);
+ Write<uint32>(VideoDmpReader::kRecordTypeVideoConfig);
+ Write<uint32>(0);
+ }
+
+ void DumpAccessUnit(const scoped_refptr<DecoderBuffer>& buffer) {
+ uint32 dump_type;
+ if (buffer->type() == DemuxerStream::AUDIO) {
+ dump_type = VideoDmpReader::kRecordTypeAudioAccessUnit;
+ } else if (buffer->type() == DemuxerStream::VIDEO) {
+ dump_type = VideoDmpReader::kRecordTypeVideoAccessUnit;
+ } else {
+ NOTREACHED() << buffer->type();
+ }
+ Write(dump_type);
+
+ const DecryptConfig* decrypt_config = buffer->decrypt_config();
+ if (decrypt_config && decrypt_config->key_id().size() == 16 &&
+ decrypt_config->iv().size() == 16) {
+ uint32 record_size =
+ sizeof(int64_t) // timestamp
+ + sizeof(uint32_t) // key_id size
+ + decrypt_config->key_id().size() + sizeof(uint32_t) // iv size
+ + decrypt_config->iv().size() + sizeof(uint32_t) // subsample count
+ +
+ sizeof(uint32_t) * 2 *
+ decrypt_config->subsamples().size() // subsample count
+ + sizeof(uint32_t) // size of encoded data
+ + buffer->data_size();
+ Write(static_cast<uint32>(record_size));
+ Write(buffer->timestamp().InMicroseconds());
+ Write<uint32>(decrypt_config->key_id().size()); // key_id size
+ Write(&decrypt_config->key_id()[0], decrypt_config->key_id().size());
+ Write<uint32>(decrypt_config->iv().size()); // iv size
+ Write(&decrypt_config->iv()[0], decrypt_config->iv().size());
+ // subsample count
+ Write<uint32>(decrypt_config->subsamples().size());
+ for (size_t i = 0; i < decrypt_config->subsamples().size(); ++i) {
+ Write<uint32>(decrypt_config->subsamples()[i].clear_bytes);
+ Write<uint32>(decrypt_config->subsamples()[i].cypher_bytes);
+ }
+ Write<uint32>(static_cast<uint32>(buffer->data_size()));
+ Write(buffer->data(), buffer->data_size());
+ } else {
+ size_t record_size = sizeof(int64_t) // timestamp
+ + sizeof(uint32_t) // key_id size
+ + sizeof(uint32_t) // iv size
+ + sizeof(uint32_t) // subsample count
+ + sizeof(uint32_t) // size of encoded data
+ + buffer->data_size();
+ Write(static_cast<uint32>(record_size));
+ Write(buffer->timestamp().InMicroseconds());
+ Write<uint32>(0); // key_id size
+ Write<uint32>(0); // iv size
+ Write<uint32>(0); // subsample count
+ Write<uint32>(static_cast<uint32>(buffer->data_size()));
+ Write(buffer->data(), buffer->data_size());
+ }
+ }
+
+ private:
+ template <typename T>
+ void Write(const T& value) {
+ int bytes_to_write = static_cast<int>(sizeof(value));
+ int bytes_written = SbFileWrite(
+ file_, reinterpret_cast<const char*>(&value), bytes_to_write);
+ DCHECK_EQ(bytes_to_write, bytes_written);
+ }
+
+ void Write(const char* buffer, size_t size) {
+ if (size == 0) {
+ return;
+ }
+ int bytes_written = SbFileWrite(file_, buffer, static_cast<int>(size));
+ DCHECK_EQ(static_cast<int>(size), bytes_written);
+ }
+
+ void Write(const uint8_t* buffer, size_t size) {
+ Write(reinterpret_cast<const char*>(buffer), size);
+ }
+
+ SbFile file_;
+};
+
+#else // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+
+class VideoDumper {
+ public:
+ explicit VideoDumper(const char*) {}
+ void StartDump(const std::vector<std::vector<uint8_t> >&) {}
+ void DumpConfigs(const AudioDecoderConfig&, const VideoDecoderConfig&) {}
+ void DumpAccessUnit(const scoped_refptr<DecoderBuffer>&) {}
+};
+
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+
+} // namespace media
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_BASE_VIDEO_DUMPER_H_
diff --git a/src/cobalt/media/decoder_buffer_allocator.cc b/src/cobalt/media/decoder_buffer_allocator.cc
index bd53da4..7097815 100644
--- a/src/cobalt/media/decoder_buffer_allocator.cc
+++ b/src/cobalt/media/decoder_buffer_allocator.cc
@@ -22,10 +22,6 @@
namespace cobalt {
namespace media {
-namespace {
-const bool kPreAllocateAllMemory = true;
-} // namespace
-
DecoderBufferAllocator::DecoderBufferAllocator() : memory_block_(NULL) {
TRACK_MEMORY_SCOPE("Media");
if (COBALT_MEDIA_BUFFER_INITIAL_CAPACITY > 0) {
@@ -36,9 +32,8 @@
if (COBALT_MEDIA_BUFFER_INITIAL_CAPACITY > 0 ||
COBALT_MEDIA_BUFFER_ALLOCATION_UNIT > 0) {
// TODO: Support COBALT_MEDIA_BUFFER_ALLOCATION_UNIT > 0.
- memory_pool_.set(starboard::make_scoped_ptr(
- new nb::MemoryPool(memory_block_, COBALT_MEDIA_BUFFER_INITIAL_CAPACITY,
- kPreAllocateAllMemory)));
+ memory_pool_.set(starboard::make_scoped_ptr(new nb::FirstFitMemoryPool(
+ memory_block_, COBALT_MEDIA_BUFFER_INITIAL_CAPACITY)));
}
}
diff --git a/src/cobalt/media/decoder_buffer_allocator.h b/src/cobalt/media/decoder_buffer_allocator.h
index b83426b..8fa87f2 100644
--- a/src/cobalt/media/decoder_buffer_allocator.h
+++ b/src/cobalt/media/decoder_buffer_allocator.h
@@ -33,7 +33,7 @@
private:
void* memory_block_;
- starboard::LockedPtr<nb::MemoryPool> memory_pool_;
+ starboard::LockedPtr<nb::FirstFitMemoryPool> memory_pool_;
};
} // namespace media
diff --git a/src/cobalt/media/filters/source_buffer_range.cc b/src/cobalt/media/filters/source_buffer_range.cc
index aab6b44..c708c57 100644
--- a/src/cobalt/media/filters/source_buffer_range.cc
+++ b/src/cobalt/media/filters/source_buffer_range.cc
@@ -62,10 +62,13 @@
buffers_.push_back(*itr);
size_in_bytes_ += (*itr)->data_size();
+ DCHECK_LE(buffers_.size(), kint32max);
if ((*itr)->is_key_frame()) {
+ int offset =
+ static_cast<int>(buffers_.size()) - 1 + keyframe_map_index_base_;
+ DCHECK_GE(offset, 0);
keyframe_map_.insert(
- std::make_pair((*itr)->GetDecodeTimestamp(),
- buffers_.size() - 1 + keyframe_map_index_base_));
+ std::make_pair((*itr)->GetDecodeTimestamp(), offset));
}
}
}
diff --git a/src/cobalt/media/media.gyp b/src/cobalt/media/media.gyp
index 383f25f..c287bdb 100644
--- a/src/cobalt/media/media.gyp
+++ b/src/cobalt/media/media.gyp
@@ -14,7 +14,7 @@
{
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
{
diff --git a/src/cobalt/media/media_buffer_allocator.h b/src/cobalt/media/media_buffer_allocator.h
deleted file mode 100644
index b14616e..0000000
--- a/src/cobalt/media/media_buffer_allocator.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef COBALT_MEDIA_MEDIA_BUFFER_ALLOCATOR_H_
-#define COBALT_MEDIA_MEDIA_BUFFER_ALLOCATOR_H_
-
-#include "nb/memory_pool.h"
-#include "starboard/common/scoped_ptr.h"
-
-namespace cobalt {
-namespace media {
-
-class MediaBufferAllocator {
- public:
- MediaBufferAllocator(void* pool, size_t main_pool_size,
- size_t small_allocation_pool_size,
- size_t small_allocation_threshold)
- : pool_(reinterpret_cast<uint8*>(pool)),
- main_pool_size_(main_pool_size),
- small_allocation_pool_size_(small_allocation_pool_size),
- small_allocation_threshold_(small_allocation_threshold),
- main_pool_(starboard::make_scoped_ptr(new nb::MemoryPool(
- pool_, main_pool_size_, true /* verify_full_capacity */))) {
- if (small_allocation_pool_size_ > 0u) {
- DCHECK_GT(small_allocation_threshold_, 0u);
- small_allocation_pool_.set(starboard::make_scoped_ptr(new nb::MemoryPool(
- pool_ + main_pool_size_, small_allocation_pool_size_,
- true /* verify_full_capacity */)));
- } else {
- DCHECK_EQ(small_allocation_pool_size_, 0u);
- DCHECK_EQ(small_allocation_threshold_, 0u);
- }
- }
-
- void* Allocate(size_t size, size_t alignment) {
- void* p = NULL;
- if (size < small_allocation_threshold_ &&
- small_allocation_pool_->is_valid()) {
- p = small_allocation_pool_->Allocate(size, alignment);
- }
- if (!p) {
- p = main_pool_->Allocate(size, alignment);
- }
- if (!p && small_allocation_pool_) {
- p = small_allocation_pool_->Allocate(size, alignment);
- }
- return p;
- }
-
- void Free(void* p) {
- if (p >= pool_ + main_pool_size_ && small_allocation_pool_->is_valid()) {
- DCHECK_LT(p, pool_ + main_pool_size_ + small_allocation_pool_size_);
- small_allocation_pool_->Free(p);
- return;
- }
- DCHECK_GE(p, pool_);
- DCHECK_LT(p, pool_ + main_pool_size_);
- main_pool_->Free(p);
- }
-
- private:
- uint8* pool_;
- size_t main_pool_size_;
- size_t small_allocation_pool_size_;
- size_t small_allocation_threshold_;
-
- starboard::LockedPtr<nb::MemoryPool> main_pool_;
- starboard::LockedPtr<nb::MemoryPool> small_allocation_pool_;
-
- DISALLOW_COPY_AND_ASSIGN(MediaBufferAllocator);
-};
-
-} // namespace media
-} // namespace cobalt
-
-#endif // COBALT_MEDIA_MEDIA_BUFFER_ALLOCATOR_H_
diff --git a/src/cobalt/media/player/web_media_player.h b/src/cobalt/media/player/web_media_player.h
index 94e372d..b3b5cb6 100644
--- a/src/cobalt/media/player/web_media_player.h
+++ b/src/cobalt/media/player/web_media_player.h
@@ -196,6 +196,7 @@
virtual void ReadyStateChanged() = 0;
virtual void TimeChanged() = 0;
virtual void DurationChanged() = 0;
+ virtual void OutputModeChanged() = 0;
virtual void PlaybackStateChanged() = 0;
// TODO: Revisit the necessity of the following function.
virtual void SetOpaque(bool /* opaque */) {}
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index 623c892..bd0d14b 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -564,6 +564,14 @@
}
}
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+void WebMediaPlayerImpl::SetEMEInitDataReadyCB(
+ const EMEInitDataReadyCB& eme_init_data_ready_cb) {
+ DCHECK(!eme_init_data_ready_cb.is_null());
+ eme_init_data_ready_cb_ = eme_init_data_ready_cb;
+}
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+
void WebMediaPlayerImpl::OnPipelineSeek(PipelineStatus status) {
DCHECK_EQ(main_loop_, MessageLoop::current());
state_.starting = false;
@@ -691,11 +699,15 @@
pipeline_->SetDecodeToTextureOutputMode(client_->PreferDecodeToTexture());
pipeline_->Start(
demuxer, BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetDrmSystemReadyCB),
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetEMEInitDataReadyCB),
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError),
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek),
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingState),
- BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged));
+ BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged),
+ BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnOutputModeChanged));
}
void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) {
@@ -777,6 +789,11 @@
GetClient()->EncryptedMediaInitDataEncountered(init_data_type, &init_data[0],
init_data.size());
+
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ DCHECK(!eme_init_data_ready_cb_.is_null());
+ eme_init_data_ready_cb_.Run(init_data);
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
}
WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() {
@@ -791,5 +808,9 @@
GetClient()->DurationChanged();
}
+void WebMediaPlayerImpl::OnOutputModeChanged() {
+ GetClient()->OutputModeChanged();
+}
+
} // namespace media
} // namespace cobalt
diff --git a/src/cobalt/media/player/web_media_player_impl.h b/src/cobalt/media/player/web_media_player_impl.h
index 5f3477b..c3804ac 100644
--- a/src/cobalt/media/player/web_media_player_impl.h
+++ b/src/cobalt/media/player/web_media_player_impl.h
@@ -184,6 +184,9 @@
void SetDrmSystem(DrmSystem* drm_system) OVERRIDE;
void SetDrmSystemReadyCB(const DrmSystemReadyCB& drm_system_ready_cb);
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ void SetEMEInitDataReadyCB(const EMEInitDataReadyCB& eme_init_data_ready_cb);
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
void OnPipelineSeek(PipelineStatus status);
void OnPipelineEnded(PipelineStatus status);
@@ -218,6 +221,7 @@
private:
// Callbacks that forward duration change from |pipeline_| to |client_|.
void OnDurationChanged();
+ void OnOutputModeChanged();
base::Thread pipeline_thread_;
@@ -309,6 +313,11 @@
media_time_and_seeking_state_cb_;
DrmSystemReadyCB drm_system_ready_cb_;
+
+#if COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+ EMEInitDataReadyCB eme_init_data_ready_cb_;
+#endif // COBALT_MEDIA_ENABLE_VIDEO_DUMPER
+
DrmSystem* drm_system_;
DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl);
diff --git a/src/cobalt/media/sandbox/web_media_player_helper.cc b/src/cobalt/media/sandbox/web_media_player_helper.cc
index adb3afe..58618a2 100644
--- a/src/cobalt/media/sandbox/web_media_player_helper.cc
+++ b/src/cobalt/media/sandbox/web_media_player_helper.cc
@@ -37,6 +37,7 @@
void ReadyStateChanged() OVERRIDE {}
void TimeChanged() OVERRIDE {}
void DurationChanged() OVERRIDE {}
+ void OutputModeChanged() OVERRIDE {}
void PlaybackStateChanged() OVERRIDE {}
void SawUnsupportedTracks() OVERRIDE {}
float Volume() const OVERRIDE { return 1.f; }
diff --git a/src/cobalt/media/shell_media_platform_starboard.cc b/src/cobalt/media/shell_media_platform_starboard.cc
deleted file mode 100644
index 92c9194..0000000
--- a/src/cobalt/media/shell_media_platform_starboard.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cobalt/media/shell_media_platform_starboard.h"
-
-#include <limits>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/aligned_memory.h"
-#include "media/audio/shell_audio_streamer.h"
-#include "media/base/shell_buffer_factory.h"
-#include "media/base/shell_cached_decoder_buffer.h"
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/configuration.h"
-#include "starboard/media.h"
-
-namespace media {
-
-namespace {
-
-const size_t kMediaBufferAlignment = SB_MEDIA_BUFFER_ALIGNMENT;
-const size_t kVideoFrameAlignment = SB_MEDIA_VIDEO_FRAME_ALIGNMENT;
-
-// This may be zero, which should disable GPU memory buffer allocations.
-const size_t kGPUMemoryBufferBudget = SB_MEDIA_GPU_BUFFER_BUDGET;
-const size_t kMainMemoryBufferBudget = SB_MEDIA_MAIN_BUFFER_BUDGET;
-
-const size_t kSmallAllocationThreshold = 768U;
-
-} // namespace
-
-ShellMediaPlatformStarboard::ShellMediaPlatformStarboard(
- cobalt::render_tree::ResourceProvider* resource_provider)
- : resource_provider_(resource_provider),
- video_data_allocator_(resource_provider_, 0,
- std::numeric_limits<size_t>::max(),
- kVideoFrameAlignment),
- video_frame_provider_(new ShellVideoFrameProvider) {
- DCHECK(!Instance());
-
- if (kGPUMemoryBufferBudget > 0) {
- gpu_memory_buffer_space_ = resource_provider->AllocateRawImageMemory(
- kGPUMemoryBufferBudget, kMediaBufferAlignment);
- DCHECK(gpu_memory_buffer_space_);
- DCHECK(gpu_memory_buffer_space_->GetMemory());
- DCHECK_GE(gpu_memory_buffer_space_->GetSizeInBytes(),
- kGPUMemoryBufferBudget);
- gpu_memory_pool_.set(starboard::make_scoped_ptr(new nb::MemoryPool(
- gpu_memory_buffer_space_->GetMemory(),
- gpu_memory_buffer_space_->GetSizeInBytes(),
- true /* verify_full_capacity */, kSmallAllocationThreshold)));
- }
-
- DCHECK_LE(0, kMainMemoryBufferBudget > 0);
- main_memory_buffer_space_.reset(static_cast<uint8*>(
- base::AlignedAlloc(kMainMemoryBufferBudget, kMediaBufferAlignment)));
- DCHECK(main_memory_buffer_space_);
- main_memory_pool_.set(starboard::make_scoped_ptr(new nb::MemoryPool(
- main_memory_buffer_space_.get(), kMainMemoryBufferBudget,
- true, /* verify_full_capacity */
- kSmallAllocationThreshold)));
-
- ShellBufferFactory::Initialize();
- ShellAudioStreamer::Initialize();
-
- SetInstance(this);
-}
-
-ShellMediaPlatformStarboard::~ShellMediaPlatformStarboard() {
- DCHECK_EQ(Instance(), this);
- SetInstance(NULL);
-
- ShellAudioStreamer::Terminate();
- ShellBufferFactory::Terminate();
-}
-
-void* ShellMediaPlatformStarboard::AllocateBuffer(size_t size) {
- if (kGPUMemoryBufferBudget) {
- return gpu_memory_pool_->Allocate(size, kMediaBufferAlignment);
- }
-
- return main_memory_pool_->Allocate(size, kMediaBufferAlignment);
-}
-
-void ShellMediaPlatformStarboard::FreeBuffer(void* ptr) {
- if (kGPUMemoryBufferBudget) {
- return gpu_memory_pool_->Free(ptr);
- }
-
- return main_memory_pool_->Free(ptr);
-}
-
-scoped_refptr<DecoderBuffer>
-ShellMediaPlatformStarboard::ProcessBeforeLeavingDemuxer(
- const scoped_refptr<DecoderBuffer>& buffer) {
- // TODO: Completely remove GPU buffer for the new DecoderBuffer
- // implementation.
- return buffer;
-}
-
-bool ShellMediaPlatformStarboard::IsOutputProtected() {
- if (SbMediaIsOutputProtected()) {
- return true;
- }
- return SbMediaSetOutputProtection(true);
-}
-
-} // namespace media
diff --git a/src/cobalt/media_session/media_session.gyp b/src/cobalt/media_session/media_session.gyp
index 4de3b9d..f9083ee 100644
--- a/src/cobalt/media_session/media_session.gyp
+++ b/src/cobalt/media_session/media_session.gyp
@@ -14,7 +14,7 @@
{
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
{
diff --git a/src/cobalt/network/network.gyp b/src/cobalt/network/network.gyp
index d6296e3..be0258f 100644
--- a/src/cobalt/network/network.gyp
+++ b/src/cobalt/network/network.gyp
@@ -16,7 +16,7 @@
'includes': [ '../build/contents_dir.gypi' ],
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
{
diff --git a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
index 7a2ddd2..6113bac 100644
--- a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
+++ b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
@@ -81,13 +81,13 @@
youtube_tv_info_->network_operator = value;
}
-#if SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
+#if SB_API_VERSION >= 5
result = SbSystemGetProperty(kSbSystemPropertyUserAgentAuxField, value,
kSystemPropertyMaxLength);
if (result) {
aux_field_ = value;
}
-#endif // SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
+#endif // SB_API_VERSION >= 5
// Device Type
switch (device_type) {
diff --git a/src/cobalt/network_bridge/network_bridge.gyp b/src/cobalt/network_bridge/network_bridge.gyp
index 0daab78..fbdc257 100644
--- a/src/cobalt/network_bridge/network_bridge.gyp
+++ b/src/cobalt/network_bridge/network_bridge.gyp
@@ -14,7 +14,7 @@
{
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
{
diff --git a/src/cobalt/page_visibility/document.idl b/src/cobalt/page_visibility/document.idl
new file mode 100644
index 0000000..b6d39a0
--- /dev/null
+++ b/src/cobalt/page_visibility/document.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.
+
+// Part of Page Visibility Level 2
+// https://www.w3.org/TR/page-visibility-2/#extensions-to-the-document-interface
+
+partial interface Document {
+ readonly attribute boolean hidden;
+ readonly attribute VisibilityState visibilityState;
+ attribute EventHandler onvisibilitychange;
+};
diff --git a/src/cobalt/page_visibility/page_visibility.gyp b/src/cobalt/page_visibility/page_visibility.gyp
new file mode 100644
index 0000000..e943727
--- /dev/null
+++ b/src/cobalt/page_visibility/page_visibility.gyp
@@ -0,0 +1,68 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'variables': {
+ 'cobalt_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'page_visibility',
+ 'type': 'static_library',
+ 'sources': [
+ 'page_visibility_state.cc',
+ 'page_visibility_state.h',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+ ],
+ # This target doesn't generate any headers, but it exposes generated
+ # header files through this module's public header files. So mark this
+ # target as a hard dependency to ensure that any dependent targets wait
+ # until this target (and its hard dependencies) are built.
+ '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.
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+ ]
+ },
+ {
+ 'target_name': 'page_visibility_test',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'page_visibility_state_test.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ 'page_visibility',
+ ],
+ },
+ {
+ 'target_name': 'page_visibility_test_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'page_visibility_test',
+ ],
+ 'variables': {
+ 'executable_name': 'page_visibility_test',
+ },
+ 'includes': [ '../../starboard/build/deploy.gypi' ],
+ },
+ ],
+}
diff --git a/src/cobalt/page_visibility/page_visibility_state.cc b/src/cobalt/page_visibility/page_visibility_state.cc
new file mode 100644
index 0000000..d8ac3ee
--- /dev/null
+++ b/src/cobalt/page_visibility/page_visibility_state.cc
@@ -0,0 +1,148 @@
+// 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/page_visibility/page_visibility_state.h"
+
+namespace cobalt {
+namespace page_visibility {
+
+namespace {
+// Converts an ApplicationState to a VisibilityState.
+VisibilityState ToVisibilityState(base::ApplicationState state) {
+ switch (state) {
+ case base::kApplicationStatePreloading:
+ return kVisibilityStatePrerender;
+ case base::kApplicationStateStarted:
+ case base::kApplicationStatePaused:
+ return kVisibilityStateVisible;
+ case base::kApplicationStateSuspended:
+ case base::kApplicationStateStopped:
+ return kVisibilityStateHidden;
+ default:
+ NOTREACHED() << "Invalid Application State: " << state;
+ return kVisibilityStateHidden;
+ }
+}
+
+bool HasFocus(base::ApplicationState state) {
+ switch (state) {
+ case base::kApplicationStateStarted:
+ return true;
+ case base::kApplicationStatePreloading:
+ case base::kApplicationStatePaused:
+ case base::kApplicationStateSuspended:
+ case base::kApplicationStateStopped:
+ return false;
+ default:
+ NOTREACHED() << "Invalid Application State: " << state;
+ return false;
+ }
+}
+} // namespace
+
+PageVisibilityState::PageVisibilityState()
+ : application_state_(base::kApplicationStateStarted) {}
+
+PageVisibilityState::PageVisibilityState(
+ base::ApplicationState initial_application_state)
+ : application_state_(initial_application_state) {
+ DCHECK((application_state_ == base::kApplicationStateStarted) ||
+ (application_state_ == base::kApplicationStatePreloading));
+
+ // TODO: Support Preloading
+ DCHECK_NE(base::kApplicationStatePreloading, application_state_);
+}
+
+bool PageVisibilityState::HasWindowFocus() const {
+ return HasFocus(application_state());
+}
+
+VisibilityState PageVisibilityState::GetVisibilityState() const {
+ return ToVisibilityState(application_state());
+}
+
+void PageVisibilityState::SetApplicationState(base::ApplicationState state) {
+ if (application_state_ == state) {
+ DLOG(WARNING) << __FUNCTION__ << ": Attempt to re-enter "
+ << application_state_;
+ return;
+ }
+
+ // Audit that the transitions are correct.
+ if (DLOG_IS_ON(FATAL)) {
+ switch (application_state_) {
+ case base::kApplicationStatePaused:
+ DCHECK(state == base::kApplicationStateSuspended ||
+ state == base::kApplicationStateStarted);
+ break;
+ case base::kApplicationStatePreloading:
+ DCHECK(state == base::kApplicationStateSuspended ||
+ state == base::kApplicationStateStarted);
+ case base::kApplicationStateStarted:
+ DCHECK(state == base::kApplicationStatePaused);
+ break;
+ case base::kApplicationStateStopped:
+ DCHECK(state == base::kApplicationStatePreloading ||
+ state == base::kApplicationStateStarted);
+ break;
+ case base::kApplicationStateSuspended:
+ DCHECK(state == base::kApplicationStatePaused ||
+ state == base::kApplicationStateStopped);
+ break;
+ default:
+ NOTREACHED() << application_state_;
+ break;
+ }
+ }
+
+ bool old_has_focus = HasFocus(application_state_);
+ VisibilityState old_visibility_state = ToVisibilityState(application_state_);
+ application_state_ = state;
+ bool has_focus = HasFocus(application_state_);
+ VisibilityState visibility_state = ToVisibilityState(application_state_);
+ bool focus_changed = has_focus != old_has_focus;
+ bool visibility_state_changed = visibility_state != old_visibility_state;
+
+ if (focus_changed && has_focus) {
+ // If going to a focused state, dispatch the visibility state first.
+ if (visibility_state_changed) {
+ DispatchVisibilityStateChanged(visibility_state);
+ }
+
+ DispatchWindowFocusChanged(has_focus);
+ return;
+ }
+
+ // Otherwise, we should dispatch the focus state first.
+ if (focus_changed) {
+ DispatchWindowFocusChanged(has_focus);
+ }
+
+ if (visibility_state_changed) {
+ DispatchVisibilityStateChanged(visibility_state);
+ }
+}
+
+void PageVisibilityState::DispatchWindowFocusChanged(bool has_focus) {
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnWindowFocusChanged(has_focus));
+}
+
+void PageVisibilityState::DispatchVisibilityStateChanged(
+ VisibilityState visibility_state) {
+ FOR_EACH_OBSERVER(Observer, observer_list_,
+ OnVisibilityStateChanged(visibility_state));
+}
+
+} // namespace page_visibility
+} // namespace cobalt
diff --git a/src/cobalt/page_visibility/page_visibility_state.h b/src/cobalt/page_visibility/page_visibility_state.h
new file mode 100644
index 0000000..9ab3a87
--- /dev/null
+++ b/src/cobalt/page_visibility/page_visibility_state.h
@@ -0,0 +1,94 @@
+// 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_PAGE_VISIBILITY_PAGE_VISIBILITY_STATE_H_
+#define COBALT_PAGE_VISIBILITY_PAGE_VISIBILITY_STATE_H_
+
+#include "base/observer_list.h"
+#include "cobalt/base/application_state.h"
+#include "cobalt/page_visibility/visibility_state.h"
+
+namespace cobalt {
+namespace page_visibility {
+
+// The visibility state of the Window and Document as controlled by the current
+// application state.
+class PageVisibilityState {
+ public:
+ // Pure virtual interface for classes that want to observe page visibility
+ // state changes.
+ class Observer {
+ public:
+ // Called when the window focus state changes.
+ virtual void OnWindowFocusChanged(bool has_focus) = 0;
+ // Called when the VisibilityState changes.
+ virtual void OnVisibilityStateChanged(VisibilityState visibility_state) = 0;
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ PageVisibilityState();
+ explicit PageVisibilityState(
+ base::ApplicationState initial_application_state);
+
+ base::ApplicationState application_state() const {
+ return application_state_;
+ }
+
+ // Whether the current window has focus based on the current application
+ // state.
+ bool HasWindowFocus() const;
+
+ // Whether the current window has focus based on the current application
+ // state.
+ VisibilityState GetVisibilityState() const;
+
+ // Sets the current application state, and dispatches appropriate observation
+ // events.
+ void SetApplicationState(base::ApplicationState state);
+
+ // Adds a PageVisibiltyState::Observer to this PageVisibilityState.
+ void AddObserver(Observer* observer) { observer_list_.AddObserver(observer); }
+
+ // Removes a PageVisibiltyState::Observer from this PageVisibilityState, if it
+ // is registered.
+ void RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+ }
+
+ // Returns whether a PageVisibiltyState::Observer is registered on this
+ // PageVisibilityState.
+ bool HasObserver(Observer* observer) const {
+ return observer_list_.HasObserver(observer);
+ }
+
+ // Clears all registered PageVisibiltyState::Observers, if any.
+ void ClearObservers() { observer_list_.Clear(); }
+
+ private:
+ void DispatchWindowFocusChanged(bool has_focus);
+ void DispatchVisibilityStateChanged(VisibilityState visibility_state);
+
+ // The current application state.
+ base::ApplicationState application_state_;
+
+ // The list of registered PageVisibiltyState::Observers;
+ ObserverList<Observer> observer_list_;
+};
+
+} // namespace page_visibility
+} // namespace cobalt
+
+#endif // COBALT_PAGE_VISIBILITY_PAGE_VISIBILITY_STATE_H_
diff --git a/src/cobalt/page_visibility/page_visibility_state_test.cc b/src/cobalt/page_visibility/page_visibility_state_test.cc
new file mode 100644
index 0000000..3a021e5
--- /dev/null
+++ b/src/cobalt/page_visibility/page_visibility_state_test.cc
@@ -0,0 +1,133 @@
+// 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/page_visibility/page_visibility_state.h"
+
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/base/application_state.h"
+#include "cobalt/page_visibility/visibility_state.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace page_visibility {
+namespace {
+
+using ::testing::_;
+
+class MockPageVisibilityStateObserver : public PageVisibilityState::Observer {
+ public:
+ static scoped_ptr<MockPageVisibilityStateObserver> Create() {
+ return make_scoped_ptr<MockPageVisibilityStateObserver>(
+ new ::testing::StrictMock<MockPageVisibilityStateObserver>());
+ }
+
+ MOCK_METHOD1(OnWindowFocusChanged, void(bool has_focus));
+ MOCK_METHOD1(OnVisibilityStateChanged,
+ void(VisibilityState visibility_state));
+
+ protected:
+ MockPageVisibilityStateObserver() {}
+};
+
+TEST(PageVisibilityStateTest, DefaultConstructor) {
+ PageVisibilityState state;
+ EXPECT_TRUE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateVisible, state.GetVisibilityState());
+}
+
+TEST(PageVisibilityStateTest, InitialStateConstructorStarted) {
+ PageVisibilityState state(base::kApplicationStateStarted);
+ EXPECT_TRUE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateVisible, state.GetVisibilityState());
+}
+
+TEST(PageVisibilityStateTest, TransitionsAndObservations) {
+ PageVisibilityState state(base::kApplicationStateStarted);
+ scoped_ptr<MockPageVisibilityStateObserver> observer =
+ MockPageVisibilityStateObserver::Create();
+
+ EXPECT_CALL(*observer, OnWindowFocusChanged(_)).Times(0);
+ EXPECT_CALL(*observer, OnVisibilityStateChanged(_)).Times(0);
+ EXPECT_TRUE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateVisible, state.GetVisibilityState());
+ ::testing::Mock::VerifyAndClearExpectations(observer.get());
+
+ state.AddObserver(observer.get());
+
+ EXPECT_CALL(*observer, OnWindowFocusChanged(false));
+ EXPECT_CALL(*observer, OnVisibilityStateChanged(_)).Times(0);
+ state.SetApplicationState(base::kApplicationStatePaused);
+ EXPECT_FALSE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateVisible, state.GetVisibilityState());
+ ::testing::Mock::VerifyAndClearExpectations(observer.get());
+
+ EXPECT_CALL(*observer, OnWindowFocusChanged(true));
+ EXPECT_CALL(*observer, OnVisibilityStateChanged(_)).Times(0);
+ state.SetApplicationState(base::kApplicationStateStarted);
+ EXPECT_TRUE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateVisible, state.GetVisibilityState());
+ ::testing::Mock::VerifyAndClearExpectations(observer.get());
+
+ EXPECT_CALL(*observer, OnWindowFocusChanged(false));
+ EXPECT_CALL(*observer, OnVisibilityStateChanged(_)).Times(0);
+ state.SetApplicationState(base::kApplicationStatePaused);
+ EXPECT_FALSE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateVisible, state.GetVisibilityState());
+ ::testing::Mock::VerifyAndClearExpectations(observer.get());
+
+ EXPECT_CALL(*observer, OnVisibilityStateChanged(kVisibilityStateHidden));
+ EXPECT_CALL(*observer, OnWindowFocusChanged(_)).Times(0);
+ state.SetApplicationState(base::kApplicationStateSuspended);
+ EXPECT_FALSE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateHidden, state.GetVisibilityState());
+ ::testing::Mock::VerifyAndClearExpectations(observer.get());
+
+ EXPECT_CALL(*observer, OnVisibilityStateChanged(kVisibilityStateVisible));
+ EXPECT_CALL(*observer, OnWindowFocusChanged(_)).Times(0);
+ state.SetApplicationState(base::kApplicationStatePaused);
+ EXPECT_FALSE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateVisible, state.GetVisibilityState());
+ ::testing::Mock::VerifyAndClearExpectations(observer.get());
+
+ EXPECT_CALL(*observer, OnWindowFocusChanged(true));
+ EXPECT_CALL(*observer, OnVisibilityStateChanged(_)).Times(0);
+ state.SetApplicationState(base::kApplicationStateStarted);
+ EXPECT_TRUE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateVisible, state.GetVisibilityState());
+ ::testing::Mock::VerifyAndClearExpectations(observer.get());
+
+ state.RemoveObserver(observer.get());
+
+ EXPECT_CALL(*observer, OnWindowFocusChanged(_)).Times(0);
+ EXPECT_CALL(*observer, OnVisibilityStateChanged(_)).Times(0);
+ state.SetApplicationState(base::kApplicationStatePaused);
+ EXPECT_FALSE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateVisible, state.GetVisibilityState());
+ ::testing::Mock::VerifyAndClearExpectations(observer.get());
+
+ EXPECT_CALL(*observer, OnWindowFocusChanged(_)).Times(0);
+ EXPECT_CALL(*observer, OnVisibilityStateChanged(_)).Times(0);
+ state.SetApplicationState(base::kApplicationStateSuspended);
+ EXPECT_FALSE(state.HasWindowFocus());
+ EXPECT_EQ(kVisibilityStateHidden, state.GetVisibilityState());
+ ::testing::Mock::VerifyAndClearExpectations(observer.get());
+}
+
+} // namespace
+} // namespace page_visibility
+} // namespace cobalt
diff --git a/src/cobalt/page_visibility/visibility_state.idl b/src/cobalt/page_visibility/visibility_state.idl
new file mode 100644
index 0000000..829cc5d
--- /dev/null
+++ b/src/cobalt/page_visibility/visibility_state.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.
+
+// Part of Page Visibility Level 2
+// https://www.w3.org/TR/page-visibility-2/#visibility-states-and-the-visibilitystate-enum
+
+enum VisibilityState {
+ "hidden",
+ "visible",
+ "prerender",
+};
diff --git a/src/cobalt/render_tree/brush.cc b/src/cobalt/render_tree/brush.cc
index 8ba6694..2edf8ae 100644
--- a/src/cobalt/render_tree/brush.cc
+++ b/src/cobalt/render_tree/brush.cc
@@ -14,6 +14,8 @@
#include "cobalt/render_tree/brush.h"
+#include <algorithm>
+
#include "cobalt/render_tree/brush_visitor.h"
namespace cobalt {
@@ -155,5 +157,96 @@
return true;
}
+namespace {
+// 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;
+}
+} // namespace
+
+std::pair<math::PointF, math::PointF> LinearGradientPointsFromAngle(
+ float ccw_radians_from_right, 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);
+
+ // 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_radians_from_right)),
+ static_cast<float>(-sin(ccw_radians_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));
+}
+
} // namespace render_tree
} // namespace cobalt
diff --git a/src/cobalt/render_tree/brush.h b/src/cobalt/render_tree/brush.h
index fba8063..b5be829 100644
--- a/src/cobalt/render_tree/brush.h
+++ b/src/cobalt/render_tree/brush.h
@@ -16,12 +16,14 @@
#define COBALT_RENDER_TREE_BRUSH_H_
#include <cmath>
+#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "cobalt/base/type_id.h"
#include "cobalt/math/point_f.h"
+#include "cobalt/math/size_f.h"
#include "cobalt/render_tree/brush_visitor.h"
#include "cobalt/render_tree/color_rgba.h"
@@ -84,23 +86,53 @@
bool IsNear(const render_tree::ColorStopList& lhs,
const render_tree::ColorStopList& rhs, float epsilon);
+// Calculate the source and destination points to use for a linear gradient
+// of the specified angle to cover the given frame.
+//
+// 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."
+std::pair<math::PointF, math::PointF> LinearGradientPointsFromAngle(
+ float ccw_radians_from_right, const math::SizeF& frame_size);
+
// Linear gradient brushes can be used to fill a shape with a linear color
// gradient with arbitrary many color stops. It is specified by a source
// and destination point, which define a line segment along which the color
// stops apply, each having a position between 0 and 1 representing the
// position of the stop along that line. Interpolation occurs in premultiplied
// alpha space.
+// NOTE: The source and destination points may lie inside or outside the shape
+// which uses the gradient brush. Always consider the shape as a mask over the
+// gradient whose first and last color stops extend infinitely in their
+// respective directions.
class LinearGradientBrush : public Brush {
public:
// The ColorStopList passed into LienarGradientBrush must have at least two
// stops and they must be sorted in order of increasing position.
LinearGradientBrush(const math::PointF& source, const math::PointF& dest,
const ColorStopList& color_stops);
+ LinearGradientBrush(const std::pair<math::PointF, math::PointF>& source_dest,
+ const ColorStopList& color_stops)
+ : LinearGradientBrush(source_dest.first, source_dest.second,
+ color_stops)
+ {}
// Creates a 2-stop linear gradient from source to dest.
LinearGradientBrush(const math::PointF& source, const math::PointF& dest,
const ColorRGBA& source_color,
const ColorRGBA& dest_color);
+ LinearGradientBrush(const std::pair<math::PointF, math::PointF>& source_dest,
+ const ColorRGBA& source_color,
+ const ColorRGBA& dest_color)
+ : LinearGradientBrush(source_dest.first, source_dest.second,
+ source_color, dest_color)
+ {}
struct Data {
Data();
@@ -130,10 +162,14 @@
const ColorStopList& color_stops() const { return data_.color_stops_; }
// Returns true if, and only if the brush is horizontal.
- bool IsHorizontal() const { return (data_.source_.y() == data_.dest_.y()); }
+ bool IsHorizontal(float epsilon = 0.001f) const {
+ return std::abs(data_.source_.y() - data_.dest_.y()) < epsilon;
+ }
// Returns true if, and only if the brush is vertical.
- bool IsVertical() const { return (data_.source_.x() == data_.dest_.x()); }
+ bool IsVertical(float epsilon = 0.001f) const {
+ return std::abs(data_.source_.x() - data_.dest_.x()) < epsilon;
+ }
const Data& data() const { return data_; }
diff --git a/src/cobalt/render_tree/composition_node.h b/src/cobalt/render_tree/composition_node.h
index 2614822..a6175a0 100644
--- a/src/cobalt/render_tree/composition_node.h
+++ b/src/cobalt/render_tree/composition_node.h
@@ -78,9 +78,6 @@
// method.
void AddChild(const scoped_refptr<Node>& node);
- // Remove all existing children.
- void ClearChildren() { children_.clear(); }
-
// Returns the specified child as a pointer so that it can be modified.
scoped_refptr<Node>* GetChild(int child_index) {
DCHECK_GE(child_index, 0);
diff --git a/src/cobalt/render_tree/render_tree.gyp b/src/cobalt/render_tree/render_tree.gyp
index aeb94fa..f6d52fb 100644
--- a/src/cobalt/render_tree/render_tree.gyp
+++ b/src/cobalt/render_tree/render_tree.gyp
@@ -14,7 +14,7 @@
{
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
diff --git a/src/cobalt/renderer/backend/blitter/display.cc b/src/cobalt/renderer/backend/blitter/display.cc
index 91d7182..7bd4d91 100644
--- a/src/cobalt/renderer/backend/blitter/display.cc
+++ b/src/cobalt/renderer/backend/blitter/display.cc
@@ -34,7 +34,7 @@
DisplayRenderTargetBlitter(SbBlitterDevice device,
system_window::SystemWindow* system_window);
- const math::Size& GetSize() OVERRIDE;
+ const math::Size& GetSize() const OVERRIDE;
SbBlitterRenderTarget GetSbRenderTarget() const OVERRIDE;
@@ -64,7 +64,7 @@
size_.SetSize(window_size.width, window_size.height);
}
-const math::Size& DisplayRenderTargetBlitter::GetSize() { return size_; }
+const math::Size& DisplayRenderTargetBlitter::GetSize() const { return size_; }
DisplayRenderTargetBlitter::~DisplayRenderTargetBlitter() {
SbBlitterDestroySwapChain(swap_chain_);
diff --git a/src/cobalt/renderer/backend/blitter/render_target.h b/src/cobalt/renderer/backend/blitter/render_target.h
index ea1c079..5f40d6f 100644
--- a/src/cobalt/renderer/backend/blitter/render_target.h
+++ b/src/cobalt/renderer/backend/blitter/render_target.h
@@ -28,7 +28,7 @@
public:
virtual SbBlitterRenderTarget GetSbRenderTarget() const = 0;
- intptr_t GetPlatformHandle() OVERRIDE {
+ intptr_t GetPlatformHandle() const OVERRIDE {
return reinterpret_cast<intptr_t>(SbBlitterRenderTarget());
}
diff --git a/src/cobalt/renderer/backend/blitter/surface_render_target.cc b/src/cobalt/renderer/backend/blitter/surface_render_target.cc
index 2bdc05c..6b71860 100644
--- a/src/cobalt/renderer/backend/blitter/surface_render_target.cc
+++ b/src/cobalt/renderer/backend/blitter/surface_render_target.cc
@@ -41,7 +41,7 @@
}
}
-const math::Size& SurfaceRenderTargetBlitter::GetSize() { return size_; }
+const math::Size& SurfaceRenderTargetBlitter::GetSize() const { return size_; }
SbBlitterRenderTarget SurfaceRenderTargetBlitter::GetSbRenderTarget() const {
return render_target_;
diff --git a/src/cobalt/renderer/backend/blitter/surface_render_target.h b/src/cobalt/renderer/backend/blitter/surface_render_target.h
index 1a19e4f..48dc88e 100644
--- a/src/cobalt/renderer/backend/blitter/surface_render_target.h
+++ b/src/cobalt/renderer/backend/blitter/surface_render_target.h
@@ -30,7 +30,7 @@
SurfaceRenderTargetBlitter(SbBlitterDevice device,
const math::Size& dimensions);
- const math::Size& GetSize() OVERRIDE;
+ const math::Size& GetSize() const OVERRIDE;
SbBlitterRenderTarget GetSbRenderTarget() const OVERRIDE;
diff --git a/src/cobalt/renderer/backend/egl/display.cc b/src/cobalt/renderer/backend/egl/display.cc
index c0603ca..5f1c382 100644
--- a/src/cobalt/renderer/backend/egl/display.cc
+++ b/src/cobalt/renderer/backend/egl/display.cc
@@ -27,7 +27,7 @@
DisplayRenderTargetEGL(EGLDisplay display, EGLConfig config,
EGLNativeWindowType window_handle);
- const math::Size& GetSize() OVERRIDE;
+ const math::Size& GetSize() const OVERRIDE;
EGLSurface GetSurface() const OVERRIDE;
@@ -72,7 +72,7 @@
size_.SetSize(egl_surface_width, egl_surface_height);
}
-const math::Size& DisplayRenderTargetEGL::GetSize() { return size_; }
+const math::Size& DisplayRenderTargetEGL::GetSize() const { return size_; }
DisplayRenderTargetEGL::~DisplayRenderTargetEGL() {
eglDestroySurface(display_, surface_);
diff --git a/src/cobalt/renderer/backend/egl/framebuffer_render_target.h b/src/cobalt/renderer/backend/egl/framebuffer_render_target.h
index bdd6a10..0df7044 100644
--- a/src/cobalt/renderer/backend/egl/framebuffer_render_target.h
+++ b/src/cobalt/renderer/backend/egl/framebuffer_render_target.h
@@ -30,31 +30,32 @@
public:
FramebufferRenderTargetEGL(GraphicsContextEGL* graphics_context,
const math::Size& size)
- : framebuffer_(
- new FramebufferEGL(graphics_context, size, GL_RGBA, GL_NONE)) {}
+ : framebuffer_(graphics_context, size, GL_RGBA, GL_NONE) {}
- const math::Size& GetSize() OVERRIDE { return framebuffer_->GetSize(); }
+ const math::Size& GetSize() const OVERRIDE { return framebuffer_.GetSize(); }
// This render target does not have an EGLSurface.
EGLSurface GetSurface() const OVERRIDE { return EGL_NO_SURFACE; }
// This handle is suitable for use with glBindFramebuffer.
- intptr_t GetPlatformHandle() OVERRIDE { return framebuffer_->gl_handle(); }
+ intptr_t GetPlatformHandle() const OVERRIDE {
+ return framebuffer_.gl_handle();
+ }
// Create a depth buffer for the render target if it doesn't already have one.
void EnsureDepthBufferAttached(GLenum depth_format) {
- framebuffer_->EnsureDepthBufferAttached(depth_format);
+ framebuffer_.EnsureDepthBufferAttached(depth_format);
}
// Get the color texture attachment of the framebuffer.
TextureEGL* GetColorTexture() const {
- return framebuffer_->GetColorTexture();
+ return framebuffer_.GetColorTexture();
}
private:
~FramebufferRenderTargetEGL() OVERRIDE {}
- scoped_ptr<FramebufferEGL> framebuffer_;
+ FramebufferEGL framebuffer_;
};
} // namespace backend
diff --git a/src/cobalt/renderer/backend/egl/graphics_system.cc b/src/cobalt/renderer/backend/egl/graphics_system.cc
index 86a40d1..ae84a45 100644
--- a/src/cobalt/renderer/backend/egl/graphics_system.cc
+++ b/src/cobalt/renderer/backend/egl/graphics_system.cc
@@ -89,10 +89,6 @@
#endif
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
-#if defined(COBALT_RASTERIZER_USES_DEPTH_BUFFER)
- EGL_DEPTH_SIZE,
- 16,
-#endif
EGL_NONE};
EGLint num_configs;
diff --git a/src/cobalt/renderer/backend/egl/pbuffer_render_target.cc b/src/cobalt/renderer/backend/egl/pbuffer_render_target.cc
index 6e8a7e2..897968f 100644
--- a/src/cobalt/renderer/backend/egl/pbuffer_render_target.cc
+++ b/src/cobalt/renderer/backend/egl/pbuffer_render_target.cc
@@ -47,7 +47,7 @@
CHECK_EQ(EGL_SUCCESS, eglGetError());
}
-const math::Size& PBufferRenderTargetEGL::GetSize() { return size_; }
+const math::Size& PBufferRenderTargetEGL::GetSize() const { return size_; }
EGLSurface PBufferRenderTargetEGL::GetSurface() const {
return surface_;
diff --git a/src/cobalt/renderer/backend/egl/pbuffer_render_target.h b/src/cobalt/renderer/backend/egl/pbuffer_render_target.h
index 66750f3..7ae3267 100644
--- a/src/cobalt/renderer/backend/egl/pbuffer_render_target.h
+++ b/src/cobalt/renderer/backend/egl/pbuffer_render_target.h
@@ -31,7 +31,7 @@
PBufferRenderTargetEGL(
EGLDisplay display, EGLConfig config, const math::Size& dimensions);
- const math::Size& GetSize() OVERRIDE;
+ const math::Size& GetSize() const OVERRIDE;
EGLSurface GetSurface() const OVERRIDE;
diff --git a/src/cobalt/renderer/backend/egl/render_target.h b/src/cobalt/renderer/backend/egl/render_target.h
index d947408..7f7fc6a 100644
--- a/src/cobalt/renderer/backend/egl/render_target.h
+++ b/src/cobalt/renderer/backend/egl/render_target.h
@@ -40,7 +40,7 @@
// Since all RenderTargets defined at this level are EGL objects, they
// will always be set via eglMakeCurrent() and so they can always be
// referenced by OpenGL by binding framebuffer 0.
- intptr_t GetPlatformHandle() OVERRIDE { return 0; }
+ intptr_t GetPlatformHandle() const OVERRIDE { return 0; }
virtual bool IsWindowRenderTarget() const { return false; }
diff --git a/src/cobalt/renderer/backend/render_target.h b/src/cobalt/renderer/backend/render_target.h
index fef9bc6..05786a6 100644
--- a/src/cobalt/renderer/backend/render_target.h
+++ b/src/cobalt/renderer/backend/render_target.h
@@ -37,11 +37,11 @@
RenderTarget();
// Return metadata about the render target such as dimensions and format.
- virtual const math::Size& GetSize() = 0;
+ virtual const math::Size& GetSize() const = 0;
// Returns a platform-specific handle to the render target that can be
// passed into platform-specific code.
- virtual intptr_t GetPlatformHandle() = 0;
+ virtual intptr_t GetPlatformHandle() const = 0;
// Each render is assigned a unique serial number on construction.
int32_t GetSerialNumber() const { return serial_number_; }
diff --git a/src/cobalt/renderer/backend/render_target_stub.h b/src/cobalt/renderer/backend/render_target_stub.h
index 56ed5d3..5a3d8c3 100644
--- a/src/cobalt/renderer/backend/render_target_stub.h
+++ b/src/cobalt/renderer/backend/render_target_stub.h
@@ -28,9 +28,9 @@
public:
explicit RenderTargetStub(const math::Size& size) : size_(size) {}
- const math::Size& GetSize() OVERRIDE { return size_; }
+ const math::Size& GetSize() const OVERRIDE { return size_; }
- intptr_t GetPlatformHandle() OVERRIDE { return 0; }
+ intptr_t GetPlatformHandle() const OVERRIDE { return 0; }
private:
~RenderTargetStub() {}
diff --git a/src/cobalt/renderer/fps_overlay.cc b/src/cobalt/renderer/fps_overlay.cc
new file mode 100644
index 0000000..090d426
--- /dev/null
+++ b/src/cobalt/renderer/fps_overlay.cc
@@ -0,0 +1,141 @@
+// 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/renderer/fps_overlay.h"
+
+#include <string>
+#include <vector>
+
+#include "base/stringprintf.h"
+#include "cobalt/render_tree/brush.h"
+#include "cobalt/render_tree/color_rgba.h"
+#include "cobalt/render_tree/composition_node.h"
+#include "cobalt/render_tree/font.h"
+#include "cobalt/render_tree/glyph_buffer.h"
+#include "cobalt/render_tree/rect_node.h"
+#include "cobalt/render_tree/resource_provider.h"
+#include "cobalt/render_tree/text_node.h"
+
+namespace cobalt {
+namespace renderer {
+
+namespace {
+const render_tree::ColorRGBA kTextColor(1.0f, 1.0f, 1.0f, 1.0f);
+const render_tree::ColorRGBA kBackgroundColor(1.0f, 0.6f, 0.6f, 1.0f);
+const char* kTypefaceName = "Roboto";
+const float kFontSize = 14.0f;
+
+// Distance between the text bounds and the background bounds.
+const int kTextMarginInPixels = 5;
+
+// Spacing between each line of text.
+const int kTextVerticalSpacingInPixels = 5;
+
+scoped_refptr<render_tree::Node> ConvertLinesToOverlay(
+ render_tree::ResourceProvider* resource_provider, render_tree::Font* font,
+ const std::vector<std::string>& lines) {
+ // First create a composition node that composes all the lines of text
+ // together.
+ float y_offset = kTextMarginInPixels;
+ render_tree::CompositionNode::Builder text_builder;
+ for (auto line : lines) {
+ scoped_refptr<render_tree::GlyphBuffer> glyph_buffer =
+ resource_provider->CreateGlyphBuffer(line, font);
+ math::RectF bounds(glyph_buffer->GetBounds());
+
+ text_builder.AddChild(new render_tree::TextNode(
+ math::Vector2dF(-bounds.x() + kTextMarginInPixels,
+ -bounds.y() + y_offset),
+ glyph_buffer, kTextColor));
+
+ y_offset += bounds.height() + kTextVerticalSpacingInPixels;
+ }
+
+ scoped_refptr<render_tree::CompositionNode> text =
+ new render_tree::CompositionNode(text_builder.Pass());
+
+ // Now compose that onto a solid background.
+ math::RectF background_bounds = text->GetBounds();
+ background_bounds.set_height(background_bounds.height() +
+ 2 * kTextMarginInPixels);
+ background_bounds.set_y(0);
+ background_bounds.set_width(background_bounds.width() +
+ 2 * kTextMarginInPixels);
+ background_bounds.set_x(0);
+
+ render_tree::CompositionNode::Builder text_with_background_builder;
+ text_with_background_builder.AddChild(new render_tree::RectNode(
+ background_bounds,
+ scoped_ptr<render_tree::Brush>(
+ new render_tree::SolidColorBrush(kBackgroundColor))));
+ text_with_background_builder.AddChild(text);
+
+ return new render_tree::CompositionNode(text_with_background_builder.Pass());
+}
+
+scoped_refptr<render_tree::Node> ConvertFPSStatsToOverlay(
+ render_tree::ResourceProvider* resource_provider, render_tree::Font* font,
+ const base::CValCollectionTimerStats<base::CValPublic>::FlushResults&
+ fps_stats) {
+ std::vector<std::string> lines;
+ lines.push_back(base::StringPrintf(
+ "Samples: %d", static_cast<unsigned int>(fps_stats.sample_count)));
+ lines.push_back(base::StringPrintf("Average: %.1fms",
+ fps_stats.average.InMillisecondsF()));
+ lines.push_back(
+ base::StringPrintf("Min: %.1fms", fps_stats.minimum.InMillisecondsF()));
+ lines.push_back(
+ base::StringPrintf("Max: %.1fms", fps_stats.maximum.InMillisecondsF()));
+ lines.push_back(base::StringPrintf(
+ "25th Pct: %.1fms", fps_stats.percentile_25th.InMillisecondsF()));
+ lines.push_back(base::StringPrintf(
+ "50th Pct: %.1fms", fps_stats.percentile_50th.InMillisecondsF()));
+ lines.push_back(base::StringPrintf(
+ "75th Pct: %.1fms", fps_stats.percentile_75th.InMillisecondsF()));
+ lines.push_back(base::StringPrintf(
+ "95th Pct: %.1fms", fps_stats.percentile_95th.InMillisecondsF()));
+
+ return ConvertLinesToOverlay(resource_provider, font, lines);
+}
+} // namespace
+
+FpsOverlay::FpsOverlay(render_tree::ResourceProvider* resource_provider)
+ : resource_provider_(resource_provider) {
+ font_ = resource_provider_
+ ->GetLocalTypeface(kTypefaceName, render_tree::FontStyle())
+ ->CreateFontWithSize(kFontSize);
+}
+
+void FpsOverlay::UpdateOverlay(
+ const base::CValCollectionTimerStats<base::CValPublic>::FlushResults&
+ fps_stats) {
+ cached_overlay_ =
+ ConvertFPSStatsToOverlay(resource_provider_, font_, fps_stats);
+}
+
+scoped_refptr<render_tree::Node> FpsOverlay::AnnotateRenderTreeWithOverlay(
+ render_tree::Node* original_tree) {
+ if (!cached_overlay_) {
+ return original_tree;
+ } else {
+ // Compose the overlay onto the top left corner of the original render tree.
+ render_tree::CompositionNode::Builder builder;
+ builder.AddChild(original_tree);
+ builder.AddChild(cached_overlay_);
+ return new render_tree::CompositionNode(builder.Pass());
+ }
+}
+
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/fps_overlay.h b/src/cobalt/renderer/fps_overlay.h
new file mode 100644
index 0000000..816e450
--- /dev/null
+++ b/src/cobalt/renderer/fps_overlay.h
@@ -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.
+
+#ifndef COBALT_RENDERER_FPS_OVERLAY_H_
+#define COBALT_RENDERER_FPS_OVERLAY_H_
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/base/c_val_collection_timer_stats.h"
+#include "cobalt/render_tree/node.h"
+#include "cobalt/render_tree/resource_provider.h"
+
+namespace cobalt {
+namespace renderer {
+
+class FpsOverlay {
+ public:
+ explicit FpsOverlay(render_tree::ResourceProvider* resource_provider);
+
+ void UpdateOverlay(
+ const base::CValCollectionTimerStats<base::CValPublic>::FlushResults&
+ fps_stats);
+ scoped_refptr<render_tree::Node> AnnotateRenderTreeWithOverlay(
+ render_tree::Node* original_tree);
+
+ private:
+ render_tree::ResourceProvider* resource_provider_;
+ scoped_refptr<render_tree::Font> font_;
+
+ scoped_refptr<render_tree::Node> cached_overlay_;
+};
+
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_FPS_OVERLAY_H_
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index c70c3ee..6baeb47 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -73,7 +73,8 @@
const scoped_refptr<backend::RenderTarget>& render_target,
backend::GraphicsContext* graphics_context,
bool submit_even_if_render_tree_is_unchanged,
- ShutdownClearMode clear_on_shutdown_mode)
+ ShutdownClearMode clear_on_shutdown_mode,
+ const Options& options)
: rasterizer_created_event_(true, false),
render_target_(render_target),
graphics_context_(graphics_context),
@@ -85,24 +86,54 @@
rasterize_periodic_timer_("Renderer.Rasterize.Duration",
kRasterizePeriodicTimerEntriesPerUpdate,
false /*enable_entry_list_c_val*/),
- rasterize_animations_timer_("Renderer.Rasterize.Animations",
- kRasterizeAnimationsTimerMaxEntries,
- true /*enable_entry_list_c_val*/),
+ ALLOW_THIS_IN_INITIALIZER_LIST(rasterize_animations_timer_(
+ "Renderer.Rasterize.Animations", kRasterizeAnimationsTimerMaxEntries,
+ true /*enable_entry_list_c_val*/,
+ base::Bind(&Pipeline::FrameStatsOnFlushCallback,
+ base::Unretained(this)))),
+ new_render_tree_rasterize_count_(
+ "Count.Renderer.Rasterize.NewRenderTree", 0,
+ "Total number of new render trees rasterized."),
+ new_render_tree_rasterize_time_(
+ "Time.Renderer.Rasterize.NewRenderTree", 0,
+ "The last time a new render tree was rasterized."),
has_active_animations_c_val_(
"Renderer.HasActiveAnimations", false,
- "Is non-zero if the current render tree has active animations.")
+ "Is non-zero if the current render tree has active animations."),
+ animations_start_time_(
+ "Time.Renderer.Rasterize.Animations.Start", 0,
+ "The most recent time animations started playing."),
+ animations_end_time_("Time.Renderer.Rasterize.Animations.End", 0,
+ "The most recent time animations ended playing."),
#if defined(ENABLE_DEBUG_CONSOLE)
- ,
ALLOW_THIS_IN_INITIALIZER_LIST(dump_current_render_tree_command_handler_(
- "dump_render_tree", base::Bind(&Pipeline::OnDumpCurrentRenderTree,
- base::Unretained(this)),
+ "dump_render_tree",
+ base::Bind(&Pipeline::OnDumpCurrentRenderTree,
+ base::Unretained(this)),
"Dumps the current render tree to text.",
"Dumps the current render tree either to the console if no parameter "
"is specified, or to a file with the specified filename relative to "
- "the debug output folder."))
+ "the debug output folder.")),
+ ALLOW_THIS_IN_INITIALIZER_LIST(toggle_fps_stdout_command_handler_(
+ "toggle_fps_stdout",
+ base::Bind(&Pipeline::OnToggleFpsStdout, base::Unretained(this)),
+ "Toggles printing framerate stats to stdout.",
+ "When enabled, at the end of each animation (or every time a maximum "
+ "number of frames are rendered), framerate statistics are printed "
+ "to stdout.")),
+ ALLOW_THIS_IN_INITIALIZER_LIST(toggle_fps_overlay_command_handler_(
+ "toggle_fps_overlay",
+ base::Bind(&Pipeline::OnToggleFpsOverlay, base::Unretained(this)),
+ "Toggles rendering framerate stats to an overlay on the display.",
+ "Framerate statistics are rendered to a display overlay. The "
+ "numbers are updated at the end of each animation (or every time a "
+ "maximum number of frames are rendered), framerate statistics are "
+ "printed to stdout.")),
#endif
- ,
- clear_on_shutdown_mode_(clear_on_shutdown_mode) {
+ clear_on_shutdown_mode_(clear_on_shutdown_mode),
+ enable_fps_stdout_(options.enable_fps_stdout),
+ enable_fps_overlay_(options.enable_fps_overlay),
+ fps_overlay_updated_(false) {
TRACE_EVENT0("cobalt::renderer", "Pipeline::Pipeline()");
// The actual Pipeline can be constructed from any thread, but we want
// rasterizer_thread_checker_ to be associated with the rasterizer thread,
@@ -128,14 +159,9 @@
// thread as it clears itself out (e.g. it may ask the rasterizer thread to
// delete textures). We wait for this shutdown to complete before proceeding
// to shutdown the rasterizer thread.
- base::WaitableEvent submission_queue_shutdown(true, false);
- rasterizer_thread_.message_loop()->PostTask(
+ rasterizer_thread_.message_loop()->PostBlockingTask(
FROM_HERE,
base::Bind(&Pipeline::ShutdownSubmissionQueue, base::Unretained(this)));
- rasterizer_thread_.message_loop()->PostTask(
- FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
- base::Unretained(&submission_queue_shutdown)));
- submission_queue_shutdown.Wait();
// This potential reference to a render tree whose animations may have ended
// must be destroyed before we shutdown the rasterizer thread since it may
@@ -167,13 +193,9 @@
void Pipeline::Clear() {
TRACE_EVENT0("cobalt::renderer", "Pipeline::Clear()");
- base::WaitableEvent wait_event(true, false);
- rasterizer_thread_.message_loop()->PostTask(
+ rasterizer_thread_.message_loop()->PostBlockingTask(
FROM_HERE,
- base::Bind(&Pipeline::ClearCurrentRenderTree, base::Unretained(this),
- base::Bind(&base::WaitableEvent::Signal,
- base::Unretained(&wait_event))));
- wait_event.Wait();
+ base::Bind(&Pipeline::ClearCurrentRenderTree, base::Unretained(this)));
}
void Pipeline::RasterizeToRGBAPixels(
@@ -225,17 +247,12 @@
}
}
-void Pipeline::ClearCurrentRenderTree(
- const base::Closure& clear_complete_callback) {
+void Pipeline::ClearCurrentRenderTree() {
DCHECK(rasterizer_thread_checker_.CalledOnValidThread());
TRACE_EVENT0("cobalt::renderer", "Pipeline::ClearCurrentRenderTree()");
submission_queue_->Reset();
rasterize_timer_ = base::nullopt;
-
- if (!clear_complete_callback.is_null()) {
- clear_complete_callback.Run();
- }
}
void Pipeline::RasterizeCurrentTree() {
@@ -246,13 +263,15 @@
base::TimeTicks now = base::TimeTicks::Now();
Submission submission = submission_queue_->GetCurrentSubmission(now);
- bool has_render_tree_changed = last_render_animations_active_ ||
- submission.render_tree != last_render_tree_;
+ bool is_new_render_tree = submission.render_tree != last_render_tree_;
+ bool has_render_tree_changed =
+ last_render_animations_active_ || is_new_render_tree;
// If our render tree hasn't changed from the one that was previously
// rendered and it's okay on this system to not flip the display buffer
// frequently, then we can just not do anything here.
- if (!submit_even_if_render_tree_is_unchanged_ && !has_render_tree_changed) {
+ if (!fps_overlay_updated_ && !submit_even_if_render_tree_is_unchanged_ &&
+ !has_render_tree_changed) {
return;
}
@@ -300,10 +319,16 @@
rasterize_animations_timer_.Stop();
}
- // If animations are going from being active to expired, then set the c_val
- // after rasterizing the final state of the tree. Now that we've finished
- // tracking the animations, it's time to flush the timer.
- if (last_render_animations_active_ && !are_animations_active) {
+ if (is_new_render_tree) {
+ ++new_render_tree_rasterize_count_;
+ new_render_tree_rasterize_time_ = base::TimeTicks::Now().ToInternalValue();
+ }
+
+ // Check for if the animations are starting or ending.
+ if (!last_render_animations_active_ && are_animations_active) {
+ animations_start_time_ = base::TimeTicks::Now().ToInternalValue();
+ } else if (last_render_animations_active_ && !are_animations_active) {
+ animations_end_time_ = base::TimeTicks::Now().ToInternalValue();
has_active_animations_c_val_ = false;
rasterize_animations_timer_.Flush();
}
@@ -351,10 +376,15 @@
}
previous_animated_area_ = rounded_bounds;
+ scoped_refptr<render_tree::Node> submit_tree = results.animated;
+ if (enable_fps_overlay_ && fps_overlay_) {
+ submit_tree = fps_overlay_->AnnotateRenderTreeWithOverlay(results.animated);
+ }
+
// Rasterize the animated render tree.
rasterizer::Rasterizer::Options rasterizer_options;
rasterizer_options.dirty = redraw_area;
- rasterizer_->Submit(results.animated, render_target, rasterizer_options);
+ rasterizer_->Submit(submit_tree, render_target, rasterizer_options);
if (!submission.on_rasterized_callback.is_null()) {
submission.on_rasterized_callback.Run();
@@ -465,6 +495,28 @@
tree_dump.length());
}
}
+
+void Pipeline::OnToggleFpsStdout(const std::string& message) {
+ if (MessageLoop::current() != rasterizer_thread_.message_loop()) {
+ rasterizer_thread_.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&Pipeline::OnToggleFpsStdout,
+ base::Unretained(this), message));
+ return;
+ }
+
+ enable_fps_stdout_ = !enable_fps_stdout_;
+}
+
+void Pipeline::OnToggleFpsOverlay(const std::string& message) {
+ if (MessageLoop::current() != rasterizer_thread_.message_loop()) {
+ rasterizer_thread_.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&Pipeline::OnToggleFpsOverlay,
+ base::Unretained(this), message));
+ return;
+ }
+
+ enable_fps_overlay_ = !enable_fps_overlay_;
+}
#endif // #if defined(ENABLE_DEBUG_CONSOLE)
Submission Pipeline::CollectAnimations(
@@ -478,5 +530,44 @@
return collected_submission;
}
+namespace {
+void PrintFPS(
+ const base::CValCollectionTimerStats<base::CValPublic>::FlushResults&
+ results) {
+ SbLogRaw(base::StringPrintf("FPS => # samples: %d, avg: %.1fms, "
+ "[min, max]: [%.1fms, %.1fms]\n"
+ " 25th : 50th : 75th : 95th pct - "
+ "%.1fms : %.1fms : %.1fms : %.1fms\n",
+ static_cast<unsigned int>(results.sample_count),
+ results.average.InMillisecondsF(),
+ results.minimum.InMillisecondsF(),
+ results.maximum.InMillisecondsF(),
+ results.percentile_25th.InMillisecondsF(),
+ results.percentile_50th.InMillisecondsF(),
+ results.percentile_75th.InMillisecondsF(),
+ results.percentile_95th.InMillisecondsF())
+ .c_str());
+}
+} // namespace
+
+void Pipeline::FrameStatsOnFlushCallback(
+ const base::CValCollectionTimerStats<base::CValPublic>::FlushResults&
+ flush_results) {
+ DCHECK(rasterizer_thread_checker_.CalledOnValidThread());
+
+ if (enable_fps_overlay_) {
+ if (!fps_overlay_) {
+ fps_overlay_.emplace(rasterizer_->GetResourceProvider());
+ }
+
+ fps_overlay_->UpdateOverlay(flush_results);
+ fps_overlay_updated_ = true;
+ }
+
+ if (enable_fps_stdout_) {
+ PrintFPS(flush_results);
+ }
+}
+
} // namespace renderer
} // namespace cobalt
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h
index 8fab055..4f2153d 100644
--- a/src/cobalt/renderer/pipeline.h
+++ b/src/cobalt/renderer/pipeline.h
@@ -28,6 +28,7 @@
#include "cobalt/render_tree/animations/animate_node.h"
#include "cobalt/render_tree/node.h"
#include "cobalt/renderer/backend/graphics_context.h"
+#include "cobalt/renderer/fps_overlay.h"
#include "cobalt/renderer/rasterizer/rasterizer.h"
#include "cobalt/renderer/submission.h"
#include "cobalt/renderer/submission_queue.h"
@@ -56,6 +57,13 @@
kNoClear,
};
+ struct Options {
+ Options() : enable_fps_stdout(false), enable_fps_overlay(false) {}
+
+ bool enable_fps_stdout;
+ bool enable_fps_overlay;
+ };
+
// Using the provided rasterizer creation function, a rasterizer will be
// created within the Pipeline on a separate rasterizer thread. Thus,
// the rasterizer created by the provided function should only reference
@@ -66,7 +74,8 @@
const scoped_refptr<backend::RenderTarget>& render_target,
backend::GraphicsContext* graphics_context,
bool submit_even_if_render_tree_is_unchanged,
- ShutdownClearMode clear_on_shutdown_mode);
+ ShutdownClearMode clear_on_shutdown_mode,
+ const Options& options = Options());
~Pipeline();
// Submit a new render tree to the renderer pipeline. After calling this
@@ -99,7 +108,7 @@
void SetNewRenderTree(const Submission& render_tree_submission);
// Clears the current render tree and calls the callback when this is done.
- void ClearCurrentRenderTree(const base::Closure& clear_complete_callback);
+ void ClearCurrentRenderTree();
// Called repeatedly (the rate is limited by the rasterizer, so likely it
// will be called every 1/60th of a second) on the rasterizer thread and
@@ -130,6 +139,8 @@
#if defined(ENABLE_DEBUG_CONSOLE)
void OnDumpCurrentRenderTree(const std::string&);
+ void OnToggleFpsStdout(const std::string&);
+ void OnToggleFpsOverlay(const std::string&);
#endif // defined(ENABLE_DEBUG_CONSOLE)
// Render trees may contain a number of AnimateNodes (or none). In order
@@ -139,6 +150,10 @@
// render tree.
Submission CollectAnimations(const Submission& render_tree_submission);
+ void FrameStatsOnFlushCallback(
+ const base::CValCollectionTimerStats<base::CValPublic>::FlushResults&
+ flush_results);
+
base::WaitableEvent rasterizer_created_event_;
// The render_target that all submitted render trees will be rasterized to.
@@ -197,20 +212,45 @@
// Timer tracking the amount of time spent in
// |RasterizeSubmissionToRenderTarget| while animations are active. The
// tracking is flushed when the animations expire.
- base::CValCollectionTimerStats<base::CValDebug> rasterize_animations_timer_;
+ base::CValCollectionTimerStats<base::CValPublic> rasterize_animations_timer_;
- // Tracks whether or not animations are currently playing.
+ // The total number of new render trees that have been rasterized.
+ base::CVal<int> new_render_tree_rasterize_count_;
+ // The last time that a newly encountered render tree was first rasterized.
+ base::CVal<int64> new_render_tree_rasterize_time_;
+
+ // Whether or not animations are currently playing.
base::CVal<bool> has_active_animations_c_val_;
+ // The most recent time animations started playing.
+ base::CVal<int64> animations_start_time_;
+ // The most recent time animations ended playing.
+ base::CVal<int64> animations_end_time_;
#if defined(ENABLE_DEBUG_CONSOLE)
// Dumps the current render tree to the console.
base::ConsoleCommandManager::CommandHandler
dump_current_render_tree_command_handler_;
+
+ base::ConsoleCommandManager::CommandHandler
+ toggle_fps_stdout_command_handler_;
+ base::ConsoleCommandManager::CommandHandler
+ toggle_fps_overlay_command_handler_;
#endif
// If true, Pipeline's destructor will clear its render target to black on
// shutdown.
const ShutdownClearMode clear_on_shutdown_mode_;
+
+ // If true, we will print framerate statistics to stdout upon completion
+ // of each animation (or after a maximum number of frames has been issued).
+ bool enable_fps_stdout_;
+
+ // If true, an overlay will be displayed over the UI output that shows the
+ // FPS statistics from the last animation.
+ bool enable_fps_overlay_;
+
+ base::optional<FpsOverlay> fps_overlay_;
+ bool fps_overlay_updated_;
};
} // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
index cdd0f00..f29b8c0 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
@@ -282,8 +282,11 @@
context, SbBlitterMakeRect(0, 0, size.width(), size.height())));
RenderTreeNodeVisitor visitor(
- context_->GetSbBlitterDevice(), context,
- initial_render_state, NULL, NULL, NULL, NULL, NULL);
+ context_->GetSbBlitterDevice(), context, initial_render_state,
+ &scratch_surface_cache_,
+ surface_cache_delegate_ ? &surface_cache_delegate_.value() : NULL,
+ surface_cache_ ? &surface_cache_.value() : NULL, &software_surface_cache_,
+ &linear_gradient_cache_);
render_tree->Accept(&visitor);
CHECK(SbBlitterFlushContext(context));
diff --git a/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc
index 68c53b2..a37b317 100644
--- a/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc
@@ -39,6 +39,7 @@
using cobalt::renderer::rasterizer::blitter::RenderState;
using cobalt::renderer::rasterizer::blitter::SkiaToBlitterPixelFormat;
using cobalt::renderer::rasterizer::blitter::RectFToRect;
+using cobalt::renderer::rasterizer::blitter::RectFToBlitterRect;
using cobalt::renderer::rasterizer::blitter::LinearGradientCache;
using cobalt::renderer::rasterizer::skia::SkiaColorStops;
@@ -223,14 +224,16 @@
// The main strategy here is to create a 1D image, and then calculate
// the gradient values in software. If the gradient is simple, this can be
- // one with optimized function (RenderSimpleGradient), which avoids calling
+ // done with optimized function (RenderSimpleGradient), which avoids calling
// Skia (and thus is faster). Otherwise, we call RenderComplexLinearGradient,
// which uses Skia.
- // One a gradient is created, a SbBlitterSurface is created and a rectangle
+ // Once a gradient is created, a SbBlitterSurface is created and a rectangle
// is blitted using the blitter API.
- int width = brush.IsHorizontal() ? rect.width() : 1;
- int height = brush.IsVertical() ? rect.height() : 1;
+ int width = brush.IsHorizontal() ?
+ std::abs(brush.dest().x() - brush.source().x()) : 1;
+ int height = brush.IsVertical() ?
+ std::abs(brush.dest().y() - brush.source().y()) : 1;
if (SbBlitterIsSurfaceValid(surface) == false) {
SkImageInfo image_info = SkImageInfo::MakeN32Premul(width, height);
@@ -288,13 +291,34 @@
SbBlitterSetModulateBlitsWithColor(context, false);
cobalt::math::Rect transformed_rect =
RectFToRect(render_state.transform.TransformRect(rect));
- SbBlitterRect source_rect = SbBlitterMakeRect(0, 0, width, height);
+
+ // It may be the case that the linear gradient is larger than the rect.
+ SbBlitterRect source_rect;
+ if (height == 1) {
+ int left = rect.x() - std::min(brush.source().x(), brush.dest().x());
+ source_rect = SbBlitterMakeRect(left, 0, rect.width(), 1);
+ } else {
+ int top = rect.y() - std::min(brush.source().y(), brush.dest().y());
+ source_rect = SbBlitterMakeRect(0, top, 1, rect.height());
+ }
+
SbBlitterRect dest_rect =
SbBlitterMakeRect(transformed_rect.x(), transformed_rect.y(),
transformed_rect.width(), transformed_rect.height());
SbBlitterBlitRectToRect(context, surface, source_rect, dest_rect);
}
}
+
+void RenderColoredRect(SbBlitterDevice device, SbBlitterContext context,
+ const RenderState& render_state, const ColorRGBA& color,
+ const cobalt::math::RectF& rect) {
+ SbBlitterSetBlending(context, color.a() < 1.0f);
+ SbBlitterSetColor(context, SbBlitterColorFromRGBA(
+ color.rgb8_r(), color.rgb8_g(), color.rgb8_b(), color.rgb8_a()));
+ SbBlitterFillRect(context, RectFToBlitterRect(
+ render_state.transform.TransformRect(rect)));
+}
+
} // namespace
namespace cobalt {
@@ -315,12 +339,66 @@
if (!linear_gradient_brush) return false;
// Currently, only vertical and horizontal gradients are accelerated.
- if ((linear_gradient_brush->IsVertical() ||
- linear_gradient_brush->IsHorizontal()) == false)
+ math::RectF content_rect = rect_node.data().rect;
+ if (linear_gradient_brush->IsVertical()) {
+ // Render solid-colored rect(s) to fill any gaps between the content_rect
+ // and the gradient.
+ float top = linear_gradient_brush->source().y();
+ float bottom = linear_gradient_brush->dest().y();
+ ColorRGBA top_color = linear_gradient_brush->color_stops().front().color;
+ ColorRGBA bottom_color = linear_gradient_brush->color_stops().back().color;
+ if (top > bottom) {
+ std::swap(top, bottom);
+ std::swap(top_color, bottom_color);
+ }
+
+ if (top > content_rect.y()) {
+ float gap = top - content_rect.y();
+ RenderColoredRect(device, context, render_state, top_color,
+ math::RectF(content_rect.x(), content_rect.y(), content_rect.width(),
+ gap));
+ content_rect.set_y(top);
+ content_rect.set_height(content_rect.height() - gap);
+ }
+ if (bottom < content_rect.bottom()) {
+ float gap = content_rect.bottom() - bottom;
+ RenderColoredRect(device, context, render_state, bottom_color,
+ math::RectF(content_rect.x(), bottom, content_rect.width(), gap));
+ content_rect.set_height(content_rect.height() - gap);
+ }
+ } else if (linear_gradient_brush->IsHorizontal()) {
+ // Render solid-colored rect(s) to fill any gaps between the content_rect
+ // and the gradient.
+ float left = linear_gradient_brush->source().x();
+ float right = linear_gradient_brush->dest().x();
+ ColorRGBA left_color = linear_gradient_brush->color_stops().front().color;
+ ColorRGBA right_color = linear_gradient_brush->color_stops().back().color;
+ if (left > right) {
+ std::swap(left, right);
+ std::swap(left_color, right_color);
+ }
+
+ if (left > content_rect.x()) {
+ float gap = left - content_rect.x();
+ RenderColoredRect(device, context, render_state, left_color,
+ math::RectF(content_rect.x(), content_rect.y(), gap,
+ content_rect.height()));
+ content_rect.set_x(left);
+ content_rect.set_width(content_rect.width() - gap);
+ }
+ if (right < content_rect.right()) {
+ float gap = content_rect.right() - right;
+ RenderColoredRect(device, context, render_state, right_color,
+ math::RectF(right, content_rect.y(), gap, content_rect.height()));
+ content_rect.set_width(content_rect.width() - gap);
+ }
+ } else {
+ // Angled gradients are not supported by the optimized path.
return false;
+ }
RenderOptimizedLinearGradient(device, context, render_state,
- rect_node.data().rect, *linear_gradient_brush,
+ content_rect, *linear_gradient_brush,
linear_gradient_cache);
return true;
}
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_callback.cc b/src/cobalt/renderer/rasterizer/egl/draw_callback.cc
new file mode 100644
index 0000000..c41246e
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_callback.cc
@@ -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.
+
+#include "cobalt/renderer/rasterizer/egl/draw_callback.h"
+
+#include <GLES2/gl2.h>
+
+#include "base/basictypes.h"
+#include "cobalt/renderer/backend/egl/utils.h"
+#include "egl/generated_shader_impl.h"
+#include "starboard/memory.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+DrawCallback::DrawCallback(const base::Closure& rasterize_callback)
+ : rasterize_callback_(rasterize_callback) {}
+
+void DrawCallback::ExecuteUpdateVertexBuffer(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ SB_UNREFERENCED_PARAMETER(graphics_state);
+ SB_UNREFERENCED_PARAMETER(program_manager);
+}
+
+void DrawCallback::ExecuteRasterize(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ SB_UNREFERENCED_PARAMETER(graphics_state);
+ SB_UNREFERENCED_PARAMETER(program_manager);
+ if (!rasterize_callback_.is_null()) {
+ rasterize_callback_.Run();
+ }
+}
+
+base::TypeId DrawCallback::GetTypeId() const {
+ return base::GetTypeId<DrawCallback>();
+}
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_callback.h b/src/cobalt/renderer/rasterizer/egl/draw_callback.h
new file mode 100644
index 0000000..3dd070f
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_callback.h
@@ -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.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_CALLBACK_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_CALLBACK_H_
+
+#include "base/callback.h"
+#include "cobalt/renderer/rasterizer/egl/draw_object.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// This is a proxy draw object that allows calling arbitrary functions.
+class DrawCallback : public DrawObject {
+ public:
+ explicit DrawCallback(const base::Closure& rasterize_callback);
+
+ void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+ void ExecuteRasterize(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+ base::TypeId GetTypeId() const OVERRIDE;
+
+ private:
+ base::Closure rasterize_callback_;
+};
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_EGL_DRAW_CALLBACK_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_clear.cc b/src/cobalt/renderer/rasterizer/egl/draw_clear.cc
new file mode 100644
index 0000000..ae883b1
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_clear.cc
@@ -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.
+
+#include "cobalt/renderer/rasterizer/egl/draw_clear.h"
+
+#include <GLES2/gl2.h>
+
+#include "cobalt/renderer/backend/egl/utils.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+DrawClear::DrawClear(GraphicsState* graphics_state,
+ const BaseState& base_state, const render_tree::ColorRGBA& clear_color)
+ : DrawObject(base_state),
+ clear_color_(clear_color) {}
+
+void DrawClear::ExecuteUpdateVertexBuffer(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ SB_UNREFERENCED_PARAMETER(graphics_state);
+ SB_UNREFERENCED_PARAMETER(program_manager);
+}
+
+void DrawClear::ExecuteRasterize(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ SB_UNREFERENCED_PARAMETER(program_manager);
+
+ graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
+ base_state_.scissor.width(), base_state_.scissor.height());
+ graphics_state->Clear(clear_color_.r(), clear_color_.g(), clear_color_.b(),
+ clear_color_.a());
+}
+
+base::TypeId DrawClear::GetTypeId() const {
+ return base::GetTypeId<DrawClear>();
+}
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_clear.h b/src/cobalt/renderer/rasterizer/egl/draw_clear.h
new file mode 100644
index 0000000..8c533de
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_clear.h
@@ -0,0 +1,49 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_CLEAR_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_CLEAR_H_
+
+#include "base/callback.h"
+#include "cobalt/render_tree/color_rgba.h"
+#include "cobalt/renderer/rasterizer/egl/draw_object.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// This object handles clearing the current scissor area with the given color.
+class DrawClear : public DrawObject {
+ public:
+ DrawClear(GraphicsState* graphics_state,
+ const BaseState& base_state,
+ const render_tree::ColorRGBA& clear_color);
+
+ void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+ void ExecuteRasterize(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+ base::TypeId GetTypeId() const OVERRIDE;
+
+ private:
+ render_tree::ColorRGBA clear_color_;
+};
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_EGL_DRAW_CLEAR_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.cc b/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.cc
deleted file mode 100644
index dc6664c..0000000
--- a/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.cc
+++ /dev/null
@@ -1,98 +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/renderer/rasterizer/egl/draw_depth_stencil.h"
-
-#include <GLES2/gl2.h>
-
-#include "cobalt/renderer/backend/egl/utils.h"
-#include "starboard/log.h"
-
-namespace cobalt {
-namespace renderer {
-namespace rasterizer {
-namespace egl {
-
-DrawDepthStencil::DrawDepthStencil(GraphicsState* graphics_state,
- const BaseState& base_state, const math::RectF& include_scissor,
- const math::RectF& exclude_scissor)
- : DrawPolyColor(base_state),
- include_scissor_first_vert_(-1),
- exclude_scissor_first_vert_(-1) {
- attributes_.reserve(MaxVertsNeededForStencil());
- AddStencil(include_scissor, exclude_scissor);
- graphics_state->ReserveVertexData(
- attributes_.size() * sizeof(VertexAttributes));
-}
-
-DrawDepthStencil::DrawDepthStencil(const BaseState& base_state)
- : DrawPolyColor(base_state),
- include_scissor_first_vert_(-1),
- exclude_scissor_first_vert_(-1) {
-}
-
-void DrawDepthStencil::ExecuteOnscreenRasterize(
- GraphicsState* graphics_state,
- ShaderProgramManager* program_manager) {
- SetupShader(graphics_state, program_manager);
- DrawStencil(graphics_state);
-}
-
-void DrawDepthStencil::UndoStencilState(GraphicsState* graphics_state) {
- graphics_state->ResetDepthFunc();
-}
-
-void DrawDepthStencil::DrawStencil(GraphicsState* graphics_state) {
- // This must occur during the transparency pass.
- SB_DCHECK(graphics_state->IsBlendEnabled());
- SB_DCHECK(!graphics_state->IsDepthWriteEnabled());
- SB_DCHECK(graphics_state->IsDepthTestEnabled());
-
- // Set depth of pixels in the scissor rects.
- graphics_state->EnableDepthWrite();
- if (exclude_scissor_first_vert_ >= 0) {
- GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, exclude_scissor_first_vert_, 4));
- }
- SB_DCHECK(include_scissor_first_vert_ >= 0);
- GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, include_scissor_first_vert_, 4));
- graphics_state->DisableDepthWrite();
-
- // Set the depth test function for subsequent draws to occur within the
- // stencil. Be sure to call UndoStencilState() when drawing in the
- // stencilled area is no longer desired.
- GL_CALL(glDepthFunc(GL_EQUAL));
-}
-
-void DrawDepthStencil::AddStencil(
- const math::RectF& include_scissor,
- const math::RectF& exclude_scissor) {
- // At least the include_scissor must be specified.
- SB_DCHECK(!include_scissor.IsEmpty());
-
- include_scissor_first_vert_ = static_cast<GLint>(attributes_.size());
- AddRect(include_scissor, 0);
- if (!exclude_scissor.IsEmpty()) {
- // Exclude scissor must use the next closest depth.
- float depth = base_state_.depth;
- base_state_.depth = GraphicsState::NextClosestDepth(depth);
- exclude_scissor_first_vert_ = static_cast<GLint>(attributes_.size());
- AddRect(exclude_scissor, 0);
- base_state_.depth = depth;
- }
-}
-
-} // namespace egl
-} // namespace rasterizer
-} // namespace renderer
-} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.h b/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.h
deleted file mode 100644
index 5233355..0000000
--- a/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.h
+++ /dev/null
@@ -1,73 +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_RENDERER_RASTERIZER_EGL_DRAW_DEPTH_STENCIL_H_
-#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_DEPTH_STENCIL_H_
-
-#include <vector>
-
-#include "cobalt/math/rect_f.h"
-#include "cobalt/render_tree/color_rgba.h"
-#include "cobalt/renderer/rasterizer/egl/draw_poly_color.h"
-
-namespace cobalt {
-namespace renderer {
-namespace rasterizer {
-namespace egl {
-
-// Handles creation of a depth stencil. Pixels within the include scissor will
-// have their depth set to base_state.depth. Pixels within the exclude scissor,
-// if specified, will have their depth set to the next closest depth value. An
-// |include_scissor| must always be specified.
-//
-// NOTE: This object leaves the graphics state modified so that only the
-// stencilled pixels are affected by later draw calls. Use UndoStencilState
-// once finished with the stencil.
-// NOTE: If an |exclude_scissor| is specified, then this object uses two depth
-// values -- the incoming value in base_state.depth and the next closest.
-// NOTE: Since scissor rects pollute the depth buffer, they should only be
-// used during the tranparency pass because subsequent draws are guaranteed to
-// be above (or not overlap) these pixels.
-class DrawDepthStencil : public DrawPolyColor {
- public:
- DrawDepthStencil(GraphicsState* graphics_state,
- const BaseState& base_state,
- const math::RectF& include_scissor,
- const math::RectF& exclude_scissor);
-
- void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
- ShaderProgramManager* program_manager) OVERRIDE;
-
- // ExecuteOnscreenRasterize / DrawDepthStencil change the graphics state
- // so that subsequent draw calls affect only the stencilled pixels. This
- // function reverts the graphics state back to normal.
- void UndoStencilState(GraphicsState* graphics_state);
-
- protected:
- explicit DrawDepthStencil(const BaseState& base_state);
- void AddStencil(const math::RectF& include_scissor,
- const math::RectF& exclude_scissor);
- void DrawStencil(GraphicsState* graphics_state);
- static size_t MaxVertsNeededForStencil() { return 8; }
-
- GLint include_scissor_first_vert_;
- GLint exclude_scissor_first_vert_;
-};
-
-} // namespace egl
-} // namespace rasterizer
-} // namespace renderer
-} // namespace cobalt
-
-#endif // COBALT_RENDERER_RASTERIZER_EGL_DRAW_DEPTH_STENCIL_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object.cc b/src/cobalt/renderer/rasterizer/egl/draw_object.cc
index 1e14af3..105077f 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object.cc
@@ -32,13 +32,11 @@
scissor(0, 0,
std::numeric_limits<int>::max(),
std::numeric_limits<int>::max()),
- depth(GraphicsState::FarthestDepth()),
opacity(1.0f) {}
DrawObject::BaseState::BaseState(const BaseState& other)
: transform(other.transform),
scissor(other.scissor),
- depth(other.depth),
opacity(other.opacity) {}
DrawObject::DrawObject(const BaseState& base_state)
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object.h b/src/cobalt/renderer/rasterizer/egl/draw_object.h
index 4924230..9410063 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object.h
@@ -37,40 +37,31 @@
math::Matrix3F transform;
math::Rect scissor;
- float depth;
float opacity;
};
virtual ~DrawObject() {}
- // This stage is used to render to offscreen targets. It specifically runs
- // before vertex data is updated for onscreen rendering in order to allow
- // this stage to modify that data. For example, this stage may rasterize to
- // an offscreen atlas, so the location within the atlas might impact the
- // texture coordinates used for the onscreen rasterize stage.
- //
- // If this stage needs to use vertex data of its own, then a new stage,
- // ExecuteOffscreenUpdateVertexBuffer, should be created. This stage should
- // be handled similarly to the current ExecuteOnscreenUpdateVertexBuffer.
- virtual void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
- ShaderProgramManager* program_manager) {}
-
- // This stage is used to update the vertex buffer for the onscreen rasterize
+ // This stage is used to update the vertex buffer for the rasterize
// stage. Vertex data is handled by the GraphicsState to minimize the number
// of vertex buffers needed. Once this stage is executed, the rasterizer will
// then notify the GraphicsState to send all vertex data from all draw
// objects to the GPU.
- virtual void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
+ virtual void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) = 0;
- // This stage is used to render to the main render target. Although it can be
- // used to rasterize to any number of render targets, it is best to use a
- // different stage for offscreen rendering in order to minimize the cost of
- // switching render targets.
- virtual void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+ // This stage is responsible for issuing the GPU commands to do the actual
+ // rendering.
+ virtual void ExecuteRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) = 0;
+ // Return a TypeId that can be used to sort draw objects in order to minimize
+ // state changes. This may be the class' TypeId, or the TypeId of the shader
+ // program which the class uses.
+ virtual base::TypeId GetTypeId() const = 0;
+
protected:
+ DrawObject() {}
explicit DrawObject(const BaseState& base_state);
// Return a uint32_t suitable to be transferred as 4 unsigned bytes
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
index a3f91c29..7662e3c 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.cc
@@ -14,98 +14,253 @@
#include "cobalt/renderer/rasterizer/egl/draw_object_manager.h"
+#include <algorithm>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/renderer/backend/egl/utils.h"
+#include "cobalt/renderer/rasterizer/egl/draw_callback.h"
+
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace egl {
-void DrawObjectManager::AddOpaqueDraw(scoped_ptr<DrawObject> object,
- OnscreenType onscreen_type,
- OffscreenType offscreen_type) {
- if (offscreen_type != kOffscreenNone) {
- offscreen_order_[offscreen_type].push_back(object.get());
- }
+DrawObjectManager::DrawObjectManager(
+ const base::Closure& reset_external_rasterizer,
+ const base::Closure& flush_external_offscreen_draws)
+ : reset_external_rasterizer_(reset_external_rasterizer),
+ flush_external_offscreen_draws_(flush_external_offscreen_draws),
+ current_draw_id_(0) {}
- draw_objects_[onscreen_type].push_back(object.release());
+uint32_t DrawObjectManager::AddOnscreenDraw(scoped_ptr<DrawObject> draw_object,
+ BlendType blend_type, base::TypeId draw_type,
+ const backend::RenderTarget* render_target,
+ const math::RectF& draw_bounds) {
+ onscreen_draws_.push_back(DrawInfo(
+ draw_object.Pass(), draw_type, blend_type, render_target,
+ draw_bounds, ++current_draw_id_));
+ return current_draw_id_;
}
-void DrawObjectManager::AddTransparentDraw(scoped_ptr<DrawObject> object,
- OnscreenType onscreen_type,
- OffscreenType offscreen_type,
- const math::RectF& bounds) {
- // Try to sort the transparent object next to another object of its type.
- // However, this can only be done as long as its bounds do not overlap with
- // the other object while swapping draw order.
- size_t position = transparent_object_info_.size();
- while (position > 0) {
- if (transparent_object_info_[position - 1].type <= onscreen_type) {
- break;
- }
- if (transparent_object_info_[position - 1].bounds.Intersects(bounds)) {
- break;
- }
- --position;
+uint32_t DrawObjectManager::AddOffscreenDraw(scoped_ptr<DrawObject> draw_object,
+ BlendType blend_type, base::TypeId draw_type,
+ const backend::RenderTarget* render_target,
+ const math::RectF& draw_bounds) {
+ // Put all draws using kBlendExternal into their own draw list since they
+ // use an external rasterizer which tracks its own state.
+ auto* draw_list = &offscreen_draws_;
+ if (blend_type == kBlendExternal) {
+ draw_list = &external_offscreen_draws_;
+
+ // Only the DrawCallback type should use kBlendExternal. All other draw
+ // object types use the native rasterizer.
+ DCHECK(base::polymorphic_downcast<DrawCallback*>(draw_object.get()));
}
- if (offscreen_type != kOffscreenNone) {
- offscreen_order_[offscreen_type].push_back(object.get());
- }
+ draw_list->push_back(DrawInfo(
+ draw_object.Pass(), draw_type, blend_type, render_target,
+ draw_bounds, ++current_draw_id_));
+ return current_draw_id_;
+}
- transparent_object_info_.insert(
- transparent_object_info_.begin() + position,
- TransparentObjectInfo(onscreen_type, bounds));
- draw_objects_[kOnscreenTransparent].insert(
- draw_objects_[kOnscreenTransparent].begin() + position,
- object.release());
+void DrawObjectManager::RemoveDraws(uint32_t last_valid_draw_id) {
+ TRACE_EVENT0("cobalt::renderer", "RemoveDraws");
+ RemoveDraws(&onscreen_draws_, last_valid_draw_id);
+ RemoveDraws(&offscreen_draws_, last_valid_draw_id);
+ RemoveDraws(&external_offscreen_draws_, last_valid_draw_id);
+}
+
+void DrawObjectManager::RemoveDraws(std::vector<DrawInfo>* draw_list,
+ uint32_t last_valid_draw_id) {
+ // Objects in the draw list should have ascending draw IDs at this point.
+ auto iter = draw_list->end();
+ for (; iter != draw_list->begin(); --iter) {
+ if ((iter - 1)->draw_id <= last_valid_draw_id) {
+ break;
+ }
+ }
+ if (iter != draw_list->end()) {
+ draw_list->erase(iter, draw_list->end());
+ }
}
void DrawObjectManager::ExecuteOffscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- graphics_state->DisableDepthTest();
- for (int type = 0; type < kOffscreenCount; ++type) {
- for (size_t index = 0; index < offscreen_order_[type].size(); ++index) {
- offscreen_order_[type][index]->ExecuteOffscreenRasterize(graphics_state,
- program_manager);
+ SortOffscreenDraws(&external_offscreen_draws_);
+
+ // Process draws handled by an external rasterizer.
+ {
+ TRACE_EVENT0("cobalt::renderer", "OffscreenExternalRasterizer");
+ for (auto draw = external_offscreen_draws_.begin();
+ draw != external_offscreen_draws_.end(); ++draw) {
+ draw->draw_object->ExecuteRasterize(graphics_state, program_manager);
}
+ if (!flush_external_offscreen_draws_.is_null()) {
+ flush_external_offscreen_draws_.Run();
+ }
+ }
+
+ SortOffscreenDraws(&offscreen_draws_);
+ SortOnscreenDraws(&onscreen_draws_);
+
+ // Update the vertex buffer for all draws.
+ {
+ TRACE_EVENT0("cobalt::renderer", "UpdateVertexBuffer");
+ ExecuteUpdateVertexBuffer(graphics_state, program_manager);
+ }
+
+ // Process the native offscreen draws.
+ {
+ TRACE_EVENT0("cobalt::renderer", "OffscreenNativeRasterizer");
+ Rasterize(offscreen_draws_, graphics_state, program_manager);
}
}
-void DrawObjectManager::ExecuteOnscreenUpdateVertexBuffer(
- GraphicsState* graphics_state,
- ShaderProgramManager* program_manager) {
- for (int type = 0; type < kOnscreenCount; ++type) {
- for (size_t index = 0; index < draw_objects_[type].size(); ++index) {
- draw_objects_[type][index]->ExecuteOnscreenUpdateVertexBuffer(
- graphics_state, program_manager);
- }
+void DrawObjectManager::ExecuteUpdateVertexBuffer(
+ GraphicsState* graphics_state, ShaderProgramManager* program_manager) {
+ for (auto draw = offscreen_draws_.begin(); draw != offscreen_draws_.end();
+ ++draw) {
+ draw->draw_object->ExecuteUpdateVertexBuffer(
+ graphics_state, program_manager);
}
+ for (auto draw = onscreen_draws_.begin(); draw != onscreen_draws_.end();
+ ++draw) {
+ draw->draw_object->ExecuteUpdateVertexBuffer(
+ graphics_state, program_manager);
+ }
+ graphics_state->UpdateVertexData();
}
void DrawObjectManager::ExecuteOnscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- graphics_state->EnableDepthTest();
+ Rasterize(onscreen_draws_, graphics_state, program_manager);
+}
- for (int type = 0; type < kOnscreenCount; ++type) {
- if (type == kOnscreenTransparent) {
- graphics_state->EnableBlend();
- graphics_state->DisableDepthWrite();
+void DrawObjectManager::Rasterize(const std::vector<DrawInfo>& draw_list,
+ GraphicsState* graphics_state, ShaderProgramManager* program_manager) {
+ const backend::RenderTarget* current_target = nullptr;
+ bool using_native_rasterizer = true;
- // Transparent objects must be drawn in the order they were added to
- // maintain correctness.
- for (size_t index = 0; index < draw_objects_[type].size(); ++index) {
- draw_objects_[type][index]->ExecuteOnscreenRasterize(graphics_state,
- program_manager);
+ // Starting from an unknown state.
+ graphics_state->SetDirty();
+
+ for (auto draw = draw_list.begin(); draw != draw_list.end(); ++draw) {
+ bool draw_uses_native_rasterizer = draw->blend_type != kBlendExternal;
+
+ if (draw_uses_native_rasterizer) {
+ if (!using_native_rasterizer) {
+ // Transitioning from external to native rasterizer. Set the native
+ // rasterizer's state as dirty.
+ graphics_state->SetDirty();
+ }
+
+ if (draw->render_target != current_target) {
+ current_target = draw->render_target;
+ graphics_state->BindFramebuffer(current_target);
+ graphics_state->Viewport(0, 0,
+ current_target->GetSize().width(),
+ current_target->GetSize().height());
+ }
+
+ if (draw->blend_type == kBlendNone) {
+ graphics_state->DisableBlend();
+ } else if (draw->blend_type == kBlendSrcAlpha) {
+ graphics_state->EnableBlend();
+ } else {
+ NOTREACHED() << "Unsupported blend type";
}
} else {
- graphics_state->DisableBlend();
- graphics_state->EnableDepthWrite();
+ if (using_native_rasterizer) {
+ // Transitioning from native to external rasterizer. Set the external
+ // rasterizer's state as dirty.
+ if (!reset_external_rasterizer_.is_null()) {
+ reset_external_rasterizer_.Run();
+ }
+ }
+ }
- // Opaque objects can be drawn in front-to-back order to take advantage
- // of z-culling.
- for (size_t index = draw_objects_[type].size(); index > 0;) {
- --index;
- draw_objects_[type][index]->ExecuteOnscreenRasterize(graphics_state,
- program_manager);
+ draw->draw_object->ExecuteRasterize(graphics_state, program_manager);
+ using_native_rasterizer = draw_uses_native_rasterizer;
+ }
+}
+
+void DrawObjectManager::SortOffscreenDraws(std::vector<DrawInfo>* draw_list) {
+ TRACE_EVENT0("cobalt::renderer", "SortOffscreenDraws");
+
+ // Sort offscreen draws to minimize GPU state changes.
+ for (auto iter = draw_list->begin(); iter != draw_list->end(); ++iter) {
+ for (auto current_draw = iter; current_draw != draw_list->begin();
+ std::swap(*current_draw, *(current_draw - 1)), current_draw--) {
+ auto prev_draw = current_draw - 1;
+
+ // Unlike onscreen draws, offscreen draws should be grouped by
+ // render target.
+ if (prev_draw->render_target > current_draw->render_target) {
+ continue;
+ } else if (prev_draw->render_target < current_draw->render_target) {
+ break;
+ }
+
+ if (prev_draw->draw_bounds.Intersects(current_draw->draw_bounds)) {
+ break;
+ }
+
+ if (prev_draw->draw_type > current_draw->draw_type) {
+ continue;
+ } else if (prev_draw->draw_type < current_draw->draw_type) {
+ break;
+ }
+
+ if (prev_draw->blend_type <= current_draw->blend_type) {
+ break;
+ }
+ }
+ }
+}
+
+void DrawObjectManager::SortOnscreenDraws(std::vector<DrawInfo>* draw_list) {
+ TRACE_EVENT0("cobalt::renderer", "SortOnscreenDraws");
+
+ // Sort onscreen draws to minimize GPU state changes.
+ for (auto iter = draw_list->begin(); iter != draw_list->end(); ++iter) {
+ for (auto current_draw = iter; current_draw != draw_list->begin();
+ std::swap(*current_draw, *(current_draw - 1)), current_draw--) {
+ auto prev_draw = current_draw - 1;
+
+ // Do not sort across different render targets since their contents may
+ // be generated just before consumed by a subsequent draw.
+ if (prev_draw->render_target != current_draw->render_target) {
+ break;
+ }
+
+ if (prev_draw->draw_bounds.Intersects(current_draw->draw_bounds)) {
+ break;
+ }
+
+ // Group native vs. non-native draws together.
+ bool next_uses_same_rasterizer = current_draw + 1 != draw_list->end() &&
+ ((current_draw + 1)->blend_type == kBlendExternal) ==
+ (current_draw->blend_type == kBlendExternal);
+ bool prev_uses_same_rasterizer =
+ (prev_draw->blend_type == kBlendExternal) ==
+ (current_draw->blend_type == kBlendExternal);
+ if (!next_uses_same_rasterizer && !prev_uses_same_rasterizer) {
+ continue;
+ }
+ if (next_uses_same_rasterizer && !prev_uses_same_rasterizer) {
+ break;
+ }
+
+ if (prev_draw->draw_type > current_draw->draw_type) {
+ continue;
+ } else if (prev_draw->draw_type < current_draw->draw_type) {
+ break;
+ }
+
+ if (prev_draw->blend_type <= current_draw->blend_type) {
+ break;
}
}
}
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
index 30963a0..0c66f88 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
@@ -17,9 +17,12 @@
#include <vector>
+#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
+#include "cobalt/base/type_id.h"
#include "cobalt/math/rect_f.h"
+#include "cobalt/renderer/backend/render_target.h"
#include "cobalt/renderer/rasterizer/egl/draw_object.h"
#include "cobalt/renderer/rasterizer/egl/graphics_state.h"
#include "cobalt/renderer/rasterizer/egl/shader_program_manager.h"
@@ -33,62 +36,93 @@
// objects to minimize GPU state changes.
class DrawObjectManager {
public:
- enum OnscreenType {
- kOnscreenRectTexture = 0,
- kOnscreenRectColorTexture,
- kOnscreenPolyColor,
- kOnscreenRectShadow,
- kOnscreenRectShadowBlur,
- kOnscreenTransparent,
- kOnscreenCount,
+ enum BlendType {
+ // These draws use an external rasterizer which sets the GPU state.
+ kBlendExternal = 0,
+
+ // These draws use the native rasterizer, and the appropriate state must
+ // be set during execution.
+ kBlendNone,
+ kBlendSrcAlpha,
};
- // Order offscreen rendering by descending expected pixel area. This helps
- // make better use of the offscreen texture atlas as smaller requests can
- // fill in gaps created by the larger requests.
- enum OffscreenType {
- kOffscreenSkiaFilter = 0,
- kOffscreenSkiaShadow,
- kOffscreenSkiaMultiPlaneImage,
- kOffscreenSkiaRectRounded,
- kOffscreenSkiaRectBrush,
- kOffscreenSkiaRectBorder,
- kOffscreenSkiaText,
- kOffscreenCount,
- kOffscreenNone, // ExecuteOffscreenRasterize will not be run for these.
- };
+ DrawObjectManager(const base::Closure& reset_external_rasterizer,
+ const base::Closure& flush_external_offscreen_draws);
- void AddOpaqueDraw(scoped_ptr<DrawObject> object,
- OnscreenType onscreen_type,
- OffscreenType offscreen_type);
- void AddTransparentDraw(scoped_ptr<DrawObject> object,
- OnscreenType onscreen_type,
- OffscreenType offscreen_type,
- const math::RectF& bounds);
+ // Add a draw object that will render to an offscreen render target. There
+ // must be a corresponding draw object added via AddOnscreenDraw() to
+ // render the contents onto the main render target. All offscreen draws are
+ // batched together and executed before any onscreen objects are processed
+ // in order to minimize the cost of switching render targets.
+ // Returns an ID which can be used to remove the queued draw.
+ uint32_t AddOffscreenDraw(scoped_ptr<DrawObject> draw_object,
+ BlendType blend_type, base::TypeId draw_type,
+ const backend::RenderTarget* render_target,
+ const math::RectF& draw_bounds);
+
+ // Add a draw object to be processed when rendering to the main render
+ // target. Although most draws are expected to go to the main render target,
+ // some draws may touch offscreen targets (e.g. when those offscreen targets
+ // are reused during the frame, so their contents must be rasterized just
+ // before being used for the main render target). Switching render targets
+ // has a major negative impact to performance, so it is preferable to avoid
+ // reusing offscreen targets during the frame.
+ // Returns an ID which can be used to remove the queued draw.
+ uint32_t AddOnscreenDraw(scoped_ptr<DrawObject> draw_object,
+ BlendType blend_type, base::TypeId draw_type,
+ const backend::RenderTarget* render_target,
+ const math::RectF& draw_bounds);
+
+ // Remove all queued draws whose ID comes after the given last_valid_draw_id.
+ // Calling RemoveDraws(0) will remove all draws that have been added.
+ void RemoveDraws(uint32_t last_valid_draw_id);
void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager);
- void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
- ShaderProgramManager* program_manager);
void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager);
private:
- struct TransparentObjectInfo {
- TransparentObjectInfo(OnscreenType onscreen_type,
- const math::RectF& object_bounds)
- : bounds(object_bounds),
- type(onscreen_type) {}
- math::RectF bounds;
- OnscreenType type;
+ struct DrawInfo {
+ DrawInfo(scoped_ptr<DrawObject> in_draw_object,
+ base::TypeId in_draw_type, BlendType in_blend_type,
+ const backend::RenderTarget* in_render_target,
+ const math::RectF& in_draw_bounds, uint32_t in_draw_id)
+ : draw_object(in_draw_object.release()),
+ render_target(in_render_target),
+ draw_bounds(in_draw_bounds),
+ draw_type(in_draw_type),
+ blend_type(in_blend_type),
+ draw_id(in_draw_id) {}
+ std::unique_ptr<DrawObject> draw_object;
+ const backend::RenderTarget* render_target;
+ math::RectF draw_bounds;
+ base::TypeId draw_type;
+ BlendType blend_type;
+ uint32_t draw_id;
};
- ScopedVector<DrawObject> draw_objects_[kOnscreenCount];
- std::vector<TransparentObjectInfo> transparent_object_info_;
+ void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager);
- // Manage execution order of objects in the draw_objects_ vectors. This does
- // not manage destruction of objects.
- std::vector<DrawObject*> offscreen_order_[kOffscreenCount];
+ void Rasterize(const std::vector<DrawInfo>& draw_list,
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager);
+
+ void RemoveDraws(std::vector<DrawInfo>* draw_list,
+ uint32_t last_valid_draw_id);
+
+ void SortOffscreenDraws(std::vector<DrawInfo>* draw_list);
+ void SortOnscreenDraws(std::vector<DrawInfo>* draw_list);
+
+ base::Closure reset_external_rasterizer_;
+ base::Closure flush_external_offscreen_draws_;
+
+ std::vector<DrawInfo> onscreen_draws_;
+ std::vector<DrawInfo> offscreen_draws_;
+ std::vector<DrawInfo> external_offscreen_draws_;
+
+ uint32_t current_draw_id_;
};
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
index dc4c7d1..7fbb6fe 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
@@ -41,7 +41,7 @@
vertex_buffer_(NULL) {
}
-void DrawPolyColor::ExecuteOnscreenUpdateVertexBuffer(
+void DrawPolyColor::ExecuteUpdateVertexBuffer(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
vertex_buffer_ = graphics_state->AllocateVertexData(
@@ -50,13 +50,18 @@
attributes_.size() * sizeof(VertexAttributes));
}
-void DrawPolyColor::ExecuteOnscreenRasterize(
+void DrawPolyColor::ExecuteRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
SetupShader(graphics_state, program_manager);
GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
}
+base::TypeId DrawPolyColor::GetTypeId() const {
+ return ShaderProgram<ShaderVertexColor,
+ ShaderFragmentColor>::GetTypeId();
+}
+
void DrawPolyColor::SetupShader(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
ShaderProgram<ShaderVertexColor,
@@ -71,7 +76,7 @@
graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
base_state_.scissor.width(), base_state_.scissor.height());
graphics_state->VertexAttribPointer(
- program->GetVertexShader().a_position(), 3, GL_FLOAT, GL_FALSE,
+ program->GetVertexShader().a_position(), 2, GL_FLOAT, GL_FALSE,
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, position));
graphics_state->VertexAttribPointer(
@@ -89,7 +94,7 @@
}
void DrawPolyColor::AddVertex(float x, float y, uint32_t color) {
- VertexAttributes attribute = { { x, y, base_state_.depth }, color };
+ VertexAttributes attribute = { { x, y }, color };
attributes_.push_back(attribute);
}
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
index bc549f6..cfce106 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
@@ -34,10 +34,11 @@
const math::RectF& rect,
const render_tree::ColorRGBA& color);
- void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
+ void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
- void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+ void ExecuteRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
+ base::TypeId GetTypeId() const OVERRIDE;
protected:
explicit DrawPolyColor(const BaseState& base_state);
@@ -47,7 +48,7 @@
void AddVertex(float x, float y, uint32_t color);
struct VertexAttributes {
- float position[3];
+ float position[2];
uint32_t color;
};
std::vector<VertexAttributes> attributes_;
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
index 2c04709..cc74dad 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
@@ -28,7 +28,7 @@
namespace {
struct VertexAttributes {
- float position[3];
+ float position[2];
float texcoord[2];
uint32_t color;
};
@@ -51,50 +51,22 @@
graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
}
-DrawRectColorTexture::DrawRectColorTexture(GraphicsState* graphics_state,
- const BaseState& base_state,
- const math::RectF& rect, const render_tree::ColorRGBA& color,
- const backend::TextureEGL* texture,
- const math::Matrix3F& texcoord_transform,
- const base::Closure& draw_offscreen,
- const base::Closure& draw_onscreen)
- : DrawObject(base_state),
- texcoord_transform_(texcoord_transform),
- rect_(rect),
- texture_(texture),
- draw_offscreen_(draw_offscreen),
- draw_onscreen_(draw_onscreen),
- vertex_buffer_(NULL),
- clamp_texcoords_(false),
- tile_texture_(false) {
- color_ = GetGLRGBA(color * base_state_.opacity);
- graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
-}
-
-void DrawRectColorTexture::ExecuteOffscreenRasterize(
- GraphicsState* graphics_state,
- ShaderProgramManager* program_manager) {
- if (!draw_offscreen_.is_null()) {
- draw_offscreen_.Run();
- }
-}
-
-void DrawRectColorTexture::ExecuteOnscreenUpdateVertexBuffer(
+void DrawRectColorTexture::ExecuteUpdateVertexBuffer(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
VertexAttributes attributes[4] = {
- { { rect_.x(), rect_.bottom(), base_state_.depth }, // uv = (0,1)
+ { { rect_.x(), rect_.bottom() }, // uv = (0,1)
{ texcoord_transform_(0, 1) + texcoord_transform_(0, 2),
texcoord_transform_(1, 1) + texcoord_transform_(1, 2) }, color_ },
- { { rect_.right(), rect_.bottom(), base_state_.depth }, // uv = (1,1)
+ { { rect_.right(), rect_.bottom() }, // uv = (1,1)
{ texcoord_transform_(0, 0) + texcoord_transform_(0, 1) +
texcoord_transform_(0, 2),
texcoord_transform_(1, 0) + texcoord_transform_(1, 1) +
texcoord_transform_(1, 2) }, color_ },
- { { rect_.right(), rect_.y(), base_state_.depth }, // uv = (1,0)
+ { { rect_.right(), rect_.y() }, // uv = (1,0)
{ texcoord_transform_(0, 0) + texcoord_transform_(0, 2),
texcoord_transform_(1, 0) + texcoord_transform_(1, 2) }, color_ },
- { { rect_.x(), rect_.y(), base_state_.depth }, // uv = (0,0)
+ { { rect_.x(), rect_.y() }, // uv = (0,0)
{ texcoord_transform_(0, 2), texcoord_transform_(1, 2) }, color_ },
};
COMPILE_ASSERT(sizeof(attributes) == 4 * sizeof(VertexAttributes),
@@ -137,13 +109,9 @@
}
}
-void DrawRectColorTexture::ExecuteOnscreenRasterize(
+void DrawRectColorTexture::ExecuteRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- if (!draw_onscreen_.is_null()) {
- draw_onscreen_.Run();
- }
-
ShaderProgram<ShaderVertexColorTexcoord,
ShaderFragmentColorTexcoord>* program;
program_manager->GetProgram(&program);
@@ -156,7 +124,7 @@
graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
base_state_.scissor.width(), base_state_.scissor.height());
graphics_state->VertexAttribPointer(
- program->GetVertexShader().a_position(), 3, GL_FLOAT, GL_FALSE,
+ program->GetVertexShader().a_position(), 2, GL_FLOAT, GL_FALSE,
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, position));
graphics_state->VertexAttribPointer(
@@ -187,6 +155,11 @@
}
}
+base::TypeId DrawRectColorTexture::GetTypeId() const {
+ return ShaderProgram<ShaderVertexColorTexcoord,
+ ShaderFragmentColorTexcoord>::GetTypeId();
+}
+
} // namespace egl
} // namespace rasterizer
} // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
index 11cec54..1cc0862 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
@@ -38,21 +38,11 @@
const math::Matrix3F& texcoord_transform,
bool clamp_texcoords);
- DrawRectColorTexture(GraphicsState* graphics_state,
- const BaseState& base_state,
- const math::RectF& rect,
- const render_tree::ColorRGBA& color,
- const backend::TextureEGL* texture,
- const math::Matrix3F& texcoord_transform,
- const base::Closure& draw_offscreen,
- const base::Closure& draw_onscreen);
-
- void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
+ void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
- void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
+ void ExecuteRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
- void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
- ShaderProgramManager* program_manager) OVERRIDE;
+ base::TypeId GetTypeId() const OVERRIDE;
private:
math::Matrix3F texcoord_transform_;
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.cc
index bf27f37..a4b3e4b 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.cc
@@ -21,6 +21,7 @@
#include "cobalt/renderer/backend/egl/utils.h"
#include "egl/generated_shader_impl.h"
#include "starboard/log.h"
+#include "starboard/memory.h"
namespace cobalt {
namespace renderer {
@@ -28,8 +29,6 @@
namespace egl {
namespace {
-const float kEpsilon = 0.001f;
-
size_t MaxVertsNeededForAlignedGradient(
const render_tree::LinearGradientBrush& brush) {
// Triangle strip for an axis-aligned rectangle. Two vertices are required
@@ -42,15 +41,16 @@
const BaseState& base_state,
const math::RectF& rect,
const render_tree::LinearGradientBrush& brush)
- : DrawDepthStencil(base_state),
- first_rect_vert_(0),
- gradient_angle_(0.0f) {
- if (std::abs(brush.dest().y() - brush.source().y()) < kEpsilon) {
- attributes_.reserve(MaxVertsNeededForAlignedGradient(brush));
+ : DrawObject(base_state),
+ transform_(math::Matrix3F::Identity()),
+ include_scissor_(rect),
+ vertex_buffer_(nullptr) {
+ attributes_.reserve(MaxVertsNeededForAlignedGradient(brush));
+
+ if (brush.IsHorizontal()) {
AddRectWithHorizontalGradient(
rect, brush.source(), brush.dest(), brush.color_stops());
- } else if (std::abs(brush.dest().x() - brush.source().x()) < kEpsilon) {
- attributes_.reserve(MaxVertsNeededForAlignedGradient(brush));
+ } else if (brush.IsVertical()) {
AddRectWithVerticalGradient(
rect, brush.source(), brush.dest(), brush.color_stops());
} else {
@@ -60,44 +60,65 @@
attributes_.size() * sizeof(VertexAttributes));
}
-void DrawRectLinearGradient::ExecuteOnscreenRasterize(
+void DrawRectLinearGradient::ExecuteUpdateVertexBuffer(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- SetupShader(graphics_state, program_manager);
+ vertex_buffer_ = graphics_state->AllocateVertexData(
+ attributes_.size() * sizeof(VertexAttributes));
+ SbMemoryCopy(vertex_buffer_, &attributes_[0],
+ attributes_.size() * sizeof(VertexAttributes));
+}
- if (first_rect_vert_ > 0) {
- // Draw using stencil.
- DrawStencil(graphics_state);
+void DrawRectLinearGradient::ExecuteRasterize(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ ShaderProgram<ShaderVertexColorOffset,
+ ShaderFragmentColorInclude>* program;
+ program_manager->GetProgram(&program);
+ graphics_state->UseProgram(program->GetHandle());
+ graphics_state->UpdateClipAdjustment(
+ program->GetVertexShader().u_clip_adjustment());
+ graphics_state->UpdateTransformMatrix(
+ program->GetVertexShader().u_view_matrix(),
+ base_state_.transform);
+ graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
+ base_state_.scissor.width(), base_state_.scissor.height());
+ graphics_state->VertexAttribPointer(
+ program->GetVertexShader().a_position(), 2, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributes), vertex_buffer_ +
+ offsetof(VertexAttributes, position));
+ graphics_state->VertexAttribPointer(
+ program->GetVertexShader().a_color(), 4, GL_UNSIGNED_BYTE, GL_TRUE,
+ sizeof(VertexAttributes), vertex_buffer_ +
+ offsetof(VertexAttributes, color));
+ graphics_state->VertexAttribPointer(
+ program->GetVertexShader().a_offset(), 2, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributes), vertex_buffer_ +
+ offsetof(VertexAttributes, offset));
+ graphics_state->VertexAttribFinish();
- // Update the transform for the shader to rotate the horizontal gradient
- // into the desired angled gradient.
- ShaderProgram<ShaderVertexColor,
- ShaderFragmentColor>* program;
- program_manager->GetProgram(&program);
- SB_DCHECK(graphics_state->GetProgram() == program->GetHandle());
- graphics_state->UpdateTransformMatrix(
- program->GetVertexShader().u_view_matrix(),
- base_state_.transform * math::RotateMatrix(gradient_angle_));
+ float include[4] = {
+ include_scissor_.x(),
+ include_scissor_.y(),
+ include_scissor_.right(),
+ include_scissor_.bottom()
+ };
+ GL_CALL(glUniform4fv(program->GetFragmentShader().u_include(), 1, include));
- GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, first_rect_vert_,
- attributes_.size() - first_rect_vert_));
- UndoStencilState(graphics_state);
- } else {
- GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
- }
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
+}
+
+base::TypeId DrawRectLinearGradient::GetTypeId() const {
+ return ShaderProgram<ShaderVertexColorOffset,
+ ShaderFragmentColorInclude>::GetTypeId();
}
void DrawRectLinearGradient::AddRectWithHorizontalGradient(
const math::RectF& rect, const math::PointF& source,
const math::PointF& dest, const render_tree::ColorStopList& color_stops) {
- SB_DCHECK(source.x() >= rect.x() - kEpsilon &&
- source.x() <= rect.right() + kEpsilon &&
- dest.x() >= rect.x() - kEpsilon &&
- dest.x() <= rect.right() + kEpsilon);
-
size_t num_colors = color_stops.size();
- float start = std::max(rect.x(), std::min(source.x(), rect.right()));
- float length = std::max(rect.x(), std::min(dest.x(), rect.right())) - start;
+ float start = source.x();
+ float length = dest.x() - source.x();
float position = color_stops[0].position * length + start;
uint32_t color32 = GetGLColor(color_stops[0]);
@@ -133,14 +154,9 @@
void DrawRectLinearGradient::AddRectWithVerticalGradient(
const math::RectF& rect, const math::PointF& source,
const math::PointF& dest, const render_tree::ColorStopList& color_stops) {
- SB_DCHECK(source.y() >= rect.y() - kEpsilon &&
- source.y() <= rect.bottom() + kEpsilon &&
- dest.y() >= rect.y() - kEpsilon &&
- dest.y() <= rect.bottom() + kEpsilon);
-
size_t num_colors = color_stops.size();
- float start = std::max(rect.y(), std::min(source.y(), rect.bottom()));
- float length = std::max(rect.y(), std::min(dest.y(), rect.bottom())) - start;
+ float start = source.y();
+ float length = dest.y() - source.y();
float position = color_stops[0].position * length + start;
uint32_t color32 = GetGLColor(color_stops[0]);
@@ -179,34 +195,25 @@
// draw a rect with horizontal gradient that will be rotated to cover the
// desired rect with angled gradient. This is not as efficient as calculating
// a triangle strip to describe the rect with angled gradient, but is simpler.
- attributes_.reserve(MaxVertsNeededForStencil() +
- MaxVertsNeededForAlignedGradient(brush));
- AddStencil(rect, math::RectF());
// Calculate the angle needed to rotate a horizontal gradient into the
// angled gradient. (Flip vertical distance because the input coordinate
// system's origin is at the top-left.)
- gradient_angle_ = atan2(brush.source().y() - brush.dest().y(),
- brush.dest().x() - brush.source().x());
+ float gradient_angle = atan2(brush.source().y() - brush.dest().y(),
+ brush.dest().x() - brush.source().x());
+ transform_ = math::RotateMatrix(gradient_angle);
// Calculate the endpoints for the horizontal gradient that, when rotated
- // by gradient_angle_, will turn into the original angled gradient.
- math::Matrix3F inverse_transform = math::RotateMatrix(-gradient_angle_);
+ // by gradient_angle, will turn into the original angled gradient.
+ math::Matrix3F inverse_transform = math::RotateMatrix(-gradient_angle);
math::PointF mapped_source(inverse_transform * brush.source());
math::PointF mapped_dest(inverse_transform * brush.dest());
SB_DCHECK(mapped_source.x() <= mapped_dest.x());
- // Calculate a rect large enough that, when rotated by gradient_angle_, it
+ // Calculate a rect large enough that, when rotated by gradient_angle, it
// will contain the original rect.
math::RectF mapped_rect = inverse_transform.MapRect(rect);
- // Adjust the mapped_rect to include the gradient endpoints if needed.
- float left = std::min(mapped_rect.x(), mapped_source.x());
- float right = std::max(mapped_rect.right(), mapped_dest.x());
- mapped_rect.SetRect(left, mapped_rect.y(),
- right - left, mapped_rect.height());
-
- first_rect_vert_ = attributes_.size();
AddRectWithHorizontalGradient(mapped_rect, mapped_source, mapped_dest,
brush.color_stops());
}
@@ -219,6 +226,16 @@
alpha);
}
+void DrawRectLinearGradient::AddVertex(float x, float y, uint32_t color) {
+ math::PointF position = transform_ * math::PointF(x, y);
+ VertexAttributes attributes = {
+ { position.x(), position.y() },
+ { position.x(), position.y() },
+ color
+ };
+ attributes_.push_back(attributes);
+}
+
} // namespace egl
} // namespace rasterizer
} // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h
index e138460..6d54e53 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h
@@ -15,26 +15,33 @@
#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_LINEAR_GRADIENT_H_
#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_LINEAR_GRADIENT_H_
+#include <vector>
+
+#include "cobalt/math/matrix3_f.h"
#include "cobalt/math/rect_f.h"
#include "cobalt/render_tree/brush.h"
-#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
+#include "cobalt/renderer/rasterizer/egl/draw_object.h"
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace egl {
-// Handles drawing a rectangle with a linear color gradient. This may use a
-// depth stencil, so it must be processed with other transparent draws.
-class DrawRectLinearGradient : public DrawDepthStencil {
+// Handles drawing a rectangle with a linear color gradient. This may use
+// transparency to mask out unwanted pixels, so it must be processed with other
+// transparent draws.
+class DrawRectLinearGradient : public DrawObject {
public:
DrawRectLinearGradient(GraphicsState* graphics_state,
const BaseState& base_state,
const math::RectF& rect,
const render_tree::LinearGradientBrush& brush);
- void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+ void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
+ void ExecuteRasterize(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+ base::TypeId GetTypeId() const OVERRIDE;
private:
void AddRectWithHorizontalGradient(
@@ -46,14 +53,23 @@
void AddRectWithAngledGradient(
const math::RectF& rect, const render_tree::LinearGradientBrush& brush);
uint32_t GetGLColor(const render_tree::ColorStop& color_stop);
+ void AddVertex(float x, float y, uint32_t color);
- // Index of the first vertex for the rect with gradient.
- size_t first_rect_vert_;
+ // For angled gradients, this is the rotation needed to transform a
+ // horizontal gradient into the desired rect with angled gradient. The
+ // include scissor will be used to ensure that only the desired pixels
+ // are modified.
+ math::Matrix3F transform_;
+ math::RectF include_scissor_;
- // For angled gradients, this is the rotation angle needed to transform the
- // submitted rect with horizontal gradient into the desired rect with angled
- // gradient.
- float gradient_angle_;
+ struct VertexAttributes {
+ float position[2];
+ float offset[2];
+ uint32_t color;
+ };
+ std::vector<VertexAttributes> attributes_;
+
+ uint8_t* vertex_buffer_;
};
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
index 6090782..e03dc0f 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
@@ -27,20 +27,23 @@
namespace rasterizer {
namespace egl {
+namespace {
+const int kVertexCount = 10;
+}
+
DrawRectShadowBlur::DrawRectShadowBlur(GraphicsState* graphics_state,
const BaseState& base_state, const math::RectF& inner_rect,
const math::RectF& outer_rect, const math::RectF& blur_edge,
- const render_tree::ColorRGBA& color, const math::RectF& exclude_scissor,
- float blur_sigma, bool inset)
+ const render_tree::ColorRGBA& color, float blur_sigma, bool inset)
: DrawObject(base_state),
- draw_stencil_(graphics_state, base_state, outer_rect, exclude_scissor),
+ inner_rect_(inner_rect),
+ outer_rect_(outer_rect),
blur_center_(blur_edge.CenterPoint()),
// The sigma scale is used to transform pixel distances to sigma-relative
// distances. The 0.5 multiplier is used to match skia's implementation.
blur_sigma_scale_(0.5f / blur_sigma),
vertex_buffer_(NULL) {
float color_scale = 1.0f;
- attributes_.reserve(10);
// The blur radius dictates the distance from blur center at which the
// shadow should be about 50% opacity of the shadow color. This is expressed
@@ -63,46 +66,43 @@
const float size_scale = 1.0f / (1.5f * blur_sigma);
color_scale = std::min(blur_edge.width() * size_scale, 1.0f) *
std::min(blur_edge.height() * size_scale, 1.0f);
+
+ // Ensure the outer_rect contains the inner_rect to avoid overlapping
+ // triangles in the draw call. The fragment shader will properly fade out
+ // the extra pixels.
+ outer_rect_.Union(inner_rect_);
}
+ color_ = GetGLRGBA(color * color_scale * base_state_.opacity);
+ graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
+}
+
+void DrawRectShadowBlur::ExecuteUpdateVertexBuffer(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
// Add a triangle-strip to draw the area between outer_rect and inner_rect.
// This is the area which the shadow covers.
- uint32_t color32 = GetGLRGBA(color * color_scale * base_state_.opacity);
- AddVertex(outer_rect.x(), outer_rect.y(), color32);
- AddVertex(inner_rect.x(), inner_rect.y(), color32);
- AddVertex(outer_rect.right(), outer_rect.y(), color32);
- AddVertex(inner_rect.right(), inner_rect.y(), color32);
- AddVertex(outer_rect.right(), outer_rect.bottom(), color32);
- AddVertex(inner_rect.right(), inner_rect.bottom(), color32);
- AddVertex(outer_rect.x(), outer_rect.bottom(), color32);
- AddVertex(inner_rect.x(), inner_rect.bottom(), color32);
- AddVertex(outer_rect.x(), outer_rect.y(), color32);
- AddVertex(inner_rect.x(), inner_rect.y(), color32);
+ VertexAttributes attributes[kVertexCount];
+ SetVertex(&attributes[0], outer_rect_.x(), outer_rect_.y());
+ SetVertex(&attributes[1], inner_rect_.x(), inner_rect_.y());
+ SetVertex(&attributes[2], outer_rect_.right(), outer_rect_.y());
+ SetVertex(&attributes[3], inner_rect_.right(), inner_rect_.y());
+ SetVertex(&attributes[4], outer_rect_.right(), outer_rect_.bottom());
+ SetVertex(&attributes[5], inner_rect_.right(), inner_rect_.bottom());
+ SetVertex(&attributes[6], outer_rect_.x(), outer_rect_.bottom());
+ SetVertex(&attributes[7], inner_rect_.x(), inner_rect_.bottom());
+ SetVertex(&attributes[8], outer_rect_.x(), outer_rect_.y());
+ SetVertex(&attributes[9], inner_rect_.x(), inner_rect_.y());
- graphics_state->ReserveVertexData(
- attributes_.size() * sizeof(VertexAttributes));
+ vertex_buffer_ = graphics_state->AllocateVertexData(sizeof(attributes));
+ SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
}
-void DrawRectShadowBlur::ExecuteOnscreenUpdateVertexBuffer(
+void DrawRectShadowBlur::ExecuteRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- draw_stencil_.ExecuteOnscreenUpdateVertexBuffer(
- graphics_state, program_manager);
- vertex_buffer_ = graphics_state->AllocateVertexData(
- attributes_.size() * sizeof(VertexAttributes));
- SbMemoryCopy(vertex_buffer_, &attributes_[0],
- attributes_.size() * sizeof(VertexAttributes));
-}
-
-void DrawRectShadowBlur::ExecuteOnscreenRasterize(
- GraphicsState* graphics_state,
- ShaderProgramManager* program_manager) {
- // Create a stencil for the pixels to be modified.
- draw_stencil_.ExecuteOnscreenRasterize(
- graphics_state, program_manager);
-
- // Draw the blurred shadow in the stencilled area.
- ShaderProgram<ShaderVertexColorBlur,
+ // Draw the blurred shadow.
+ ShaderProgram<ShaderVertexColorOffset,
ShaderFragmentColorBlur>* program;
program_manager->GetProgram(&program);
graphics_state->UseProgram(program->GetHandle());
@@ -114,7 +114,7 @@
graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
base_state_.scissor.width(), base_state_.scissor.height());
graphics_state->VertexAttribPointer(
- program->GetVertexShader().a_position(), 3, GL_FLOAT, GL_FALSE,
+ program->GetVertexShader().a_position(), 2, GL_FLOAT, GL_FALSE,
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, position));
graphics_state->VertexAttribPointer(
@@ -122,25 +122,32 @@
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, color));
graphics_state->VertexAttribPointer(
- program->GetVertexShader().a_blur_position(), 2, GL_FLOAT, GL_FALSE,
+ program->GetVertexShader().a_offset(), 2, GL_FLOAT, GL_FALSE,
sizeof(VertexAttributes), vertex_buffer_ +
- offsetof(VertexAttributes, blur_position));
+ offsetof(VertexAttributes, offset));
graphics_state->VertexAttribFinish();
GL_CALL(glUniform2fv(program->GetFragmentShader().u_blur_radius(), 1,
blur_radius_));
GL_CALL(glUniform2fv(program->GetFragmentShader().u_scale_add(), 1,
blur_scale_add_));
- GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
- draw_stencil_.UndoStencilState(graphics_state);
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, kVertexCount));
}
-void DrawRectShadowBlur::AddVertex(float x, float y, uint32_t color) {
- float blur_x = (x - blur_center_.x()) * blur_sigma_scale_;
- float blur_y = (y - blur_center_.y()) * blur_sigma_scale_;
- VertexAttributes attribute = { { x, y, base_state_.depth }, color,
- { blur_x, blur_y } };
- attributes_.push_back(attribute);
+base::TypeId DrawRectShadowBlur::GetTypeId() const {
+ return ShaderProgram<ShaderVertexColorOffset,
+ ShaderFragmentColorBlur>::GetTypeId();
+}
+
+void DrawRectShadowBlur::SetVertex(VertexAttributes* vertex,
+ float x, float y) {
+ vertex->position[0] = x;
+ vertex->position[1] = y;
+
+ vertex->offset[0] = (x - blur_center_.x()) * blur_sigma_scale_;
+ vertex->offset[1] = (y - blur_center_.y()) * blur_sigma_scale_;
+
+ vertex->color = color_;
}
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
index 4d17b25..0add5be 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
@@ -19,7 +19,6 @@
#include "cobalt/math/rect_f.h"
#include "cobalt/render_tree/color_rgba.h"
-#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
#include "cobalt/renderer/rasterizer/egl/draw_object.h"
namespace cobalt {
@@ -34,7 +33,7 @@
// | | Box shadow "spread" region | |
// | | +---------------------+ | |
// | | | Box shadow rect | | |
-// | | | (exclude scissor) | | |
+// | | | (exclude geometry) | | |
// | | +---------------------+ | |
// | | | |
// | +-----------------------------+ |
@@ -44,8 +43,7 @@
// "spread" region.
// Handles drawing a box shadow with blur. This uses a gaussian kernel to fade
-// the "blur" region. A stencil is used to ensure only the desired pixels are
-// touched.
+// the "blur" region.
//
// This uses a shader to mimic skia's SkBlurMask.cpp.
// See also http://stereopsis.com/shadowrect/ as reference for the formula
@@ -63,32 +61,30 @@
const math::RectF& outer_rect,
const math::RectF& blur_edge,
const render_tree::ColorRGBA& color,
- const math::RectF& exclude_scissor,
float blur_sigma, bool inset);
- void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
+ void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
- void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+ void ExecuteRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
+ base::TypeId GetTypeId() const OVERRIDE;
private:
- void AddVertex(float x, float y, uint32_t color);
-
struct VertexAttributes {
- float position[3];
+ float position[2];
+ float offset[2]; // Expressed in terms of blur_sigma from center.
uint32_t color;
- float blur_position[2]; // Expressed in terms of blur_sigma from center.
};
- DrawDepthStencil draw_stencil_;
- std::vector<VertexAttributes> attributes_;
+ void SetVertex(VertexAttributes* vertex, float x, float y);
+
+ math::RectF inner_rect_;
+ math::RectF outer_rect_;
math::PointF blur_center_;
float blur_radius_[2]; // Expressed in terms of blur_sigma.
float blur_scale_add_[2];
-
- // The sigma scale is used to transform pixel distances to sigma-relative
- // distances.
- float blur_sigma_scale_;
+ float blur_sigma_scale_; // Used to express distances as sigma-relative.
+ uint32_t color_;
uint8_t* vertex_buffer_;
};
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
index 8f61e32..1a4e597 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
@@ -17,48 +17,103 @@
#include <GLES2/gl2.h>
#include "cobalt/renderer/backend/egl/utils.h"
-#include "starboard/log.h"
+#include "egl/generated_shader_impl.h"
+#include "starboard/memory.h"
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace egl {
+namespace {
+const int kVertexCount = 10;
+}
+
DrawRectShadowSpread::DrawRectShadowSpread(GraphicsState* graphics_state,
const BaseState& base_state, const math::RectF& inner_rect,
const math::RectF& outer_rect, const render_tree::ColorRGBA& color,
- const math::RectF& include_scissor, const math::RectF& exclude_scissor)
- : DrawDepthStencil(base_state) {
- // Reserve space for the box shadow's spread and possible scissor rects.
- attributes_.reserve(10 + MaxVertsNeededForStencil());
-
- // Draw the box shadow's spread. This is a triangle strip covering the area
- // between outer rect and inner rect.
- uint32_t color32 = GetGLRGBA(color * base_state_.opacity);
- AddVertex(outer_rect.x(), outer_rect.y(), color32);
- AddVertex(inner_rect.x(), inner_rect.y(), color32);
- AddVertex(outer_rect.right(), outer_rect.y(), color32);
- AddVertex(inner_rect.right(), inner_rect.y(), color32);
- AddVertex(outer_rect.right(), outer_rect.bottom(), color32);
- AddVertex(inner_rect.right(), inner_rect.bottom(), color32);
- AddVertex(outer_rect.x(), outer_rect.bottom(), color32);
- AddVertex(inner_rect.x(), inner_rect.bottom(), color32);
- AddVertex(outer_rect.x(), outer_rect.y(), color32);
- AddVertex(inner_rect.x(), inner_rect.y(), color32);
-
- AddStencil(include_scissor, exclude_scissor);
-
- graphics_state->ReserveVertexData(
- attributes_.size() * sizeof(VertexAttributes));
+ const math::RectF& include_scissor)
+ : DrawObject(base_state),
+ inner_rect_(inner_rect),
+ outer_rect_(outer_rect),
+ include_scissor_(include_scissor),
+ vertex_buffer_(nullptr) {
+ color_ = GetGLRGBA(color * base_state_.opacity);
+ graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
}
-void DrawRectShadowSpread::ExecuteOnscreenRasterize(
+void DrawRectShadowSpread::ExecuteUpdateVertexBuffer(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- SetupShader(graphics_state, program_manager);
- DrawStencil(graphics_state);
- GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 10));
- UndoStencilState(graphics_state);
+ // Draw the box shadow's spread. This is a triangle strip covering the area
+ // between outer rect and inner rect.
+ VertexAttributes attributes[kVertexCount];
+ SetVertex(&attributes[0], outer_rect_.x(), outer_rect_.y());
+ SetVertex(&attributes[1], inner_rect_.x(), inner_rect_.y());
+ SetVertex(&attributes[2], outer_rect_.right(), outer_rect_.y());
+ SetVertex(&attributes[3], inner_rect_.right(), inner_rect_.y());
+ SetVertex(&attributes[4], outer_rect_.right(), outer_rect_.bottom());
+ SetVertex(&attributes[5], inner_rect_.right(), inner_rect_.bottom());
+ SetVertex(&attributes[6], outer_rect_.x(), outer_rect_.bottom());
+ SetVertex(&attributes[7], inner_rect_.x(), inner_rect_.bottom());
+ SetVertex(&attributes[8], outer_rect_.x(), outer_rect_.y());
+ SetVertex(&attributes[9], inner_rect_.x(), inner_rect_.y());
+
+ vertex_buffer_ = graphics_state->AllocateVertexData(sizeof(attributes));
+ SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
+}
+
+void DrawRectShadowSpread::ExecuteRasterize(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ ShaderProgram<ShaderVertexColorOffset,
+ ShaderFragmentColorInclude>* program;
+ program_manager->GetProgram(&program);
+ graphics_state->UseProgram(program->GetHandle());
+ graphics_state->UpdateClipAdjustment(
+ program->GetVertexShader().u_clip_adjustment());
+ graphics_state->UpdateTransformMatrix(
+ program->GetVertexShader().u_view_matrix(),
+ base_state_.transform);
+ graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
+ base_state_.scissor.width(), base_state_.scissor.height());
+ graphics_state->VertexAttribPointer(
+ program->GetVertexShader().a_position(), 2, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributes), vertex_buffer_ +
+ offsetof(VertexAttributes, position));
+ graphics_state->VertexAttribPointer(
+ program->GetVertexShader().a_color(), 4, GL_UNSIGNED_BYTE, GL_TRUE,
+ sizeof(VertexAttributes), vertex_buffer_ +
+ offsetof(VertexAttributes, color));
+ graphics_state->VertexAttribPointer(
+ program->GetVertexShader().a_offset(), 2, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributes), vertex_buffer_ +
+ offsetof(VertexAttributes, offset));
+ graphics_state->VertexAttribFinish();
+
+ float include[4] = {
+ include_scissor_.x(),
+ include_scissor_.y(),
+ include_scissor_.right(),
+ include_scissor_.bottom()
+ };
+ GL_CALL(glUniform4fv(program->GetFragmentShader().u_include(), 1, include));
+
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, kVertexCount));
+}
+
+base::TypeId DrawRectShadowSpread::GetTypeId() const {
+ return ShaderProgram<ShaderVertexColorOffset,
+ ShaderFragmentColorInclude>::GetTypeId();
+}
+
+void DrawRectShadowSpread::SetVertex(VertexAttributes* vertex,
+ float x, float y) {
+ vertex->position[0] = x;
+ vertex->position[1] = y;
+ vertex->offset[0] = x;
+ vertex->offset[1] = y;
+ vertex->color = color_;
}
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
index 28d1cef..0621a45 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
@@ -19,7 +19,7 @@
#include "cobalt/math/rect_f.h"
#include "cobalt/render_tree/color_rgba.h"
-#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
+#include "cobalt/renderer/rasterizer/egl/draw_object.h"
namespace cobalt {
namespace renderer {
@@ -33,19 +33,16 @@
// | | Box shadow "spread" region | |
// | | +---------------------+ | |
// | | | Box shadow rect | | |
-// | | | (exclude scissor) | | |
+// | | | (exclude geometry) | | |
// | | +---------------------+ | |
// | | | |
// | +-----------------------------+ |
// | (include scissor) |
// +-------------------------------------+
-// When an offset is used for the shadow, the include and exclude scissors
-// play a critical role.
-// Handles drawing the solid "spread" portion of a box shadow. This also
-// creates a stencil, using the depth buffer, for the pixels inside
-// |include_scissor| excluding those in |exclude_scissor|.
-class DrawRectShadowSpread : public DrawDepthStencil {
+// Handles drawing the solid "spread" portion of a box shadow. The
+// |include_scissor| specifies which pixels can be touched.
+class DrawRectShadowSpread : public DrawObject {
public:
// Fill the area between |inner_rect| and |outer_rect| with the specified
// color.
@@ -54,11 +51,29 @@
const math::RectF& inner_rect,
const math::RectF& outer_rect,
const render_tree::ColorRGBA& color,
- const math::RectF& include_scissor,
- const math::RectF& exclude_scissor);
+ const math::RectF& include_scissor);
- void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+ void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
+ void ExecuteRasterize(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+ base::TypeId GetTypeId() const OVERRIDE;
+
+ private:
+ struct VertexAttributes {
+ float position[2];
+ float offset[2];
+ uint32_t color;
+ };
+
+ void SetVertex(VertexAttributes* vertex, float x, float y);
+
+ math::RectF inner_rect_;
+ math::RectF outer_rect_;
+ math::RectF include_scissor_;
+ uint32_t color_;
+
+ uint8_t* vertex_buffer_;
};
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
index 107f103..c5c0431 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
@@ -28,7 +28,7 @@
namespace {
struct VertexAttributes {
- float position[3];
+ float position[2];
float texcoord[2];
};
} // namespace
@@ -47,48 +47,22 @@
graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
}
-DrawRectTexture::DrawRectTexture(GraphicsState* graphics_state,
- const BaseState& base_state,
- const math::RectF& rect,
- const backend::TextureEGL* texture,
- const math::Matrix3F& texcoord_transform,
- const base::Closure& draw_offscreen,
- const base::Closure& draw_onscreen)
- : DrawObject(base_state),
- texcoord_transform_(texcoord_transform),
- rect_(rect),
- texture_(texture),
- draw_offscreen_(draw_offscreen),
- draw_onscreen_(draw_onscreen),
- vertex_buffer_(NULL),
- tile_texture_(false) {
- graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
-}
-
-void DrawRectTexture::ExecuteOffscreenRasterize(
- GraphicsState* graphics_state,
- ShaderProgramManager* program_manager) {
- if (!draw_offscreen_.is_null()) {
- draw_offscreen_.Run();
- }
-}
-
-void DrawRectTexture::ExecuteOnscreenUpdateVertexBuffer(
+void DrawRectTexture::ExecuteUpdateVertexBuffer(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
VertexAttributes attributes[4] = {
- { { rect_.x(), rect_.bottom(), base_state_.depth }, // uv = (0,1)
+ { { rect_.x(), rect_.bottom() }, // uv = (0,1)
{ texcoord_transform_(0, 1) + texcoord_transform_(0, 2),
texcoord_transform_(1, 1) + texcoord_transform_(1, 2) } },
- { { rect_.right(), rect_.bottom(), base_state_.depth }, // uv = (1,1)
+ { { rect_.right(), rect_.bottom() }, // uv = (1,1)
{ texcoord_transform_(0, 0) + texcoord_transform_(0, 1) +
texcoord_transform_(0, 2),
texcoord_transform_(1, 0) + texcoord_transform_(1, 1) +
texcoord_transform_(1, 2) } },
- { { rect_.right(), rect_.y(), base_state_.depth }, // uv = (1,0)
+ { { rect_.right(), rect_.y() }, // uv = (1,0)
{ texcoord_transform_(0, 0) + texcoord_transform_(0, 2),
texcoord_transform_(1, 0) + texcoord_transform_(1, 2) } },
- { { rect_.x(), rect_.y(), base_state_.depth }, // uv = (0,0)
+ { { rect_.x(), rect_.y() }, // uv = (0,0)
{ texcoord_transform_(0, 2), texcoord_transform_(1, 2) } },
};
COMPILE_ASSERT(sizeof(attributes) == 4 * sizeof(VertexAttributes),
@@ -104,13 +78,9 @@
}
}
-void DrawRectTexture::ExecuteOnscreenRasterize(
+void DrawRectTexture::ExecuteRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- if (!draw_onscreen_.is_null()) {
- draw_onscreen_.Run();
- }
-
ShaderProgram<ShaderVertexTexcoord,
ShaderFragmentTexcoord>* program;
program_manager->GetProgram(&program);
@@ -123,7 +93,7 @@
graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
base_state_.scissor.width(), base_state_.scissor.height());
graphics_state->VertexAttribPointer(
- program->GetVertexShader().a_position(), 3, GL_FLOAT, GL_FALSE,
+ program->GetVertexShader().a_position(), 2, GL_FLOAT, GL_FALSE,
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, position));
graphics_state->VertexAttribPointer(
@@ -148,6 +118,11 @@
}
}
+base::TypeId DrawRectTexture::GetTypeId() const {
+ return ShaderProgram<ShaderVertexTexcoord,
+ ShaderFragmentTexcoord>::GetTypeId();
+}
+
} // namespace egl
} // namespace rasterizer
} // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
index a0a284b..18363a8 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
@@ -35,27 +35,16 @@
const backend::TextureEGL* texture,
const math::Matrix3F& texcoord_transform);
- DrawRectTexture(GraphicsState* graphics_state,
- const BaseState& base_state,
- const math::RectF& rect,
- const backend::TextureEGL* texture,
- const math::Matrix3F& texcoord_transform,
- const base::Closure& draw_offscreen,
- const base::Closure& draw_onscreen);
-
- void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
+ void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
- void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
+ void ExecuteRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
- void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
- ShaderProgramManager* program_manager) OVERRIDE;
+ base::TypeId GetTypeId() const OVERRIDE;
private:
math::Matrix3F texcoord_transform_;
math::RectF rect_;
const backend::TextureEGL* texture_;
- base::Closure draw_offscreen_;
- base::Closure draw_onscreen_;
uint8_t* vertex_buffer_;
bool tile_texture_;
diff --git a/src/cobalt/renderer/rasterizer/egl/graphics_state.cc b/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
index 3c2d1e9..d97d12a 100644
--- a/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
+++ b/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
@@ -23,8 +23,28 @@
namespace rasterizer {
namespace egl {
+namespace {
+
+void GLViewport(const math::Rect& viewport, const math::Size& target_size) {
+ // Incoming origin is top-left, but GL origin is bottom-left, so flip
+ // vertically.
+ GL_CALL(glViewport(viewport.x(), target_size.height() - viewport.bottom(),
+ viewport.width(), viewport.height()));
+}
+
+void GLScissor(const math::Rect& scissor, const math::Size& target_size) {
+ // Incoming origin is top-left, but GL origin is bottom-left, so flip
+ // vertically.
+ GL_CALL(glScissor(scissor.x(), target_size.height() - scissor.bottom(),
+ scissor.width(), scissor.height()));
+}
+
+} // namespace
+
GraphicsState::GraphicsState()
- : frame_index_(0),
+ : render_target_handle_(0),
+ render_target_serial_(0),
+ frame_index_(0),
vertex_data_reserved_(kVertexDataAlignment - 1),
vertex_data_allocated_(0),
vertex_data_buffer_updated_(false) {
@@ -32,12 +52,9 @@
memset(clip_adjustment_, 0, sizeof(clip_adjustment_));
SetDirty();
blend_enabled_ = false;
- depth_test_enabled_ = false;
- depth_write_enabled_ = true;
Reset();
// These settings should only need to be set once. Nothing should touch them.
- GL_CALL(glDepthRangef(0.0f, 1.0f));
GL_CALL(glDisable(GL_DITHER));
GL_CALL(glDisable(GL_CULL_FACE));
GL_CALL(glDisable(GL_STENCIL_TEST));
@@ -63,8 +80,6 @@
// cached state when needed.
SetDirty();
blend_enabled_ = false;
- depth_test_enabled_ = false;
- depth_write_enabled_ = true;
}
void GraphicsState::EndFrame() {
@@ -77,8 +92,6 @@
// Force default GL state. The current state may be marked dirty, so don't
// rely on any functions which check the cached state before issuing GL calls.
GL_CALL(glDisable(GL_BLEND));
- GL_CALL(glDisable(GL_DEPTH_TEST));
- GL_CALL(glDepthMask(GL_TRUE));
GL_CALL(glUseProgram(0));
// Since the GL state was changed without going through the cache, mark it
@@ -87,13 +100,16 @@
}
void GraphicsState::Clear() {
+ Clear(0.0f, 0.0f, 0.0f, 0.0f);
+}
+
+void GraphicsState::Clear(float r, float g, float b, float a) {
if (state_dirty_) {
Reset();
}
- GL_CALL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
- GL_CALL(glClearDepthf(FarthestDepth()));
- GL_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
+ GL_CALL(glClearColor(r, g, b, a));
+ GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
}
void GraphicsState::UseProgram(GLuint program) {
@@ -116,25 +132,48 @@
disable_vertex_attrib_array_mask_ = enabled_vertex_attrib_array_mask_;
}
+void GraphicsState::BindFramebuffer(
+ const backend::RenderTarget* render_target) {
+ if (render_target == nullptr) {
+ // Unbind the framebuffer immediately.
+ if (render_target_handle_ != 0) {
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+ }
+ render_target_handle_ = 0;
+ render_target_serial_ = 0;
+ render_target_size_.SetSize(0, 0);
+ SetClipAdjustment();
+ } else if (render_target->GetPlatformHandle() != render_target_handle_ ||
+ render_target->GetSerialNumber() != render_target_serial_ ||
+ render_target->GetSize() != render_target_size_) {
+ render_target_handle_ = render_target->GetPlatformHandle();
+ render_target_serial_ = render_target->GetSerialNumber();
+ render_target_size_ = render_target->GetSize();
+ SetClipAdjustment();
+
+ if (!state_dirty_) {
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, render_target_handle_));
+ }
+ }
+}
+
void GraphicsState::Viewport(int x, int y, int width, int height) {
- // Incoming origin is top-left, but GL origin is bottom-left, so flip
- // vertically.
- if (state_dirty_ || viewport_.x() != x || viewport_.y() != y ||
+ if (viewport_.x() != x || viewport_.y() != y ||
viewport_.width() != width || viewport_.height() != height) {
viewport_.SetRect(x, y, width, height);
- GL_CALL(glViewport(x, render_target_size_.height() - y - height,
- width, height));
+ if (!state_dirty_) {
+ GLViewport(viewport_, render_target_size_);
+ }
}
}
void GraphicsState::Scissor(int x, int y, int width, int height) {
- // Incoming origin is top-left, but GL origin is bottom-left, so flip
- // vertically.
- if (state_dirty_ || scissor_.x() != x || scissor_.y() != y ||
+ if (scissor_.x() != x || scissor_.y() != y ||
scissor_.width() != width || scissor_.height() != height) {
scissor_.SetRect(x, y, width, height);
- GL_CALL(glScissor(x, render_target_size_.height() - y - height,
- width, height));
+ if (!state_dirty_) {
+ GLScissor(scissor_, render_target_size_);
+ }
}
}
@@ -152,38 +191,6 @@
}
}
-void GraphicsState::EnableDepthTest() {
- if (!depth_test_enabled_) {
- depth_test_enabled_ = true;
- GL_CALL(glEnable(GL_DEPTH_TEST));
- }
-}
-
-void GraphicsState::DisableDepthTest() {
- if (depth_test_enabled_) {
- depth_test_enabled_ = false;
- GL_CALL(glDisable(GL_DEPTH_TEST));
- }
-}
-
-void GraphicsState::EnableDepthWrite() {
- if (!depth_write_enabled_) {
- depth_write_enabled_ = true;
- GL_CALL(glDepthMask(GL_TRUE));
- }
-}
-
-void GraphicsState::DisableDepthWrite() {
- if (depth_write_enabled_) {
- depth_write_enabled_ = false;
- GL_CALL(glDepthMask(GL_FALSE));
- }
-}
-
-void GraphicsState::ResetDepthFunc() {
- GL_CALL(glDepthFunc(GL_LESS));
-}
-
void GraphicsState::ActiveBindTexture(GLenum texture_unit, GLenum target,
GLuint texture) {
int texunit_index = texture_unit - GL_TEXTURE0;
@@ -228,26 +235,25 @@
GL_CALL(glTexParameteri(target, GL_TEXTURE_WRAP_T, texture_wrap_mode));
}
-void GraphicsState::SetClipAdjustment(const math::Size& render_target_size) {
- render_target_size_ = render_target_size;
+void GraphicsState::SetClipAdjustment() {
clip_adjustment_dirty_ = true;
// Clip adjustment is a vec4 used to transform a given 2D position from view
// space to clip space. Given a 2D position, pos, the output is:
// output = pos * clip_adjustment_.xy + clip_adjustment_.zw
- if (render_target_size.width() > 0) {
- clip_adjustment_[0] = 2.0f / render_target_size.width();
+ if (render_target_size_.width() > 0) {
+ clip_adjustment_[0] = 2.0f / render_target_size_.width();
clip_adjustment_[2] = -1.0f;
} else {
clip_adjustment_[0] = 0.0f;
clip_adjustment_[2] = 0.0f;
}
- if (render_target_size.height() > 0) {
+ if (render_target_size_.height() > 0) {
// Incoming origin is top-left, but GL origin is bottom-left, so flip the
// image vertically.
- clip_adjustment_[1] = -2.0f / render_target_size.height();
+ clip_adjustment_[1] = -2.0f / render_target_size_.height();
clip_adjustment_[3] = 1.0f;
} else {
clip_adjustment_[1] = 0.0f;
@@ -337,31 +343,12 @@
}
}
-// static
-float GraphicsState::FarthestDepth() {
- return 1.0f;
-}
-
-// static
-float GraphicsState::NextClosestDepth(float depth) {
- // Our vertex shaders pass depth straight to gl_Position without any
- // transformation, and gl_Position.w is always 1. To avoid clipping,
- // |depth| should be [-1,1]. However, this range is then converted to [0,1],
- // then scaled by (2^N - 1) to be the final value in the depth buffer, where
- // N = number of bits in the depth buffer.
-
- // In the worst case, we're using a 16-bit depth buffer. So each step in
- // depth is 2 / (2^N - 1) (since depth is converted from [-1,1] to [0,1]).
- // Also, because the default depth compare function is GL_LESS, vertices with
- // smaller depth values appear on top of others.
- return depth - 2.0f / 65535.0f;
-}
-
void GraphicsState::Reset() {
program_ = 0;
- Viewport(viewport_.x(), viewport_.y(), viewport_.width(), viewport_.height());
- Scissor(scissor_.x(), scissor_.y(), scissor_.width(), scissor_.height());
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, render_target_handle_));
+ GLViewport(viewport_, render_target_size_);
+ GLScissor(scissor_, render_target_size_);
GL_CALL(glEnable(GL_SCISSOR_TEST));
array_buffer_handle_ = 0;
@@ -384,13 +371,7 @@
}
GL_CALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
- if (depth_test_enabled_) {
- GL_CALL(glEnable(GL_DEPTH_TEST));
- } else {
- GL_CALL(glDisable(GL_DEPTH_TEST));
- }
- GL_CALL(glDepthMask(depth_write_enabled_ ? GL_TRUE : GL_FALSE));
- ResetDepthFunc();
+ GL_CALL(glDisable(GL_DEPTH_TEST));
state_dirty_ = false;
}
diff --git a/src/cobalt/renderer/rasterizer/egl/graphics_state.h b/src/cobalt/renderer/rasterizer/egl/graphics_state.h
index a8c48f4..dcc8c3c 100644
--- a/src/cobalt/renderer/rasterizer/egl/graphics_state.h
+++ b/src/cobalt/renderer/rasterizer/egl/graphics_state.h
@@ -22,6 +22,7 @@
#include "cobalt/math/matrix3_f.h"
#include "cobalt/math/rect.h"
#include "cobalt/math/size.h"
+#include "cobalt/renderer/backend/render_target.h"
namespace cobalt {
namespace renderer {
@@ -47,16 +48,27 @@
// Clear the color and depth buffers.
void Clear();
+ void Clear(float r, float g, float b, float a);
// Set the current shader program to be used.
void UseProgram(GLuint program);
GLuint GetProgram() const { return program_; }
- // Set the viewport.
+ // Bind the specified framebuffer. This only goes through glBindFramebuffer
+ // and does not call eglMakeCurrent. This also SetClipAdjustment() to the
+ // render target's dimensions.
+ // |render_target| may be null to unbind the current framebuffer.
+ // NOTE: Be sure to call Viewport() and Scissor() after binding a new
+ // framebuffer.
+ void BindFramebuffer(const backend::RenderTarget* render_target);
+
+ // Set the viewport. If changing render targets, then be sure to
+ // BindFramebuffer() before calling this.
void Viewport(int x, int y, int width, int height);
const math::Rect& GetViewport() const { return viewport_; }
- // Set the scissor box.
+ // Set the scissor box. If changing render targets, then be sure to
+ // BindFramebuffer() before calling this.
void Scissor(int x, int y, int width, int height);
const math::Rect& GetScissor() const { return scissor_; }
@@ -66,21 +78,6 @@
void DisableBlend();
bool IsBlendEnabled() const { return blend_enabled_; }
- // Control depth testing.
- // Default = enabled.
- void EnableDepthTest();
- void DisableDepthTest();
- bool IsDepthTestEnabled() const { return depth_test_enabled_; }
-
- // Control writing to the depth buffer.
- // Default = enabled.
- void EnableDepthWrite();
- void DisableDepthWrite();
- bool IsDepthWriteEnabled() const { return depth_write_enabled_; }
-
- // Reset to the default depth function.
- void ResetDepthFunc();
-
// Bind a texture to a given texture unit. Combines glActiveTexture and
// glBindTexture.
void ActiveBindTexture(GLenum texture_unit, GLenum target, GLuint texture);
@@ -90,10 +87,6 @@
void ActiveBindTexture(GLenum texture_unit, GLenum target, GLuint texture,
GLint texture_wrap_mode);
- // Set the clip adjustment to be used with vertex shaders. This transforms
- // the vertex coordinates from view space to clip space.
- void SetClipAdjustment(const math::Size& render_target_size);
-
// Update the GPU with the current clip adjustment settings.
void UpdateClipAdjustment(GLint handle);
@@ -125,19 +118,19 @@
// VertexAttribPointer), but the current program does not.
void VertexAttribFinish();
- // Return the farthest depth value.
- static float FarthestDepth();
-
- // Return the next closest depth value. Vertices using the output depth are
- // guaranteed to render on top of vertices using the incoming depth.
- static float NextClosestDepth(float depth);
-
private:
void Reset();
+ // Set the clip adjustment to be used with vertex shaders. This transforms
+ // the vertex coordinates from view space to clip space.
+ void SetClipAdjustment();
+
math::Rect viewport_;
math::Rect scissor_;
+
math::Size render_target_size_;
+ GLuint render_target_handle_;
+ int32_t render_target_serial_;
GLuint program_;
GLuint array_buffer_handle_;
@@ -145,11 +138,10 @@
uint32_t enabled_vertex_attrib_array_mask_;
uint32_t disable_vertex_attrib_array_mask_;
float clip_adjustment_[4];
+
bool clip_adjustment_dirty_;
bool state_dirty_;
bool blend_enabled_;
- bool depth_test_enabled_;
- bool depth_write_enabled_;
static const int kNumTextureUnitsCached = 8;
GLenum texunit_target_[kNumTextureUnitsCached];
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
index 10c3a9d..336efc7 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
@@ -20,6 +20,7 @@
#include "base/debug/trace_event.h"
#include "base/memory/scoped_vector.h"
#include "base/threading/thread_checker.h"
+#include "cobalt/render_tree/filter_node.h"
#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
#include "cobalt/renderer/backend/egl/graphics_system.h"
@@ -33,6 +34,8 @@
#include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
#include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrContext.h"
namespace cobalt {
@@ -56,8 +59,8 @@
void SubmitToFallbackRasterizer(
const scoped_refptr<render_tree::Node>& render_tree,
- const math::Matrix3F& transform,
- const OffscreenTargetManager::TargetInfo& target);
+ SkCanvas* fallback_render_target, const math::Matrix3F& transform,
+ const math::RectF& scissor, float opacity, uint32_t rasterize_flags);
render_tree::ResourceProvider* GetResourceProvider() {
return fallback_rasterizer_->GetResourceProvider();
@@ -69,9 +72,14 @@
}
void ResetFallbackContextDuringFrame();
+ void FlushFallbackOffscreenDraws();
void RasterizeTree(const scoped_refptr<render_tree::Node>& render_tree,
- backend::RenderTargetEGL* render_target);
+ backend::RenderTargetEGL* render_target,
+ const math::Rect& content_rect);
+
+ SkSurface* CreateFallbackSurface(
+ const backend::RenderTarget* render_target);
scoped_ptr<skia::HardwareRasterizer> fallback_rasterizer_;
scoped_ptr<GraphicsState> graphics_state_;
@@ -103,7 +111,10 @@
graphics_state_.reset(new GraphicsState());
shader_program_manager_.reset(new ShaderProgramManager());
offscreen_target_manager_.reset(new OffscreenTargetManager(
- graphics_context_, GetFallbackContext(), surface_cache_size_in_bytes));
+ graphics_context_,
+ base::Bind(&HardwareRasterizer::Impl::CreateFallbackSurface,
+ base::Unretained(this)),
+ surface_cache_size_in_bytes));
}
HardwareRasterizer::Impl::~Impl() {
@@ -128,73 +139,59 @@
backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_, render_target_egl);
- // Make sure this render target has a depth buffer. This is only relevant
- // for framebuffer render targets. Other render target types should already
- // have a depth buffer set up by the graphics system's config.
- if (render_target_egl->GetSurface() == EGL_NO_SURFACE) {
- backend::FramebufferRenderTargetEGL* framebuffer_render_target =
- base::polymorphic_downcast<backend::FramebufferRenderTargetEGL*>(
- render_target_egl);
- framebuffer_render_target->EnsureDepthBufferAttached(GL_DEPTH_COMPONENT16);
- }
-
fallback_rasterizer_->AdvanceFrame();
- const math::Size& target_size = render_target->GetSize();
- graphics_state_->SetClipAdjustment(target_size);
- graphics_state_->Viewport(0, 0, target_size.width(), target_size.height());
-
// Update only the dirty pixels if the render target contents are preserved
// between frames.
+ math::Rect content_rect(render_target->GetSize());
if (options.dirty && render_target_egl->ContentWasPreservedAfterSwap()) {
- graphics_state_->Scissor(options.dirty->x(), options.dirty->y(),
- options.dirty->width(), options.dirty->height());
- } else {
- graphics_state_->Scissor(0, 0, target_size.width(), target_size.height());
+ content_rect = *options.dirty;
}
- offscreen_target_manager_->Update(target_size);
+ offscreen_target_manager_->Update(render_target->GetSize());
- RasterizeTree(render_tree, render_target_egl);
+ RasterizeTree(render_tree, render_target_egl, content_rect);
graphics_context_->SwapBuffers(render_target_egl);
}
void HardwareRasterizer::Impl::SubmitToFallbackRasterizer(
const scoped_refptr<render_tree::Node>& render_tree,
- const math::Matrix3F& transform,
- const OffscreenTargetManager::TargetInfo& target) {
+ SkCanvas* fallback_render_target, const math::Matrix3F& transform,
+ const math::RectF& scissor, float opacity, uint32_t rasterize_flags) {
DCHECK(thread_checker_.CalledOnValidThread());
TRACE_EVENT0("cobalt::renderer", "SubmitToFallbackRasterizer");
// Use skia to rasterize to the allocated offscreen target.
- target.skia_canvas->save();
+ fallback_render_target->save();
- if (target.is_scratch_surface) {
- // The scratch surface is used immediately after rendering to it. So we
- // are switching from this rasterizer to skia, then will switch back to
- // our rasterizer context.
- ResetFallbackContextDuringFrame();
- target.skia_canvas->clear(SK_ColorTRANSPARENT);
+ fallback_render_target->clipRect(SkRect::MakeXYWH(
+ scissor.x(), scissor.y(), scissor.width(), scissor.height()));
+ fallback_render_target->concat(skia::CobaltMatrixToSkia(transform));
+
+ if ((rasterize_flags & RenderTreeNodeVisitor::kFallbackShouldClear) != 0) {
+ fallback_render_target->clear(SK_ColorTRANSPARENT);
}
- target.skia_canvas->clipRect(SkRect::MakeXYWH(
- target.region.x(), target.region.y(),
- target.region.width(), target.region.height()));
- target.skia_canvas->translate(target.region.x(), target.region.y());
- target.skia_canvas->concat(skia::CobaltMatrixToSkia(transform));
- fallback_rasterizer_->SubmitOffscreen(render_tree, target.skia_canvas);
-
- if (target.is_scratch_surface) {
- // Flush the skia draw calls so the contents can be used immediately.
- target.skia_canvas->flush();
-
- // Switch back to the current render target and context.
- graphics_context_->ResetCurrentSurface();
- graphics_state_->SetDirty();
+ if (opacity < 1.0f) {
+ scoped_refptr<render_tree::Node> opacity_node =
+ new render_tree::FilterNode(render_tree::OpacityFilter(opacity),
+ render_tree);
+ fallback_rasterizer_->SubmitOffscreen(opacity_node, fallback_render_target);
+ } else {
+ fallback_rasterizer_->SubmitOffscreen(render_tree, fallback_render_target);
}
- target.skia_canvas->restore();
+ if ((rasterize_flags & RenderTreeNodeVisitor::kFallbackShouldFlush) != 0) {
+ fallback_render_target->flush();
+ }
+
+ fallback_render_target->restore();
+}
+
+void HardwareRasterizer::Impl::FlushFallbackOffscreenDraws() {
+ TRACE_EVENT0("cobalt::renderer", "Skia Flush");
+ offscreen_target_manager_->Flush();
}
void HardwareRasterizer::Impl::ResetFallbackContextDuringFrame() {
@@ -202,27 +199,28 @@
// states that this rasterizer pollutes.
uint32_t untouched_states = kMSAAEnable_GrGLBackendState |
kStencil_GrGLBackendState | kPixelStore_GrGLBackendState |
- kFixedFunction_GrGLBackendState | kPathRendering_GrGLBackendState;
-
- // Manually reset a subset of kMisc_GrGLBackendState
- untouched_states |= kMisc_GrGLBackendState;
- GL_CALL(glDisable(GL_DEPTH_TEST));
- GL_CALL(glDepthMask(GL_FALSE));
+ kFixedFunction_GrGLBackendState | kPathRendering_GrGLBackendState |
+ kMisc_GrGLBackendState;
GetFallbackContext()->resetContext(~untouched_states & kAll_GrBackendState);
}
void HardwareRasterizer::Impl::RasterizeTree(
const scoped_refptr<render_tree::Node>& render_tree,
- backend::RenderTargetEGL* render_target) {
- DrawObjectManager draw_object_manager;
- RenderTreeNodeVisitor::FallbackRasterizeFunction fallback_rasterize =
+ backend::RenderTargetEGL* render_target,
+ const math::Rect& content_rect) {
+ DrawObjectManager draw_object_manager(
+ base::Bind(&HardwareRasterizer::Impl::ResetFallbackContextDuringFrame,
+ base::Unretained(this)),
+ base::Bind(&HardwareRasterizer::Impl::FlushFallbackOffscreenDraws,
+ base::Unretained(this)));
+ RenderTreeNodeVisitor visitor(
+ graphics_state_.get(), &draw_object_manager,
+ offscreen_target_manager_.get(),
base::Bind(&HardwareRasterizer::Impl::SubmitToFallbackRasterizer,
- base::Unretained(this));
- RenderTreeNodeVisitor visitor(graphics_state_.get(),
- &draw_object_manager,
- offscreen_target_manager_.get(),
- &fallback_rasterize);
+ base::Unretained(this)),
+ fallback_rasterizer_->GetCachedCanvas(render_target),
+ render_target, content_rect);
// Traverse the render tree to populate the draw object manager.
{
@@ -235,42 +233,53 @@
// Rasterize to offscreen targets using skia.
{
TRACE_EVENT0("cobalt::renderer", "OffscreenRasterize");
- backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
- graphics_context_, render_target);
- // Reset the skia graphics context since the egl rasterizer dirtied it.
+ // Ensure the skia context is fully reset.
GetFallbackContext()->resetContext();
draw_object_manager.ExecuteOffscreenRasterize(graphics_state_.get(),
shader_program_manager_.get());
-
- {
- TRACE_EVENT0("cobalt::renderer", "Skia Flush");
- offscreen_target_manager_->Flush();
- }
-
- // Reset the egl graphics state since skia dirtied it.
- graphics_state_->SetDirty();
- GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
}
+ // Clear the dirty region of the render target.
+ graphics_state_->BindFramebuffer(render_target);
+ graphics_state_->Viewport(0, 0,
+ render_target->GetSize().width(),
+ render_target->GetSize().height());
+ graphics_state_->Scissor(content_rect.x(), content_rect.y(),
+ content_rect.width(), content_rect.height());
graphics_state_->Clear();
{
- TRACE_EVENT0("cobalt::renderer", "OnscreenUpdateVertexBuffer");
- draw_object_manager.ExecuteOnscreenUpdateVertexBuffer(graphics_state_.get(),
- shader_program_manager_.get());
- graphics_state_->UpdateVertexData();
- }
-
- {
TRACE_EVENT0("cobalt::renderer", "OnscreenRasterize");
draw_object_manager.ExecuteOnscreenRasterize(graphics_state_.get(),
shader_program_manager_.get());
}
+ graphics_context_->ResetCurrentSurface();
graphics_state_->EndFrame();
}
+SkSurface* HardwareRasterizer::Impl::CreateFallbackSurface(
+ const backend::RenderTarget* render_target) {
+ // Wrap the given render target in a new skia surface.
+ GrBackendRenderTargetDesc skia_desc;
+ skia_desc.fWidth = render_target->GetSize().width();
+ skia_desc.fHeight = render_target->GetSize().height();
+ skia_desc.fConfig = kRGBA_8888_GrPixelConfig;
+ skia_desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+ skia_desc.fSampleCnt = 0;
+ skia_desc.fStencilBits = 0;
+ skia_desc.fRenderTargetHandle = render_target->GetPlatformHandle();
+
+ SkAutoTUnref<GrRenderTarget> skia_render_target(
+ GetFallbackContext()->wrapBackendRenderTarget(skia_desc));
+ SkSurfaceProps skia_surface_props(
+ SkSurfaceProps::kUseDistanceFieldFonts_Flag,
+ SkSurfaceProps::kLegacyFontHost_InitType);
+ return SkSurface::NewRenderTargetDirect(
+ skia_render_target, &skia_surface_props);
+}
+
HardwareRasterizer::HardwareRasterizer(
backend::GraphicsContext* graphics_context, int skia_atlas_width,
int skia_atlas_height, int skia_cache_size_in_bytes,
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
index a530a84..d3feebb 100644
--- a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
@@ -19,10 +19,6 @@
#include "base/hash_tables.h"
#include "cobalt/renderer/rasterizer/egl/rect_allocator.h"
#include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/core/SkSurfaceProps.h"
-#include "third_party/skia/include/gpu/GrRenderTarget.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
namespace {
// Structure describing the key for render target allocations in a given
@@ -111,16 +107,17 @@
RectAllocator allocator;
AllocationMap allocation_map;
size_t allocations_used;
- scoped_ptr<backend::FramebufferEGL> framebuffer;
+ scoped_refptr<backend::FramebufferRenderTargetEGL> framebuffer;
SkAutoTUnref<SkSurface> skia_surface;
bool needs_flush;
};
OffscreenTargetManager::OffscreenTargetManager(
- backend::GraphicsContextEGL* graphics_context, GrContext* skia_context,
+ backend::GraphicsContextEGL* graphics_context,
+ const CreateFallbackSurfaceFunction& create_fallback_surface,
size_t memory_limit)
: graphics_context_(graphics_context),
- skia_context_(skia_context),
+ create_fallback_surface_(create_fallback_surface),
offscreen_target_size_mask_(0, 0),
memory_limit_(memory_limit) {
}
@@ -185,7 +182,6 @@
out_target_info->framebuffer = offscreen_cache_->framebuffer.get();
out_target_info->skia_canvas = offscreen_cache_->skia_surface->getCanvas();
out_target_info->region = iter->second;
- out_target_info->is_scratch_surface = false;
return true;
}
return false;
@@ -228,12 +224,11 @@
}
}
- // Use the scratch surface if needed.
- bool scratch_surface_used = false;
if (target_rect.IsEmpty()) {
- scratch_surface_used = true;
- atlas = scratch_surface_.get();
- target_rect = math::Rect(atlas->framebuffer->GetSize());
+ // There wasn't enough room for the requested offscreen target.
+ out_target_info->framebuffer = nullptr;
+ out_target_info->skia_canvas = nullptr;
+ out_target_info->region.SetRect(0, 0, 0, 0);
} else {
// Inset to prevent interpolation with unwanted pixels at the edge.
target_rect.Inset(kInterpolatePad, kInterpolatePad);
@@ -247,12 +242,11 @@
AllocationKey(node, size), target_rect));
atlas->allocations_used += 1;
atlas->needs_flush = true;
- }
- out_target_info->framebuffer = atlas->framebuffer.get();
- out_target_info->skia_canvas = atlas->skia_surface->getCanvas();
- out_target_info->region = target_rect;
- out_target_info->is_scratch_surface = scratch_surface_used;
+ out_target_info->framebuffer = atlas->framebuffer.get();
+ out_target_info->skia_canvas = atlas->skia_surface->getCanvas();
+ out_target_info->region = target_rect;
+ }
}
void OffscreenTargetManager::InitializeTargets(const math::Size& frame_size) {
@@ -266,43 +260,34 @@
offscreen_target_size_mask_.SetSize(0, 0);
}
- math::Size max_size(NextPowerOf2(frame_size.width()) / 2,
- NextPowerOf2(frame_size.height()) / 2);
- max_size.SetToMax(math::Size(1, 1));
+ // Allow offscreen targets to be as large as the frame.
+ math::Size max_size(std::max(frame_size.width(), 1),
+ std::max(frame_size.height(), 1));
- // A full-screen scratch surface is required. This avoids pixelation when an
- // offscreen target is needed.
- scratch_surface_.reset(CreateOffscreenAtlas(frame_size));
-
- // Other offscreen render targets are optional but highly recommended. These
+ // Offscreen render targets are optional but highly recommended. These
// allow caching of render results for improved performance. At least two
// must exist -- one for the cache and the other a working scratch.
size_t half_memory_limit = memory_limit_ / 2;
math::Size atlas_size(1, 1);
for (;;) {
- if (atlas_size == max_size) {
- break;
- }
- if (GetMemorySize(atlas_size) * 2 <= half_memory_limit) {
- // Memory limit allows a bigger atlas.
- if (atlas_size.width() <= atlas_size.height()) {
- // Prefer growing by width.
- if (atlas_size.width() < max_size.width()) {
- atlas_size.set_width(atlas_size.width() * 2);
- } else {
- atlas_size.set_height(atlas_size.height() * 2);
- }
- } else {
- // Prefer growing by height.
- if (atlas_size.height() < max_size.height()) {
- atlas_size.set_height(atlas_size.height() * 2);
- } else {
- atlas_size.set_width(atlas_size.width() * 2);
- }
- }
+ // See if the next atlas size will fit in the memory budget.
+ // Try to keep the atlas square-ish.
+ math::Size next_size(atlas_size);
+ if (atlas_size.width() < max_size.width() &&
+ (atlas_size.width() <= atlas_size.height() ||
+ atlas_size.height() >= max_size.height())) {
+ next_size.set_width(
+ std::min(atlas_size.width() * 2, max_size.width()));
+ } else if (atlas_size.height() < max_size.height()) {
+ next_size.set_height(
+ std::min(atlas_size.height() * 2, max_size.height()));
} else {
break;
}
+ if (GetMemorySize(next_size) > half_memory_limit) {
+ break;
+ }
+ atlas_size = next_size;
}
// It is better to have fewer, large atlases than many small atlases to
@@ -335,26 +320,11 @@
OffscreenAtlas* atlas = new OffscreenAtlas(size);
// Create a new framebuffer.
- atlas->framebuffer.reset(new backend::FramebufferEGL(
- graphics_context_, size, GL_RGBA, GL_NONE));
+ atlas->framebuffer = new backend::FramebufferRenderTargetEGL(
+ graphics_context_, size);
// Wrap the framebuffer as a skia surface.
- GrBackendRenderTargetDesc skia_desc;
- skia_desc.fWidth = size.width();
- skia_desc.fHeight = size.height();
- skia_desc.fConfig = kRGBA_8888_GrPixelConfig;
- skia_desc.fOrigin = kTopLeft_GrSurfaceOrigin;
- skia_desc.fSampleCnt = 0;
- skia_desc.fStencilBits = 0;
- skia_desc.fRenderTargetHandle = atlas->framebuffer->gl_handle();
-
- SkAutoTUnref<GrRenderTarget> skia_render_target(
- skia_context_->wrapBackendRenderTarget(skia_desc));
- SkSurfaceProps skia_surface_props(
- SkSurfaceProps::kUseDistanceFieldFonts_Flag,
- SkSurfaceProps::kLegacyFontHost_InitType);
- atlas->skia_surface.reset(SkSurface::NewRenderTargetDirect(
- skia_render_target, &skia_surface_props));
+ atlas->skia_surface.reset(create_fallback_surface_.Run(atlas->framebuffer));
return atlas;
}
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
index 5d23334..02c0704 100644
--- a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
@@ -15,15 +15,16 @@
#ifndef COBALT_RENDERER_RASTERIZER_EGL_OFFSCREEN_TARGET_MANAGER_H_
#define COBALT_RENDERER_RASTERIZER_EGL_OFFSCREEN_TARGET_MANAGER_H_
+#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "cobalt/math/rect_f.h"
#include "cobalt/math/size.h"
#include "cobalt/render_tree/node.h"
-#include "cobalt/renderer/backend/egl/framebuffer.h"
+#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/core/SkSurface.h"
namespace cobalt {
namespace renderer {
@@ -35,19 +36,21 @@
// minimize the cost of switching render targets.
class OffscreenTargetManager {
public:
+ typedef base::Callback<SkSurface*(const backend::RenderTarget*)>
+ CreateFallbackSurfaceFunction;
+
struct TargetInfo {
TargetInfo()
: framebuffer(NULL),
- skia_canvas(NULL),
- is_scratch_surface(false) {}
- backend::FramebufferEGL* framebuffer;
+ skia_canvas(NULL) {}
+ backend::FramebufferRenderTargetEGL* framebuffer;
SkCanvas* skia_canvas;
math::RectF region;
- bool is_scratch_surface;
};
OffscreenTargetManager(backend::GraphicsContextEGL* graphics_context,
- GrContext* skia_context, size_t memory_limit);
+ const CreateFallbackSurfaceFunction& create_fallback_surface,
+ size_t memory_limit);
~OffscreenTargetManager();
// Update must be called once per frame, before any allocation requests are
@@ -79,11 +82,10 @@
OffscreenAtlas* CreateOffscreenAtlas(const math::Size& size);
backend::GraphicsContextEGL* graphics_context_;
- GrContext* skia_context_;
+ CreateFallbackSurfaceFunction create_fallback_surface_;
ScopedVector<OffscreenAtlas> offscreen_atlases_;
scoped_ptr<OffscreenAtlas> offscreen_cache_;
- scoped_ptr<OffscreenAtlas> scratch_surface_;
// Align offscreen targets to a particular size to more efficiently use the
// offscreen target atlas. Use a power of 2 for the alignment so that a bit
diff --git a/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp b/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
index 6ff941a..42e592e 100644
--- a/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
@@ -37,8 +37,10 @@
'type': 'static_library',
'sources': [
- 'draw_depth_stencil.h',
- 'draw_depth_stencil.cc',
+ 'draw_callback.h',
+ 'draw_callback.cc',
+ 'draw_clear.h',
+ 'draw_clear.cc',
'draw_object.h',
'draw_object.cc',
'draw_object_manager.h',
diff --git a/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc b/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc
index 2ebb99e..90d8d44 100644
--- a/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc
+++ b/src/cobalt/renderer/rasterizer/egl/rect_allocator.cc
@@ -26,11 +26,10 @@
// blocks at the end of the free list. Allocations will use the block with
// the smallest area of sufficient dimensions in order to minimize waste.
bool FirstRectIsBigger(const math::Rect& a, const math::Rect& b) {
- const float area_a = a.width() * a.height();
- const float area_b = b.width() * b.height();
- return (area_a > area_b) ||
- (area_a == area_b && (a.width() > b.width() ||
- a.height() > b.height()));
+ const int area_a = a.width() * a.height();
+ const int area_b = b.width() * b.height();
+ return (area_a > area_b) || (area_a == area_b && (a.width() > b.width() ||
+ a.height() > b.height()));
}
} // namespace
@@ -82,8 +81,8 @@
if (memory.height() != allocation.height()) {
// Return bottom and right of memory block as unused.
// Preserve the largest block.
- const float remaining_width = memory.width() - allocation.width();
- const float remaining_height = memory.height() - allocation.height();
+ const int remaining_width = memory.width() - allocation.width();
+ const int remaining_height = memory.height() - allocation.height();
if (memory.width() * remaining_height >=
remaining_width * memory.height()) {
// Bottom portion is bigger.
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index 9622bb7..1685153 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -16,14 +16,18 @@
#include <algorithm>
#include <cmath>
+#include <limits>
#include "base/debug/trace_event.h"
+#include "base/logging.h"
#include "base/optional.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/base/type_id.h"
#include "cobalt/math/matrix3_f.h"
#include "cobalt/math/transform_2d.h"
#include "cobalt/renderer/rasterizer/common/utils.h"
+#include "cobalt/renderer/rasterizer/egl/draw_callback.h"
+#include "cobalt/renderer/rasterizer/egl/draw_clear.h"
#include "cobalt/renderer/rasterizer/egl/draw_poly_color.h"
#include "cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h"
#include "cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h"
@@ -40,6 +44,9 @@
namespace {
+const render_tree::ColorRGBA kOpaqueWhite(1.0f, 1.0f, 1.0f, 1.0f);
+const render_tree::ColorRGBA kTransparentBlack(0.0f, 0.0f, 0.0f, 0.0f);
+
math::Rect RoundRectFToInt(const math::RectF& input) {
int left = static_cast<int>(input.x() + 0.5f);
int right = static_cast<int>(input.right() + 0.5f);
@@ -55,11 +62,12 @@
math::Matrix3F GetTexcoordTransform(
const OffscreenTargetManager::TargetInfo& target) {
+ // Flip the texture vertically to accommodate OpenGL's bottom-left origin.
float scale_x = 1.0f / target.framebuffer->GetSize().width();
- float scale_y = 1.0f / target.framebuffer->GetSize().height();
+ float scale_y = -1.0f / target.framebuffer->GetSize().height();
return math::Matrix3F::FromValues(
target.region.width() * scale_x, 0, target.region.x() * scale_x,
- 0, target.region.height() * scale_y, target.region.y() * scale_y,
+ 0, target.region.height() * scale_y, 1.0f + target.region.y() * scale_y,
0, 0, 1);
}
@@ -68,16 +76,21 @@
RenderTreeNodeVisitor::RenderTreeNodeVisitor(GraphicsState* graphics_state,
DrawObjectManager* draw_object_manager,
OffscreenTargetManager* offscreen_target_manager,
- const FallbackRasterizeFunction* fallback_rasterize)
+ const FallbackRasterizeFunction& fallback_rasterize,
+ SkCanvas* fallback_render_target,
+ backend::RenderTarget* render_target,
+ const math::Rect& content_rect)
: graphics_state_(graphics_state),
draw_object_manager_(draw_object_manager),
offscreen_target_manager_(offscreen_target_manager),
- fallback_rasterize_(fallback_rasterize) {
- // Let the first draw object render in front of the clear depth.
- draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
-
- draw_state_.scissor.Intersect(graphics_state->GetViewport());
- draw_state_.scissor.Intersect(graphics_state->GetScissor());
+ fallback_rasterize_(fallback_rasterize),
+ fallback_render_target_(fallback_render_target),
+ render_target_(render_target),
+ render_target_is_offscreen_(false),
+ allow_offscreen_targets_(true),
+ failed_offscreen_target_request_(false),
+ last_draw_id_(0) {
+ draw_state_.scissor.Intersect(content_rect);
}
void RenderTreeNodeVisitor::Visit(
@@ -97,8 +110,10 @@
void RenderTreeNodeVisitor::Visit(
render_tree::MatrixTransform3DNode* transform_3d_node) {
- // TODO: Ignore the 3D transform matrix for now.
- transform_3d_node->data().source->Accept(this);
+ // This is used in conjunction with a map-to-mesh filter. If that filter is
+ // implemented natively, then this transform 3D must be handled natively
+ // as well. Otherwise, use the fallback rasterizer for both.
+ FallbackRasterize(transform_3d_node);
}
void RenderTreeNodeVisitor::Visit(
@@ -158,20 +173,46 @@
!data.viewport_filter &&
!data.blur_filter &&
!data.map_to_mesh_filter) {
- int opacity = static_cast<int>(data.opacity_filter->opacity() * 255.0f);
- if (opacity <= 0) {
+ const float filter_opacity = data.opacity_filter->opacity();
+ if (filter_opacity <= 0.0f) {
// Totally transparent. Ignore the source.
return;
- } else if (opacity >= 255) {
+ } else if (filter_opacity >= 1.0f) {
// Totally opaque. Render like normal.
data.source->Accept(this);
return;
} else if (common::utils::NodeCanRenderWithOpacity(data.source)) {
+ // Simple opacity that does not require an offscreen target.
float old_opacity = draw_state_.opacity;
- draw_state_.opacity *= data.opacity_filter->opacity();
+ draw_state_.opacity *= filter_opacity;
data.source->Accept(this);
draw_state_.opacity = old_opacity;
return;
+ } else {
+ // Complex opacity that requires an offscreen target.
+ math::Matrix3F texcoord_transform(math::Matrix3F::Identity());
+ math::RectF content_rect;
+ const backend::TextureEGL* texture = nullptr;
+
+ // Render source at 100% opacity to an offscreen target, then render
+ // that result with the specified filter opacity.
+ OffscreenRasterize(data.source, &texture, &texcoord_transform,
+ &content_rect);
+ if (texture != nullptr) {
+ if (content_rect.IsEmpty()) {
+ return;
+ }
+
+ // The content rect is already in screen space, so reset the transform.
+ math::Matrix3F old_transform = draw_state_.transform;
+ draw_state_.transform = math::Matrix3F::Identity();
+ scoped_ptr<DrawObject> draw(new DrawRectColorTexture(graphics_state_,
+ draw_state_, content_rect, kOpaqueWhite * filter_opacity, texture,
+ texcoord_transform, false /* clamp_texcoords */));
+ AddTransparentDraw(draw.Pass(), content_rect);
+ draw_state_.transform = old_transform;
+ return;
+ }
}
}
@@ -197,7 +238,7 @@
}
// Use the fallback rasterizer to handle everything else.
- FallbackRasterize(filter_node, DrawObjectManager::kOffscreenSkiaFilter);
+ FallbackRasterize(filter_node);
}
void RenderTreeNodeVisitor::Visit(render_tree::ImageNode* image_node) {
@@ -247,28 +288,23 @@
// Different shaders are used depending on whether the image has a single
// plane or multiple planes.
scoped_ptr<DrawObject> draw;
- DrawObjectManager::OnscreenType onscreen_type;
if (skia_image->GetTypeId() == base::GetTypeId<skia::SinglePlaneImage>()) {
skia::HardwareFrontendImage* hardware_image =
base::polymorphic_downcast<skia::HardwareFrontendImage*>(skia_image);
if (clamp_texcoords || !is_opaque) {
- onscreen_type = DrawObjectManager::kOnscreenRectColorTexture;
draw.reset(new DrawRectColorTexture(graphics_state_, draw_state_,
- data.destination_rect,
- render_tree::ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
+ data.destination_rect, kOpaqueWhite,
hardware_image->GetTextureEGL(), texcoord_transform,
clamp_texcoords));
} else {
- onscreen_type = DrawObjectManager::kOnscreenRectTexture;
draw.reset(new DrawRectTexture(graphics_state_, draw_state_,
data.destination_rect, hardware_image->GetTextureEGL(),
texcoord_transform));
}
} else if (skia_image->GetTypeId() ==
base::GetTypeId<skia::MultiPlaneImage>()) {
- FallbackRasterize(image_node,
- DrawObjectManager::kOffscreenSkiaMultiPlaneImage);
+ FallbackRasterize(image_node);
return;
} else {
NOTREACHED();
@@ -276,11 +312,9 @@
}
if (is_opaque) {
- AddOpaqueDraw(draw.Pass(), onscreen_type,
- DrawObjectManager::kOffscreenNone);
+ AddOpaqueDraw(draw.Pass(), image_node->GetBounds());
} else {
- AddTransparentDraw(draw.Pass(), onscreen_type,
- DrawObjectManager::kOffscreenNone, image_node->GetBounds());
+ AddTransparentDraw(draw.Pass(), image_node->GetBounds());
}
}
@@ -299,9 +333,8 @@
static_cast<int>(mapped_rect.height())));
scoped_ptr<DrawObject> draw(new DrawPolyColor(graphics_state_,
- draw_state_, data.rect, render_tree::ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f)));
- AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
- DrawObjectManager::kOffscreenNone);
+ draw_state_, data.rect, kTransparentBlack));
+ AddOpaqueDraw(draw.Pass(), video_node->GetBounds());
}
void RenderTreeNodeVisitor::Visit(render_tree::RectNode* rect_node) {
@@ -326,11 +359,11 @@
const bool border_supported = !data.border;
if (data.rounded_corners) {
- FallbackRasterize(rect_node, DrawObjectManager::kOffscreenSkiaRectRounded);
+ FallbackRasterize(rect_node);
} else if (!brush_supported) {
- FallbackRasterize(rect_node, DrawObjectManager::kOffscreenSkiaRectBrush);
+ FallbackRasterize(rect_node);
} else if (!border_supported) {
- FallbackRasterize(rect_node, DrawObjectManager::kOffscreenSkiaRectBorder);
+ FallbackRasterize(rect_node);
} else {
DCHECK(!data.border);
const math::RectF& content_rect(data.rect);
@@ -351,11 +384,9 @@
scoped_ptr<DrawObject> draw(new DrawPolyColor(graphics_state_,
draw_state_, content_rect, content_color));
if (draw_state_.opacity * content_color.a() == 1.0f) {
- AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
- DrawObjectManager::kOffscreenNone);
+ AddOpaqueDraw(draw.Pass(), rect_node->GetBounds());
} else {
- AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
- DrawObjectManager::kOffscreenNone, rect_node->GetBounds());
+ AddTransparentDraw(draw.Pass(), rect_node->GetBounds());
}
} else {
const render_tree::LinearGradientBrush* linear_brush =
@@ -363,11 +394,9 @@
(brush.get());
scoped_ptr<DrawObject> draw(new DrawRectLinearGradient(graphics_state_,
draw_state_, content_rect, *linear_brush));
- // The draw may use transparent colors or a depth stencil (but only
- // the inclusive one, so only one depth value is needed), so make it
- // a transparent draw.
- AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
- DrawObjectManager::kOffscreenNone, rect_node->GetBounds());
+ // The draw may use transparent pixels to ensure only pixels in the
+ // content_rect are modified.
+ AddTransparentDraw(draw.Pass(), rect_node->GetBounds());
}
}
}
@@ -381,7 +410,7 @@
const render_tree::RectShadowNode::Builder& data = shadow_node->data();
if (data.rounded_corners) {
- FallbackRasterize(shadow_node, DrawObjectManager::kOffscreenSkiaShadow);
+ FallbackRasterize(shadow_node);
return;
}
@@ -391,8 +420,6 @@
data.shadow.color.g() * data.shadow.color.a(),
data.shadow.color.b() * data.shadow.color.a(),
data.shadow.color.a());
- DrawObjectManager::OnscreenType onscreen_type =
- DrawObjectManager::kOnscreenRectShadow;
math::RectF spread_rect(data.rect);
spread_rect.Offset(data.shadow.offset);
@@ -418,12 +445,11 @@
blur_rect.set_size(math::SizeF());
}
draw.reset(new DrawRectShadowBlur(graphics_state_, draw_state_,
- blur_rect, data.rect, spread_rect, shadow_color, math::RectF(),
+ blur_rect, data.rect, spread_rect, shadow_color,
data.shadow.blur_sigma, data.inset));
- onscreen_type = DrawObjectManager::kOnscreenRectShadowBlur;
} else {
draw.reset(new DrawRectShadowSpread(graphics_state_, draw_state_,
- spread_rect, data.rect, shadow_color, data.rect, math::RectF()));
+ spread_rect, data.rect, shadow_color, data.rect));
}
} else {
// blur_rect is outermost.
@@ -439,30 +465,17 @@
math::Vector2dF blur_extent(data.shadow.BlurExtent());
blur_rect.Outset(blur_extent.x(), blur_extent.y());
draw.reset(new DrawRectShadowBlur(graphics_state_, draw_state_,
- data.rect, blur_rect, spread_rect, shadow_color, data.rect,
+ data.rect, blur_rect, spread_rect, shadow_color,
data.shadow.blur_sigma, data.inset));
- onscreen_type = DrawObjectManager::kOnscreenRectShadowBlur;
} else {
draw.reset(new DrawRectShadowSpread(graphics_state_, draw_state_,
- data.rect, spread_rect, shadow_color, spread_rect, data.rect));
+ data.rect, spread_rect, shadow_color, spread_rect));
}
node_bounds.Union(blur_rect);
}
- // Include or exclude scissor will touch these pixels.
- node_bounds.Union(data.rect);
-
- // Since the depth buffer is polluted to create a stencil for pixels to be
- // modified by the shadow, this draw must occur during the transparency
- // pass. During this pass, all subsequent draws are guaranteed to be closer
- // (i.e. pass the depth test) than pixels modified by previous transparency
- // draws.
- AddTransparentDraw(draw.Pass(), onscreen_type,
- DrawObjectManager::kOffscreenNone, node_bounds);
-
- // Since the box shadow draw objects use the depth stencil object, two depth
- // values were used. So skip an additional depth value.
- draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
+ // Transparency is used to skip pixels that are not shadowed.
+ AddTransparentDraw(draw.Pass(), node_bounds);
}
void RenderTreeNodeVisitor::Visit(render_tree::TextNode* text_node) {
@@ -470,13 +483,29 @@
return;
}
- FallbackRasterize(text_node, DrawObjectManager::kOffscreenSkiaText);
+ FallbackRasterize(text_node);
}
-void RenderTreeNodeVisitor::FallbackRasterize(
+// Get an offscreen target to render |node|.
+// |out_content_cached| is true if the node's contents are already cached in
+// the returned offscreen target.
+// |out_target_info| describes the offscreen surface into which |node| should
+// be rendered.
+// |out_content_rect| is the onscreen rect (already in screen space) where the
+// offscreen contents should be rendered.
+void RenderTreeNodeVisitor::GetOffscreenTarget(
scoped_refptr<render_tree::Node> node,
- DrawObjectManager::OffscreenType offscreen_type) {
- DCHECK_NE(offscreen_type, DrawObjectManager::kOffscreenNone);
+ bool* out_content_cached,
+ OffscreenTargetManager::TargetInfo* out_target_info,
+ math::RectF* out_content_rect) {
+ // Default to telling the caller that nothing should be rendered.
+ *out_content_cached = true;
+ out_content_rect->SetRect(0, 0, 0, 0);
+
+ if (!allow_offscreen_targets_) {
+ failed_offscreen_target_request_ = true;
+ return;
+ }
math::RectF node_bounds(node->GetBounds());
math::RectF mapped_bounds(draw_state_.transform.MapRect(node_bounds));
@@ -484,14 +513,8 @@
return;
}
- // Use the fallback rasterizer to render the tree. In order to preserve
- // sharpness, let the fallback rasterizer handle the current transform.
- math::Matrix3F old_transform = draw_state_.transform;
- draw_state_.transform = math::Matrix3F::Identity();
-
// Request a slightly larger render target than the calculated bounds. The
- // fallback rasterizer may use an extra pixel along the edge of anti-aliased
- // objects.
+ // rasterizer may use an extra pixel along the edge of anti-aliased objects.
const float kBorderWidth = 1.0f;
// The render target cache keys off the render_tree Node and target size.
@@ -502,23 +525,21 @@
float offset_x = std::floor(mapped_bounds.x() + 0.5f);
float offset_y = std::floor(mapped_bounds.y() + 0.5f);
math::PointF content_offset(
- // Shift contents towards the origin of the render target.
- kBorderWidth + kFractionPad - offset_x,
- kBorderWidth + kFractionPad - offset_y);
+ offset_x - kBorderWidth - kFractionPad,
+ offset_y - kBorderWidth - kFractionPad);
math::SizeF content_size(
std::ceil(mapped_bounds.width() + 2.0f * kBorderWidth + kFractionPad),
std::ceil(mapped_bounds.height() + 2.0f * kBorderWidth + kFractionPad));
- OffscreenTargetManager::TargetInfo target_info;
- bool is_cached = offscreen_target_manager_->GetCachedOffscreenTarget(node,
- content_size, &target_info);
- if (!is_cached) {
+ *out_content_cached = offscreen_target_manager_->GetCachedOffscreenTarget(
+ node, content_size, out_target_info);
+ if (!(*out_content_cached)) {
offscreen_target_manager_->AllocateOffscreenTarget(node,
- content_size, &target_info);
+ content_size, out_target_info);
}
- // If the render target is the scratch surface, then just render what fits
- // onscreen for better performance.
- if (target_info.is_scratch_surface) {
+ // If no offscreen target was available, then set the content_rect as if
+ // rendering will occur on the current render target.
+ if (out_target_info->framebuffer == nullptr) {
mapped_bounds.Outset(kBorderWidth, kBorderWidth);
mapped_bounds.Intersect(draw_state_.scissor);
if (mapped_bounds.IsEmpty()) {
@@ -528,60 +549,193 @@
float right = std::ceil(mapped_bounds.right());
float top = std::floor(mapped_bounds.y());
float bottom = std::ceil(mapped_bounds.bottom());
- content_offset.SetPoint(-left, -top);
+ content_offset.SetPoint(left, top);
content_size.SetSize(right - left, bottom - top);
+ } else {
+ DCHECK_LE(content_size.width(), out_target_info->region.width());
+ DCHECK_LE(content_size.height(), out_target_info->region.height());
}
- // The returned target may be larger than actually needed. Clamp to just the
- // needed size.
- DCHECK_LE(content_size.width(), target_info.region.width());
- DCHECK_LE(content_size.height(), target_info.region.height());
- target_info.region.set_size(content_size);
- math::RectF draw_rect(-content_offset.x(), -content_offset.y(),
- content_size.width(), content_size.height());
+ out_target_info->region.set_size(content_size);
- // Setup draw callbacks as needed.
- base::Closure draw_offscreen;
- base::Closure draw_onscreen;
- if (!is_cached) {
- // Pre-translate the contents so it starts near the origin.
- math::Matrix3F content_transform(old_transform);
- content_transform(0, 2) += content_offset.x();
- content_transform(1, 2) += content_offset.y();
+ out_content_rect->set_origin(content_offset);
+ out_content_rect->set_size(content_size);
+}
- if (target_info.is_scratch_surface) {
- draw_onscreen = base::Bind(*fallback_rasterize_,
- scoped_refptr<render_tree::Node>(node), content_transform,
- target_info);
- } else {
- draw_offscreen = base::Bind(*fallback_rasterize_,
- scoped_refptr<render_tree::Node>(node), content_transform,
- target_info);
- }
+void RenderTreeNodeVisitor::FallbackRasterize(
+ scoped_refptr<render_tree::Node> node) {
+ OffscreenTargetManager::TargetInfo target_info;
+ math::RectF content_rect;
+ bool content_is_cached = false;
+ GetOffscreenTarget(node, &content_is_cached, &target_info, &content_rect);
+
+ if (content_rect.IsEmpty()) {
+ return;
}
+ // If no offscreen target was available, then just render directly onto the
+ // current render target.
+ if (target_info.framebuffer == nullptr) {
+ base::Closure rasterize_callback = base::Bind(fallback_rasterize_,
+ node, fallback_render_target_, draw_state_.transform, content_rect,
+ draw_state_.opacity, kFallbackShouldFlush);
+ scoped_ptr<DrawObject> draw(new DrawCallback(rasterize_callback));
+ AddExternalDraw(draw.Pass(), content_rect, node->GetTypeId());
+ return;
+ }
+
+ // Setup draw for the contents as needed.
+ if (!content_is_cached) {
+ FallbackRasterize(node, target_info, content_rect);
+ }
+
+ // Sub-pixel offsets are passed to the fallback rasterizer to preserve
+ // sharpness. The results should be drawn to |content_rect| which is already
+ // in screen space.
+ math::Matrix3F old_transform = draw_state_.transform;
+ draw_state_.transform = math::Matrix3F::Identity();
+
// Create the appropriate draw object to call the draw callback, then render
- // its results onscreen.
+ // its results onscreen. A transparent draw must be used even if the current
+ // opacity is 100% because the contents may have transparency.
backend::TextureEGL* texture = target_info.framebuffer->GetColorTexture();
math::Matrix3F texcoord_transform = GetTexcoordTransform(target_info);
if (draw_state_.opacity == 1.0f) {
scoped_ptr<DrawObject> draw(new DrawRectTexture(graphics_state_,
- draw_state_, draw_rect, texture, texcoord_transform,
- draw_offscreen, draw_onscreen));
- AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenRectTexture,
- offscreen_type, draw_rect);
+ draw_state_, content_rect, texture, texcoord_transform));
+ AddTransparentDraw(draw.Pass(), content_rect);
} else {
scoped_ptr<DrawObject> draw(new DrawRectColorTexture(graphics_state_,
- draw_state_, draw_rect, render_tree::ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
- texture, texcoord_transform, draw_offscreen, draw_onscreen));
- AddTransparentDraw(draw.Pass(),
- DrawObjectManager::kOnscreenRectColorTexture, offscreen_type,
- draw_rect);
+ draw_state_, content_rect, kOpaqueWhite, texture, texcoord_transform,
+ false /* clamp_texcoords */));
+ AddTransparentDraw(draw.Pass(), content_rect);
}
draw_state_.transform = old_transform;
}
+void RenderTreeNodeVisitor::FallbackRasterize(
+ scoped_refptr<render_tree::Node> node,
+ const OffscreenTargetManager::TargetInfo& target_info,
+ const math::RectF& content_rect) {
+ // It is not permitted to render to an offscreen target while already
+ // rendering to an offscreen target. To allow this path, ensure that
+ // render targets are not used as both the read and write targets for any
+ // call. (Although these reads and writes should occur in different regions
+ // of the target, not all drivers may handle this properly.) Also ensure the
+ // draw object manager will sort these draws properly.
+ DCHECK(!render_target_is_offscreen_);
+
+ uint32_t rasterize_flags = 0;
+
+ // Pre-translate the content so it starts in target_info.region.
+ math::Matrix3F content_transform =
+ math::TranslateMatrix(target_info.region.x() - content_rect.x(),
+ target_info.region.y() - content_rect.y()) *
+ draw_state_.transform;
+ base::Closure rasterize_callback = base::Bind(fallback_rasterize_,
+ node, target_info.skia_canvas, content_transform, target_info.region,
+ draw_state_.opacity, rasterize_flags);
+ scoped_ptr<DrawObject> draw(new DrawCallback(rasterize_callback));
+
+ backend::RenderTarget* old_render_target = render_target_;
+ bool old_render_target_is_offscreen = render_target_is_offscreen_;
+
+ render_target_ = target_info.framebuffer;
+ render_target_is_offscreen_ = true;
+ AddExternalDraw(draw.Pass(), target_info.region, node->GetTypeId());
+
+ render_target_ = old_render_target;
+ render_target_is_offscreen_ = old_render_target_is_offscreen;
+}
+
+// Add draw objects to render |node| to an offscreen render target.
+// |out_texture| and |out_texcoord_transform| describe the texture subregion
+// that will contain the result of rendering |node|. If not enough memory
+// is available for the offscreen target, then |out_texture| will be null.
+// |out_content_rect| describes the onscreen rect (in screen space) which
+// should be used to render node's contents.
+void RenderTreeNodeVisitor::OffscreenRasterize(
+ scoped_refptr<render_tree::Node> node,
+ const backend::TextureEGL** out_texture,
+ math::Matrix3F* out_texcoord_transform,
+ math::RectF* out_content_rect) {
+ OffscreenTargetManager::TargetInfo target_info;
+ bool content_is_cached = false;
+ GetOffscreenTarget(node, &content_is_cached, &target_info, out_content_rect);
+
+ if (out_content_rect->IsEmpty()) {
+ return;
+ }
+
+ if (target_info.framebuffer == nullptr) {
+ // No offscreen target was available.
+ *out_texture = nullptr;
+ return;
+ }
+
+ *out_texture = target_info.framebuffer->GetColorTexture();
+ *out_texcoord_transform = GetTexcoordTransform(target_info);
+
+ if (!content_is_cached) {
+ // Try to use the native rasterizer to handle the offscreen rendering.
+ // However, because offscreen targets are actually regions of a texture
+ // atlas, some drivers may not properly handle reading and writing to
+ // the same texture -- even if the operations occur in different regions.
+ // So native offscreen handling can only occur if |node| or its children
+ // do not also need offscreen targets (or this particular target). The
+ // alternative is to use the fallback rasterizer since it allocates its
+ // own render targets as needed.
+ //
+ // Ideally, pre-check |node| and its children to see if they will need
+ // an offscreen target. However, this would result in a lot of duplicate
+ // code. So just process the nodes into draws, and if any of them requests
+ // an offscreen target, then remove all the recently added draws, and use
+ // the fallback rasterizer instead of the native rasterizer.
+ uint32_t last_valid_draw_id = last_draw_id_;
+ bool old_allow_offscreen_targets = allow_offscreen_targets_;
+ bool old_failed_offscreen_target_request = failed_offscreen_target_request_;
+ allow_offscreen_targets_ = false;
+ failed_offscreen_target_request_ = false;
+
+ // Push a new render state to rasterize to the offscreen render target.
+ DrawObject::BaseState old_draw_state = draw_state_;
+ SkCanvas* old_fallback_render_target = fallback_render_target_;
+ backend::RenderTarget* old_render_target = render_target_;
+ bool old_render_target_is_offscreen = render_target_is_offscreen_;
+
+ // Adjust the transform to render into target_info.region.
+ draw_state_.transform =
+ math::TranslateMatrix(target_info.region.x() - out_content_rect->x(),
+ target_info.region.y() - out_content_rect->y()) *
+ draw_state_.transform;
+ draw_state_.scissor = RoundRectFToInt(target_info.region);
+ draw_state_.opacity = 1.0f;
+ fallback_render_target_ = target_info.skia_canvas;
+ render_target_ = target_info.framebuffer;
+ render_target_is_offscreen_ = true;
+
+ node->Accept(this);
+
+ draw_state_ = old_draw_state;
+ fallback_render_target_ = old_fallback_render_target;
+ render_target_ = old_render_target;
+ render_target_is_offscreen_ = old_render_target_is_offscreen;
+
+ bool use_fallback_rasterizer = failed_offscreen_target_request_;
+ allow_offscreen_targets_ = old_allow_offscreen_targets;
+ failed_offscreen_target_request_ = old_failed_offscreen_target_request;
+
+ if (use_fallback_rasterizer) {
+ // The node or one of its children needed an offscreen target, so this
+ // cannot be rendered natively. Remove all the draws added for |node|,
+ // and just use the fallback rasterizer instead.
+ draw_object_manager_->RemoveDraws(last_valid_draw_id);
+ FallbackRasterize(node, target_info, *out_content_rect);
+ }
+ }
+}
+
bool RenderTreeNodeVisitor::IsVisible(const math::RectF& bounds) {
math::RectF intersection = IntersectRects(
draw_state_.transform.MapRect(bounds), draw_state_.scissor);
@@ -589,20 +743,44 @@
}
void RenderTreeNodeVisitor::AddOpaqueDraw(scoped_ptr<DrawObject> object,
- DrawObjectManager::OnscreenType onscreen_type,
- DrawObjectManager::OffscreenType offscreen_type) {
- draw_object_manager_->AddOpaqueDraw(object.Pass(), onscreen_type,
- offscreen_type);
- draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
+ const math::RectF& local_bounds) {
+ base::TypeId draw_type = object->GetTypeId();
+ if (render_target_is_offscreen_) {
+ last_draw_id_ = draw_object_manager_->AddOffscreenDraw(object.Pass(),
+ DrawObjectManager::kBlendNone, draw_type, render_target_,
+ draw_state_.transform.MapRect(local_bounds));
+ } else {
+ last_draw_id_ = draw_object_manager_->AddOnscreenDraw(object.Pass(),
+ DrawObjectManager::kBlendNone, draw_type, render_target_,
+ draw_state_.transform.MapRect(local_bounds));
+ }
}
void RenderTreeNodeVisitor::AddTransparentDraw(scoped_ptr<DrawObject> object,
- DrawObjectManager::OnscreenType onscreen_type,
- DrawObjectManager::OffscreenType offscreen_type,
const math::RectF& local_bounds) {
- draw_object_manager_->AddTransparentDraw(object.Pass(), onscreen_type,
- offscreen_type, draw_state_.transform.MapRect(local_bounds));
- draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
+ base::TypeId draw_type = object->GetTypeId();
+ if (render_target_is_offscreen_) {
+ last_draw_id_ = draw_object_manager_->AddOffscreenDraw(object.Pass(),
+ DrawObjectManager::kBlendSrcAlpha, draw_type, render_target_,
+ draw_state_.transform.MapRect(local_bounds));
+ } else {
+ last_draw_id_ = draw_object_manager_->AddOnscreenDraw(object.Pass(),
+ DrawObjectManager::kBlendSrcAlpha, draw_type, render_target_,
+ draw_state_.transform.MapRect(local_bounds));
+ }
+}
+
+void RenderTreeNodeVisitor::AddExternalDraw(scoped_ptr<DrawObject> object,
+ const math::RectF& world_bounds, base::TypeId draw_type) {
+ if (render_target_is_offscreen_) {
+ last_draw_id_ = draw_object_manager_->AddOffscreenDraw(object.Pass(),
+ DrawObjectManager::kBlendExternal, draw_type, render_target_,
+ world_bounds);
+ } else {
+ last_draw_id_ = draw_object_manager_->AddOnscreenDraw(object.Pass(),
+ DrawObjectManager::kBlendExternal, draw_type, render_target_,
+ world_bounds);
+ }
}
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
index c4e70d3..51044f8 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
@@ -33,10 +33,12 @@
#include "cobalt/render_tree/rect_shadow_node.h"
#include "cobalt/render_tree/text_node.h"
#include "cobalt/renderer/backend/egl/texture.h"
+#include "cobalt/renderer/backend/render_target.h"
#include "cobalt/renderer/rasterizer/egl/draw_object.h"
#include "cobalt/renderer/rasterizer/egl/draw_object_manager.h"
#include "cobalt/renderer/rasterizer/egl/graphics_state.h"
#include "cobalt/renderer/rasterizer/egl/offscreen_target_manager.h"
+#include "third_party/skia/include/core/SkCanvas.h"
namespace cobalt {
namespace renderer {
@@ -47,16 +49,23 @@
// DrawObjects which must then be processed using calls to ExecuteDraw.
class RenderTreeNodeVisitor : public render_tree::NodeVisitor {
public:
+ enum FallbackRasterizeFlags {
+ kFallbackShouldClear = 1 << 0,
+ kFallbackShouldFlush = 1 << 1,
+ };
typedef base::Callback<void(
const scoped_refptr<render_tree::Node>& render_tree,
- const math::Matrix3F& transform,
- const OffscreenTargetManager::TargetInfo& target)>
+ SkCanvas* fallback_render_target, const math::Matrix3F& transform,
+ const math::RectF& scissor, float opacity, uint32_t rasterize_flags)>
FallbackRasterizeFunction;
RenderTreeNodeVisitor(GraphicsState* graphics_state,
DrawObjectManager* draw_object_manager,
OffscreenTargetManager* offscreen_target_manager,
- const FallbackRasterizeFunction* fallback_rasterize);
+ const FallbackRasterizeFunction& fallback_rasterize,
+ SkCanvas* fallback_render_target,
+ backend::RenderTarget* render_target,
+ const math::Rect& content_rect);
void Visit(render_tree::animations::AnimateNode* /* animate */) OVERRIDE {
NOTREACHED();
@@ -72,23 +81,41 @@
void Visit(render_tree::TextNode* text_node) OVERRIDE;
private:
+ void GetOffscreenTarget(scoped_refptr<render_tree::Node> node,
+ bool* out_content_cached,
+ OffscreenTargetManager::TargetInfo* out_target_info,
+ math::RectF* out_content_rect);
+ void FallbackRasterize(scoped_refptr<render_tree::Node> node);
void FallbackRasterize(scoped_refptr<render_tree::Node> node,
- DrawObjectManager::OffscreenType offscreen_type);
+ const OffscreenTargetManager::TargetInfo& target_info,
+ const math::RectF& content_rect);
+ void OffscreenRasterize(scoped_refptr<render_tree::Node> node,
+ const backend::TextureEGL** out_texture,
+ math::Matrix3F* out_texcoord_transform,
+ math::RectF* out_content_rect);
+
bool IsVisible(const math::RectF& bounds);
void AddOpaqueDraw(scoped_ptr<DrawObject> object,
- DrawObjectManager::OnscreenType onscreen_type,
- DrawObjectManager::OffscreenType offscreen_type);
+ const math::RectF& local_bounds);
void AddTransparentDraw(scoped_ptr<DrawObject> object,
- DrawObjectManager::OnscreenType onscreen_type,
- DrawObjectManager::OffscreenType offscreen_type,
const math::RectF& local_bounds);
+ void AddExternalDraw(scoped_ptr<DrawObject> object,
+ const math::RectF& world_bounds, base::TypeId draw_type);
GraphicsState* graphics_state_;
DrawObjectManager* draw_object_manager_;
OffscreenTargetManager* offscreen_target_manager_;
- const FallbackRasterizeFunction* fallback_rasterize_;
+ FallbackRasterizeFunction fallback_rasterize_;
DrawObject::BaseState draw_state_;
+ SkCanvas* fallback_render_target_;
+ backend::RenderTarget* render_target_;
+ bool render_target_is_offscreen_;
+
+ bool allow_offscreen_targets_;
+ bool failed_offscreen_target_request_;
+
+ uint32_t last_draw_id_;
};
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/shader_program.h b/src/cobalt/renderer/rasterizer/egl/shader_program.h
index 0abebbe..d21695b 100644
--- a/src/cobalt/renderer/rasterizer/egl/shader_program.h
+++ b/src/cobalt/renderer/rasterizer/egl/shader_program.h
@@ -16,6 +16,7 @@
#define COBALT_RENDERER_RASTERIZER_EGL_SHADER_PROGRAM_H_
#include "base/compiler_specific.h"
+#include "cobalt/base/type_id.h"
#include "cobalt/renderer/rasterizer/egl/shader_base.h"
namespace cobalt {
@@ -57,6 +58,9 @@
const FragmentShader& GetFragmentShader() const {
return fragment_shader_;
}
+ static base::TypeId GetTypeId() {
+ return base::GetTypeId<ShaderProgram<VertexShader, FragmentShader> >();
+ }
private:
// Shader programs should only be created and destroyed by the
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
index a7831c4..9f04388 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
@@ -1,13 +1,13 @@
precision mediump float;
uniform vec2 u_blur_radius;
uniform vec2 u_scale_add;
-varying vec2 v_blur_position; // Relative to the blur center.
+varying vec2 v_offset; // Relative to the blur center.
varying vec4 v_color;
void main() {
// Distance from the blur radius.
- // Both v_blur_position and u_blur_radius are expressed in terms of the
+ // Both v_offset and u_blur_radius are expressed in terms of the
// blur sigma.
- vec2 pos = abs(v_blur_position) - u_blur_radius;
+ vec2 pos = abs(v_offset) - u_blur_radius;
vec2 pos2 = pos * pos;
vec2 pos3 = pos2 * pos;
vec4 posx = vec4(1.0, pos.x, pos2.x, pos3.x);
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_include.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_include.glsl
new file mode 100644
index 0000000..ed660d6
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_include.glsl
@@ -0,0 +1,8 @@
+precision mediump float;
+uniform vec4 u_include; // include scissor (x_min, y_min, x_max, y_max)
+varying vec2 v_offset;
+varying vec4 v_color;
+void main() {
+ vec2 include = step(u_include.xy, v_offset) * step(v_offset, u_include.zw);
+ gl_FragColor = v_color * include.x * include.y;
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
index 140b6b5..8738d12 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
@@ -22,10 +22,11 @@
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_include.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_texcoord.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color.glsl',
- '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_offset.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_texcoord.glsl',
],
},
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color.glsl
index 5b6cd81..6a9389e 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color.glsl
@@ -1,11 +1,11 @@
uniform vec4 u_clip_adjustment;
uniform mat3 u_view_matrix;
-attribute vec3 a_position;
+attribute vec2 a_position;
attribute vec4 a_color;
varying vec4 v_color;
void main() {
- vec3 pos2d = u_view_matrix * vec3(a_position.xy, 1);
+ vec3 pos2d = u_view_matrix * vec3(a_position, 1);
gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
- u_clip_adjustment.zw, a_position.z, pos2d.z);
+ u_clip_adjustment.zw, 0, pos2d.z);
v_color = a_color;
}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl
deleted file mode 100644
index 0b543d1..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl
+++ /dev/null
@@ -1,14 +0,0 @@
-uniform vec4 u_clip_adjustment;
-uniform mat3 u_view_matrix;
-attribute vec3 a_position;
-attribute vec4 a_color;
-attribute vec2 a_blur_position;
-varying vec4 v_color;
-varying vec2 v_blur_position;
-void main() {
- vec3 pos2d = u_view_matrix * vec3(a_position.xy, 1);
- gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
- u_clip_adjustment.zw, a_position.z, pos2d.z);
- v_color = a_color;
- v_blur_position = a_blur_position;
-}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_offset.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_offset.glsl
new file mode 100644
index 0000000..879696f
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_offset.glsl
@@ -0,0 +1,14 @@
+uniform vec4 u_clip_adjustment;
+uniform mat3 u_view_matrix;
+attribute vec2 a_position;
+attribute vec4 a_color;
+attribute vec2 a_offset;
+varying vec4 v_color;
+varying vec2 v_offset;
+void main() {
+ vec3 pos2d = u_view_matrix * vec3(a_position, 1);
+ gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
+ u_clip_adjustment.zw, 0, pos2d.z);
+ v_color = a_color;
+ v_offset = a_offset;
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_texcoord.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_texcoord.glsl
index c7df12c..ef26a5b 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_texcoord.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_texcoord.glsl
@@ -1,14 +1,14 @@
uniform vec4 u_clip_adjustment;
uniform mat3 u_view_matrix;
-attribute vec3 a_position;
+attribute vec2 a_position;
attribute vec4 a_color;
attribute vec2 a_texcoord;
varying vec4 v_color;
varying vec2 v_texcoord;
void main() {
- vec3 pos2d = u_view_matrix * vec3(a_position.xy, 1);
+ vec3 pos2d = u_view_matrix * vec3(a_position, 1);
gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
- u_clip_adjustment.zw, a_position.z, pos2d.z);
+ u_clip_adjustment.zw, 0, pos2d.z);
v_color = a_color;
v_texcoord = a_texcoord;
}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_texcoord.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_texcoord.glsl
index bbbdbf4..1527625 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_texcoord.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_texcoord.glsl
@@ -1,11 +1,11 @@
uniform vec4 u_clip_adjustment;
uniform mat3 u_view_matrix;
-attribute vec3 a_position;
+attribute vec2 a_position;
attribute vec2 a_texcoord;
varying vec2 v_texcoord;
void main() {
- vec3 pos2d = u_view_matrix * vec3(a_position.xy, 1);
+ vec3 pos2d = u_view_matrix * vec3(a_position, 1);
gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
- u_clip_adjustment.zw, a_position.z, pos2d.z);
+ u_clip_adjustment.zw, 0, pos2d.z);
v_texcoord = a_texcoord;
}
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index fb95695..9effe9e 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -475,6 +475,19 @@
TestTree(new ImageNode(image));
}
+TEST_F(PixelTest, SingleRGBAImageWithReflection) {
+ SizeF half_output_size = ScaleSize(output_surface_size(), 0.5f, 0.5f);
+ scoped_refptr<Image> image =
+ CreateColoredCheckersImage(GetResourceProvider(), output_surface_size());
+
+ TestTree(new MatrixTransformNode(
+ new ImageNode(image),
+ TranslateMatrix(half_output_size.width(), half_output_size.height()) *
+ ScaleMatrix(1.0f, -1.0f) *
+ TranslateMatrix(-half_output_size.width(),
+ -half_output_size.height())));
+}
+
TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners) {
scoped_refptr<Image> image = CreateColoredCheckersImageForAlphaFormat(
GetResourceProvider(), output_surface_size(),
@@ -1065,6 +1078,17 @@
-half_output_size.height())));
}
+TEST_F(PixelTest, ThreePlaneYUVImageWithReflection) {
+ SizeF half_output_size = ScaleSize(output_surface_size(), 0.5f, 0.5f);
+ TestTree(new MatrixTransformNode(
+ new ImageNode(
+ MakeI420Image(GetResourceProvider(), output_surface_size())),
+ TranslateMatrix(half_output_size.width(), half_output_size.height()) *
+ ScaleMatrix(1.0f, -1.0f) *
+ TranslateMatrix(-half_output_size.width(),
+ -half_output_size.height())));
+}
+
// The software rasterizer does not support NV12 images.
#if NV12_TEXTURE_SUPPORTED
@@ -2138,27 +2162,63 @@
20));
}
+namespace {
+std::pair<PointF, PointF> LinearGradientPointsFromDegrees(
+ float ccw_degrees_from_right, const SizeF& frame_size, float frame_scale) {
+ float ccw_radians_from_right = static_cast<float>(
+ ccw_degrees_from_right * M_PI / 180.0f);
+
+ // Scale |frame_size| by |frame_scale|, and offset the source and destination
+ // points for the gradient so their midpoint coincides with the center of
+ // the original frame.
+ float offset_x = frame_size.width() * (1.0f - frame_scale) * 0.5f;
+ float offset_y = frame_size.height() * (1.0f - frame_scale) * 0.5f;
+ std::pair<PointF, PointF> source_and_dest =
+ render_tree::LinearGradientPointsFromAngle(ccw_radians_from_right,
+ ScaleSize(frame_size, frame_scale));
+ source_and_dest.first.Offset(offset_x, offset_y);
+ source_and_dest.second.Offset(offset_x, offset_y);
+ return source_and_dest;
+}
+
+RectF ScaledCenteredSurface(const SizeF& frame_size, float scale) {
+ math::PointF origin(frame_size.width() * (1.0f - scale) * 0.5f,
+ frame_size.height() * (1.0f - scale) * 0.5f);
+ return math::RectF(origin, ScaleSize(frame_size, scale));
+}
+} // namespace
+
TEST_F(PixelTest, LinearGradient2StopsLeftRight) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new LinearGradientBrush(
- PointF(0, 0), PointF(output_surface_size().width(), 0),
+ LinearGradientPointsFromDegrees(0, output_surface_size(), 0.9f),
ColorRGBA(1.0, 0.0, 0.0, 1), ColorRGBA(0.0, 1.0, 0.0, 1)))));
}
TEST_F(PixelTest, LinearGradient2StopsTopBottom) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new LinearGradientBrush(
- PointF(0, 0), PointF(0, output_surface_size().height()),
+ LinearGradientPointsFromDegrees(-90, output_surface_size(), 0.9f),
ColorRGBA(1.0, 0.0, 0.0, 1), ColorRGBA(0.0, 1.0, 0.0, 1)))));
}
-TEST_F(PixelTest, LinearGradient2StopsArbitraryAngle) {
+TEST_F(PixelTest, LinearGradient2Stops45Degrees) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new LinearGradientBrush(
- PointF(30, 30), PointF(130, 180), ColorRGBA(1.0, 0.0, 0.0, 1),
+ LinearGradientPointsFromDegrees(45, output_surface_size(), 0.9f),
+ ColorRGBA(1.0, 0.0, 0.0, 1),
+ ColorRGBA(0.0, 1.0, 0.0, 1)))));
+}
+
+TEST_F(PixelTest, LinearGradient2Stops315Degrees) {
+ TestTree(new RectNode(
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
+ scoped_ptr<Brush>(new LinearGradientBrush(
+ LinearGradientPointsFromDegrees(315, output_surface_size(), 0.9f),
+ ColorRGBA(1.0, 0.0, 0.0, 1),
ColorRGBA(0.0, 1.0, 0.0, 1)))));
}
@@ -2172,27 +2232,36 @@
}
} // namespace
-TEST_F(PixelTest, LinearGradient3StopsLeftRight) {
- TestTree(
- new RectNode(RectF(output_surface_size()),
- scoped_ptr<Brush>(new LinearGradientBrush(
- PointF(0, 0), PointF(output_surface_size().width(), 0),
- Make3ColorEvenlySpacedStopList()))));
+TEST_F(PixelTest, LinearGradient3StopsLeftRightInset) {
+ TestTree(new RectNode(
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
+ scoped_ptr<Brush>(new LinearGradientBrush(
+ LinearGradientPointsFromDegrees(0, output_surface_size(), 0.8f),
+ Make3ColorEvenlySpacedStopList()))));
}
-TEST_F(PixelTest, LinearGradient3StopsTopBottom) {
- TestTree(
- new RectNode(RectF(output_surface_size()),
- scoped_ptr<Brush>(new LinearGradientBrush(
- PointF(0, 0), PointF(0, output_surface_size().height()),
- Make3ColorEvenlySpacedStopList()))));
+TEST_F(PixelTest, LinearGradient3StopsTopBottomInset) {
+ TestTree(new RectNode(
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
+ scoped_ptr<Brush>(new LinearGradientBrush(
+ LinearGradientPointsFromDegrees(-90, output_surface_size(), 0.8f),
+ Make3ColorEvenlySpacedStopList()))));
}
-TEST_F(PixelTest, LinearGradient3StopsArbitraryAngle) {
- TestTree(new RectNode(RectF(output_surface_size()),
- scoped_ptr<Brush>(new LinearGradientBrush(
- PointF(30, 30), PointF(130, 180),
- Make3ColorEvenlySpacedStopList()))));
+TEST_F(PixelTest, LinearGradient3Stops30DegreesInset) {
+ TestTree(new RectNode(
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
+ scoped_ptr<Brush>(new LinearGradientBrush(
+ LinearGradientPointsFromDegrees(30, output_surface_size(), 0.8f),
+ Make3ColorEvenlySpacedStopList()))));
+}
+
+TEST_F(PixelTest, LinearGradient3Stops210DegreesInset) {
+ TestTree(new RectNode(
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
+ scoped_ptr<Brush>(new LinearGradientBrush(
+ LinearGradientPointsFromDegrees(210, output_surface_size(), 0.8f),
+ Make3ColorEvenlySpacedStopList()))));
}
namespace {
@@ -2207,27 +2276,36 @@
}
} // namespace
-TEST_F(PixelTest, LinearGradient5StopsLeftRight) {
- TestTree(
- new RectNode(RectF(output_surface_size()),
- scoped_ptr<Brush>(new LinearGradientBrush(
- PointF(0, 0), PointF(output_surface_size().width(), 0),
- Make5ColorEvenlySpacedStopList()))));
+TEST_F(PixelTest, LinearGradient5StopsLeftRightOutset) {
+ TestTree(new RectNode(
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
+ scoped_ptr<Brush>(new LinearGradientBrush(
+ LinearGradientPointsFromDegrees(0, output_surface_size(), 1.0f),
+ Make5ColorEvenlySpacedStopList()))));
}
-TEST_F(PixelTest, LinearGradient5StopsTopBottom) {
- TestTree(
- new RectNode(RectF(output_surface_size()),
- scoped_ptr<Brush>(new LinearGradientBrush(
- PointF(0, 0), PointF(0, output_surface_size().height()),
- Make5ColorEvenlySpacedStopList()))));
+TEST_F(PixelTest, LinearGradient5StopsTopBottomOutset) {
+ TestTree(new RectNode(
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
+ scoped_ptr<Brush>(new LinearGradientBrush(
+ LinearGradientPointsFromDegrees(-90, output_surface_size(), 1.0f),
+ Make5ColorEvenlySpacedStopList()))));
}
-TEST_F(PixelTest, LinearGradient5StopsArbitraryAngle) {
- TestTree(new RectNode(RectF(output_surface_size()),
- scoped_ptr<Brush>(new LinearGradientBrush(
- PointF(30, 30), PointF(130, 180),
- Make5ColorEvenlySpacedStopList()))));
+TEST_F(PixelTest, LinearGradient5Stops150DegreesOutset) {
+ TestTree(new RectNode(
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
+ scoped_ptr<Brush>(new LinearGradientBrush(
+ LinearGradientPointsFromDegrees(150, output_surface_size(), 1.0f),
+ Make5ColorEvenlySpacedStopList()))));
+}
+
+TEST_F(PixelTest, LinearGradient5Stops330DegreesOutset) {
+ TestTree(new RectNode(
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
+ scoped_ptr<Brush>(new LinearGradientBrush(
+ LinearGradientPointsFromDegrees(330, output_surface_size(), 1.0f),
+ Make5ColorEvenlySpacedStopList()))));
}
TEST_F(PixelTest, LinearGradientWithTransparencyOnWhiteBackground) {
@@ -2253,29 +2331,30 @@
}
TEST_F(PixelTest, RadialGradient2Stops) {
- TestTree(new RectNode(RectF(output_surface_size()),
- scoped_ptr<Brush>(new RadialGradientBrush(
- PointF(75, 100), 75, ColorRGBA(1.0, 0.0, 0.0, 1),
- ColorRGBA(0.0, 1.0, 0.0, 1)))));
+ TestTree(new RectNode(
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
+ scoped_ptr<Brush>(new RadialGradientBrush(
+ PointF(75, 100), 75, ColorRGBA(1.0, 0.0, 0.0, 1),
+ ColorRGBA(0.0, 1.0, 0.0, 1)))));
}
TEST_F(PixelTest, RadialGradient3Stops) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new RadialGradientBrush(
PointF(75, 100), 75, Make3ColorEvenlySpacedStopList()))));
}
TEST_F(PixelTest, RadialGradient5Stops) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new RadialGradientBrush(
PointF(75, 100), 75, Make5ColorEvenlySpacedStopList()))));
}
TEST_F(PixelTest, VerticalEllipseGradient2Stops) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new RadialGradientBrush(PointF(75, 100), 75, 100,
ColorRGBA(1.0, 0.0, 0.0, 1),
ColorRGBA(0.0, 1.0, 0.0, 1)))));
@@ -2283,21 +2362,21 @@
TEST_F(PixelTest, VerticalEllipseGradient3Stops) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new RadialGradientBrush(
PointF(75, 100), 75, 100, Make3ColorEvenlySpacedStopList()))));
}
TEST_F(PixelTest, VerticalEllipseGradient5Stops) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new RadialGradientBrush(
PointF(75, 100), 75, 100, Make5ColorEvenlySpacedStopList()))));
}
TEST_F(PixelTest, HorizontalEllipseGradient2Stops) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new RadialGradientBrush(PointF(75, 100), 100, 75,
ColorRGBA(1.0, 0.0, 0.0, 1),
ColorRGBA(0.0, 1.0, 0.0, 1)))));
@@ -2305,7 +2384,7 @@
TEST_F(PixelTest, HorizontalEllipseGradient3Stops) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new RadialGradientBrush(
PointF(75, 100), 100, 75, Make3ColorEvenlySpacedStopList()))));
}
@@ -2332,7 +2411,7 @@
TEST_F(PixelTest, HorizontalEllipseGradient5Stops) {
TestTree(new RectNode(
- RectF(output_surface_size()),
+ ScaledCenteredSurface(output_surface_size(), 0.9f),
scoped_ptr<Brush>(new RadialGradientBrush(
PointF(75, 100), 100, 75, Make5ColorEvenlySpacedStopList()))));
}
@@ -3297,6 +3376,17 @@
TestTree(new ImageNode(offscreen_image));
}
+// Tests that offscreen rendering works fine with YUV images.
+TEST_F(PixelTest, DrawOffscreenYUVImage) {
+ scoped_refptr<Image> image =
+ MakeI420Image(GetResourceProvider(), output_surface_size());
+
+ scoped_refptr<Image> offscreen_rendered_image =
+ GetResourceProvider()->DrawOffscreenImage(new ImageNode(image));
+
+ TestTree(new ImageNode(offscreen_rendered_image));
+}
+
#if defined(ENABLE_MAP_TO_MESH)
namespace {
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_image.h b/src/cobalt/renderer/rasterizer/skia/hardware_image.h
index eb9bb45..d0bff4f 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_image.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_image.h
@@ -215,6 +215,17 @@
return planes_[plane_index];
}
+ // Always fallback to custom non-skia code for rendering multi-plane images.
+ // The main reason to unconditionally fallback here is because Skia does a
+ // check internally to see if GL_RED is supported, and if so it will use
+ // GL_RED for 1-channel textures, and if not it will use GL_ALPHA for
+ // 1-channel textures. If we want to create textures like this manually (and
+ // later wrap it into a Skia texture), we must know in advance which GL format
+ // to set it up as, but Skia's GL_RED support decision is private information
+ // that we can't access. So, we choose instead to just not rely on Skia
+ // for this so that we don't have to worry about format mismatches.
+ bool CanRenderInSkia() const OVERRIDE { return false; }
+
bool EnsureInitialized() OVERRIDE;
private:
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index f88b6ee..78c8f60 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -32,6 +32,7 @@
#include "cobalt/renderer/rasterizer/skia/surface_cache_delegate.h"
#include "cobalt/renderer/rasterizer/skia/vertex_buffer_object.h"
#include "third_party/glm/glm/gtc/matrix_inverse.hpp"
+#include "third_party/glm/glm/gtx/transform.hpp"
#include "third_party/glm/glm/mat3x3.hpp"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkSurface.h"
@@ -73,6 +74,9 @@
const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target);
+ SkCanvas* GetCanvasFromRenderTarget(
+ const scoped_refptr<backend::RenderTarget>& render_target);
+
render_tree::ResourceProvider* GetResourceProvider();
GrContext* GetGrContext();
@@ -102,10 +106,8 @@
const math::Size& size);
void RasterizeRenderTreeToCanvas(
- const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas);
-
- SkCanvas* GetCanvasFromRenderTarget(
- const scoped_refptr<backend::RenderTarget>& render_target);
+ const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas,
+ GrSurfaceOrigin origin);
void ResetSkiaState();
@@ -131,6 +133,12 @@
base::optional<common::SurfaceCache> surface_cache_;
base::optional<egl::TexturedMeshRenderer> textured_mesh_renderer_;
+
+ // Valid only for the duration of a call to RasterizeRenderTreeToCanvas().
+ // Useful for directing textured_mesh_renderer_ on whether to flip its y-axis
+ // or not since Skia does not let us pull that information out of the
+ // SkCanvas object (which Skia would internally use to get this information).
+ base::optional<GrSurfaceOrigin> current_surface_origin_;
};
namespace {
@@ -166,7 +174,17 @@
return skia_desc;
}
+glm::mat4 ModelViewMatrixSurfaceOriginAdjustment(
+ GrSurfaceOrigin origin) {
+ if (origin == kTopLeft_GrSurfaceOrigin) {
+ return glm::scale(glm::vec3(1.0f, -1.0f, 1.0f));
+ } else {
+ return glm::mat4(1.0f);
+ }
+}
+
glm::mat4 GetFallbackTextureModelViewProjectionMatrix(
+ GrSurfaceOrigin origin,
const SkISize& canvas_size, const SkMatrix& total_matrix,
const math::RectF& destination_rect) {
// We define a transformation from GLES normalized device coordinates (e.g.
@@ -193,7 +211,9 @@
// Since these matrices are applied in LIFO order, read the followin inlined
// comments in reverse order.
- return
+ glm::mat4 result =
+ // Flip the y axis depending on the destination surface's origin.
+ ModelViewMatrixSurfaceOriginAdjustment(origin) *
// Finally transform back into normalized device coordinates so that
// GL can digest the results.
glm::affineInverse(gl_norm_coords_to_skia_canvas_coords) *
@@ -206,6 +226,8 @@
// referenced by the RenderQuad() function will have its positions defined
// within (e.g. [-1, 1]).
gl_norm_coords_to_skia_canvas_coords;
+
+ return result;
}
// For stereoscopic video, the actual video is split (either horizontally or
@@ -301,7 +323,13 @@
return result;
}
-void SetupGLStateForImageRender(Image* image) {
+enum FaceOrientation {
+ FaceOrientation_Ccw,
+ FaceOrientation_Cw,
+};
+
+void SetupGLStateForImageRender(Image* image,
+ FaceOrientation face_orientation) {
if (image->IsOpaque()) {
GL_CALL(glDisable(GL_BLEND));
} else {
@@ -310,9 +338,25 @@
GL_CALL(glDisable(GL_DEPTH_TEST));
GL_CALL(glDisable(GL_STENCIL_TEST));
GL_CALL(glEnable(GL_SCISSOR_TEST));
- GL_CALL(glEnable(GL_CULL_FACE));
GL_CALL(glCullFace(GL_BACK));
GL_CALL(glFrontFace(GL_CCW));
+ if (face_orientation == FaceOrientation_Ccw) {
+ GL_CALL(glEnable(GL_CULL_FACE));
+ } else {
+ // Unfortunately, some GLES implementations (like software Mesa) have a
+ // problem with flipping glCullFrace() from GL_BACK to GL_FRONT, they seem
+ // to ignore it. We need to render back faces though if the face
+ // orientation is flipped, so the only compatible solution is to disable
+ // back-face culling.
+ GL_CALL(glDisable(GL_CULL_FACE));
+ }
+}
+
+FaceOrientation GetFaceOrientationFromModelViewProjectionMatrix(
+ const glm::mat4& model_view_projection_matrix) {
+ return glm::determinant(model_view_projection_matrix) >= 0 ?
+ FaceOrientation_Ccw :
+ FaceOrientation_Cw;
}
} // namespace
@@ -341,10 +385,21 @@
// corner origin.
GL_CALL(glScissor(
canvas_boundsi.x(),
- canvas_size.height() - canvas_boundsi.height() - canvas_boundsi.y(),
+ *current_surface_origin_ == kBottomLeft_GrSurfaceOrigin ?
+ canvas_size.height() - canvas_boundsi.height() - canvas_boundsi.y() :
+ canvas_boundsi.y(),
canvas_boundsi.width(), canvas_boundsi.height()));
- SetupGLStateForImageRender(image);
+ glm::mat4 model_view_projection_matrix =
+ GetFallbackTextureModelViewProjectionMatrix(
+ *current_surface_origin_,
+ canvas_size, draw_state->render_target->getTotalMatrix(),
+ image_node->data().destination_rect);
+
+ SetupGLStateForImageRender(
+ image,
+ GetFaceOrientationFromModelViewProjectionMatrix(
+ model_view_projection_matrix));
if (!textured_mesh_renderer_) {
textured_mesh_renderer_.emplace(graphics_context_);
@@ -353,9 +408,7 @@
// Invoke our TexturedMeshRenderer to actually perform the draw call.
textured_mesh_renderer_->RenderQuad(
SkiaImageToTexturedMeshRendererImage(image, render_tree::kMono),
- GetFallbackTextureModelViewProjectionMatrix(
- canvas_size, draw_state->render_target->getTotalMatrix(),
- image_node->data().destination_rect));
+ model_view_projection_matrix);
// Let Skia know that we've modified GL state.
uint32_t untouched_states =
@@ -387,7 +440,14 @@
GL_CALL(glViewport(0, 0, canvas_size.width(), canvas_size.height()));
GL_CALL(glScissor(0, 0, canvas_size.width(), canvas_size.height()));
- SetupGLStateForImageRender(image);
+ glm::mat4 model_view_projection_matrix =
+ ModelViewMatrixSurfaceOriginAdjustment(*current_surface_origin_) *
+ draw_state->transform_3d;
+
+ SetupGLStateForImageRender(
+ image,
+ GetFaceOrientationFromModelViewProjectionMatrix(
+ model_view_projection_matrix));
if (!textured_mesh_renderer_) {
textured_mesh_renderer_.emplace(graphics_context_);
@@ -403,7 +463,7 @@
mono_vbo->GetHandle(), mono_vbo->GetVertexCount(),
mono_vbo->GetDrawMode(),
SkiaImageToTexturedMeshRendererImage(image, mesh_filter.stereo_mode()),
- draw_state->transform_3d);
+ model_view_projection_matrix);
// Let Skia know that we've modified GL state.
gr_context_->resetContext();
@@ -538,7 +598,7 @@
}
// Rasterize the passed in render tree to our hardware render target.
- RasterizeRenderTreeToCanvas(render_tree, canvas);
+ RasterizeRenderTreeToCanvas(render_tree, canvas, kBottomLeft_GrSurfaceOrigin);
{
TRACE_EVENT0("cobalt::renderer", "Skia Flush");
@@ -552,7 +612,7 @@
void HardwareRasterizer::Impl::SubmitOffscreen(
const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas) {
DCHECK(thread_checker_.CalledOnValidThread());
- RasterizeRenderTreeToCanvas(render_tree, canvas);
+ RasterizeRenderTreeToCanvas(render_tree, canvas, kBottomLeft_GrSurfaceOrigin);
}
void HardwareRasterizer::Impl::SubmitOffscreenToRenderTarget(
@@ -573,7 +633,7 @@
canvas->clear(SkColorSetARGB(0, 0, 0, 0));
// Render to the canvas and clean up.
- RasterizeRenderTreeToCanvas(render_tree, canvas);
+ RasterizeRenderTreeToCanvas(render_tree, canvas, kTopLeft_GrSurfaceOrigin);
canvas->flush();
sk_output_surface->unref();
}
@@ -679,11 +739,15 @@
}
void HardwareRasterizer::Impl::RasterizeRenderTreeToCanvas(
- const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas) {
+ const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas,
+ GrSurfaceOrigin origin) {
TRACE_EVENT0("cobalt::renderer", "RasterizeRenderTreeToCanvas");
// TODO: This trace uses the name in the current benchmark to keep it work as
// expected. Remove after switching to webdriver benchmark.
TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
+
+ current_surface_origin_.emplace(origin);
+
RenderTreeNodeVisitor::CreateScratchSurfaceFunction
create_scratch_surface_function =
base::Bind(&HardwareRasterizer::Impl::CreateScratchSurface,
@@ -700,6 +764,8 @@
surface_cache_ ? &surface_cache_.value() : NULL);
DCHECK(render_tree);
render_tree->Accept(&visitor);
+
+ current_surface_origin_ = base::nullopt;
}
void HardwareRasterizer::Impl::ResetSkiaState() { gr_context_->resetContext(); }
@@ -732,6 +798,11 @@
impl_->SubmitOffscreen(render_tree, canvas);
}
+SkCanvas* HardwareRasterizer::GetCachedCanvas(
+ const scoped_refptr<backend::RenderTarget>& render_target) {
+ return impl_->GetCanvasFromRenderTarget(render_target);
+}
+
void HardwareRasterizer::AdvanceFrame() {
impl_->AdvanceFrame();
}
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
index aba9fd4..268d03c 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
@@ -68,6 +68,12 @@
void SubmitOffscreen(const scoped_refptr<render_tree::Node>& render_tree,
SkCanvas* canvas);
+ // Get the cached canvas for a render target that would normally go through
+ // Submit(). The cache size is limited, so this should not be used for
+ // generic offscreen render targets.
+ SkCanvas* GetCachedCanvas(
+ const scoped_refptr<backend::RenderTarget>& render_target);
+
// If Submit() is not called, then use this function to tell rasterizer that
// a frame has been submitted.
void AdvanceFrame();
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index 847f2b5..77e0de7 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -86,11 +86,7 @@
// Wait for any resource-related to complete (by waiting for all tasks to
// complete).
if (MessageLoop::current() != self_message_loop_) {
- base::WaitableEvent completion(true, false);
- self_message_loop_->PostTask(FROM_HERE,
- base::Bind(&base::WaitableEvent::Signal,
- base::Unretained(&completion)));
- completion.Wait();
+ self_message_loop_->WaitForFence();
}
}
diff --git a/src/cobalt/renderer/rasterizer/skia/image.h b/src/cobalt/renderer/rasterizer/skia/image.h
index 39aaf3f..453e730 100644
--- a/src/cobalt/renderer/rasterizer/skia/image.h
+++ b/src/cobalt/renderer/rasterizer/skia/image.h
@@ -60,6 +60,12 @@
int source_pitch_in_bytes,
render_tree::PixelFormat pixel_format,
const uint8_t* source_pixels);
+
+ // While of course most skia::Image objects can be rendered in Skia, sometimes
+ // this is not true, such as when they are backed by SbDecodeTarget objects
+ // that assume a specific rasterizer such as GLES2. In this case, we can
+ // fallback to a rasterizer-provided renderer function.
+ virtual bool CanRenderInSkia() const { return true; }
};
// A single-plane image is an image where all data to describe a single pixel
@@ -79,12 +85,6 @@
// If not-null, indicates a rectangle within the image in which the valid
// pixel data is to be found.
virtual const math::Rect* GetContentRegion() const { return NULL; }
-
- // While of course most skia::Image objects can be rendered in Skia, sometimes
- // this is not true, such as when they are backed by SbDecodeTarget objects
- // that assume a specific rasterizer such as GLES2. In this case, we can
- // fallback to a rasterizer-provided renderer function.
- virtual bool CanRenderInSkia() const { return true; }
};
// A multi-plane image is one where different channels may have different planes
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
index f8b34ab..0f2159b 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -696,23 +696,23 @@
// We issue different skia rasterization commands to render the image
// depending on whether it's single or multi planed.
- if (image->GetTypeId() == base::GetTypeId<SinglePlaneImage>()) {
- SinglePlaneImage* single_plane_image =
- base::polymorphic_downcast<SinglePlaneImage*>(image);
+ if (!image->CanRenderInSkia()) {
+ render_image_fallback_function_.Run(image_node, &draw_state_);
+ } else {
+ if (image->GetTypeId() == base::GetTypeId<SinglePlaneImage>()) {
+ SinglePlaneImage* single_plane_image =
+ base::polymorphic_downcast<SinglePlaneImage*>(image);
- if (!single_plane_image->CanRenderInSkia()) {
- render_image_fallback_function_.Run(image_node, &draw_state_);
- } else {
RenderSinglePlaneImage(single_plane_image, &draw_state_,
image_node->data().destination_rect,
&(image_node->data().local_transform));
+ } else if (image->GetTypeId() == base::GetTypeId<MultiPlaneImage>()) {
+ RenderMultiPlaneImage(base::polymorphic_downcast<MultiPlaneImage*>(image),
+ &draw_state_, image_node->data().destination_rect,
+ &(image_node->data().local_transform));
+ } else {
+ NOTREACHED();
}
- } else if (image->GetTypeId() == base::GetTypeId<MultiPlaneImage>()) {
- RenderMultiPlaneImage(base::polymorphic_downcast<MultiPlaneImage*>(image),
- &draw_state_, image_node->data().destination_rect,
- &(image_node->data().local_transform));
- } else {
- NOTREACHED();
}
#if ENABLE_FLUSH_AFTER_EVERY_NODE
diff --git a/src/cobalt/renderer/rasterizer/testdata/DrawOffscreenYUVImage-expected.png b/src/cobalt/renderer/rasterizer/testdata/DrawOffscreenYUVImage-expected.png
new file mode 100644
index 0000000..94d8db6
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/DrawOffscreenYUVImage-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient2Stops-expected.png b/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient2Stops-expected.png
index cf6a82e..8d03f81 100644
--- a/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient2Stops-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient2Stops-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient3Stops-expected.png b/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient3Stops-expected.png
index 08755a5..0dfa9d3 100644
--- a/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient3Stops-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient3Stops-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient5Stops-expected.png b/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient5Stops-expected.png
index c69c5ed..0ae6de9 100644
--- a/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient5Stops-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/HorizontalEllipseGradient5Stops-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient2Stops315Degrees-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient2Stops315Degrees-expected.png
new file mode 100644
index 0000000..fd3b33d
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient2Stops315Degrees-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient2Stops45Degrees-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient2Stops45Degrees-expected.png
new file mode 100644
index 0000000..1c04513
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient2Stops45Degrees-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsArbitraryAngle-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsArbitraryAngle-expected.png
deleted file mode 100644
index 16b7f1b..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsArbitraryAngle-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsLeftRight-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsLeftRight-expected.png
index 5ca9d51..bbe0bf0 100644
--- a/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsLeftRight-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsLeftRight-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsTopBottom-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsTopBottom-expected.png
index b5a2e93..8e5000d 100644
--- a/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsTopBottom-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient2StopsTopBottom-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient3Stops210DegreesInset-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3Stops210DegreesInset-expected.png
new file mode 100644
index 0000000..7ca5cd0
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3Stops210DegreesInset-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient3Stops30DegreesInset-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3Stops30DegreesInset-expected.png
new file mode 100644
index 0000000..3d5a3b5
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3Stops30DegreesInset-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsArbitraryAngle-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsArbitraryAngle-expected.png
deleted file mode 100644
index 3fdd4f3..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsArbitraryAngle-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsLeftRight-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsLeftRight-expected.png
deleted file mode 100644
index e728fed..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsLeftRight-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsLeftRightInset-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsLeftRightInset-expected.png
new file mode 100644
index 0000000..934dfca
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsLeftRightInset-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsTopBottom-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsTopBottom-expected.png
deleted file mode 100644
index 9bcbc01..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsTopBottom-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsTopBottomInset-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsTopBottomInset-expected.png
new file mode 100644
index 0000000..2f998ce
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient3StopsTopBottomInset-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient5Stops150DegreesOutset-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5Stops150DegreesOutset-expected.png
new file mode 100644
index 0000000..1917730
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5Stops150DegreesOutset-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient5Stops330DegreesOutset-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5Stops330DegreesOutset-expected.png
new file mode 100644
index 0000000..a4ddeb5
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5Stops330DegreesOutset-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsArbitraryAngle-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsArbitraryAngle-expected.png
deleted file mode 100644
index bc936c2..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsArbitraryAngle-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsLeftRight-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsLeftRight-expected.png
deleted file mode 100644
index cc2bbf8..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsLeftRight-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsLeftRightOutset-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsLeftRightOutset-expected.png
new file mode 100644
index 0000000..9c380e3
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsLeftRightOutset-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsTopBottom-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsTopBottom-expected.png
deleted file mode 100644
index 8a4b0c4..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsTopBottom-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsTopBottomOutset-expected.png b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsTopBottomOutset-expected.png
new file mode 100644
index 0000000..a61d06c
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LinearGradient5StopsTopBottomOutset-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/RadialGradient2Stops-expected.png b/src/cobalt/renderer/rasterizer/testdata/RadialGradient2Stops-expected.png
index 52c0789..6d34fad 100644
--- a/src/cobalt/renderer/rasterizer/testdata/RadialGradient2Stops-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/RadialGradient2Stops-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/RadialGradient3Stops-expected.png b/src/cobalt/renderer/rasterizer/testdata/RadialGradient3Stops-expected.png
index e0e4204..a1468e3 100644
--- a/src/cobalt/renderer/rasterizer/testdata/RadialGradient3Stops-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/RadialGradient3Stops-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/RadialGradient5Stops-expected.png b/src/cobalt/renderer/rasterizer/testdata/RadialGradient5Stops-expected.png
index 7ba845e..2152126 100644
--- a/src/cobalt/renderer/rasterizer/testdata/RadialGradient5Stops-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/RadialGradient5Stops-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithReflection-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithReflection-expected.png
new file mode 100644
index 0000000..9100c4a
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithReflection-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ThreePlaneYUVImageWithReflection-expected.png b/src/cobalt/renderer/rasterizer/testdata/ThreePlaneYUVImageWithReflection-expected.png
new file mode 100644
index 0000000..4c07e04
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ThreePlaneYUVImageWithReflection-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient2Stops-expected.png b/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient2Stops-expected.png
index db6e775..fd1e097 100644
--- a/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient2Stops-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient2Stops-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient3Stops-expected.png b/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient3Stops-expected.png
index 544b88e..8f4522d 100644
--- a/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient3Stops-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient3Stops-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient5Stops-expected.png b/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient5Stops-expected.png
index b06e3d6..b19c591 100644
--- a/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient5Stops-expected.png
+++ b/src/cobalt/renderer/rasterizer/testdata/VerticalEllipseGradient5Stops-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/renderer.gyp b/src/cobalt/renderer/renderer.gyp
index 3a92967..c57e6f7 100644
--- a/src/cobalt/renderer/renderer.gyp
+++ b/src/cobalt/renderer/renderer.gyp
@@ -22,6 +22,8 @@
'target_name': 'renderer',
'type': 'static_library',
'sources': [
+ 'fps_overlay.cc',
+ 'fps_overlay.h',
'pipeline.cc',
'pipeline.h',
'renderer_module.cc',
diff --git a/src/cobalt/renderer/renderer_module.cc b/src/cobalt/renderer/renderer_module.cc
index 084390e..a983e1b 100644
--- a/src/cobalt/renderer/renderer_module.cc
+++ b/src/cobalt/renderer/renderer_module.cc
@@ -25,7 +25,9 @@
RendererModule::Options::Options()
: skia_glyph_texture_atlas_dimensions(2048, 2048),
- purge_skia_font_caches_on_destruction(true) {
+ purge_skia_font_caches_on_destruction(true),
+ enable_fps_stdout(false),
+ enable_fps_overlay(false) {
// Call into platform-specific code for setting up render module options.
SetPerPlatformDefaultOptions();
}
@@ -76,12 +78,16 @@
// Direct it to render directly to the display.
{
TRACE_EVENT0("cobalt::renderer", "new renderer::Pipeline()");
+ renderer::Pipeline::Options pipeline_options;
+ pipeline_options.enable_fps_stdout = options_.enable_fps_stdout;
+ pipeline_options.enable_fps_overlay = options_.enable_fps_overlay;
+
pipeline_ = make_scoped_ptr(new renderer::Pipeline(
base::Bind(options_.create_rasterizer_function, graphics_context_.get(),
options_),
display_->GetRenderTarget(), graphics_context_.get(),
options_.submit_even_if_render_tree_is_unchanged,
- renderer::Pipeline::kClearToBlack));
+ renderer::Pipeline::kClearToBlack, pipeline_options));
}
}
diff --git a/src/cobalt/renderer/renderer_module.h b/src/cobalt/renderer/renderer_module.h
index 70d0364..49a3187 100644
--- a/src/cobalt/renderer/renderer_module.h
+++ b/src/cobalt/renderer/renderer_module.h
@@ -87,6 +87,9 @@
// transforms needed to render the scene from the camera's view.
GetCameraTransformCallback get_camera_transform;
+ bool enable_fps_stdout;
+ bool enable_fps_overlay;
+
private:
// Implemented per-platform, and allows each platform to customize
// the renderer options.
@@ -105,6 +108,14 @@
return display_->GetRenderTarget();
}
+ render_tree::ResourceProvider* resource_provider() {
+ if (!pipeline_) {
+ return NULL;
+ }
+
+ return pipeline_->GetResourceProvider();
+ }
+
private:
system_window::SystemWindow* system_window_;
Options options_;
diff --git a/src/cobalt/renderer/renderer_parameters_setup.gypi b/src/cobalt/renderer/renderer_parameters_setup.gypi
index ce438b5..6bc5d4f 100644
--- a/src/cobalt/renderer/renderer_parameters_setup.gypi
+++ b/src/cobalt/renderer/renderer_parameters_setup.gypi
@@ -31,7 +31,6 @@
['rasterizer_type == "direct-gles"', {
'defines': [
'COBALT_FORCE_DIRECT_GLES_RASTERIZER',
- 'COBALT_RASTERIZER_USES_DEPTH_BUFFER',
],
}],
],
diff --git a/src/cobalt/script/javascript_engine.h b/src/cobalt/script/javascript_engine.h
index 39f9c0d..4ea8881 100644
--- a/src/cobalt/script/javascript_engine.h
+++ b/src/cobalt/script/javascript_engine.h
@@ -50,6 +50,12 @@
static scoped_ptr<JavaScriptEngine> CreateEngine(
const Options& options = Options());
+ // Updates the memory usage and returns the total memory that is reserved
+ // across all of the engines. This includes the part that is actually occupied
+ // by JS objects, and the part that is not yet.
+ // This function is defined per-implementation.
+ static size_t UpdateMemoryStatsAndReturnReserved();
+
// Create a new JavaScript global object proxy.
virtual scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() = 0;
@@ -60,11 +66,6 @@
// Javascript object. This may mean collection needs to happen sooner.
virtual void ReportExtraMemoryCost(size_t bytes) = 0;
- // Updates the memory usage and returns the total memory that is reserved by
- // the engine. This includes the part that is actually occupied by JS objects,
- // and the part that is not yet.
- virtual size_t UpdateMemoryStatsAndReturnReserved() = 0;
-
// Installs an ErrorHandler for listening to javascript errors.
// Returns true if the error handler could be installed. False otherwise.
virtual bool RegisterErrorHandler(ErrorHandler handler) = 0;
diff --git a/src/cobalt/script/mozjs-45/conversion_helpers.h b/src/cobalt/script/mozjs-45/conversion_helpers.h
index c8e4c93..c23b660 100644
--- a/src/cobalt/script/mozjs-45/conversion_helpers.h
+++ b/src/cobalt/script/mozjs-45/conversion_helpers.h
@@ -489,6 +489,14 @@
out_value.setObject(*object);
}
+// raw object pointer -> JSValue
+template <typename T>
+inline void ToJSValue(JSContext* context, T* in_object,
+ JS::MutableHandleValue out_value) {
+ TRACK_MEMORY_SCOPE("Javascript");
+ ToJSValue(context, scoped_refptr<T>(in_object), out_value);
+}
+
// JSValue -> object
template <typename T>
inline void FromJSValue(JSContext* context, JS::HandleValue value,
@@ -509,25 +517,24 @@
return;
}
DCHECK(js_object);
- if (js::IsProxy(js_object)) {
- JS::RootedObject wrapper(context, js::GetProxyTargetObject(js_object));
- MozjsGlobalEnvironment* global_environment =
- static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
- const WrapperFactory* wrapper_factory =
- global_environment->wrapper_factory();
- if (wrapper_factory->IsWrapper(wrapper)) {
- bool object_implements_interface =
- wrapper_factory->DoesObjectImplementInterface(js_object,
- base::GetTypeId<T>());
- if (!object_implements_interface) {
- exception_state->SetSimpleException(kDoesNotImplementInterface);
- return;
- }
- WrapperPrivate* wrapper_private =
- WrapperPrivate::GetFromWrapperObject(wrapper);
- *out_object = wrapper_private->wrappable<T>();
+ JS::RootedObject wrapper(context, js::IsProxy(js_object)
+ ? js::GetProxyTargetObject(js_object)
+ : js_object);
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ const WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (wrapper_factory->IsWrapper(wrapper)) {
+ bool object_implements_interface =
+ wrapper_factory->DoesObjectImplementInterface(js_object,
+ base::GetTypeId<T>());
+ if (!object_implements_interface) {
+ exception_state->SetSimpleException(kDoesNotImplementInterface);
return;
}
+ WrapperPrivate* wrapper_private =
+ WrapperPrivate::GetFromWrapperObject(wrapper);
+ *out_object = wrapper_private->wrappable<T>();
+ return;
}
// This is not a platform object. Return a type error.
exception_state->SetSimpleException(kDoesNotImplementInterface);
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.cc b/src/cobalt/script/mozjs-45/mozjs_engine.cc
index e1d482d..6882f71 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.cc
@@ -26,6 +26,7 @@
#include "starboard/once.h"
#include "third_party/mozjs-45/js/public/Initialization.h"
#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "third_party/mozjs-45/js/src/vm/Runtime.h"
namespace cobalt {
namespace script {
@@ -79,38 +80,35 @@
StaticMemorySingletonTraits<EngineStats> >::get();
}
- void EngineCreated() {
- base::AutoLock auto_lock(lock_);
- ++engine_count_;
- }
-
- void EngineDestroyed() {
- base::AutoLock auto_lock(lock_);
- --engine_count_;
- }
+ void EngineCreated() { ++engine_count_; }
+ void EngineDestroyed() { --engine_count_; }
size_t UpdateMemoryStatsAndReturnReserved() {
- base::AutoLock auto_lock(lock_);
- if (engine_count_.value() == 0) {
- return 0u;
- }
+ // Accessing CVals triggers a lock, so rely on local variables when
+ // possible to avoid unecessary locking.
+ size_t allocated_memory =
+ MemoryAllocatorReporter::Get()->GetCurrentBytesAllocated();
+ size_t mapped_memory =
+ MemoryAllocatorReporter::Get()->GetCurrentBytesMapped();
- return 0u;
+ allocated_memory_ = allocated_memory;
+ mapped_memory_ = mapped_memory;
+
+ return allocated_memory + mapped_memory;
}
private:
- base::Lock lock_;
- base::CVal<size_t, base::CValPublic> allocated_memory_;
- base::CVal<size_t, base::CValPublic> mapped_memory_;
- base::CVal<size_t> engine_count_;
+ base::CVal<int> engine_count_;
+ base::CVal<base::cval::SizeInBytes, base::CValPublic> allocated_memory_;
+ base::CVal<base::cval::SizeInBytes, base::CValPublic> mapped_memory_;
};
EngineStats::EngineStats()
- : allocated_memory_("Memory.JS.AllocatedMemory", 0,
+ : engine_count_("Count.JS.Engine", 0,
+ "Total JavaScript engine registered."),
+ allocated_memory_("Memory.JS.AllocatedMemory", 0,
"JS memory occupied by the Mozjs allocator."),
- mapped_memory_("Memory.JS.MappedMemory", 0, "JS mapped memory."),
- engine_count_("Count.JS.Engine", 0,
- "Total JavaScript engine registered.") {}
+ mapped_memory_("Memory.JS.MappedMemory", 0, "JS mapped memory.") {}
// Pretend we always preserve wrappers since we never call
// SetPreserveWrapperCallback anywhere else. This is necessary for
@@ -126,6 +124,7 @@
void CallShutDown(void*) { JS_ShutDown(); }
void CallInitAndRegisterShutDownOnce() {
+ js::DisableExtraThreads();
const bool js_init_result = JS_Init();
CHECK(js_init_result);
base::AtExitManager::RegisterCallback(CallShutDown, NULL);
@@ -231,10 +230,6 @@
}
}
-size_t MozjsEngine::UpdateMemoryStatsAndReturnReserved() {
- return EngineStats::GetInstance()->UpdateMemoryStatsAndReturnReserved();
-}
-
bool MozjsEngine::RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) {
error_handler_ = handler;
return true;
@@ -331,5 +326,10 @@
return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine(moz_options));
}
+size_t JavaScriptEngine::UpdateMemoryStatsAndReturnReserved() {
+ return mozjs::EngineStats::GetInstance()
+ ->UpdateMemoryStatsAndReturnReserved();
+}
+
} // namespace script
} // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.h b/src/cobalt/script/mozjs-45/mozjs_engine.h
index e4a3765..9adb4e2 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.h
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.h
@@ -39,7 +39,6 @@
scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() OVERRIDE;
void CollectGarbage() OVERRIDE;
void ReportExtraMemoryCost(size_t bytes) OVERRIDE;
- size_t UpdateMemoryStatsAndReturnReserved() OVERRIDE;
bool RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) OVERRIDE;
private:
diff --git a/src/cobalt/script/mozjs-45/native_promise.h b/src/cobalt/script/mozjs-45/native_promise.h
index cc4be5d..fbe330c 100644
--- a/src/cobalt/script/mozjs-45/native_promise.h
+++ b/src/cobalt/script/mozjs-45/native_promise.h
@@ -169,6 +169,19 @@
out_value.setObjectOrNull(native_promise->promise());
}
+// Explicitly defer to the const version here so that a more generic non-const
+// version of this function does not get called instead, in the case that
+// |promise_holder| is not const.
+template <typename T>
+inline void ToJSValue(JSContext* context,
+ ScriptValue<Promise<T> >* promise_holder,
+ JS::MutableHandleValue out_value) {
+ TRACK_MEMORY_SCOPE("Javascript");
+ ToJSValue(context,
+ const_cast<const ScriptValue<Promise<T> >*>(promise_holder),
+ out_value);
+}
+
// Destroys |promise_holder| as soon as the conversion is done.
// This is useful when a wrappable is not interested in retaining a reference
// to a promise, typically when a promise is resolved or rejected synchronously.
diff --git a/src/cobalt/script/mozjs-45/wrapper_factory.cc b/src/cobalt/script/mozjs-45/wrapper_factory.cc
index 9395b73..dd429b9 100644
--- a/src/cobalt/script/mozjs-45/wrapper_factory.cc
+++ b/src/cobalt/script/mozjs-45/wrapper_factory.cc
@@ -64,7 +64,10 @@
}
bool WrapperFactory::IsWrapper(JS::HandleObject wrapper) const {
- return JS_GetPrivate(wrapper) != NULL;
+ // If the object doesn't have a wrapper private, it means that it is not a
+ // platform object.
+ return (JS_GetClass(wrapper)->flags & JSCLASS_HAS_PRIVATE) &&
+ JS_GetPrivate(wrapper) != NULL;
}
scoped_ptr<Wrappable::WeakWrapperHandle> WrapperFactory::CreateWrapper(
diff --git a/src/cobalt/script/mozjs/conversion_helpers.h b/src/cobalt/script/mozjs/conversion_helpers.h
index 0aa9192..bb5a90d 100644
--- a/src/cobalt/script/mozjs/conversion_helpers.h
+++ b/src/cobalt/script/mozjs/conversion_helpers.h
@@ -471,6 +471,14 @@
out_value.set(OBJECT_TO_JSVAL(object));
}
+// raw object pointer -> JSValue
+template <typename T>
+inline void ToJSValue(JSContext* context, T* in_object,
+ JS::MutableHandleValue out_value) {
+ TRACK_MEMORY_SCOPE("Javascript");
+ ToJSValue(context, scoped_refptr<T>(in_object), out_value);
+}
+
// JSValue -> object
template <typename T>
inline void FromJSValue(JSContext* context, JS::HandleValue value,
@@ -491,25 +499,24 @@
return;
}
DCHECK(js_object);
- if (js::IsProxy(js_object)) {
- JS::RootedObject wrapper(context, js::GetProxyTargetObject(js_object));
- MozjsGlobalEnvironment* global_environment =
- static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
- const WrapperFactory* wrapper_factory =
- global_environment->wrapper_factory();
- if (wrapper_factory->IsWrapper(wrapper)) {
- bool object_implements_interface =
- wrapper_factory->DoesObjectImplementInterface(js_object,
- base::GetTypeId<T>());
- if (!object_implements_interface) {
- exception_state->SetSimpleException(kDoesNotImplementInterface);
- return;
- }
- WrapperPrivate* wrapper_private =
- WrapperPrivate::GetFromWrapperObject(wrapper);
- *out_object = wrapper_private->wrappable<T>();
+ JS::RootedObject wrapper(context, js::IsProxy(js_object)
+ ? js::GetProxyTargetObject(js_object)
+ : js_object);
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ const WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (wrapper_factory->IsWrapper(wrapper)) {
+ bool object_implements_interface =
+ wrapper_factory->DoesObjectImplementInterface(js_object,
+ base::GetTypeId<T>());
+ if (!object_implements_interface) {
+ exception_state->SetSimpleException(kDoesNotImplementInterface);
return;
}
+ WrapperPrivate* wrapper_private =
+ WrapperPrivate::GetFromWrapperObject(wrapper);
+ *out_object = wrapper_private->wrappable<T>();
+ return;
}
// This is not a platform object. Return a type error.
exception_state->SetSimpleException(kDoesNotImplementInterface);
diff --git a/src/cobalt/script/mozjs/mozjs_engine.cc b/src/cobalt/script/mozjs/mozjs_engine.cc
index 7040fb3..0f1760e 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs/mozjs_engine.cc
@@ -55,41 +55,35 @@
StaticMemorySingletonTraits<EngineStats> >::get();
}
- void EngineCreated() {
- base::AutoLock auto_lock(lock_);
- ++engine_count_;
- }
-
- void EngineDestroyed() {
- base::AutoLock auto_lock(lock_);
- --engine_count_;
- }
+ void EngineCreated() { ++engine_count_; }
+ void EngineDestroyed() { --engine_count_; }
size_t UpdateMemoryStatsAndReturnReserved() {
- base::AutoLock auto_lock(lock_);
- if (engine_count_.value() == 0) {
- return 0;
- }
- allocated_memory_ =
+ // Accessing CVals triggers a lock, so rely on local variables when
+ // possible to avoid unecessary locking.
+ size_t allocated_memory =
MemoryAllocatorReporter::Get()->GetCurrentBytesAllocated();
- mapped_memory_ = MemoryAllocatorReporter::Get()->GetCurrentBytesMapped();
- return allocated_memory_ + mapped_memory_;
+ size_t mapped_memory =
+ MemoryAllocatorReporter::Get()->GetCurrentBytesMapped();
+
+ allocated_memory_ = allocated_memory;
+ mapped_memory_ = mapped_memory;
+
+ return allocated_memory + mapped_memory;
}
private:
- base::Lock lock_;
- base::CVal<size_t, base::CValPublic> allocated_memory_;
- base::CVal<size_t, base::CValPublic> mapped_memory_;
- base::CVal<size_t> engine_count_;
+ base::CVal<int> engine_count_;
+ base::CVal<base::cval::SizeInBytes, base::CValPublic> allocated_memory_;
+ base::CVal<base::cval::SizeInBytes, base::CValPublic> mapped_memory_;
};
EngineStats::EngineStats()
- : allocated_memory_("Memory.JS.AllocatedMemory", 0,
+ : engine_count_("Count.JS.Engine", 0,
+ "Total JavaScript engine registered."),
+ allocated_memory_("Memory.JS.AllocatedMemory", 0,
"JS memory occupied by the Mozjs allocator."),
- mapped_memory_("Memory.JS.MappedMemory", 0, "JS mapped memory."),
- engine_count_("Count.JS.Engine", 0,
- "Total JavaScript engine registered.") {
-}
+ mapped_memory_("Memory.JS.MappedMemory", 0, "JS mapped memory.") {}
// Pretend we always preserve wrappers since we never call
// SetPreserveWrapperCallback anywhere else. This is necessary for
@@ -182,10 +176,6 @@
}
}
-size_t MozjsEngine::UpdateMemoryStatsAndReturnReserved() {
- return EngineStats::GetInstance()->UpdateMemoryStatsAndReturnReserved();
-}
-
bool MozjsEngine::RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) {
error_handler_ = handler;
JSDebugErrorHook hook = ErrorHookCallback;
@@ -303,5 +293,10 @@
new mozjs::MozjsEngine(moz_options));
}
+size_t JavaScriptEngine::UpdateMemoryStatsAndReturnReserved() {
+ return mozjs::EngineStats::GetInstance()
+ ->UpdateMemoryStatsAndReturnReserved();
+}
+
} // namespace script
} // namespace cobalt
diff --git a/src/cobalt/script/mozjs/mozjs_engine.h b/src/cobalt/script/mozjs/mozjs_engine.h
index 3bd5235..5c3d460 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.h
+++ b/src/cobalt/script/mozjs/mozjs_engine.h
@@ -41,7 +41,6 @@
void CollectGarbage() OVERRIDE;
void ReportExtraMemoryCost(size_t bytes) OVERRIDE;
- size_t UpdateMemoryStatsAndReturnReserved() OVERRIDE;
bool RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) OVERRIDE;
private:
diff --git a/src/cobalt/script/mozjs/native_promise.h b/src/cobalt/script/mozjs/native_promise.h
index f184076..25f9e99 100644
--- a/src/cobalt/script/mozjs/native_promise.h
+++ b/src/cobalt/script/mozjs/native_promise.h
@@ -169,6 +169,19 @@
out_value.setObjectOrNull(native_promise->promise());
}
+// Explicitly defer to the const version here so that a more generic non-const
+// version of this function does not get called instead, in the case that
+// |promise_holder| is not const.
+template <typename T>
+inline void ToJSValue(JSContext* context,
+ ScriptValue<Promise<T> >* promise_holder,
+ JS::MutableHandleValue out_value) {
+ TRACK_MEMORY_SCOPE("Javascript");
+ ToJSValue(context,
+ const_cast<const ScriptValue<Promise<T> >*>(promise_holder),
+ out_value);
+}
+
// Destroys |promise_holder| as soon as the conversion is done.
// This is useful when a wrappable is not interested in retaining a reference
// to a promise, typically when a promise is resolved or rejected synchronously.
diff --git a/src/cobalt/script/mozjs/wrapper_factory.cc b/src/cobalt/script/mozjs/wrapper_factory.cc
index c456283..649bb9a 100644
--- a/src/cobalt/script/mozjs/wrapper_factory.cc
+++ b/src/cobalt/script/mozjs/wrapper_factory.cc
@@ -64,7 +64,10 @@
}
bool WrapperFactory::IsWrapper(JS::HandleObject wrapper) const {
- return JS_GetPrivate(wrapper) != NULL;
+ // If the object doesn't have a wrapper private, it means that it is not a
+ // platform object.
+ return (JS_GetClass(wrapper)->flags & JSCLASS_HAS_PRIVATE) &&
+ JS_GetPrivate(wrapper) != NULL;
}
scoped_ptr<Wrappable::WeakWrapperHandle> WrapperFactory::CreateWrapper(
diff --git a/src/cobalt/speech/google_speech_service.cc b/src/cobalt/speech/google_speech_service.cc
index 1b49b80..23dedfd 100644
--- a/src/cobalt/speech/google_speech_service.cc
+++ b/src/cobalt/speech/google_speech_service.cc
@@ -290,14 +290,14 @@
const char* speech_api_key = "";
#if defined(OS_STARBOARD)
-#if SB_VERSION(2)
+#if SB_API_VERSION >= 2
const int kSpeechApiKeyLength = 100;
char buffer[kSpeechApiKeyLength] = {0};
bool result = SbSystemGetProperty(kSbSystemPropertySpeechApiKey, buffer,
SB_ARRAY_SIZE_INT(buffer));
SB_DCHECK(result);
speech_api_key = result ? buffer : "";
-#endif // SB_VERSION(2)
+#endif // SB_API_VERSION >= 2
#endif // defined(OS_STARBOARD)
up_url = AppendQueryParameter(up_url, "key", speech_api_key);
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp
index c3b77f8..8b3e15b 100644
--- a/src/cobalt/speech/speech.gyp
+++ b/src/cobalt/speech/speech.gyp
@@ -14,7 +14,7 @@
{
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
{
diff --git a/src/cobalt/speech/speech_configuration.h b/src/cobalt/speech/speech_configuration.h
index 0c421e3..8b9e0ba 100644
--- a/src/cobalt/speech/speech_configuration.h
+++ b/src/cobalt/speech/speech_configuration.h
@@ -19,14 +19,12 @@
#if defined(OS_STARBOARD)
#include "starboard/configuration.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#define SB_USE_SB_MICROPHONE 1
#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#define SB_USE_SB_SPEECH_RECOGNIZER 1
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#endif // defined(OS_STARBOARD)
#endif // COBALT_SPEECH_SPEECH_CONFIGURATION_H_
diff --git a/src/cobalt/speech/speech_synthesis_utterance.cc b/src/cobalt/speech/speech_synthesis_utterance.cc
index e6d67f5..010dfab 100644
--- a/src/cobalt/speech/speech_synthesis_utterance.cc
+++ b/src/cobalt/speech/speech_synthesis_utterance.cc
@@ -20,7 +20,11 @@
SpeechSynthesisUtterance::SpeechSynthesisUtterance()
: volume_(1.0f), rate_(1.0f), pitch_(1.0f), pending_speak_(false) {}
SpeechSynthesisUtterance::SpeechSynthesisUtterance(const std::string& text)
- : text_(text), pending_speak_(false) {}
+ : text_(text),
+ volume_(1.0f),
+ rate_(1.0f),
+ pitch_(1.0f),
+ pending_speak_(false) {}
SpeechSynthesisUtterance::SpeechSynthesisUtterance(
const scoped_refptr<SpeechSynthesisUtterance>& utterance)
diff --git a/src/cobalt/storage/storage.gyp b/src/cobalt/storage/storage.gyp
index 994fe1c..94bfd46 100644
--- a/src/cobalt/storage/storage.gyp
+++ b/src/cobalt/storage/storage.gyp
@@ -16,7 +16,7 @@
'includes': [ '../build/contents_dir.gypi' ],
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
{
diff --git a/src/cobalt/storage/storage_manager.cc b/src/cobalt/storage/storage_manager.cc
index 0f4670f..ac4d7a2 100644
--- a/src/cobalt/storage/storage_manager.cc
+++ b/src/cobalt/storage/storage_manager.cc
@@ -416,12 +416,7 @@
// it is possible that there are no flushes pending at this instant, but there
// are tasks queued on |sql_message_loop_| that will begin a flush, and so
// we make sure that these are executed first.
- base::WaitableEvent current_queue_finished_event_(true, false);
- sql_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&base::WaitableEvent::Signal,
- base::Unretained(¤t_queue_finished_event_)));
- current_queue_finished_event_.Wait();
+ sql_message_loop_->WaitForFence();
// Now wait for all pending flushes to wrap themselves up. This may involve
// the savegame I/O thread and the SQL thread posting tasks to each other.
diff --git a/src/cobalt/streams/embedded_scripts/readable_stream.js b/src/cobalt/streams/embedded_scripts/readable_stream.js
index 6a27d8c..837f0b7 100644
--- a/src/cobalt/streams/embedded_scripts/readable_stream.js
+++ b/src/cobalt/streams/embedded_scripts/readable_stream.js
@@ -1,48 +1,20 @@
-'use strict';(function(global){const v8_InternalPackedArray=global.Array;function v8_createPrivateSymbol(x){return Symbol(x)}function v8_simpleBind(fn,obj){return fn.bind(obj)}const arraySlice=global.Array.prototype.slice;function v8_uncurryThis(fn){return function(obj){return fn.apply(obj,arraySlice.call(arguments,1))}}const v8_PromiseBase=global.Promise;const _promiseResolve=v8_createPrivateSymbol("[[Resolve]]");const _promiseReject=v8_createPrivateSymbol("[[Reject]]");function v8_Promise(){var that=
-this;v8_PromiseBase.call(this,function(resolve,reject){that[_promiseResolve]=resolve;that[_promiseReject]=reject})}v8_Promise.prototype=v8_PromiseBase.prototype;function v8_createPromise(){return new v8_Promise}function v8_isPromise(obj){return obj instanceof v8_Promise}function v8_resolvePromise(promise,value){promise[_promiseResolve](value)}function v8_rejectPromise(promise,reason){promise[_promiseReject](reason)}function v8_markPromiseAsHandled(promise){try{thenPromise(promise,undefined,()=>{})}catch(error){}}
-const QUEUE_MAX_ARRAY_SIZE=16384;class SimpleQueue{constructor(){this.front={elements:new v8_InternalPackedArray,next:undefined};this.back=this.front;this.cursor=0;this.size=0}get length(){return this.size}push(element){++this.size;if(this.back.elements.length===QUEUE_MAX_ARRAY_SIZE){const oldBack=this.back;this.back={elements:new v8_InternalPackedArray,next:undefined};oldBack.next=this.back}this.back.elements.push(element)}shift(){--this.size;if(this.front.elements.length===this.cursor){this.front=
-this.front.next;this.cursor=0}const element=this.front.elements[this.cursor];this.front.elements[this.cursor]=undefined;++this.cursor;return element}forEach(callback){let i=this.cursor;let node=this.front;let elements=node.elements;while(i!==elements.length||node.next!==undefined){if(i===elements.length){node=node.next;elements=node.elements;i=0}callback(elements[i]);++i}}peek(){if(this.front.elements.length===this.cursor)return this.front.next.elements[0];return this.front.elements[this.cursor]}}
-const streamErrors_illegalInvocation="Illegal invocation";const streamErrors_illegalConstructor="Illegal constructor";const streamErrors_invalidType="Invalid type is specified";const streamErrors_invalidSize="The return value of a queuing strategy's size function must be a finite, non-NaN, non-negative number";const streamErrors_sizeNotAFunction="A queuing strategy's size property must be a function";const streamErrors_invalidHWM="A queueing strategy's highWaterMark property must be a nonnegative, non-NaN number";
-const _reader=v8_createPrivateSymbol("[[reader]]");const _storedError=v8_createPrivateSymbol("[[storedError]]");const _controller=v8_createPrivateSymbol("[[controller]]");const _closedPromise=v8_createPrivateSymbol("[[closedPromise]]");const _ownerReadableStream=v8_createPrivateSymbol("[[ownerReadableStream]]");const _readRequests=v8_createPrivateSymbol("[[readRequests]]");const createWithExternalControllerSentinel=v8_createPrivateSymbol("flag for UA-created ReadableStream to pass");const _readableStreamBits=
-v8_createPrivateSymbol("bit field for [[state]] and [[disturbed]]");const DISTURBED=1;const STATE_MASK=6;const STATE_BITS_OFFSET=1;const STATE_READABLE=0;const STATE_CLOSED=1;const STATE_ERRORED=2;const _underlyingSource=v8_createPrivateSymbol("[[underlyingSource]]");const _controlledReadableStream=v8_createPrivateSymbol("[[controlledReadableStream]]");const _queue=v8_createPrivateSymbol("[[queue]]");const _totalQueuedSize=v8_createPrivateSymbol("[[totalQueuedSize]]");const _strategySize=v8_createPrivateSymbol("[[strategySize]]");
-const _strategyHWM=v8_createPrivateSymbol("[[strategyHWM]]");const _readableStreamDefaultControllerBits=v8_createPrivateSymbol("bit field for [[started]], [[closeRequested]], [[pulling]], [[pullAgain]]");const STARTED=1;const CLOSE_REQUESTED=2;const PULLING=4;const PULL_AGAIN=8;const EXTERNALLY_CONTROLLED=16;const undefined=global.undefined;const Infinity=global.Infinity;const defineProperty=global.Object.defineProperty;const hasOwnProperty=v8_uncurryThis(global.Object.hasOwnProperty);const callFunction=
-v8_uncurryThis(global.Function.prototype.call);const applyFunction=v8_uncurryThis(global.Function.prototype.apply);const TypeError=global.TypeError;const RangeError=global.RangeError;const Number=global.Number;const Number_isNaN=Number.isNaN;const Number_isFinite=Number.isFinite;const Promise=global.Promise;const thenPromise=v8_uncurryThis(Promise.prototype.then);const Promise_resolve=v8_simpleBind(Promise.resolve,Promise);const Promise_reject=v8_simpleBind(Promise.reject,Promise);const errCancelLockedStream=
-"Cannot cancel a readable stream that is locked to a reader";const errEnqueueCloseRequestedStream="Cannot enqueue a chunk into a readable stream that is closed or has been requested to be closed";const errCancelReleasedReader="This readable stream reader has been released and cannot be used to cancel its previous owner stream";const errReadReleasedReader="This readable stream reader has been released and cannot be used to read from its previous owner stream";const errCloseCloseRequestedStream="Cannot close a readable stream that has already been requested to be closed";
-const errEnqueueClosedStream="Cannot enqueue a chunk into a closed readable stream";const errEnqueueErroredStream="Cannot enqueue a chunk into an errored readable stream";const errCloseClosedStream="Cannot close a closed readable stream";const errCloseErroredStream="Cannot close an errored readable stream";const errErrorClosedStream="Cannot error a close readable stream";const errErrorErroredStream="Cannot error a readable stream that is already errored";const errGetReaderNotByteStream="This readable stream does not support BYOB readers";
-const errGetReaderBadMode='Invalid reader mode given: expected undefined or "byob"';const errReaderConstructorBadArgument="ReadableStreamReader constructor argument is not a readable stream";const errReaderConstructorStreamAlreadyLocked="ReadableStreamReader constructor can only accept readable streams that are not yet locked to a reader";const errReleaseReaderWithPendingRead="Cannot release a readable stream reader when it still has outstanding read() calls that have not yet settled";const errReleasedReaderClosedPromise=
-"This readable stream reader has been released and cannot be used to monitor the stream's state";const errTmplMustBeFunctionOrUndefined=(name)=>`${name} must be a function or undefined`;class ReadableStream{constructor(){const underlyingSource=arguments[0]===undefined?{}:arguments[0];const strategy=arguments[1]===undefined?{}:arguments[1];const size=strategy.size;let highWaterMark=strategy.highWaterMark;if(highWaterMark===undefined)highWaterMark=1;this[_readableStreamBits]=0;ReadableStreamSetState(this,
-STATE_READABLE);this[_reader]=undefined;this[_storedError]=undefined;this[_controller]=undefined;const type=underlyingSource.type;const typeString=String(type);if(typeString==="bytes")throw new RangeError("bytes type is not yet implemented");else if(type!==undefined)throw new RangeError(streamErrors_invalidType);this[_controller]=new ReadableStreamDefaultController(this,underlyingSource,size,highWaterMark,arguments[2])}get locked(){if(IsReadableStream(this)===false)throw new TypeError(streamErrors_illegalInvocation);
-return IsReadableStreamLocked(this)}cancel(reason){if(IsReadableStream(this)===false)return Promise_reject(new TypeError(streamErrors_illegalInvocation));if(IsReadableStreamLocked(this)===true)return Promise_reject(new TypeError(errCancelLockedStream));return ReadableStreamCancel(this,reason)}getReader({mode}={}){if(IsReadableStream(this)===false)throw new TypeError(streamErrors_illegalInvocation);if(mode==="byob")throw new TypeError(errGetReaderNotByteStream);if(mode===undefined)return AcquireReadableStreamDefaultReader(this);
-throw new RangeError(errGetReaderBadMode);}pipeThrough({writable,readable},options){throw new TypeError("pipeThrough not implemented");}pipeTo(dest,{preventClose,preventAbort,preventCancel}={}){throw new TypeError("pipeTo not implemented");}tee(){if(IsReadableStream(this)===false)throw new TypeError(streamErrors_illegalInvocation);return ReadableStreamTee(this)}}class ReadableStreamDefaultController{constructor(stream,underlyingSource,size,highWaterMark){if(IsReadableStream(stream)===false)throw new TypeError(streamErrors_illegalConstructor);
-if(stream[_controller]!==undefined)throw new TypeError(streamErrors_illegalConstructor);this[_controlledReadableStream]=stream;this[_underlyingSource]=underlyingSource;this[_queue]=new SimpleQueue;this[_totalQueuedSize]=0;this[_readableStreamDefaultControllerBits]=0;if(arguments[4]===createWithExternalControllerSentinel)this[_readableStreamDefaultControllerBits]|=EXTERNALLY_CONTROLLED;const normalizedStrategy=ValidateAndNormalizeQueuingStrategy(size,highWaterMark);this[_strategySize]=normalizedStrategy.size;
-this[_strategyHWM]=normalizedStrategy.highWaterMark;const controller=this;const startResult=CallOrNoop(underlyingSource,"start",this,"underlyingSource.start");thenPromise(Promise_resolve(startResult),()=>{controller[_readableStreamDefaultControllerBits]|=STARTED;ReadableStreamDefaultControllerCallPullIfNeeded(controller)},(r)=>{if(ReadableStreamGetState(stream)===STATE_READABLE)ReadableStreamDefaultControllerError(controller,r)})}get desiredSize(){if(IsReadableStreamDefaultController(this)===false)throw new TypeError(streamErrors_illegalInvocation);
-return ReadableStreamDefaultControllerGetDesiredSize(this)}close(){if(IsReadableStreamDefaultController(this)===false)throw new TypeError(streamErrors_illegalInvocation);const stream=this[_controlledReadableStream];if(this[_readableStreamDefaultControllerBits]&CLOSE_REQUESTED)throw new TypeError(errCloseCloseRequestedStream);const state=ReadableStreamGetState(stream);if(state===STATE_ERRORED)throw new TypeError(errCloseErroredStream);if(state===STATE_CLOSED)throw new TypeError(errCloseClosedStream);
-return ReadableStreamDefaultControllerClose(this)}enqueue(chunk){if(IsReadableStreamDefaultController(this)===false)throw new TypeError(streamErrors_illegalInvocation);const stream=this[_controlledReadableStream];if(this[_readableStreamDefaultControllerBits]&CLOSE_REQUESTED)throw new TypeError(errEnqueueCloseRequestedStream);const state=ReadableStreamGetState(stream);if(state===STATE_ERRORED)throw new TypeError(errEnqueueErroredStream);if(state===STATE_CLOSED)throw new TypeError(errEnqueueClosedStream);
-return ReadableStreamDefaultControllerEnqueue(this,chunk)}error(e){if(IsReadableStreamDefaultController(this)===false)throw new TypeError(streamErrors_illegalInvocation);const stream=this[_controlledReadableStream];const state=ReadableStreamGetState(stream);if(state===STATE_ERRORED)throw new TypeError(errErrorErroredStream);if(state===STATE_CLOSED)throw new TypeError(errErrorClosedStream);return ReadableStreamDefaultControllerError(this,e)}}function ReadableStreamDefaultControllerCancel(controller,
-reason){controller[_queue]=new SimpleQueue;const underlyingSource=controller[_underlyingSource];return PromiseCallOrNoop(underlyingSource,"cancel",reason,"underlyingSource.cancel")}function ReadableStreamDefaultControllerPull(controller){const stream=controller[_controlledReadableStream];if(controller[_queue].length>0){const chunk=DequeueValue(controller);if(controller[_readableStreamDefaultControllerBits]&CLOSE_REQUESTED&&controller[_queue].length===0)ReadableStreamClose(stream);else ReadableStreamDefaultControllerCallPullIfNeeded(controller);
-return Promise_resolve(CreateIterResultObject(chunk,false))}const pendingPromise=ReadableStreamAddReadRequest(stream);ReadableStreamDefaultControllerCallPullIfNeeded(controller);return pendingPromise}function ReadableStreamAddReadRequest(stream){const promise=v8_createPromise();stream[_reader][_readRequests].push(promise);return promise}class ReadableStreamDefaultReader{constructor(stream){if(IsReadableStream(stream)===false)throw new TypeError(errReaderConstructorBadArgument);if(IsReadableStreamLocked(stream)===
-true)throw new TypeError(errReaderConstructorStreamAlreadyLocked);ReadableStreamReaderGenericInitialize(this,stream);this[_readRequests]=new SimpleQueue}get closed(){if(IsReadableStreamDefaultReader(this)===false)return Promise_reject(new TypeError(streamErrors_illegalInvocation));return this[_closedPromise]}cancel(reason){if(IsReadableStreamDefaultReader(this)===false)return Promise_reject(new TypeError(streamErrors_illegalInvocation));const stream=this[_ownerReadableStream];if(stream===undefined)return Promise_reject(new TypeError(errCancelReleasedReader));
-return ReadableStreamReaderGenericCancel(this,reason)}read(){if(IsReadableStreamDefaultReader(this)===false)return Promise_reject(new TypeError(streamErrors_illegalInvocation));if(this[_ownerReadableStream]===undefined)return Promise_reject(new TypeError(errReadReleasedReader));return ReadableStreamDefaultReaderRead(this)}releaseLock(){if(IsReadableStreamDefaultReader(this)===false)throw new TypeError(streamErrors_illegalInvocation);const stream=this[_ownerReadableStream];if(stream===undefined)return undefined;
-if(this[_readRequests].length>0)throw new TypeError(errReleaseReaderWithPendingRead);ReadableStreamReaderGenericRelease(this)}}function ReadableStreamReaderGenericCancel(reader,reason){return ReadableStreamCancel(reader[_ownerReadableStream],reason)}function AcquireReadableStreamDefaultReader(stream){return new ReadableStreamDefaultReader(stream)}function ReadableStreamCancel(stream,reason){stream[_readableStreamBits]|=DISTURBED;const state=ReadableStreamGetState(stream);if(state===STATE_CLOSED)return Promise_resolve(undefined);
-if(state===STATE_ERRORED)return Promise_reject(stream[_storedError]);ReadableStreamClose(stream);const sourceCancelPromise=ReadableStreamDefaultControllerCancel(stream[_controller],reason);return thenPromise(sourceCancelPromise,()=>undefined)}function ReadableStreamDefaultControllerClose(controller){const stream=controller[_controlledReadableStream];controller[_readableStreamDefaultControllerBits]|=CLOSE_REQUESTED;if(controller[_queue].length===0)ReadableStreamClose(stream)}function ReadableStreamFulfillReadRequest(stream,
-chunk,done){const reader=stream[_reader];const readRequest=stream[_reader][_readRequests].shift();v8_resolvePromise(readRequest,CreateIterResultObject(chunk,done))}function ReadableStreamDefaultControllerEnqueue(controller,chunk){const stream=controller[_controlledReadableStream];if(IsReadableStreamLocked(stream)===true&&ReadableStreamGetNumReadRequests(stream)>0)ReadableStreamFulfillReadRequest(stream,chunk,false);else{let chunkSize=1;const strategySize=controller[_strategySize];if(strategySize!==
-undefined)try{chunkSize=strategySize(chunk)}catch(chunkSizeE){if(ReadableStreamGetState(stream)===STATE_READABLE)ReadableStreamDefaultControllerError(controller,chunkSizeE);throw chunkSizeE;}try{EnqueueValueWithSize(controller,chunk,chunkSize)}catch(enqueueE){if(ReadableStreamGetState(stream)===STATE_READABLE)ReadableStreamDefaultControllerError(controller,enqueueE);throw enqueueE;}}ReadableStreamDefaultControllerCallPullIfNeeded(controller)}function ReadableStreamGetState(stream){return(stream[_readableStreamBits]&
-STATE_MASK)>>STATE_BITS_OFFSET}function ReadableStreamSetState(stream,state){stream[_readableStreamBits]=stream[_readableStreamBits]&~STATE_MASK|state<<STATE_BITS_OFFSET}function ReadableStreamDefaultControllerError(controller,e){controller[_queue]=new SimpleQueue;const stream=controller[_controlledReadableStream];ReadableStreamError(stream,e)}function ReadableStreamError(stream,e){stream[_storedError]=e;ReadableStreamSetState(stream,STATE_ERRORED);const reader=stream[_reader];if(reader===undefined)return undefined;
-if(IsReadableStreamDefaultReader(reader)===true){reader[_readRequests].forEach((request)=>v8_rejectPromise(request,e));reader[_readRequests]=new SimpleQueue}v8_rejectPromise(reader[_closedPromise],e);v8_markPromiseAsHandled(reader[_closedPromise])}function ReadableStreamClose(stream){ReadableStreamSetState(stream,STATE_CLOSED);const reader=stream[_reader];if(reader===undefined)return undefined;if(IsReadableStreamDefaultReader(reader)===true){reader[_readRequests].forEach((request)=>v8_resolvePromise(request,
-CreateIterResultObject(undefined,true)));reader[_readRequests]=new SimpleQueue}v8_resolvePromise(reader[_closedPromise],undefined)}function ReadableStreamDefaultControllerGetDesiredSize(controller){const stream=controller[_controlledReadableStream];const state=ReadableStreamGetState(stream);if(state===STATE_CLOSED)return 0;else if(state===STATE_ERRORED)return null;const queueSize=GetTotalQueueSize(controller);return controller[_strategyHWM]-queueSize}function IsReadableStream(x){return hasOwnProperty(x,
-_controller)}function IsReadableStreamDisturbed(stream){return stream[_readableStreamBits]&DISTURBED}function IsReadableStreamLocked(stream){return stream[_reader]!==undefined}function IsReadableStreamDefaultController(x){return hasOwnProperty(x,_controlledReadableStream)}function IsReadableStreamDefaultReader(x){return hasOwnProperty(x,_readRequests)}function IsReadableStreamReadable(stream){return ReadableStreamGetState(stream)===STATE_READABLE}function IsReadableStreamClosed(stream){return ReadableStreamGetState(stream)===
-STATE_CLOSED}function IsReadableStreamErrored(stream){return ReadableStreamGetState(stream)===STATE_ERRORED}function ReadableStreamReaderGenericInitialize(reader,stream){const controller=stream[_controller];if(controller[_readableStreamDefaultControllerBits]&EXTERNALLY_CONTROLLED){const underlyingSource=controller[_underlyingSource];callFunction(underlyingSource.notifyLockAcquired,underlyingSource)}reader[_ownerReadableStream]=stream;stream[_reader]=reader;switch(ReadableStreamGetState(stream)){case STATE_READABLE:reader[_closedPromise]=
-v8_createPromise();break;case STATE_CLOSED:reader[_closedPromise]=Promise_resolve(undefined);break;case STATE_ERRORED:reader[_closedPromise]=Promise_reject(stream[_storedError]);v8_markPromiseAsHandled(reader[_closedPromise]);break}}function ReadableStreamReaderGenericRelease(reader){const controller=reader[_ownerReadableStream][_controller];if(controller[_readableStreamDefaultControllerBits]&EXTERNALLY_CONTROLLED){const underlyingSource=controller[_underlyingSource];callFunction(underlyingSource.notifyLockReleased,
-underlyingSource)}if(ReadableStreamGetState(reader[_ownerReadableStream])===STATE_READABLE)v8_rejectPromise(reader[_closedPromise],new TypeError(errReleasedReaderClosedPromise));else reader[_closedPromise]=Promise_reject(new TypeError(errReleasedReaderClosedPromise));v8_markPromiseAsHandled(reader[_closedPromise]);reader[_ownerReadableStream][_reader]=undefined;reader[_ownerReadableStream]=undefined}function ReadableStreamDefaultReaderRead(reader){const stream=reader[_ownerReadableStream];stream[_readableStreamBits]|=
-DISTURBED;if(ReadableStreamGetState(stream)===STATE_CLOSED)return Promise_resolve(CreateIterResultObject(undefined,true));if(ReadableStreamGetState(stream)===STATE_ERRORED)return Promise_reject(stream[_storedError]);return ReadableStreamDefaultControllerPull(stream[_controller])}function ReadableStreamDefaultControllerCallPullIfNeeded(controller){const shouldPull=ReadableStreamDefaultControllerShouldCallPull(controller);if(shouldPull===false)return undefined;if(controller[_readableStreamDefaultControllerBits]&
-PULLING){controller[_readableStreamDefaultControllerBits]|=PULL_AGAIN;return undefined}controller[_readableStreamDefaultControllerBits]|=PULLING;const underlyingSource=controller[_underlyingSource];const pullPromise=PromiseCallOrNoop(underlyingSource,"pull",controller,"underlyingSource.pull");thenPromise(pullPromise,()=>{controller[_readableStreamDefaultControllerBits]&=~PULLING;if(controller[_readableStreamDefaultControllerBits]&PULL_AGAIN){controller[_readableStreamDefaultControllerBits]&=~PULL_AGAIN;
-ReadableStreamDefaultControllerCallPullIfNeeded(controller)}},(e)=>{if(ReadableStreamGetState(controller[_controlledReadableStream])===STATE_READABLE)ReadableStreamDefaultControllerError(controller,e)})}function ReadableStreamDefaultControllerShouldCallPull(controller){const stream=controller[_controlledReadableStream];const state=ReadableStreamGetState(stream);if(state===STATE_CLOSED||state===STATE_ERRORED)return false;if(controller[_readableStreamDefaultControllerBits]&CLOSE_REQUESTED)return false;
-if(!(controller[_readableStreamDefaultControllerBits]&STARTED))return false;if(IsReadableStreamLocked(stream)===true&&ReadableStreamGetNumReadRequests(stream)>0)return true;const desiredSize=ReadableStreamDefaultControllerGetDesiredSize(controller);if(desiredSize>0)return true;return false}function ReadableStreamGetNumReadRequests(stream){const reader=stream[_reader];const readRequests=reader[_readRequests];return readRequests.length}function ReadableStreamTee(stream){const reader=AcquireReadableStreamDefaultReader(stream);
-let closedOrErrored=false;let canceled1=false;let canceled2=false;let reason1;let reason2;let promise=v8_createPromise();const branch1Stream=new ReadableStream({pull,cancel:cancel1});const branch2Stream=new ReadableStream({pull,cancel:cancel2});const branch1=branch1Stream[_controller];const branch2=branch2Stream[_controller];thenPromise(reader[_closedPromise],undefined,function(r){if(closedOrErrored===true)return;ReadableStreamDefaultControllerError(branch1,r);ReadableStreamDefaultControllerError(branch2,
-r);closedOrErrored=true});return[branch1Stream,branch2Stream];function pull(){return thenPromise(ReadableStreamDefaultReaderRead(reader),function(result){const value=result.value;const done=result.done;if(done===true&&closedOrErrored===false){if(canceled1===false)ReadableStreamDefaultControllerClose(branch1);if(canceled2===false)ReadableStreamDefaultControllerClose(branch2);closedOrErrored=true}if(closedOrErrored===true)return;if(canceled1===false)ReadableStreamDefaultControllerEnqueue(branch1,value);
-if(canceled2===false)ReadableStreamDefaultControllerEnqueue(branch2,value)})}function cancel1(reason){canceled1=true;reason1=reason;if(canceled2===true){const compositeReason=[reason1,reason2];const cancelResult=ReadableStreamCancel(stream,compositeReason);v8_resolvePromise(promise,cancelResult)}return promise}function cancel2(reason){canceled2=true;reason2=reason;if(canceled1===true){const compositeReason=[reason1,reason2];const cancelResult=ReadableStreamCancel(stream,compositeReason);v8_resolvePromise(promise,
-cancelResult)}return promise}}function DequeueValue(controller){const result=controller[_queue].shift();controller[_totalQueuedSize]-=result.size;return result.value}function EnqueueValueWithSize(controller,value,size){size=Number(size);if(Number_isNaN(size)||size===+Infinity||size<0)throw new RangeError(streamErrors_invalidSize);controller[_totalQueuedSize]+=size;controller[_queue].push({value,size})}function GetTotalQueueSize(controller){return controller[_totalQueuedSize]}function ValidateAndNormalizeQueuingStrategy(size,
-highWaterMark){if(size!==undefined&&typeof size!=="function")throw new TypeError(streamErrors_sizeNotAFunction);highWaterMark=Number(highWaterMark);if(Number_isNaN(highWaterMark))throw new RangeError(streamErrors_invalidHWM);if(highWaterMark<0)throw new RangeError(streamErrors_invalidHWM);return{size,highWaterMark}}function CallOrNoop(O,P,arg,nameForError){const method=O[P];if(method===undefined)return undefined;if(typeof method!=="function")throw new TypeError(errTmplMustBeFunctionOrUndefined(nameForError));
-return callFunction(method,O,arg)}function PromiseCallOrNoop(O,P,arg,nameForError){let method;try{method=O[P]}catch(methodE){return Promise_reject(methodE)}if(method===undefined)return Promise_resolve(undefined);if(typeof method!=="function")return Promise_reject(new TypeError(errTmplMustBeFunctionOrUndefined(nameForError)));try{return Promise_resolve(callFunction(method,O,arg))}catch(e){return Promise_reject(e)}}function CreateIterResultObject(value,done){return{value,done}}defineProperty(global,
-"ReadableStream",{value:ReadableStream,enumerable:false,configurable:true,writable:true});global.AcquireReadableStreamDefaultReader=AcquireReadableStreamDefaultReader;global.IsReadableStream=IsReadableStream;global.IsReadableStreamDisturbed=IsReadableStreamDisturbed;global.IsReadableStreamLocked=IsReadableStreamLocked;global.IsReadableStreamReadable=IsReadableStreamReadable;global.IsReadableStreamClosed=IsReadableStreamClosed;global.IsReadableStreamErrored=IsReadableStreamErrored;global.IsReadableStreamDefaultReader=
-IsReadableStreamDefaultReader;global.ReadableStreamDefaultReaderRead=ReadableStreamDefaultReaderRead;global.ReadableStreamTee=ReadableStreamTee;global.ReadableStreamDefaultControllerClose=ReadableStreamDefaultControllerClose;global.ReadableStreamDefaultControllerGetDesiredSize=ReadableStreamDefaultControllerGetDesiredSize;global.ReadableStreamDefaultControllerEnqueue=ReadableStreamDefaultControllerEnqueue;global.ReadableStreamDefaultControllerError=ReadableStreamDefaultControllerError})(this);
\ No newline at end of file
+'use strict';(function(d){function O(a){return function(b){return a.apply(b,la.call(arguments,1))}}function P(){var a=this;ca.call(this,function(b,m){a[E]=b;a[Q]=m})}function V(a){try{F(a,e,function(){})}catch(b){}}function q(){this.back=this.front={elements:new da,next:e};this.size=this.cursor=0}function v(a,b,m){a=a===e?{}:a;var c=b===e?{}:b;b=c.size;c=c.highWaterMark;c===e&&(c=1);this[p]=0;this[p]=this[p]&-7|0;this[u]=e;this[K]=e;this[y]=e;var d=a.type;if("bytes"===String(d))throw new G("bytes type is not yet implemented");
+if(d!==e)throw new G("Invalid type is specified");this[y]=new L(this,a,b,c,m)}function L(a,b,m,d){if(!1===B(a))throw new c("Illegal constructor");if(a[y]!==e)throw new c("Illegal constructor");this[k]=a;this[W]=b;this[z]=new q;this[H]=0;this[g]=0;m=ma(m,d);this[ea]=m.size;this[fa]=m.highWaterMark;var R=this;b=na(b,"start",this,"underlyingSource.start");F(C(b),function(){R[g]|=1;M(R)},function(b){0===h(a)&&A(R,b)})}function oa(a,b){a[z]=new q;return ga(a[W],"cancel",b,"underlyingSource.cancel")}function N(a){if(!1===
+B(a))throw new c("ReadableStreamReader constructor argument is not a readable stream");if(!0===I(a))throw new c("ReadableStreamReader constructor can only accept readable streams that are not yet locked to a reader");this[x]=a;a[u]=this;switch(h(a)){case 0:this[t]=new P;break;case 1:this[t]=C(e);break;case 2:this[t]=n(a[K]),V(this[t])}this[r]=new q}function X(a){return new N(a)}function S(a,b){a[p]|=1;var m=h(a);if(1===m)return C(e);if(2===m)return n(a[K]);Y(a);a=oa(a[y],b);return F(a,function(){return e})}
+function T(a){var b=a[k];a[g]|=2;0===a[z].length&&Y(b)}function U(a,b){var m=a[k];if(!0===I(m)&&0<m[u][r].length)m[u][r].shift()[E]({value:b,done:!1});else{var c=1,d=a[ea];if(d!==e)try{c=d(b)}catch(w){throw 0===h(m)&&A(a,w),w;}try{c=Z(c);if(ha(c)||c===+pa||0>c)throw new G("The return value of a queuing strategy's size function must be a finite, non-NaN, non-negative number");a[H]+=c;a[z].push({value:b,size:c})}catch(w){throw 0===h(m)&&A(a,w),w;}}M(a)}function h(a){return(a[p]&6)>>1}function A(a,b){a[z]=
+new q;qa(a[k],b)}function qa(a,b){a[K]=b;a[p]=a[p]&-7|4;a=a[u];if(a===e)return e;!0===D(a)&&(a[r].forEach(function(a){a[Q](b)}),a[r]=new q);a[t][Q](b);V(a[t])}function Y(a){a[p]=a[p]&-7|2;a=a[u];if(a===e)return e;!0===D(a)&&(a[r].forEach(function(a){a[E]({value:e,done:!0})}),a[r]=new q);a[t][E](e)}function aa(a){var b=h(a[k]);return 1===b?0:2===b?null:a[fa]-a[H]}function B(a){return J(a,y)}function I(a){return a[u]!==e}function D(a){return J(a,r)}function ba(a){a=a[x];a[p]|=1;if(1===h(a))return C({value:e,
+done:!0});if(2===h(a))return n(a[K]);a=a[y];var b=a[k];if(0<a[z].length){var c=a[z].shift();a[H]-=c.size;c=c.value;a[g]&2&&0===a[z].length?Y(b):M(a);a=C({value:c,done:!1})}else c=new P,b[u][r].push(c),M(a),a=c;return a}function M(a){if(!1===ra(a))return e;if(a[g]&4)return a[g]|=8,e;a[g]|=4;var b=ga(a[W],"pull",a,"underlyingSource.pull");F(b,function(){a[g]&=-5;a[g]&8&&(a[g]&=-9,M(a))},function(b){0===h(a[k])&&A(a,b)})}function ra(a){var b=a[k],c=h(b);return 1===c||2===c||a[g]&2||!(a[g]&1)?!1:!0===
+I(b)&&0<b[u][r].length||0<aa(a)?!0:!1}function ia(a){function b(){return F(ba(c),function(a){var b=a.value;!0===a.done&&!1===d&&(!1===f&&T(q),!1===g&&T(r),d=!0);!0!==d&&(!1===f&&U(q,b),!1===g&&U(r,b))})}var c=X(a),d=!1,f=!1,g=!1,h,l,k=new P,n=new v({pull:b,cancel:function(b){f=!0;h=b;!0===g&&(b=S(a,[h,l]),k[E](b));return k}}),p=new v({pull:b,cancel:function(b){g=!0;l=b;!0===f&&(b=S(a,[h,l]),k[E](b));return k}}),q=n[y],r=p[y];F(c[t],e,function(a){!0!==d&&(A(q,a),A(r,a),d=!0)});return[n,p]}function ma(a,
+b){if(a!==e&&"function"!==typeof a)throw new c("A queuing strategy's size property must be a function");b=Z(b);if(ha(b))throw new G("A queueing strategy's highWaterMark property must be a nonnegative, non-NaN number");if(0>b)throw new G("A queueing strategy's highWaterMark property must be a nonnegative, non-NaN number");return{size:a,highWaterMark:b}}function na(a,b,d,f){b=a[b];if(b===e)return e;if("function"!==typeof b)throw new c(ja(f));return ka(b,a,d)}function ga(a,b,d,f){try{var g=a[b]}catch(w){return n(w)}if(g===
+e)return C(e);if("function"!==typeof g)return n(new c(ja(f)));try{return C(ka(g,a,d))}catch(w){return n(w)}}var l=d.Object.defineProperty,da=d.Array,f=d.Symbol,la=d.Array.prototype.slice,ca=d.Promise,E=f("[[Resolve]]"),Q=f("[[Reject]]");P.prototype=ca.prototype;l(q.prototype,"length",{get:function(){return this.size}});q.prototype.push=function(a){++this.size;if(16384===this.back.elements.length){var b=this.back;this.back={elements:new da,next:e};b.next=this.back}this.back.elements.push(a)};q.prototype.shift=
+function(){--this.size;this.front.elements.length===this.cursor&&(this.front=this.front.next,this.cursor=0);var a=this.front.elements[this.cursor];this.front.elements[this.cursor]=e;++this.cursor;return a};q.prototype.forEach=function(a){for(var b=this.cursor,c=this.front,d=c.elements;b!==d.length||c.next!==e;)b===d.length&&(c=c.next,d=c.elements,b=0),a(d[b]),++b};q.prototype.peek=function(){return this.front.elements.length===this.cursor?this.front.next.elements[0]:this.front.elements[this.cursor]};
+var u=f("[[reader]]"),K=f("[[storedError]]"),y=f("[[controller]]"),t=f("[[closedPromise]]"),x=f("[[ownerReadableStream]]"),r=f("[[readRequests]]"),p=f("bit field for [[state]] and [[disturbed]]"),W=f("[[underlyingSource]]"),k=f("[[controlledReadableStream]]"),z=f("[[queue]]"),H=f("[[totalQueuedSize]]"),ea=f("[[strategySize]]"),fa=f("[[strategyHWM]]"),g=f("bit field for [[started]], [[closeRequested]], [[pulling]], [[pullAgain]]"),e=d.undefined,pa=d.Infinity,J=O(d.Object.hasOwnProperty),ka=O(d.Function.prototype.call);
+O(d.Function.prototype.apply);var c=d.TypeError,G=d.RangeError,Z=d.Number,ha=Z.isNaN,f=d.Promise,F=O(f.prototype.then),C=f.resolve.bind(f),n=f.reject.bind(f),ja=function(a){return a+" must be a function or undefined"};l(v.prototype,"locked",{enumerable:!1,configurable:!0,get:function(){if(!1===B(this))throw new c("Illegal invocation");return I(this)}});l(v.prototype,"cancel",{enumerable:!1,configurable:!0,writable:!0,value:function(a){return!1===B(this)?n(new c("Illegal invocation")):!0===I(this)?
+n(new c("Cannot cancel a readable stream that is locked to a reader")):S(this,a)}});l(v.prototype,"getReader",{enumerable:!1,configurable:!0,writable:!0,value:function(a){a=(a===e?{}:a).mode;if(!1===B(this))throw new c("Illegal invocation");if("byob"===a)throw new c("This readable stream does not support BYOB readers");if(a===e)return X(this);throw new G('Invalid reader mode given: expected undefined or "byob"');}});l(v.prototype,"pipeThrough",{enumerable:!1,configurable:!0,writable:!0,value:function(a,
+b){throw new c("pipeThrough not implemented");}});l(v.prototype,"pipeTo",{enumerable:!1,configurable:!0,writable:!0,value:function(a){throw new c("pipeTo not implemented");}});l(v.prototype,"tee",{enumerable:!1,configurable:!0,writable:!0,value:function(){if(!1===B(this))throw new c("Illegal invocation");return ia(this)}});l(L.prototype,"desiredSize",{enumerable:!1,configurable:!0,get:function(){if(!1===J(this,k))throw new c("Illegal invocation");return aa(this)}});l(L.prototype,"close",{enumerable:!1,
+configurable:!0,writable:!0,value:function(){if(!1===J(this,k))throw new c("Illegal invocation");var a=this[k];if(this[g]&2)throw new c("Cannot close a readable stream that has already been requested to be closed");a=h(a);if(2===a)throw new c("Cannot close an errored readable stream");if(1===a)throw new c("Cannot close a closed readable stream");return T(this)}});l(L.prototype,"enqueue",{enumerable:!1,configurable:!0,writable:!0,value:function(a){if(!1===J(this,k))throw new c("Illegal invocation");
+var b=this[k];if(this[g]&2)throw new c("Cannot enqueue a chunk into a readable stream that is closed or has been requested to be closed");b=h(b);if(2===b)throw new c("Cannot enqueue a chunk into an errored readable stream");if(1===b)throw new c("Cannot enqueue a chunk into a closed readable stream");return U(this,a)}});l(L.prototype,"error",{enumerable:!1,configurable:!0,writable:!0,value:function(a){if(!1===J(this,k))throw new c("Illegal invocation");var b=h(this[k]);if(2===b)throw new c("Cannot error a readable stream that is already errored");
+if(1===b)throw new c("Cannot error a close readable stream");return A(this,a)}});l(N.prototype,"closed",{enumerable:!1,configurable:!0,get:function(){return!1===D(this)?n(new c("Illegal invocation")):this[t]}});l(N.prototype,"cancel",{enumerable:!1,configurable:!0,writable:!0,value:function(a){return!1===D(this)?n(new c("Illegal invocation")):this[x]===e?n(new c("This readable stream reader has been released and cannot be used to cancel its previous owner stream")):S(this[x],a)}});l(N.prototype,"read",
+{enumerable:!1,configurable:!0,writable:!0,value:function(){return!1===D(this)?n(new c("Illegal invocation")):this[x]===e?n(new c("This readable stream reader has been released and cannot be used to read from its previous owner stream")):ba(this)}});l(N.prototype,"releaseLock",{enumerable:!1,configurable:!0,writable:!0,value:function(){if(!1===D(this))throw new c("Illegal invocation");if(this[x]===e)return e;if(0<this[r].length)throw new c("Cannot release a readable stream reader when it still has outstanding read() calls that have not yet settled");
+if(0===h(this[x])){var a=new c("This readable stream reader has been released and cannot be used to monitor the stream's state");this[t][Q](a)}else this[t]=n(new c("This readable stream reader has been released and cannot be used to monitor the stream's state"));V(this[t]);this[x][u]=e;this[x]=e}});l(d,"ReadableStream",{value:v,enumerable:!1,configurable:!0,writable:!0});d.AcquireReadableStreamDefaultReader=X;d.IsReadableStream=B;d.IsReadableStreamDisturbed=function(a){return a[p]&1};d.IsReadableStreamLocked=
+I;d.IsReadableStreamReadable=function(a){return 0===h(a)};d.IsReadableStreamClosed=function(a){return 1===h(a)};d.IsReadableStreamErrored=function(a){return 2===h(a)};d.IsReadableStreamDefaultReader=D;d.ReadableStreamDefaultReaderRead=ba;d.ReadableStreamTee=ia;d.ReadableStreamDefaultControllerClose=T;d.ReadableStreamDefaultControllerGetDesiredSize=aa;d.ReadableStreamDefaultControllerEnqueue=U;d.ReadableStreamDefaultControllerError=A})(this);
\ No newline at end of file
diff --git a/src/cobalt/streams/readable_stream.js b/src/cobalt/streams/readable_stream.js
index edeb9cd..27915ef 100644
--- a/src/cobalt/streams/readable_stream.js
+++ b/src/cobalt/streams/readable_stream.js
@@ -1,7 +1,7 @@
// ==ClosureCompiler==
// @output_file_name readable_stream.js
-// @compilation_level WHITESPACE_ONLY
-// @language_out ES6_STRICT
+// @compilation_level SIMPLE_OPTIMIZATIONS
+// @language_out ES5_STRICT
// ==/ClosureCompiler==
// Copyright 2015 The Chromium Authors. All rights reserved.
@@ -11,12 +11,12 @@
(function(global) {
'use strict';
+ const defineProperty = global.Object.defineProperty;
+
// Mimic functionality provided to v8 extras.
const v8_InternalPackedArray = global.Array;
- function v8_createPrivateSymbol(x) {
- return Symbol(x);
- }
+ const v8_createPrivateSymbol = global.Symbol;
function v8_simpleBind(fn, obj) {
return fn.bind(obj);
@@ -71,80 +71,78 @@
// InternalPackedArray directly by using multiple arrays in a linked list and
// keeping the array size bounded.
const QUEUE_MAX_ARRAY_SIZE = 16384;
- class SimpleQueue {
- constructor() {
- this.front = {
+ function SimpleQueue() {
+ this.front = {
+ elements: new v8_InternalPackedArray(),
+ next: undefined,
+ };
+ this.back = this.front;
+ // The cursor is used to avoid calling InternalPackedArray.shift().
+ this.cursor = 0;
+ this.size = 0;
+ }
+
+ defineProperty(SimpleQueue.prototype, 'length', {
+ get: function() { return this.size; }
+ });
+
+ SimpleQueue.prototype.push = function(element) {
+ ++this.size;
+ if (this.back.elements.length === QUEUE_MAX_ARRAY_SIZE) {
+ const oldBack = this.back;
+ this.back = {
elements: new v8_InternalPackedArray(),
next: undefined,
};
- this.back = this.front;
- // The cursor is used to avoid calling InternalPackedArray.shift().
+ oldBack.next = this.back;
+ }
+ this.back.elements.push(element);
+ }
+
+ SimpleQueue.prototype.shift = function() {
+ // assert(this.size > 0);
+ --this.size;
+ if (this.front.elements.length === this.cursor) {
+ // assert(this.cursor === QUEUE_MAX_ARRAY_SIZE);
+ // assert(this.front.next !== undefined);
+ this.front = this.front.next;
this.cursor = 0;
- this.size = 0;
}
+ const element = this.front.elements[this.cursor];
+ // Permit shifted element to be garbage collected.
+ this.front.elements[this.cursor] = undefined;
+ ++this.cursor;
- get length() {
- return this.size;
- }
+ return element;
+ }
- push(element) {
- ++this.size;
- if (this.back.elements.length === QUEUE_MAX_ARRAY_SIZE) {
- const oldBack = this.back;
- this.back = {
- elements: new v8_InternalPackedArray(),
- next: undefined,
- };
- oldBack.next = this.back;
+ SimpleQueue.prototype.forEach = function(callback) {
+ let i = this.cursor;
+ let node = this.front;
+ let elements = node.elements;
+ while (i !== elements.length || node.next !== undefined) {
+ if (i === elements.length) {
+ // assert(node.next !== undefined);
+ // assert(i === QUEUE_MAX_ARRAY_SIZE);
+ node = node.next;
+ elements = node.elements;
+ i = 0;
}
- this.back.elements.push(element);
+ callback(elements[i]);
+ ++i;
}
+ }
- shift() {
- // assert(this.size > 0);
- --this.size;
- if (this.front.elements.length === this.cursor) {
- // assert(this.cursor === QUEUE_MAX_ARRAY_SIZE);
- // assert(this.front.next !== undefined);
- this.front = this.front.next;
- this.cursor = 0;
- }
- const element = this.front.elements[this.cursor];
- // Permit shifted element to be garbage collected.
- this.front.elements[this.cursor] = undefined;
- ++this.cursor;
-
- return element;
+ // Return the element that would be returned if shift() was called now,
+ // without modifying the queue.
+ SimpleQueue.prototype.peek = function() {
+ // assert(this.size > 0);
+ if (this.front.elements.length === this.cursor) {
+ // assert(this.cursor === QUEUE_MAX_ARRAY_SIZE)
+ // assert(this.front.next !== undefined);
+ return this.front.next.elements[0];
}
-
- forEach(callback) {
- let i = this.cursor;
- let node = this.front;
- let elements = node.elements;
- while (i !== elements.length || node.next !== undefined) {
- if (i === elements.length) {
- // assert(node.next !== undefined);
- // assert(i === QUEUE_MAX_ARRAY_SIZE);
- node = node.next;
- elements = node.elements;
- i = 0;
- }
- callback(elements[i]);
- ++i;
- }
- }
-
- // Return the element that would be returned if shift() was called now,
- // without modifying the queue.
- peek() {
- // assert(this.size > 0);
- if (this.front.elements.length === this.cursor) {
- // assert(this.cursor === QUEUE_MAX_ARRAY_SIZE)
- // assert(this.front.next !== undefined);
- return this.front.next.elements[0];
- }
- return this.front.elements[this.cursor];
- }
+ return this.front.elements[this.cursor];
}
/* SimpleQueue.js: end */
@@ -167,9 +165,6 @@
const _readRequests = v8_createPrivateSymbol('[[readRequests]]');
- const createWithExternalControllerSentinel =
- v8_createPrivateSymbol('flag for UA-created ReadableStream to pass');
-
const _readableStreamBits = v8_createPrivateSymbol('bit field for [[state]] and [[disturbed]]');
const DISTURBED = 0b1;
// The 2nd and 3rd bit are for [[state]].
@@ -193,12 +188,10 @@
const CLOSE_REQUESTED = 0b10;
const PULLING = 0b100;
const PULL_AGAIN = 0b1000;
- const EXTERNALLY_CONTROLLED = 0b10000;
const undefined = global.undefined;
const Infinity = global.Infinity;
- const defineProperty = global.Object.defineProperty;
const hasOwnProperty = v8_uncurryThis(global.Object.hasOwnProperty);
const callFunction = v8_uncurryThis(global.Function.prototype.call);
const applyFunction = v8_uncurryThis(global.Function.prototype.apply);
@@ -246,51 +239,58 @@
const errTmplMustBeFunctionOrUndefined = name =>
`${name} must be a function or undefined`;
- class ReadableStream {
- constructor() {
- // TODO(domenic): when V8 gets default parameters and destructuring, all
- // this can be cleaned up.
- const underlyingSource = arguments[0] === undefined ? {} : arguments[0];
- const strategy = arguments[1] === undefined ? {} : arguments[1];
- const size = strategy.size;
- let highWaterMark = strategy.highWaterMark;
- if (highWaterMark === undefined) {
- highWaterMark = 1;
- }
-
- this[_readableStreamBits] = 0b0;
- ReadableStreamSetState(this, STATE_READABLE);
- this[_reader] = undefined;
- this[_storedError] = undefined;
-
- // Avoid allocating the controller if the stream is going to be controlled
- // externally (i.e. from C++) anyway. All calls to underlyingSource
- // methods will disregard their controller argument in such situations
- // (but see below).
-
- this[_controller] = undefined;
-
- const type = underlyingSource.type;
- const typeString = String(type);
- if (typeString === 'bytes') {
- throw new RangeError('bytes type is not yet implemented');
- } else if (type !== undefined) {
- throw new RangeError(streamErrors_invalidType);
- }
-
- this[_controller] =
- new ReadableStreamDefaultController(this, underlyingSource, size, highWaterMark, arguments[2]);
+ function ReadableStream() {
+ // TODO(domenic): when V8 gets default parameters and destructuring, all
+ // this can be cleaned up.
+ const underlyingSource = arguments[0] === undefined ? {} : arguments[0];
+ const strategy = arguments[1] === undefined ? {} : arguments[1];
+ const size = strategy.size;
+ let highWaterMark = strategy.highWaterMark;
+ if (highWaterMark === undefined) {
+ highWaterMark = 1;
}
- get locked() {
+ this[_readableStreamBits] = 0b0;
+ ReadableStreamSetState(this, STATE_READABLE);
+ this[_reader] = undefined;
+ this[_storedError] = undefined;
+
+ // Avoid allocating the controller if the stream is going to be controlled
+ // externally (i.e. from C++) anyway. All calls to underlyingSource
+ // methods will disregard their controller argument in such situations
+ // (but see below).
+
+ this[_controller] = undefined;
+
+ const type = underlyingSource.type;
+ const typeString = String(type);
+ if (typeString === 'bytes') {
+ throw new RangeError('bytes type is not yet implemented');
+ } else if (type !== undefined) {
+ throw new RangeError(streamErrors_invalidType);
+ }
+
+ this[_controller] =
+ new ReadableStreamDefaultController(this, underlyingSource, size, highWaterMark, arguments[2]);
+ }
+
+ defineProperty(ReadableStream.prototype, 'locked', {
+ enumerable: false,
+ configurable: true,
+ get: function() {
if (IsReadableStream(this) === false) {
throw new TypeError(streamErrors_illegalInvocation);
}
return IsReadableStreamLocked(this);
}
+ });
- cancel(reason) {
+ defineProperty(ReadableStream.prototype, 'cancel', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function(reason) {
if (IsReadableStream(this) === false) {
return Promise_reject(new TypeError(streamErrors_illegalInvocation));
}
@@ -301,8 +301,13 @@
return ReadableStreamCancel(this, reason);
}
+ });
- getReader({ mode } = {}) {
+ defineProperty(ReadableStream.prototype, 'getReader', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function({ mode } = {}) {
if (IsReadableStream(this) === false) {
throw new TypeError(streamErrors_illegalInvocation);
}
@@ -323,82 +328,95 @@
throw new RangeError(errGetReaderBadMode);
}
+ });
- pipeThrough({writable, readable}, options) {
+ defineProperty(ReadableStream.prototype, 'pipeThrough', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function({writable, readable}, options) {
throw new TypeError('pipeThrough not implemented');
}
+ });
- pipeTo(dest, {preventClose, preventAbort, preventCancel} = {}) {
+ defineProperty(ReadableStream.prototype, 'pipeTo', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function(dest) {
throw new TypeError('pipeTo not implemented');
}
+ });
- tee() {
+ defineProperty(ReadableStream.prototype, 'tee', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function() {
if (IsReadableStream(this) === false) {
throw new TypeError(streamErrors_illegalInvocation);
}
return ReadableStreamTee(this);
}
- }
+ });
- class ReadableStreamDefaultController {
- // Cobalt: Constructor has a hidden argument at the end to designate
- // whether it is externally controlled. Exposing this would fail the
- // web platform test checking the number of parameters the constructor
- // accepts.
- constructor(stream, underlyingSource, size, highWaterMark) {
- if (IsReadableStream(stream) === false) {
- throw new TypeError(streamErrors_illegalConstructor);
- }
-
- if (stream[_controller] !== undefined) {
- throw new TypeError(streamErrors_illegalConstructor);
- }
-
- this[_controlledReadableStream] = stream;
-
- this[_underlyingSource] = underlyingSource;
-
- this[_queue] = new SimpleQueue();
- this[_totalQueuedSize] = 0;
-
- this[_readableStreamDefaultControllerBits] = 0b0;
- // Cobalt: Hidden constructor parameter to designate whether the
- // is externally controlled.
- if (arguments[4] === createWithExternalControllerSentinel) {
- this[_readableStreamDefaultControllerBits] |= EXTERNALLY_CONTROLLED;
- }
-
- const normalizedStrategy =
- ValidateAndNormalizeQueuingStrategy(size, highWaterMark);
- this[_strategySize] = normalizedStrategy.size;
- this[_strategyHWM] = normalizedStrategy.highWaterMark;
-
- const controller = this;
-
- const startResult = CallOrNoop(
- underlyingSource, 'start', this, 'underlyingSource.start');
- thenPromise(Promise_resolve(startResult),
- () => {
- controller[_readableStreamDefaultControllerBits] |= STARTED;
- ReadableStreamDefaultControllerCallPullIfNeeded(controller);
- },
- r => {
- if (ReadableStreamGetState(stream) === STATE_READABLE) {
- ReadableStreamDefaultControllerError(controller, r);
- }
- });
+ function ReadableStreamDefaultController(stream, underlyingSource, size, highWaterMark) {
+ if (IsReadableStream(stream) === false) {
+ throw new TypeError(streamErrors_illegalConstructor);
}
- get desiredSize() {
+ if (stream[_controller] !== undefined) {
+ throw new TypeError(streamErrors_illegalConstructor);
+ }
+
+ this[_controlledReadableStream] = stream;
+
+ this[_underlyingSource] = underlyingSource;
+
+ this[_queue] = new SimpleQueue();
+ this[_totalQueuedSize] = 0;
+
+ this[_readableStreamDefaultControllerBits] = 0b0;
+
+ const normalizedStrategy =
+ ValidateAndNormalizeQueuingStrategy(size, highWaterMark);
+ this[_strategySize] = normalizedStrategy.size;
+ this[_strategyHWM] = normalizedStrategy.highWaterMark;
+
+ const controller = this;
+
+ const startResult = CallOrNoop(
+ underlyingSource, 'start', this, 'underlyingSource.start');
+ thenPromise(Promise_resolve(startResult),
+ () => {
+ controller[_readableStreamDefaultControllerBits] |= STARTED;
+ ReadableStreamDefaultControllerCallPullIfNeeded(controller);
+ },
+ r => {
+ if (ReadableStreamGetState(stream) === STATE_READABLE) {
+ ReadableStreamDefaultControllerError(controller, r);
+ }
+ });
+ }
+
+ defineProperty(ReadableStreamDefaultController.prototype, 'desiredSize', {
+ enumerable: false,
+ configurable: true,
+ get: function() {
if (IsReadableStreamDefaultController(this) === false) {
throw new TypeError(streamErrors_illegalInvocation);
}
return ReadableStreamDefaultControllerGetDesiredSize(this);
}
+ });
- close() {
+ defineProperty(ReadableStreamDefaultController.prototype, 'close', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function() {
if (IsReadableStreamDefaultController(this) === false) {
throw new TypeError(streamErrors_illegalInvocation);
}
@@ -419,8 +437,13 @@
return ReadableStreamDefaultControllerClose(this);
}
+ });
- enqueue(chunk) {
+ defineProperty(ReadableStreamDefaultController.prototype, 'enqueue', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function(chunk) {
if (IsReadableStreamDefaultController(this) === false) {
throw new TypeError(streamErrors_illegalInvocation);
}
@@ -441,8 +464,13 @@
return ReadableStreamDefaultControllerEnqueue(this, chunk);
}
+ });
- error(e) {
+ defineProperty(ReadableStreamDefaultController.prototype, 'error', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function(e) {
if (IsReadableStreamDefaultController(this) === false) {
throw new TypeError(streamErrors_illegalInvocation);
}
@@ -459,7 +487,7 @@
return ReadableStreamDefaultControllerError(this, e);
}
- }
+ });
function ReadableStreamDefaultControllerCancel(controller, reason) {
controller[_queue] = new SimpleQueue();
@@ -495,29 +523,36 @@
return promise;
}
- class ReadableStreamDefaultReader {
- constructor(stream) {
- if (IsReadableStream(stream) === false) {
- throw new TypeError(errReaderConstructorBadArgument);
- }
- if (IsReadableStreamLocked(stream) === true) {
- throw new TypeError(errReaderConstructorStreamAlreadyLocked);
- }
-
- ReadableStreamReaderGenericInitialize(this, stream);
-
- this[_readRequests] = new SimpleQueue();
+ function ReadableStreamDefaultReader(stream) {
+ if (IsReadableStream(stream) === false) {
+ throw new TypeError(errReaderConstructorBadArgument);
+ }
+ if (IsReadableStreamLocked(stream) === true) {
+ throw new TypeError(errReaderConstructorStreamAlreadyLocked);
}
- get closed() {
+ ReadableStreamReaderGenericInitialize(this, stream);
+
+ this[_readRequests] = new SimpleQueue();
+ }
+
+ defineProperty(ReadableStreamDefaultReader.prototype, 'closed', {
+ enumerable: false,
+ configurable: true,
+ get: function() {
if (IsReadableStreamDefaultReader(this) === false) {
return Promise_reject(new TypeError(streamErrors_illegalInvocation));
}
return this[_closedPromise];
}
+ });
- cancel(reason) {
+ defineProperty(ReadableStreamDefaultReader.prototype, 'cancel', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function(reason) {
if (IsReadableStreamDefaultReader(this) === false) {
return Promise_reject(new TypeError(streamErrors_illegalInvocation));
}
@@ -529,8 +564,13 @@
return ReadableStreamReaderGenericCancel(this, reason);
}
+ });
- read() {
+ defineProperty(ReadableStreamDefaultReader.prototype, 'read', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function() {
if (IsReadableStreamDefaultReader(this) === false) {
return Promise_reject(new TypeError(streamErrors_illegalInvocation));
}
@@ -541,8 +581,13 @@
return ReadableStreamDefaultReaderRead(this);
}
+ });
- releaseLock() {
+ defineProperty(ReadableStreamDefaultReader.prototype, 'releaseLock', {
+ enumerable: false,
+ configurable: true,
+ writable: true,
+ value: function() {
if (IsReadableStreamDefaultReader(this) === false) {
throw new TypeError(streamErrors_illegalInvocation);
}
@@ -558,7 +603,7 @@
ReadableStreamReaderGenericRelease(this);
}
- }
+ });
function ReadableStreamReaderGenericCancel(reader, reason) {
return ReadableStreamCancel(reader[_ownerReadableStream], reason);
@@ -739,12 +784,6 @@
// TODO(yhirano): Remove this when we don't need hasPendingActivity in
// blink::UnderlyingSourceBase.
const controller = stream[_controller];
- if (controller[_readableStreamDefaultControllerBits] & EXTERNALLY_CONTROLLED) {
- // The stream is created with an external controller (i.e. made in
- // Blink).
- const underlyingSource = controller[_underlyingSource];
- callFunction(underlyingSource.notifyLockAcquired, underlyingSource);
- }
reader[_ownerReadableStream] = stream;
stream[_reader] = reader;
@@ -767,12 +806,6 @@
// TODO(yhirano): Remove this when we don't need hasPendingActivity in
// blink::UnderlyingSourceBase.
const controller = reader[_ownerReadableStream][_controller];
- if (controller[_readableStreamDefaultControllerBits] & EXTERNALLY_CONTROLLED) {
- // The stream is created with an external controller (i.e. made in
- // Blink).
- const underlyingSource = controller[_underlyingSource];
- callFunction(underlyingSource.notifyLockReleased, underlyingSource);
- }
if (ReadableStreamGetState(reader[_ownerReadableStream]) === STATE_READABLE) {
v8_rejectPromise(reader[_closedPromise], new TypeError(errReleasedReaderClosedPromise));
@@ -1072,12 +1105,4 @@
global.ReadableStreamDefaultControllerGetDesiredSize = ReadableStreamDefaultControllerGetDesiredSize;
global.ReadableStreamDefaultControllerEnqueue = ReadableStreamDefaultControllerEnqueue;
global.ReadableStreamDefaultControllerError = ReadableStreamDefaultControllerError;
-
-/*
- binding.createReadableStreamWithExternalController =
- (underlyingSource, strategy) => {
- return new ReadableStream(
- underlyingSource, strategy, createWithExternalControllerSentinel);
- };
-*/
})(this);
diff --git a/src/cobalt/system_window/application_event.h b/src/cobalt/system_window/application_event.h
index 8151472..eb0f1dd 100644
--- a/src/cobalt/system_window/application_event.h
+++ b/src/cobalt/system_window/application_event.h
@@ -23,11 +23,13 @@
class ApplicationEvent : public base::Event {
public:
enum Type {
- kQuit,
kPause,
- kUnpause,
- kSuspend,
+ kPreload,
+ kQuit,
kResume,
+ kStart,
+ kSuspend,
+ kUnpause,
};
explicit ApplicationEvent(Type type) : type_(type) {}
diff --git a/src/cobalt/system_window/input_event.h b/src/cobalt/system_window/input_event.h
index a17c02e..78c3620 100644
--- a/src/cobalt/system_window/input_event.h
+++ b/src/cobalt/system_window/input_event.h
@@ -28,6 +28,10 @@
kKeyDown,
kKeyUp,
kKeyMove,
+ kPointerDown,
+ kPointerUp,
+ kPointerMove,
+ kWheel,
};
// Bit-mask of key modifiers. These correspond to the |SbKeyModifiers| values
@@ -38,30 +42,66 @@
kCtrlKey = 1 << 1,
kMetaKey = 1 << 2,
kShiftKey = 1 << 3,
+ kLeftButton = 1 << 4,
+ kRightButton = 1 << 5,
+ kMiddleButton = 1 << 6,
+ kBackButton = 1 << 7,
+ kForwardButton = 1 << 8,
};
- InputEvent(Type type, int key_code, uint32 modifiers, bool is_repeat,
- const math::PointF& position = math::PointF())
+ InputEvent(Type type, int device_id, int key_code, uint32 modifiers,
+ bool is_repeat, const math::PointF& position = math::PointF(),
+ const math::PointF& delta = math::PointF()
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+ ,
+ float pressure = 0, const math::PointF& size = math::PointF(),
+ const math::PointF& tilt = math::PointF()
+#endif
+ )
: type_(type),
+ device_id_(device_id),
key_code_(key_code),
modifiers_(modifiers),
is_repeat_(is_repeat),
- position_(position) {}
+ position_(position),
+ delta_(delta)
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+ ,
+ pressure_(pressure),
+ size_(size),
+ tilt_(tilt)
+#endif
+ {
+ }
Type type() const { return type_; }
int key_code() const { return key_code_; }
+ int device_id() const { return device_id_; }
uint32 modifiers() const { return modifiers_; }
bool is_repeat() const { return is_repeat_; }
const math::PointF& position() const { return position_; }
+ const math::PointF& delta() const { return delta_; }
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+ float pressure() const { return pressure_; }
+ const math::PointF& size() const { return size_; }
+ const math::PointF& tilt() const { return tilt_; }
+#endif
BASE_EVENT_SUBCLASS(InputEvent);
private:
Type type_;
+ int device_id_;
int key_code_;
uint32 modifiers_;
bool is_repeat_;
math::PointF position_;
+ math::PointF delta_;
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+ float pressure_;
+ math::PointF size_;
+ math::PointF tilt_;
+#endif
};
// The Starboard Event handler SbHandleEvent should call this function on
diff --git a/src/cobalt/system_window/system_window.cc b/src/cobalt/system_window/system_window.cc
index ef9c73a..9ca6339 100644
--- a/src/cobalt/system_window/system_window.cc
+++ b/src/cobalt/system_window/system_window.cc
@@ -81,31 +81,80 @@
return SbWindowGetPlatformHandle(window_);
}
+void SystemWindow::DispatchInputEvent(const SbInputData& data,
+ InputEvent::Type type, bool is_repeat) {
+ // Starboard handily uses the Microsoft key mapping, which is also what Cobalt
+ // uses.
+ int key_code = static_cast<int>(data.key);
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+ scoped_ptr<InputEvent> input_event(
+ new InputEvent(type, data.device_id, key_code, data.key_modifiers,
+ is_repeat, math::PointF(data.position.x, data.position.y),
+ math::PointF(data.delta.x, data.delta.y), data.pressure,
+ math::PointF(data.size.x, data.size.y),
+ math::PointF(data.tilt.x, data.tilt.y)));
+#else
+ scoped_ptr<InputEvent> input_event(
+ new InputEvent(type, data.device_id, key_code, data.key_modifiers,
+ is_repeat, math::PointF(data.position.x, data.position.y),
+ math::PointF(data.delta.x, data.delta.y)));
+#endif
+ event_dispatcher()->DispatchEvent(input_event.PassAs<base::Event>());
+}
+
+void SystemWindow::HandlePointerInputEvent(const SbInputData& data) {
+ switch (data.type) {
+ case kSbInputEventTypePress:
+ case kSbInputEventTypeUnpress: {
+ InputEvent::Type input_event_type = data.type == kSbInputEventTypePress
+ ? InputEvent::kPointerDown
+ : InputEvent::kPointerUp;
+ DispatchInputEvent(data, input_event_type, false /* is_repeat */);
+ break;
+ }
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+ case kSbInputEventTypeWheel: {
+ DispatchInputEvent(data, InputEvent::kWheel, false /* is_repeat */);
+ break;
+ }
+#endif
+ case kSbInputEventTypeMove:
+ DispatchInputEvent(data, InputEvent::kPointerMove, false /* is_repeat */);
+ break;
+ default:
+ SB_NOTREACHED();
+ break;
+ }
+}
+
void SystemWindow::HandleInputEvent(const SbInputData& data) {
DCHECK_EQ(window_, data.window);
- if (data.type == kSbInputEventTypePress) {
- // Starboard handily uses the Microsoft key mapping, which is also what
- // Cobalt uses.
- int key_code = static_cast<int>(data.key);
- scoped_ptr<InputEvent> input_event(
- new InputEvent(InputEvent::kKeyDown, key_code, data.key_modifiers,
- key_down_ /* is_repeat */));
- key_down_ = true;
- event_dispatcher()->DispatchEvent(input_event.PassAs<base::Event>());
- } else if (data.type == kSbInputEventTypeUnpress) {
- key_down_ = false;
- int key_code = static_cast<int>(data.key);
- scoped_ptr<InputEvent> input_event(
- new InputEvent(InputEvent::kKeyUp, key_code, data.key_modifiers,
- false /* is_repeat */));
- event_dispatcher()->DispatchEvent(input_event.PassAs<base::Event>());
- } else if (data.type == kSbInputEventTypeMove) {
- int key_code = static_cast<int>(data.key);
- scoped_ptr<InputEvent> input_event(new InputEvent(
- InputEvent::kKeyMove, key_code, data.key_modifiers,
- false /* is_repeat */, math::PointF(data.position.x, data.position.y)));
- event_dispatcher()->DispatchEvent(input_event.PassAs<base::Event>());
+ // Handle supported pointer device types.
+ if ((kSbInputDeviceTypeMouse == data.device_type) ||
+ (kSbInputDeviceTypeTouchScreen == data.device_type)) {
+ HandlePointerInputEvent(data);
+ return;
+ }
+
+ // Handle all other input device types.
+ switch (data.type) {
+ case kSbInputEventTypePress: {
+ DispatchInputEvent(data, InputEvent::kKeyDown, key_down_);
+ key_down_ = true;
+ break;
+ }
+ case kSbInputEventTypeUnpress: {
+ DispatchInputEvent(data, InputEvent::kKeyUp, false /* is_repeat */);
+ key_down_ = false;
+ break;
+ }
+ case kSbInputEventTypeMove: {
+ DispatchInputEvent(data, InputEvent::kKeyMove, false /* is_repeat */);
+ break;
+ }
+ default:
+ break;
}
}
diff --git a/src/cobalt/system_window/system_window.gyp b/src/cobalt/system_window/system_window.gyp
index c746f72..50f3b8b 100644
--- a/src/cobalt/system_window/system_window.gyp
+++ b/src/cobalt/system_window/system_window.gyp
@@ -14,7 +14,7 @@
{
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
{
diff --git a/src/cobalt/system_window/system_window.h b/src/cobalt/system_window/system_window.h
index 1f76815..08aee67 100644
--- a/src/cobalt/system_window/system_window.h
+++ b/src/cobalt/system_window/system_window.h
@@ -93,6 +93,9 @@
private:
void UpdateModifiers(SbKey key, bool pressed);
InputEvent::Modifiers GetModifiers();
+ void DispatchInputEvent(const SbInputData& data, InputEvent::Type type,
+ bool is_repeat);
+ void HandlePointerInputEvent(const SbInputData& data);
base::EventDispatcher* event_dispatcher_;
diff --git a/src/cobalt/test/document_loader.h b/src/cobalt/test/document_loader.h
index df0ecdd..f6eb285 100644
--- a/src/cobalt/test/document_loader.h
+++ b/src/cobalt/test/document_loader.h
@@ -63,7 +63,8 @@
&resource_provider_, NULL /* animated_image_tracker */,
image_cache_.get(), NULL /* reduced_image_cache_capacity_manager */,
NULL /* remote_font_cache */, NULL /* mesh_cache */,
- dom_stat_tracker_.get(), "" /* language */) {}
+ dom_stat_tracker_.get(), "" /* language */,
+ base::kApplicationStateStarted) {}
void Load(const GURL& url) {
// Load the document in a nested message loop.
dom::Document::Options options(url);
diff --git a/src/cobalt/test/empty_document.h b/src/cobalt/test/empty_document.h
index 9b6ca37..bb36626 100644
--- a/src/cobalt/test/empty_document.h
+++ b/src/cobalt/test/empty_document.h
@@ -15,6 +15,7 @@
#ifndef COBALT_TEST_EMPTY_DOCUMENT_H_
#define COBALT_TEST_EMPTY_DOCUMENT_H_
+#include "cobalt/base/application_state.h"
#include "cobalt/css_parser/parser.h"
#include "cobalt/dom/document.h"
#include "cobalt/dom/dom_stat_tracker.h"
@@ -32,7 +33,8 @@
dom_stat_tracker_(new dom::DomStatTracker("EmptyDocument")),
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),
document_(new dom::Document(&html_element_context_)) {}
dom::Document* document() { return document_.get(); }
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index 09c5600..e9daa78 100644
--- a/src/cobalt/version.h
+++ b/src/cobalt/version.h
@@ -15,6 +15,6 @@
#define COBALT_VERSION_H_
// Cobalt release number.
-#define COBALT_VERSION "11"
+#define COBALT_VERSION "12"
#endif // COBALT_VERSION_H_
diff --git a/src/cobalt/web_animations/web_animations.gyp b/src/cobalt/web_animations/web_animations.gyp
index 495b0f2..c600a0f 100644
--- a/src/cobalt/web_animations/web_animations.gyp
+++ b/src/cobalt/web_animations/web_animations.gyp
@@ -14,7 +14,7 @@
{
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
{
diff --git a/src/cobalt/webdriver/algorithms.cc b/src/cobalt/webdriver/algorithms.cc
index 8fbce6e..83379cf 100644
--- a/src/cobalt/webdriver/algorithms.cc
+++ b/src/cobalt/webdriver/algorithms.cc
@@ -318,8 +318,7 @@
for (uint32 i = 0; i < child_nodes->length(); ++i) {
scoped_refptr<dom::Node> child = child_nodes->Item(i);
if (child->IsText() ||
- (child->IsElement() &&
- HasPositiveSizeDimensions(child->AsElement().get()))) {
+ (child->IsElement() && HasPositiveSizeDimensions(child->AsElement()))) {
return true;
}
}
@@ -339,7 +338,7 @@
// Check each ancestor of this element and if the ancestor's overflow style
// is set to hidden, check if this element completely overflows or underflows
// the element and is thus not visible.
- dom::Element* parent = element->parent_element().get();
+ dom::Element* parent = element->parent_element();
while (parent) {
// Only block level elements will hide children due to overflow.
if (IsBlockLevelElement(parent)) {
@@ -356,7 +355,7 @@
}
}
}
- parent = parent->parent_element().get();
+ parent = parent->parent_element();
}
return false;
}
diff --git a/src/cobalt/webdriver/element_driver.cc b/src/cobalt/webdriver/element_driver.cc
index 139738a..56c1450 100644
--- a/src/cobalt/webdriver/element_driver.cc
+++ b/src/cobalt/webdriver/element_driver.cc
@@ -63,11 +63,15 @@
const protocol::ElementId& element_id,
const base::WeakPtr<dom::Element>& element, ElementMapping* element_mapping,
KeyboardEventInjector keyboard_event_injector,
+ PointerEventInjector pointer_event_injector,
+ WheelEventInjector wheel_event_injector,
const scoped_refptr<base::MessageLoopProxy>& message_loop)
: element_id_(element_id),
element_(element),
element_mapping_(element_mapping),
- keyboard_injector_(keyboard_event_injector),
+ keyboard_event_injector_(keyboard_event_injector),
+ pointer_event_injector_(pointer_event_injector),
+ wheel_event_injector_(wheel_event_injector),
element_message_loop_(message_loop) {}
util::CommandResult<std::string> ElementDriver::GetTagName() {
@@ -179,7 +183,8 @@
return CommandResult(protocol::Response::kStaleElementReference);
}
- keyboard_injector_.Run(element_.get(), (*events)[i]);
+ keyboard_event_injector_.Run(element_.get(), (*events)[i].first,
+ (*events)[i].second);
}
return CommandResult(protocol::Response::kSuccess);
}
diff --git a/src/cobalt/webdriver/element_driver.h b/src/cobalt/webdriver/element_driver.h
index 4ba2725..5cdad82 100644
--- a/src/cobalt/webdriver/element_driver.h
+++ b/src/cobalt/webdriver/element_driver.h
@@ -23,8 +23,11 @@
#include "base/memory/weak_ptr.h"
#include "base/message_loop_proxy.h"
#include "base/threading/thread_checker.h"
+#include "cobalt/base/token.h"
#include "cobalt/dom/element.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/webdriver/element_mapping.h"
#include "cobalt/webdriver/keyboard.h"
#include "cobalt/webdriver/protocol/element_id.h"
@@ -45,14 +48,22 @@
// will map to a method on this class.
class ElementDriver {
public:
- typedef base::Callback<void(scoped_refptr<dom::Element>,
- const dom::KeyboardEvent::Data&)>
+ typedef base::Callback<void(scoped_refptr<dom::Element>, const base::Token,
+ const dom::KeyboardEventInit&)>
KeyboardEventInjector;
+ typedef base::Callback<void(scoped_refptr<dom::Element>, const base::Token,
+ const dom::PointerEventInit&)>
+ PointerEventInjector;
+ typedef base::Callback<void(scoped_refptr<dom::Element>, const base::Token,
+ const dom::WheelEventInit&)>
+ WheelEventInjector;
ElementDriver(const protocol::ElementId& element_id,
const base::WeakPtr<dom::Element>& element,
ElementMapping* element_mapping,
- KeyboardEventInjector keyboard_injector,
+ KeyboardEventInjector keyboard_event_injector,
+ PointerEventInjector pointer_event_injector,
+ WheelEventInjector wheel_event_injector,
const scoped_refptr<base::MessageLoopProxy>& message_loop);
const protocol::ElementId& element_id() { return element_id_; }
@@ -93,7 +104,9 @@
// These should only be accessed from |element_message_loop_|.
base::WeakPtr<dom::Element> element_;
ElementMapping* element_mapping_;
- KeyboardEventInjector keyboard_injector_;
+ KeyboardEventInjector keyboard_event_injector_;
+ PointerEventInjector pointer_event_injector_;
+ WheelEventInjector wheel_event_injector_;
scoped_refptr<base::MessageLoopProxy> element_message_loop_;
friend class WindowDriver;
diff --git a/src/cobalt/webdriver/get_element_text_test.cc b/src/cobalt/webdriver/get_element_text_test.cc
index 29d70f3..a25c693 100644
--- a/src/cobalt/webdriver/get_element_text_test.cc
+++ b/src/cobalt/webdriver/get_element_text_test.cc
@@ -42,7 +42,8 @@
dom_stat_tracker_(new dom::DomStatTracker("GetElementTextTest")),
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) {}
void SetUp() OVERRIDE {
dom::Document::Options options;
diff --git a/src/cobalt/webdriver/keyboard.cc b/src/cobalt/webdriver/keyboard.cc
index 215d5a2..576c83a 100644
--- a/src/cobalt/webdriver/keyboard.cc
+++ b/src/cobalt/webdriver/keyboard.cc
@@ -17,6 +17,8 @@
#include <limits>
#include "base/i18n/char_iterator.h"
+#include "cobalt/base/token.h"
+#include "cobalt/base/tokens.h"
#include "cobalt/dom/keycode.h"
using cobalt::dom::KeyboardEvent;
@@ -404,37 +406,28 @@
}
void AddKeyDownEvent(int key_code, int char_code, KeyLocationCode location) {
- AddKeyEvent(KeyboardEvent::kTypeKeyDown, key_code, char_code, location);
+ AddKeyEvent(base::Tokens::keydown(), key_code, char_code, location);
}
void AddKeyPressEvent(int key_code, int char_code, KeyLocationCode location) {
- AddKeyEvent(KeyboardEvent::kTypeKeyPress, key_code, char_code, location);
+ AddKeyEvent(base::Tokens::keypress(), key_code, char_code, location);
}
void AddKeyUpEvent(int key_code, int char_code, KeyLocationCode location) {
- AddKeyEvent(KeyboardEvent::kTypeKeyUp, key_code, char_code, location);
+ AddKeyEvent(base::Tokens::keyup(), key_code, char_code, location);
}
- void AddKeyEvent(KeyboardEvent::Type type, int key_code, int char_code,
+ void AddKeyEvent(base::Token type, int key_code, int char_code,
KeyLocationCode location) {
- const bool kIsRepeat = false;
- uint32 modifiers = GetModifierStateBitfield();
- event_vector_->push_back(dom::KeyboardEvent::Data(
- type, location, modifiers, key_code, char_code, kIsRepeat));
- }
-
- uint32 GetModifierStateBitfield() const {
- uint32 modifier_state = 0;
- if (shift_pressed_) {
- modifier_state |= KeyboardEvent::kShiftKey;
- }
- if (ctrl_pressed_) {
- modifier_state |= KeyboardEvent::kCtrlKey;
- }
- if (alt_pressed_) {
- modifier_state |= KeyboardEvent::kAltKey;
- }
- return modifier_state;
+ dom::KeyboardEventInit event;
+ event.set_location(location);
+ event.set_shift_key(shift_pressed_);
+ event.set_ctrl_key(ctrl_pressed_);
+ event.set_alt_key(alt_pressed_);
+ event.set_key_code(key_code);
+ event.set_char_code(char_code);
+ event.set_repeat(false);
+ event_vector_->push_back(std::make_pair(type, event));
}
bool shift_pressed_;
diff --git a/src/cobalt/webdriver/keyboard.h b/src/cobalt/webdriver/keyboard.h
index 1acbc74..356f40b 100644
--- a/src/cobalt/webdriver/keyboard.h
+++ b/src/cobalt/webdriver/keyboard.h
@@ -16,6 +16,7 @@
#define COBALT_WEBDRIVER_KEYBOARD_H_
#include <string>
+#include <utility>
#include <vector>
#include "base/memory/ref_counted.h"
@@ -30,7 +31,7 @@
kReleaseModifiers,
kKeepModifiers,
};
- typedef std::vector<dom::KeyboardEvent::Data>
+ typedef std::vector<std::pair<base::Token, dom::KeyboardEventInit> >
KeyboardEventVector;
static void TranslateToKeyEvents(const std::string& utf8_keys,
TerminationBehaviour termination_behaviour,
diff --git a/src/cobalt/webdriver/keyboard_test.cc b/src/cobalt/webdriver/keyboard_test.cc
index f85679c..b961587 100644
--- a/src/cobalt/webdriver/keyboard_test.cc
+++ b/src/cobalt/webdriver/keyboard_test.cc
@@ -15,7 +15,9 @@
#include <algorithm>
#include <vector>
+#include "cobalt/base/tokens.h"
#include "cobalt/dom/keyboard_event.h"
+#include "cobalt/dom/keyboard_event_init.h"
#include "cobalt/dom/keycode.h"
#include "cobalt/webdriver/keyboard.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -29,42 +31,54 @@
namespace webdriver {
namespace {
-int32 GetKeyCode(const dom::KeyboardEvent::Data& event) {
+int32 GetKeyCode(std::pair<base::Token, const dom::KeyboardEventInit&> event) {
scoped_refptr<dom::KeyboardEvent> keyboard_event(
- new dom::KeyboardEvent(event));
+ new dom::KeyboardEvent(event.first.c_str(), event.second));
return keyboard_event->key_code();
}
-int32 GetCharCode(const dom::KeyboardEvent::Data& event) {
+int32 GetCharCode(std::pair<base::Token, const dom::KeyboardEventInit&> event) {
scoped_refptr<dom::KeyboardEvent> keyboard_event(
- new dom::KeyboardEvent(event));
+ new dom::KeyboardEvent(event.first.c_str(), event.second));
return keyboard_event->char_code();
}
-uint32 GetModifierBitfield(const dom::KeyboardEvent::Data& event) {
+const uint32 kNoModifier = 0;
+const uint32 kShift = 1;
+const uint32 kAlt = 2;
+const uint32 kOtherModifier = 4;
+const uint32 kAltAndShift = kAlt | kShift;
+
+uint32 GetModifierBitfield(
+ std::pair<base::Token, const dom::KeyboardEventInit&> event) {
scoped_refptr<dom::KeyboardEvent> keyboard_event(
- new dom::KeyboardEvent(event));
- return keyboard_event->modifiers();
+ new dom::KeyboardEvent(event.first.c_str(), event.second));
+ uint32 modifiers = kNoModifier;
+ if (keyboard_event->shift_key()) {
+ modifiers |= kShift;
+ }
+ if (keyboard_event->alt_key()) {
+ modifiers |= kAlt;
+ }
+ if (keyboard_event->ctrl_key() || keyboard_event->meta_key()) {
+ modifiers |= kOtherModifier;
+ }
+ return modifiers;
}
-std::string GetType(const dom::KeyboardEvent::Data& event) {
+std::string GetType(
+ std::pair<base::Token, const dom::KeyboardEventInit&> event) {
scoped_refptr<dom::KeyboardEvent> keyboard_event(
- new dom::KeyboardEvent(event));
+ new dom::KeyboardEvent(event.first.c_str(), event.second));
return keyboard_event->type().c_str();
}
-int GetLocation(const dom::KeyboardEvent::Data& event) {
+int GetLocation(std::pair<base::Token, const dom::KeyboardEventInit&> event) {
scoped_refptr<dom::KeyboardEvent> keyboard_event(
- new dom::KeyboardEvent(event));
+ new dom::KeyboardEvent(event.first.c_str(), event.second));
return keyboard_event->location();
}
-// Less verbose redefinitions.
-const int kNoModifier = dom::UIEventWithKeyState::kNoModifier;
-const int kShift = dom::UIEventWithKeyState::kShiftKey;
-const int kAlt = dom::UIEventWithKeyState::kAltKey;
-const int kAltAndShift = kAlt | kShift;
-
const int kLocationStandard =
dom::KeyboardEvent::kDomKeyLocationStandard;
const int kLocationLeft = dom::KeyboardEvent::kDomKeyLocationLeft;
diff --git a/src/cobalt/webdriver/window_driver.cc b/src/cobalt/webdriver/window_driver.cc
index 92721b7..45fcfc2 100644
--- a/src/cobalt/webdriver/window_driver.cc
+++ b/src/cobalt/webdriver/window_driver.cc
@@ -108,12 +108,16 @@
const protocol::WindowId& window_id,
const base::WeakPtr<dom::Window>& window,
const GetGlobalEnvironmentFunction& get_global_environment_function,
- KeyboardEventInjector keyboard_injector,
+ KeyboardEventInjector keyboard_event_injector,
+ PointerEventInjector pointer_event_injector,
+ WheelEventInjector wheel_event_injector,
const scoped_refptr<base::MessageLoopProxy>& message_loop)
: window_id_(window_id),
window_(window),
get_global_environment_(get_global_environment_function),
- keyboard_injector_(keyboard_injector),
+ keyboard_event_injector_(keyboard_event_injector),
+ pointer_event_injector_(pointer_event_injector),
+ wheel_event_injector_(wheel_event_injector),
window_message_loop_(message_loop),
element_driver_map_deleter_(&element_drivers_),
next_element_id_(0) {
@@ -354,9 +358,10 @@
base::StringPrintf("element-%d", next_element_id_++));
std::pair<ElementDriverMapIt, bool> pair_it =
element_drivers_.insert(std::make_pair(
- element_id.id(), new ElementDriver(element_id, weak_element, this,
- keyboard_injector_,
- window_message_loop_)));
+ element_id.id(),
+ new ElementDriver(element_id, weak_element, this,
+ keyboard_event_injector_, pointer_event_injector_,
+ wheel_event_injector_, window_message_loop_)));
DCHECK(pair_it.second)
<< "An ElementDriver was already mapped to the element id: "
<< element_id.id();
@@ -429,7 +434,8 @@
}
for (size_t i = 0; i < events->size(); ++i) {
- keyboard_injector_.Run(scoped_refptr<dom::Element>(), (*events)[i]);
+ keyboard_event_injector_.Run(scoped_refptr<dom::Element>(),
+ (*events)[i].first, (*events)[i].second);
}
return CommandResult(protocol::Response::kSuccess);
}
diff --git a/src/cobalt/webdriver/window_driver.h b/src/cobalt/webdriver/window_driver.h
index 4f4973b..3e2b47c 100644
--- a/src/cobalt/webdriver/window_driver.h
+++ b/src/cobalt/webdriver/window_driver.h
@@ -29,6 +29,8 @@
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "cobalt/dom/keyboard_event.h"
+#include "cobalt/dom/pointer_event.h"
+#include "cobalt/dom/wheel_event.h"
#include "cobalt/dom/window.h"
#include "cobalt/webdriver/element_driver.h"
#include "cobalt/webdriver/element_mapping.h"
@@ -53,15 +55,24 @@
// will map to a method on this class.
class WindowDriver : private ElementMapping {
public:
- typedef base::Callback<void(scoped_refptr<dom::Element>,
- const dom::KeyboardEvent::Data&)>
+ typedef base::Callback<void(scoped_refptr<dom::Element>, const base::Token,
+ const dom::KeyboardEventInit&)>
KeyboardEventInjector;
+ typedef base::Callback<void(scoped_refptr<dom::Element>, const base::Token,
+ const dom::PointerEventInit&)>
+ PointerEventInjector;
+ typedef base::Callback<void(scoped_refptr<dom::Element>, const base::Token,
+ const dom::WheelEventInit&)>
+ WheelEventInjector;
+
typedef base::Callback<scoped_refptr<script::GlobalEnvironment>()>
GetGlobalEnvironmentFunction;
WindowDriver(const protocol::WindowId& window_id,
const base::WeakPtr<dom::Window>& window,
const GetGlobalEnvironmentFunction& get_global_environment,
- KeyboardEventInjector keyboard_injector,
+ KeyboardEventInjector keyboard_event_injector,
+ PointerEventInjector pointer_event_injector,
+ WheelEventInjector wheel_event_injector,
const scoped_refptr<base::MessageLoopProxy>& message_loop);
~WindowDriver();
const protocol::WindowId& window_id() { return window_id_; }
@@ -135,7 +146,9 @@
// Bound to the WebDriver thread.
base::ThreadChecker thread_checker_;
- KeyboardEventInjector keyboard_injector_;
+ KeyboardEventInjector keyboard_event_injector_;
+ PointerEventInjector pointer_event_injector_;
+ WheelEventInjector wheel_event_injector_;
// Anything that interacts with the window must be run on this message loop.
scoped_refptr<base::MessageLoopProxy> window_message_loop_;
diff --git a/src/cobalt/webdriver_benchmarks/c_val_names.py b/src/cobalt/webdriver_benchmarks/c_val_names.py
index 3256e48..6ce4f4d 100644
--- a/src/cobalt/webdriver_benchmarks/c_val_names.py
+++ b/src/cobalt/webdriver_benchmarks/c_val_names.py
@@ -5,18 +5,46 @@
from __future__ import print_function
-def count_dom_active_dispatch_events():
- return "Count.DOM.ActiveDispatchEvents"
+def count_dom_active_java_script_events():
+ return "Count.DOM.ActiveJavaScriptEvents"
+
+
+def count_dom_html_elements_document():
+ return "Count.MainWebModule.DOM.HtmlElement.Document"
+
+
+def count_dom_html_elements_total():
+ return "Count.MainWebModule.DOM.HtmlElement.Total"
+
+
+def count_dom_html_script_element_execute():
+ return "Count.MainWebModule.DOM.HtmlScriptElement.Execute"
+
+
+def count_layout_boxes():
+ return "Count.MainWebModule.Layout.Box"
def count_image_cache_loading_resources():
return "Count.MainWebModule.ImageCache.LoadingResources"
+def count_image_cache_requested_resources():
+ return "Count.MainWebModule.ImageCache.RequestedResources"
+
+
+def count_rasterize_new_render_tree():
+ return "Count.Renderer.Rasterize.NewRenderTree"
+
+
def event_duration_dom_video_start_delay():
return "Event.Duration.MainWebModule.DOM.VideoStartDelay"
+def event_is_processing():
+ return "Event.MainWebModule.IsProcessing"
+
+
def event_value_dictionary(event_type):
return "Event.MainWebModule.{}.ValueDictionary".format(event_type)
@@ -31,3 +59,31 @@
def renderer_has_active_animations():
return "Renderer.HasActiveAnimations"
+
+
+def time_browser_navigate():
+ return "Time.Browser.Navigate"
+
+
+def time_browser_on_load_event():
+ return "Time.Browser.OnLoadEvent"
+
+
+def time_cobalt_start():
+ return "Time.Cobalt.Start"
+
+
+def time_dom_html_script_element_execute():
+ return "Time.MainWebModule.DOM.HtmlScriptElement.Execute"
+
+
+def time_rasterize_animations_start():
+ return "Time.Renderer.Rasterize.Animations.Start"
+
+
+def time_rasterize_animations_end():
+ return "Time.Renderer.Rasterize.Animations.End"
+
+
+def time_rasterize_new_render_tree():
+ return "Time.Renderer.Rasterize.NewRenderTree"
diff --git a/src/cobalt/webdriver_benchmarks/container_util.py b/src/cobalt/webdriver_benchmarks/container_util.py
index 56e948a..2544b11 100644
--- a/src/cobalt/webdriver_benchmarks/container_util.py
+++ b/src/cobalt/webdriver_benchmarks/container_util.py
@@ -52,3 +52,39 @@
return sorted_values[index] * (
1 - fractional) + sorted_values[index + 1] * fractional
+
+
+def sample_variance(list_values):
+ """Returns the variance of a list of numeric values.
+
+ Args:
+ list_values: A list of numeric values.
+ Returns:
+ Appropriate value.
+ """
+ if not list_values:
+ return None
+ if len(list_values) <= 1:
+ return 0
+
+ mean_of_values = mean(list_values)
+ dif_squared_sum = 0
+ for value in list_values:
+ dif = value - mean_of_values
+ dif_squared_sum += dif * dif
+
+ return dif_squared_sum / (len(list_values) - 1)
+
+
+def sample_standard_deviation(list_values):
+ """Returns the standard deviation of a list of numeric values.
+
+ Args:
+ list_values: A list of numeric values.
+ Returns:
+ Appropriate value.
+ """
+ if not list_values:
+ return None
+
+ return math.sqrt(sample_variance(list_values))
diff --git a/src/cobalt/webdriver_benchmarks/default_query_param_constants.py b/src/cobalt/webdriver_benchmarks/default_query_param_constants.py
new file mode 100644
index 0000000..c2347f2
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/default_query_param_constants.py
@@ -0,0 +1,10 @@
+"""Default query params to use when loading URLs."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+BASE_QUERY_PARAMS = {}
+
+INIT_QUERY_PARAMS = {}
+INIT_QUERY_PARAMS_TRIGGER_RELOAD = False
diff --git a/src/cobalt/webdriver_benchmarks/tests/README.md b/src/cobalt/webdriver_benchmarks/tests/README.md
index bd9f865..81fde36 100644
--- a/src/cobalt/webdriver_benchmarks/tests/README.md
+++ b/src/cobalt/webdriver_benchmarks/tests/README.md
@@ -80,24 +80,73 @@
Note that most time-based measurements are in microseconds.
-### Interesting benchmarks
-
-#### Timing-related
+### Interesting Timing-Related Benchmarks
Some particularly interesting timing-related benchmark results are:
- - `wbStartupDurBlankToBrowseUs*`: Measures the startup time, until all images
- finish loading.
- - `wbBrowseToWatchDurVideoStartDelay*`: Measures the browse-to-watch time.
- - `wbBrowseVerticalDurTotalUs*`: Measures the input latency (i.e. JavaScript
- execution time + layout time) during vertical scroll events.
+#### Startup
+ - `wbStartupDurLaunchToBrowseUs`: Measures the time it takes to launch Cobalt
+ and load the desired URL. The measurement ends when all images finish loading
+ and the final render tree is produced. This is only run once so it will be
+ noiser than other values but provides the most accurate measurement of the
+ requirement startup time.
+ - `wbStartupDurLaunchToUsableUs`: Measures the time it takes to launch Cobalt,
+ and fully load the desired URL, including loading all scripts. The
+ measurement ends when the Player JavaScript code finishes loading on the
+ Browse page, which is the point when Cobalt is fully usable. This is only run
+ once so it will be noiser than other values but provides the most accurate
+ measurement of the time when Cobalt is usable.
+ - `wbStartupDurNavigateToBrowseUs*`: Measures the time it takes to navigate to
+ the desired URL when Cobalt is already loaded. The measurement ends when all
+ images finish loading and the final render tree is produced. This is run
+ multiple times, so it will be less noisy than `wbStartupDurLaunchToBrowseUs`,
+ but it does not include Cobalt initialization so it is not as accurate of a
+ measurement.
+ - `wbStartupDurNavigateToUsableUs`: Measures the time it takes to navigate to
+ the desired URL when Cobalt is already loaded, including loading all scripts.
+ The measurement ends when the Player JavaScript code finishes loading on the
+ Browse page, which is the point when Cobalt is fully usable. This is run
+ multiple times, so it will be less noisy than `wbStartupDurLaunchToUsableUs`,
+ but it does not include Cobalt initialization so it is not as accurate of a
+ measurement.
+
+#### Browse Horizontal Scroll Events
+ - `wbBrowseHorizontalDurTotalUs*`: Measures the latency (i.e. JavaScript
+ execution time + layout time) during horizontal scroll events from keypress
+ until the render tree is submitted to the rasterize thread. It does not
+ include the time taken to rasterize the render tree so it will be smaller
+ than the observed latency.
+ - `wbBrowseHorizontalDurAnimationsStartDelayUs*`: Measures the input latency
+ during horizontal scroll events from the keypress until the animation starts.
+ This is the most accurate measure of input latency.
+ - `wbBrowseHorizontalDurAnimationsEndDelayUs*`: Measures the latency during
+ horizontal scroll events from the keypress until the animation ends.
+ - `wbBrowseHorizontalDurFinalRenderTreeDelayUs*`: Measures the latency during
+ horizontal scroll events from the keypress until the final render tree is
+ rendered and processing stops.
+ - `wbBrowseHorizontalDurRasterizeAnimationsUs*`: Measures the time it takes to
+ render each frame of the animation triggered by a horizontal scroll event.
+ The inverse of this number is the framerate.
+
+#### Browse Vertical Scroll Events
+ - `wbBrowseVerticalDurTotalUs*`: Measures the latency (i.e. JavaScript
+ execution time + layout time) during vertical scroll events from keypress
+ until the render tree is submitted to the rasterize thread. It does not
+ include the time taken to rasterize the render tree so it will be smaller
+ than the observed latency.
+ - `wbBrowseVerticalDurAnimationsStartDelayUs*`: Measures the input latency
+ during vertical scroll events from the keypress until the animation starts.
+ This is the most accurate measure of input latency.
+ - `wbBrowseVerticalDurAnimationsEndDelayUs*`: Measures the latency during
+ vertical scroll events from the keypress until the animation ends.
+ - `wbBrowseVerticalDurFinalRenderTreeDelayUs*`: Measures the latency during
+ vertical scroll events from the keypress until the final render tree is
+ rendered and processing stops.
- `wbBrowseVerticalDurRasterizeAnimationsUs*`: Measures the time it takes to
render each frame of the animation triggered by a vertical scroll event.
The inverse of this number is the framerate.
- - `wbBrowseHorizontalDurTotalUs*`: Same as `wbBrowseVerticalDurTotalUs*` except
- for horizontal scroll events.
- - `wbBrowseHorizontalDurRasterizeAnimationsUs*`: Same as
- `wbBrowseVerticalDurRasterizeAnimationsUs*` except for horizontal scroll
- events.
+
+#### Browse-to-Watch
+ - `wbBrowseToWatchDurVideoStartDelay*`: Measures the browse-to-watch time.
In each case above, the `*` symbol can be one of either `Mean`, `Pct25`,
`Pct50`, `Pct75` or `Pct95`. For example, `wbStartupDurBlankToBrowseUsMean` or
@@ -106,22 +155,51 @@
can drill into the data by exploring either the mean, or the various
percentiles.
-#### Object count-related
+### Interesting Count-Related Benchmarks
Some particularly interesting count-related benchmark results are:
+#### Startup
+ - `wbStartupCntDomHtmlElements*`: Lists the number of HTML elements in
+ existence after startup completes. This includes HTML elements that are no
+ longer in the DOM but have not been garbage collected yet.
+ - `wbStartupCntDocumentDomHtmlElements*`: Lists the number of HTML
+ elements within the DOM tree after startup completes.
+ - `wbStartupCntLayoutBoxes*`: Lists the number of layout boxes within
+ the layout tree after startup completes.
+ - `wbStartupCntRenderTrees*`: Lists the number of render trees that were
+ generated during startup.
+ - `wbStartupCntRequestedImages*`: Lists the number of images that were
+ requested during startup.
+
+#### Browse Horizontal Scroll Events
+ - `wbBrowseHorizontalCntDomHtmlElements*`: Lists the number of HTML elements in
+ existence after the event. This includes HTML elements that are no longer in
+ the DOM but have not been garbage collected yet.
+ - `wbBrowseHorizontalCntDocumentDomHtmlElements*`: Lists the number of HTML
+ elements within the DOM tree after the event.
+ - `wbBrowseHorizontalCntLayoutBoxes*`: Lists the number of layout boxes within
+ the layout tree after the event.
+ - `wbBrowseHorizontalCntLayoutBoxesCreated*`: Lists the number of new layout
+ boxes that were created during the event.
+ - `wbBrowseHorizontalCntRenderTrees*`: Lists the number of render trees that
+ were generated by the event.
+ - `wbBrowseHorizontalCntRequestedImages*`: Lists the number of images that were
+ requested as a result of the event.
+
+#### Browse Vertical Scroll Events
- `wbBrowseVerticalCntDomHtmlElements*`: Lists the number of HTML elements in
existence after the event. This includes HTML elements that are no longer in
the DOM but have not been garbage collected yet.
+ - `wbBrowseVerticalCntDocumentDomHtmlElements*`: Lists the number of HTML
+ elements within the DOM tree after the event.
- `wbBrowseVerticalCntLayoutBoxes*`: Lists the number of layout boxes within
the layout tree after the event.
- `wbBrowseVerticalCntLayoutBoxesCreated*`: Lists the number of new layout
boxes that were created during the event.
- - `wbBrowseHorizontalCntDomHtmlElements*`: Same as
- `wbBrowseVerticalCntDomHtmlElements*` except for horizontal scroll events.
- - `wbBrowseHorizontalCntLayoutBoxes*`: Same as
- `wbBrowseVerticalCntLayoutBoxes*` except for horizontal scroll events.
- - `wbBrowseHorizontalCntLayoutBoxesCreated*`: Same as
- `wbBrowseVerticalCntLayoutBoxesCreated*` except for horizontal scroll events.
+ - `wbBrowseVerticalCntRenderTrees*`: Lists the number of render trees that
+ were generated by the event.
+ - `wbBrowseVerticalCntRequestedImages*`: Lists the number of images that were
+ requested as a result of the event.
In each case above, the `*` symbol can be one of either `Max`, `Median`, or
`Mean`. For example, `wbBrowseVerticalCntDomHtmlElementsMax` or
@@ -139,16 +217,76 @@
```
python performance.py -p raspi-2 -c qa -d $RASPI_ADDR > results.txt
echo "" > filtered_results.txt
-grep -o "wbStartupDurBlankToBrowseUs.*$" results.txt >> filtered_results.txt
-grep -o "wbBrowseToWatchDurVideoStartDelay.*$" results.txt >> filtered_results.txt
-grep -o "wbBrowseVerticalDurTotalUs.*$" results.txt >> filtered_results.txt
-grep -o "wbBrowseVerticalDurRasterizeAnimationsUs.*$" results.txt >> filtered_results.txt
+printf "=================================TIMING-RELATED=================================\n" >> filtered_results.txt
+printf "\nSTARTUP\n" >> filtered_results.txt
+grep -o "wbStartupDurLaunchToBrowseUs.*$" results.txt >> filtered_results.txt
+grep -o "wbStartupDurLaunchToUsableUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbStartupDurNavigateToBrowseUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbStartupDurNavigateToUsableUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+printf "\nBROWSE HORIZONTAL SCROLL EVENTS\n" >> filtered_results.txt
grep -o "wbBrowseHorizontalDurTotalUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseHorizontalDurAnimationsStartDelayUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseHorizontalDurAnimationsEndDelayUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseHorizontalDurFinalRenderTreeDelayUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
grep -o "wbBrowseHorizontalDurRasterizeAnimationsUs.*$" results.txt >> filtered_results.txt
-grep -o "wbBrowseVerticalCntDomHtmlElements.*$" results.txt >> filtered_results.txt
-grep -o "wbBrowseVerticalCntLayoutBoxes.*$" results.txt >> filtered_results.txt
-grep -o "wbBrowseHorizontalCntDomHtmlElements.*$" results.txt >> filtered_results.txt
-grep -o "wbBrowseHorizontalCntLayoutBoxes.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+printf "\nBROWSE VERTICAL SCROLL EVENTS\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalDurTotalUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalDurAnimationsStartDelayUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalDurAnimationsEndDelayUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalDurFinalRenderTreeDelayUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalDurRasterizeAnimationsUs.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+printf "\nBROWSE TO WATCH\n" >> filtered_results.txt
+grep -o "wbBrowseToWatchDurVideoStartDelay.*$" results.txt >> filtered_results.txt
+printf "\n\n=================================COUNT-RELATED==================================\n" >> filtered_results.txt
+printf "\nSTARTUP\n" >> filtered_results.txt
+grep -o "wbStartupCntDomHtmlElements.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbStartupCntDomDocumentHtmlElements.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbStartupCntLayoutBoxes.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbStartupCntRenderTrees.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbStartupCntRequestedImages.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+printf "\nBROWSE HORIZONTAL SCROLL EVENTS\n" >> filtered_results.txt
+grep -o "wbBrowseHorizontalCntDomHtmlElementsM.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseHorizontalCntDomDocumentHtmlElements.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseHorizontalCntLayoutBoxesM.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseHorizontalCntLayoutBoxesCreated.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseHorizontalCntRenderTrees.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseHorizontalCntRequestedImages.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+printf "\nBROWSE VERTICAL SCROLL EVENTS\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalCntDomHtmlElementsM.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalCntDomDocumentHtmlElements.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalCntLayoutBoxesM.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalCntLayoutBoxesCreated.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalCntRenderTrees.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
+grep -o "wbBrowseVerticalCntRequestedImages.*$" results.txt >> filtered_results.txt
+printf "\n" >> filtered_results.txt
cat filtered_results.txt
```
-
diff --git a/src/cobalt/webdriver_benchmarks/tests/performance/non_video/browse_to_search.py b/src/cobalt/webdriver_benchmarks/tests/performance/non_video/browse_to_search.py
index b04e8ab..a2d82de 100755
--- a/src/cobalt/webdriver_benchmarks/tests/performance/non_video/browse_to_search.py
+++ b/src/cobalt/webdriver_benchmarks/tests/performance/non_video/browse_to_search.py
@@ -38,13 +38,13 @@
def test_simple(self):
recorder_options = tv_testcase_event_recorder.EventRecorderOptions(
self, BROWSE_TO_SEARCH_EVENT_NAME, BROWSE_TO_SEARCH_EVENT_TYPE)
- recorder_options.record_rasterize_animations = False
+ recorder_options.record_animations = False
browse_to_search_recorder = tv_testcase_event_recorder.EventRecorder(
recorder_options)
recorder_options = tv_testcase_event_recorder.EventRecorderOptions(
self, SEARCH_TO_BROWSE_EVENT_NAME, SEARCH_TO_BROWSE_EVENT_TYPE)
- recorder_options.record_rasterize_animations = False
+ recorder_options.record_animations = False
search_to_browse_recorder = tv_testcase_event_recorder.EventRecorder(
recorder_options)
diff --git a/src/cobalt/webdriver_benchmarks/tests/performance/non_video/startup.py b/src/cobalt/webdriver_benchmarks/tests/performance/non_video/startup.py
index 997a0fc..c37008e 100755
--- a/src/cobalt/webdriver_benchmarks/tests/performance/non_video/startup.py
+++ b/src/cobalt/webdriver_benchmarks/tests/performance/non_video/startup.py
@@ -15,14 +15,13 @@
os.path.dirname(os.path.realpath(__file__)))))))
# pylint: disable=C6204,C6203
-import timer
+import c_val_names
import tv_testcase
import tv_testcase_util
-NUM_BLANK_TO_BROWSE_ITERATIONS = 10
+NUM_LOAD_TV_ITERATIONS = 20
-LAUNCH_TO_BLANK = "wbStartupDurLaunchToBlankUs"
-BLANK_TO_BROWSE = "wbStartupDurBlankToBrowseUs"
+STARTUP_RECORD_NAME = "wbStartup"
class StartupTest(tv_testcase.TvTestCase):
@@ -32,45 +31,133 @@
pass
def test_simple(self):
- """This test tries to measure the startup time for the YouTube TV page.
+ """This test tries to measure the startup time for the YouTube TV page."""
+ self.wait_for_processing_complete()
+ self.wait_for_html_script_element_execute_count(2)
- Specifically, this test uses the Cobalt CVal Cobalt.Lifetime, which gets
- updated ~60Hz on a best effort basis and is in microseconds, to determine
- "wbStartupDurLaunchToBlankUs" and uses Timer to determine
- "wbStartupDurBlankToBrowseUs".
- """
+ # Measure durations for the intial launch.
+ launch_time = self.get_cval(c_val_names.time_cobalt_start())
+ navigate_time = self.get_cval(c_val_names.time_browser_navigate())
+ on_load_event_time = self.get_cval(c_val_names.time_browser_on_load_event())
+ browse_time = self.get_cval(c_val_names.time_rasterize_new_render_tree())
+ usable_time = self.get_cval(
+ c_val_names.time_dom_html_script_element_execute())
- dur_launch_to_blank_us = self.get_cval("Cobalt.Lifetime")
+ dur_launch_to_navigate_us = navigate_time - launch_time
+ dur_launch_to_on_load_event_us = on_load_event_time - launch_time
+ dur_launch_to_browse_us = browse_time - launch_time
+ dur_launch_to_usable_us = usable_time - launch_time
- # Call TvTestCase's setUp() now that the blank startup time has been
- # measured.
+ # Call TvTestCase's setUp() now that the launch times have been measured.
super(StartupTest, self).setUp()
- # Blank to browser record strategies
- blank_to_browse_record_strategies = []
- blank_to_browse_record_strategies.append(
- tv_testcase_util.RecordStrategyMean())
- blank_to_browse_record_strategies.append(
+ # Count record strategies
+ count_record_strategies = []
+ count_record_strategies.append(tv_testcase_util.RecordStrategyMean())
+ count_record_strategies.append(tv_testcase_util.RecordStrategyMin())
+ count_record_strategies.append(tv_testcase_util.RecordStrategyMedian())
+ count_record_strategies.append(tv_testcase_util.RecordStrategyMax())
+
+ # Duration record strategies
+ duration_record_strategies = []
+ duration_record_strategies.append(tv_testcase_util.RecordStrategyMean())
+ duration_record_strategies.append(tv_testcase_util.RecordStrategyMin())
+ duration_record_strategies.append(
tv_testcase_util.RecordStrategyPercentile(25))
- blank_to_browse_record_strategies.append(
+ duration_record_strategies.append(
tv_testcase_util.RecordStrategyPercentile(50))
- blank_to_browse_record_strategies.append(
+ duration_record_strategies.append(
tv_testcase_util.RecordStrategyPercentile(75))
- blank_to_browse_record_strategies.append(
+ duration_record_strategies.append(
tv_testcase_util.RecordStrategyPercentile(95))
+ duration_record_strategies.append(tv_testcase_util.RecordStrategyMax())
- # Blank to browser recorder
- blank_to_browse_recorder = tv_testcase_util.ResultsRecorder(
- BLANK_TO_BROWSE, blank_to_browse_record_strategies)
+ # Count recorders
+ count_total_html_element_recorder = tv_testcase_util.ResultsRecorder(
+ STARTUP_RECORD_NAME + "CntDomHtmlElements", count_record_strategies)
+ count_document_html_element_recorder = tv_testcase_util.ResultsRecorder(
+ STARTUP_RECORD_NAME + "CntDomDocumentHtmlElements",
+ count_record_strategies)
+ count_layout_box_recorder = tv_testcase_util.ResultsRecorder(
+ STARTUP_RECORD_NAME + "CntLayoutBoxes", count_record_strategies)
+ count_render_trees_recorder = tv_testcase_util.ResultsRecorder(
+ STARTUP_RECORD_NAME + "CntRenderTrees", count_record_strategies)
+ count_requested_images_recorder = tv_testcase_util.ResultsRecorder(
+ STARTUP_RECORD_NAME + "CntRequestedImages", count_record_strategies)
- for _ in range(NUM_BLANK_TO_BROWSE_ITERATIONS):
- self.load_blank()
- with timer.Timer(BLANK_TO_BROWSE) as t:
- self.load_tv()
- blank_to_browse_recorder.collect_value(int(t.seconds_elapsed * 1000000))
+ # Duration recorders
+ duration_navigate_to_on_load_recorder = tv_testcase_util.ResultsRecorder(
+ STARTUP_RECORD_NAME + "DurNavigateToOnLoadUs",
+ duration_record_strategies)
+ duration_navigate_to_browse_recorder = tv_testcase_util.ResultsRecorder(
+ STARTUP_RECORD_NAME + "DurNavigateToBrowseUs",
+ duration_record_strategies)
+ duration_navigate_to_usable_recorder = tv_testcase_util.ResultsRecorder(
+ STARTUP_RECORD_NAME + "DurNavigateToUsableUs",
+ duration_record_strategies)
- tv_testcase_util.record_test_result(LAUNCH_TO_BLANK, dur_launch_to_blank_us)
- blank_to_browse_recorder.on_end_test()
+ # Now measure counts and durations from reloading the URL.
+ for _ in range(NUM_LOAD_TV_ITERATIONS):
+ count_render_trees_start = self.get_cval(
+ c_val_names.count_rasterize_new_render_tree())
+
+ self.load_tv()
+
+ count_render_trees_end = self.get_cval(
+ c_val_names.count_rasterize_new_render_tree())
+
+ count_total_html_elements = self.get_cval(
+ c_val_names.count_dom_html_elements_total())
+ count_document_html_elements = self.get_cval(
+ c_val_names.count_dom_html_elements_document())
+ count_layout_boxes = self.get_cval(c_val_names.count_layout_boxes())
+ count_requested_images = self.get_cval(
+ c_val_names.count_image_cache_requested_resources())
+
+ navigate_time = self.get_cval(c_val_names.time_browser_navigate())
+ on_load_event_time = self.get_cval(
+ c_val_names.time_browser_on_load_event())
+ browse_time = self.get_cval(c_val_names.time_rasterize_new_render_tree())
+ usable_time = self.get_cval(
+ c_val_names.time_dom_html_script_element_execute())
+
+ count_total_html_element_recorder.collect_value(count_total_html_elements)
+ count_document_html_element_recorder.collect_value(
+ count_document_html_elements)
+ count_layout_box_recorder.collect_value(count_layout_boxes)
+ count_render_trees_recorder.collect_value(count_render_trees_end -
+ count_render_trees_start)
+ count_requested_images_recorder.collect_value(count_requested_images)
+
+ duration_navigate_to_on_load_recorder.collect_value(
+ on_load_event_time - navigate_time)
+ duration_navigate_to_browse_recorder.collect_value(
+ browse_time - navigate_time)
+ duration_navigate_to_usable_recorder.collect_value(
+ usable_time - navigate_time)
+
+ # Record the counts
+ count_total_html_element_recorder.on_end_test()
+ count_document_html_element_recorder.on_end_test()
+ count_layout_box_recorder.on_end_test()
+ count_render_trees_recorder.on_end_test()
+ count_requested_images_recorder.on_end_test()
+
+ # Record the durations
+ tv_testcase_util.record_test_result(
+ STARTUP_RECORD_NAME + "DurLaunchToNavigateUs",
+ dur_launch_to_navigate_us)
+ tv_testcase_util.record_test_result(
+ STARTUP_RECORD_NAME + "DurLaunchToOnLoadUs",
+ dur_launch_to_on_load_event_us)
+ tv_testcase_util.record_test_result(
+ STARTUP_RECORD_NAME + "DurLaunchToBrowseUs", dur_launch_to_browse_us)
+ tv_testcase_util.record_test_result(
+ STARTUP_RECORD_NAME + "DurLaunchToUsableUs", dur_launch_to_usable_us)
+
+ duration_navigate_to_on_load_recorder.on_end_test()
+ duration_navigate_to_browse_recorder.on_end_test()
+ duration_navigate_to_usable_recorder.on_end_test()
if __name__ == "__main__":
diff --git a/src/cobalt/webdriver_benchmarks/tests/performance/video/browse_to_watch.py b/src/cobalt/webdriver_benchmarks/tests/performance/video/browse_to_watch.py
index 0c958fb..248e73c 100755
--- a/src/cobalt/webdriver_benchmarks/tests/performance/video/browse_to_watch.py
+++ b/src/cobalt/webdriver_benchmarks/tests/performance/video/browse_to_watch.py
@@ -22,8 +22,11 @@
# selenium imports
keys = tv_testcase_util.import_selenium_module("webdriver.common.keys")
-NUM_LOAD_TV_CALLS = 1
-NUM_ITERATIONS_PER_LOAD_TV_CALL = 10
+MAX_VIDEO_FAILURE_COUNT = 10
+MAX_SKIPPABLE_AD_COUNT = 8
+
+NUM_LOAD_TV_CALLS = 4
+NUM_ITERATIONS_PER_LOAD_TV_CALL = 25
BROWSE_TO_WATCH_EVENT_NAME = "wbBrowseToWatch"
BROWSE_TO_WATCH_EVENT_TYPE = tv_testcase_util.EVENT_TYPE_KEY_UP
@@ -34,20 +37,29 @@
class BrowseToWatchTest(tv_testcase.TvTestCase):
+ class VideoFailureException(BaseException):
+ """Exception thrown when MAX_VIDEO_FAILURE_COUNT is exceeded."""
+
+ class AdvertisementFailureException(BaseException):
+ """Exception thrown when MAX_SKIPPABLE_AD_COUNT is exceeded."""
+
def test_simple(self):
recorder_options = tv_testcase_event_recorder.EventRecorderOptions(
self, BROWSE_TO_WATCH_EVENT_NAME, BROWSE_TO_WATCH_EVENT_TYPE)
- recorder_options.record_rasterize_animations = False
- recorder_options.record_video_start_delay = True
+ recorder_options.record_animations = False
+ recorder_options.record_video = True
browse_to_watch_recorder = tv_testcase_event_recorder.EventRecorder(
recorder_options)
recorder_options = tv_testcase_event_recorder.EventRecorderOptions(
self, WATCH_TO_BROWSE_EVENT_NAME, WATCH_TO_BROWSE_EVENT_TYPE)
- recorder_options.record_rasterize_animations = False
+ recorder_options.record_animations = False
watch_to_browse_recorder = tv_testcase_event_recorder.EventRecorder(
recorder_options)
+ failure_count = 0
+ skippable_ad_count = 0
+
for _ in xrange(NUM_LOAD_TV_CALLS):
self.load_tv()
@@ -57,7 +69,29 @@
browse_to_watch_recorder.on_start_event()
self.send_keys(keys.Keys.ENTER)
- self.wait_for_media_element_playing()
+
+ if not self.wait_for_media_element_playing():
+ failure_count += 1
+ print("Video failed to play! {} events failed.".format(failure_count))
+ if failure_count > MAX_VIDEO_FAILURE_COUNT:
+ raise BrowseToWatchTest.VideoFailureException()
+
+ self.send_keys(keys.Keys.ESCAPE)
+ self.wait_for_processing_complete_after_focused_shelf()
+ continue
+
+ if self.skip_advertisement_if_playing():
+ skippable_ad_count += 1
+ print(
+ "Encountered skippable ad! {} total.".format(skippable_ad_count))
+ if skippable_ad_count > MAX_SKIPPABLE_AD_COUNT:
+ raise BrowseToWatchTest.AdvertisementFailureException()
+
+ self.wait_for_title_card_hidden()
+ self.send_keys(keys.Keys.ESCAPE)
+ self.wait_for_processing_complete_after_focused_shelf()
+ continue
+
browse_to_watch_recorder.on_end_event()
# Wait for the title card hidden before sending the escape. Otherwise,
diff --git a/src/cobalt/webdriver_benchmarks/tests/performance_non_video.py b/src/cobalt/webdriver_benchmarks/tests/performance_non_video.py
index d385177..f71a433 100644
--- a/src/cobalt/webdriver_benchmarks/tests/performance_non_video.py
+++ b/src/cobalt/webdriver_benchmarks/tests/performance_non_video.py
@@ -38,7 +38,6 @@
_add_test(test_suite, dir_path, "browse_vertical")
_add_test(test_suite, dir_path, "browse_to_guide")
_add_test(test_suite, dir_path, "browse_to_search")
- _add_test(test_suite, dir_path, "csi")
return test_suite
diff --git a/src/cobalt/webdriver_benchmarks/timer.py b/src/cobalt/webdriver_benchmarks/timer.py
deleted file mode 100644
index 0ee08dc..0000000
--- a/src/cobalt/webdriver_benchmarks/timer.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright 2016 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ==============================================================================
-"""Contains a contextmanager for a timer.
-
- Example usage:
-
- from timer import Timer
-
- with Timer('SomeTask') as t:
- # Do some time consuming task
- print('So far {} seconds have passed'.format(t.seconds_elapsed))
-
- print(t) # This will print something like 'SomeTask took 1.2 seconds'
-"""
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import timeit
-
-
-class Timer(object):
- """ContextManager for measuring time since an event."""
-
- def __init__(self, description):
- """Initializes the timer.
-
- Args:
- description: A string containing the description of the timer.
- """
- self.description = description
- self._timer = timeit.default_timer # Choose best timer for the platform.
- self.start_time = None
- self._seconds_elapsed = None
-
- def __enter__(self):
- """Starts the timer.
-
- __enter__ allows this class to be used as a ContextManager.
-
- Returns:
- The object itself so it works with the |with| statement.
- """
- self.start_time = self._timer()
- return self
-
- def __exit__(self, unused_ex_type, unused_ex, unused_ex_trace):
- """Stops the timer and records the duration.
-
- __enter__ allows this class to be used as a ContextManager.
-
- Returns:
- False. Any exception raised within the body of the contextmanager will
- propogate up the stack.
- """
- self._seconds_elapsed = self._timer() - self.start_time
- return False
-
- @property
- def seconds_elapsed(self):
- """Number of seconds elapsed since start until now or time when timer ended.
-
- This property will return the number of seconds since the timer was started,
- or the duration of the timer's context manager.
-
- Returns:
- A float containing the number of seconds elapsed.
- """
- if self._seconds_elapsed is None:
- return self._timer() - self.start_time
-
- return self._seconds_elapsed
-
- def __str__(self):
- """A magic method for generating a string representation of the object.
-
- Returns:
- A string containing a description and a human readable version of timer's
- value.
- """
- return '{} took {} seconds.'.format(self.description, self.seconds_elapsed)
diff --git a/src/cobalt/webdriver_benchmarks/tv.py b/src/cobalt/webdriver_benchmarks/tv.py
index b3e6d29..332b3c9 100644
--- a/src/cobalt/webdriver_benchmarks/tv.py
+++ b/src/cobalt/webdriver_benchmarks/tv.py
@@ -8,4 +8,6 @@
FOCUSED_SEARCH = '.focused.search'
FOCUSED_SHELF = '.focused.selected.shelf'
FOCUSED_SHELF_TITLE = '.focused.selected.shelf .shelf-header-title .main'
+SKIP_AD_BUTTON_CAN_SKIP = '.skip-ad-button.canskip'
+SKIP_AD_BUTTON_HIDDEN = '.skip-ad-button.hidden'
TITLE_CARD_HIDDEN = '.title-card.hidden'
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase.py b/src/cobalt/webdriver_benchmarks/tv_testcase.py
index 9840228..dc49278 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase.py
@@ -19,16 +19,24 @@
import tv_testcase_runner
import tv_testcase_util
+try:
+ import custom_query_param_constants as query_param_constants
+except ImportError:
+ import default_query_param_constants as query_param_constants
+
# selenium imports
# pylint: disable=C0103
ActionChains = tv_testcase_util.import_selenium_module(
submodule="webdriver.common.action_chains").ActionChains
+keys = tv_testcase_util.import_selenium_module("webdriver.common.keys")
WINDOWDRIVER_CREATED_TIMEOUT_SECONDS = 30
WEBMODULE_LOADED_TIMEOUT_SECONDS = 30
PAGE_LOAD_WAIT_SECONDS = 30
-PROCESSING_TIMEOUT_SECONDS = 15
+PROCESSING_TIMEOUT_SECONDS = 60
+HTML_SCRIPT_ELEMENT_EXECUTE_TIMEOUT_SECONDS = 30
MEDIA_TIMEOUT_SECONDS = 30
+SKIP_AD_TIMEOUT_SECONDS = 30
TITLE_CARD_HIDDEN_TIMEOUT_SECONDS = 30
_is_initialized = False
@@ -50,8 +58,8 @@
class ProcessingTimeoutException(BaseException):
"""Exception thrown when processing did not complete in time."""
- class MediaTimeoutException(BaseException):
- """Exception thrown when media did not complete in time."""
+ class HtmlScriptElementExecuteTimeoutException(BaseException):
+ """Exception thrown when processing did not complete in time."""
class TitleCardHiddenTimeoutException(BaseException):
"""Exception thrown when title card did not disappear in time."""
@@ -67,11 +75,9 @@
def setUp(self):
global _is_initialized
if not _is_initialized:
- # Initialize the tests. This involves loading a URL which applies the
- # forcedOffAllExperiments cookies, ensuring that no subsequent loads
- # include experiments. Additionally, loading this URL triggers a reload.
- query_params = {"env_forcedOffAllExperiments": True}
- triggers_reload = True
+ # Initialize the tests.
+ query_params = query_param_constants.INIT_QUERY_PARAMS
+ triggers_reload = query_param_constants.INIT_QUERY_PARAMS_TRIGGER_RELOAD
self.load_tv(query_params, triggers_reload)
_is_initialized = True
@@ -115,6 +121,7 @@
Raises:
Underlying WebDriver exceptions
"""
+ self.get_webdriver().execute_script("h5vcc.storage.clearCookies()")
self.clear_url_loaded_events()
self.get_webdriver().get(
tv_testcase_util.generate_url(self.get_default_url(), query_params))
@@ -186,16 +193,16 @@
self.assertEqual(len(elements), expected_num)
return elements
- def send_keys(self, keys):
+ def send_keys(self, key_events):
"""Sends keys to whichever element currently has focus.
Args:
- keys: key events
+ key_events: key events
Raises:
Underlying WebDriver exceptions
"""
- ActionChains(self.get_webdriver()).send_keys(keys).perform()
+ ActionChains(self.get_webdriver()).send_keys(key_events).perform()
def clear_url_loaded_events(self):
"""Clear the events that indicate that Cobalt finished loading a URL."""
@@ -235,6 +242,18 @@
required time.
"""
start_time = time.time()
+
+ # First simply check for whether or not the event is still processing.
+ # There's no need to check anything else while the event is still going on.
+ # Once it is done processing, it won't get re-set, so there's no need to
+ # re-check it.
+ while self.get_cval(c_val_names.event_is_processing()):
+ if time.time() - start_time > PROCESSING_TIMEOUT_SECONDS:
+ raise TvTestCase.ProcessingTimeoutException()
+
+ time.sleep(0.1)
+
+ # Now wait for all processing to complete in Cobalt.
count = 0
while count < 2:
if self.is_processing(check_animations):
@@ -249,26 +268,62 @@
def is_processing(self, check_animations):
"""Checks to see if Cobalt is currently processing."""
- return (self.get_cval(c_val_names.count_dom_active_dispatch_events()) or
+ return (self.get_cval(c_val_names.count_dom_active_java_script_events()) or
self.get_cval(c_val_names.layout_is_dirty()) or
(check_animations and
self.get_cval(c_val_names.renderer_has_active_animations())) or
self.get_cval(c_val_names.count_image_cache_loading_resources()))
+ def wait_for_html_script_element_execute_count(self, required_count):
+ """Waits for specified number of html script element Execute() calls.
+
+ Args:
+ required_count: the number of executions that must occur
+
+ Raises:
+ HtmlScriptElementExecuteTimeoutException: The required html script element
+ executions did not occur within the required time.
+ """
+ start_time = time.time()
+ while self.get_cval(
+ c_val_names.count_dom_html_script_element_execute()) < required_count:
+ if time.time() - start_time > HTML_SCRIPT_ELEMENT_EXECUTE_TIMEOUT_SECONDS:
+ raise TvTestCase.HtmlScriptElementExecuteTimeoutException()
+ time.sleep(0.1)
+
def wait_for_media_element_playing(self):
"""Waits for a video to begin playing.
- Raises:
- MediaTimeoutException: The video does not start playing within the
- required time.
+ Returns:
+ Whether or not the video started.
"""
start_time = time.time()
while self.get_cval(
c_val_names.event_duration_dom_video_start_delay()) == 0:
if time.time() - start_time > MEDIA_TIMEOUT_SECONDS:
- raise TvTestCase.MediaTimeoutException()
+ return False
time.sleep(0.1)
+ return True
+
+ def skip_advertisement_if_playing(self):
+ """Waits to skip an ad if it is encountered.
+
+ Returns:
+ True if a skippable advertisement was encountered
+ """
+ start_time = time.time()
+ if not self.find_elements(tv.SKIP_AD_BUTTON_HIDDEN):
+ while not self.find_elements(tv.SKIP_AD_BUTTON_CAN_SKIP):
+ if time.time() - start_time > SKIP_AD_TIMEOUT_SECONDS:
+ return True
+ time.sleep(0.1)
+ self.send_keys(keys.Keys.ENTER)
+ self.wait_for_processing_complete(False)
+ return True
+
+ return False
+
def wait_for_title_card_hidden(self):
"""Waits for the title to disappear while a video is playing.
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py b/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
index cb49624..363bde4 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
@@ -24,8 +24,8 @@
self.event_name = event_name
self.event_type = event_type
- self.record_rasterize_animations = True
- self.record_video_start_delay = False
+ self.record_animations = True
+ self.record_video = False
class EventRecorder(object):
@@ -37,8 +37,8 @@
tv_testcase_util.py.
Both rasterize animations and video start delay data can potentially be
- recorded, depending on the |record_rasterize_animations| and
- |record_video_start_delay| option flags.
+ recorded, depending on the |record_animations| and |record_video| option
+ flags.
"""
def __init__(self, options):
@@ -59,21 +59,56 @@
# Each entry in the list contains a tuple with a key and value recorder.
self.value_dictionary_recorders = []
+ # Optional animations recorders
self.animations_recorder = None
+ self.animations_start_delay_recorder = None
+ self.animations_end_delay_recorder = None
+
+ # Optional video recorders
self.video_delay_recorder = None
# Count record strategies
count_record_strategies = []
- count_record_strategies.append(tv_testcase_util.RecordStrategyMax())
- count_record_strategies.append(tv_testcase_util.RecordStrategyMedian())
count_record_strategies.append(tv_testcase_util.RecordStrategyMean())
+ count_record_strategies.append(tv_testcase_util.RecordStrategyMedian())
+ count_record_strategies.append(tv_testcase_util.RecordStrategyMax())
- # Count recorders
+ # Duration record strategies
+ duration_record_strategies = []
+ duration_record_strategies.append(tv_testcase_util.RecordStrategyMean())
+ duration_record_strategies.append(
+ tv_testcase_util.RecordStrategyPercentile(25))
+ duration_record_strategies.append(
+ tv_testcase_util.RecordStrategyPercentile(50))
+ duration_record_strategies.append(
+ tv_testcase_util.RecordStrategyPercentile(75))
+ duration_record_strategies.append(
+ tv_testcase_util.RecordStrategyPercentile(95))
+
+ # Video delay record strategies
+ video_delay_record_strategies = []
+ video_delay_record_strategies.append(tv_testcase_util.RecordStrategyMean())
+ video_delay_record_strategies.append(tv_testcase_util.RecordStrategyMin())
+ video_delay_record_strategies.append(
+ tv_testcase_util.RecordStrategyPercentile(25))
+ video_delay_record_strategies.append(
+ tv_testcase_util.RecordStrategyPercentile(50))
+ video_delay_record_strategies.append(
+ tv_testcase_util.RecordStrategyPercentile(75))
+ video_delay_record_strategies.append(
+ tv_testcase_util.RecordStrategyPercentile(95))
+ video_delay_record_strategies.append(tv_testcase_util.RecordStrategyMax())
+ video_delay_record_strategies.append(
+ tv_testcase_util.RecordStrategyStandardDeviation())
+
+ # Dictionary count recorders
self._add_value_dictionary_recorder("CntDomEventListeners",
count_record_strategies)
self._add_value_dictionary_recorder("CntDomNodes", count_record_strategies)
self._add_value_dictionary_recorder("CntDomHtmlElements",
count_record_strategies)
+ self._add_value_dictionary_recorder("CntDomDocumentHtmlElements",
+ count_record_strategies)
self._add_value_dictionary_recorder("CntDomHtmlElementsCreated",
count_record_strategies)
self._add_value_dictionary_recorder("CntDomUpdateMatchingRules",
@@ -95,19 +130,7 @@
self._add_value_dictionary_recorder("CntLayoutUpdateCrossReferences",
count_record_strategies)
- # Duration record strategies
- duration_record_strategies = []
- duration_record_strategies.append(tv_testcase_util.RecordStrategyMean())
- duration_record_strategies.append(
- tv_testcase_util.RecordStrategyPercentile(25))
- duration_record_strategies.append(
- tv_testcase_util.RecordStrategyPercentile(50))
- duration_record_strategies.append(
- tv_testcase_util.RecordStrategyPercentile(75))
- duration_record_strategies.append(
- tv_testcase_util.RecordStrategyPercentile(95))
-
- # Duration recorders
+ # Dictionary duration recorders
self._add_value_dictionary_recorder("DurTotalUs",
duration_record_strategies)
self._add_value_dictionary_recorder("DurDomInjectEventUs",
@@ -125,16 +148,32 @@
self._add_value_dictionary_recorder("DurLayoutRenderAndAnimateUs",
duration_record_strategies)
- # Optional rasterize animations recorders
- if options.record_rasterize_animations:
+ self.count_render_trees_recorder = tv_testcase_util.ResultsRecorder(
+ self.event_name + "CntRenderTrees", count_record_strategies)
+ self.count_requested_images_recorder = tv_testcase_util.ResultsRecorder(
+ self.event_name + "CntRequestedImages", count_record_strategies)
+
+ # Optional animations recorder
+ if options.record_animations:
self.animations_recorder = tv_testcase_util.ResultsRecorder(
self.event_name + "DurRasterizeAnimationsUs",
duration_record_strategies)
+ self.animations_start_delay_recorder = tv_testcase_util.ResultsRecorder(
+ self.event_name + "DurAnimationsStartDelayUs",
+ duration_record_strategies)
+ self.animations_end_delay_recorder = tv_testcase_util.ResultsRecorder(
+ self.event_name + "DurAnimationsEndDelayUs",
+ duration_record_strategies)
- # Optional video start delay recorder
- if options.record_video_start_delay:
+ self.final_render_tree_delay_recorder = tv_testcase_util.ResultsRecorder(
+ self.event_name + "DurFinalRenderTreeDelayUs",
+ duration_record_strategies)
+
+ # Optional video recorder
+ if options.record_video:
self.video_delay_recorder = tv_testcase_util.ResultsRecorder(
- self.event_name + "DurVideoStartDelayUs", duration_record_strategies)
+ self.event_name + "DurVideoStartDelayUs",
+ video_delay_record_strategies)
def _add_value_dictionary_recorder(self, key, record_strategies):
recorder = tv_testcase_util.ResultsRecorder(self.event_name + key,
@@ -143,7 +182,10 @@
def on_start_event(self):
"""Handles logic related to the start of the event instance."""
- pass
+ self.count_render_trees_start = self.test.get_cval(
+ c_val_names.count_rasterize_new_render_tree())
+ self.count_requested_images_start = self.test.get_cval(
+ c_val_names.count_image_cache_requested_resources())
def on_end_event(self):
"""Handles logic related to the end of the event instance."""
@@ -159,18 +201,44 @@
self.event_name, self.render_tree_failure_count))
return
+ event_start_time = value_dictionary.get("StartTime")
+
# Record all of the values from the event.
for value_dictionary_recorder in self.value_dictionary_recorders:
value = value_dictionary.get(value_dictionary_recorder[0])
if value is not None:
value_dictionary_recorder[1].collect_value(value)
+ self.count_render_trees_end = self.test.get_cval(
+ c_val_names.count_rasterize_new_render_tree())
+ self.count_render_trees_recorder.collect_value(
+ self.count_render_trees_end - self.count_render_trees_start)
+
+ self.count_requested_images_end = self.test.get_cval(
+ c_val_names.count_image_cache_requested_resources())
+ self.count_requested_images_recorder.collect_value(
+ self.count_requested_images_end - self.count_requested_images_start)
+
if self.animations_recorder:
animation_entries = self.test.get_cval(
c_val_names.rasterize_animations_entry_list())
for value in animation_entries:
self.animations_recorder.collect_value(value)
+ animations_start_time = self.test.get_cval(
+ c_val_names.time_rasterize_animations_start())
+ self.animations_start_delay_recorder.collect_value(
+ animations_start_time - event_start_time)
+ animations_end_time = self.test.get_cval(
+ c_val_names.time_rasterize_animations_end())
+ self.animations_end_delay_recorder.collect_value(animations_end_time -
+ event_start_time)
+
+ final_render_tree_time = self.test.get_cval(
+ c_val_names.time_rasterize_new_render_tree())
+ self.final_render_tree_delay_recorder.collect_value(final_render_tree_time -
+ event_start_time)
+
if self.video_delay_recorder:
self.video_delay_recorder.collect_value(
self.test.get_cval(
@@ -182,8 +250,15 @@
for value_dictionary_recorder in self.value_dictionary_recorders:
value_dictionary_recorder[1].on_end_test()
+ self.count_render_trees_recorder.on_end_test()
+ self.count_requested_images_recorder.on_end_test()
+
if self.animations_recorder:
self.animations_recorder.on_end_test()
+ self.animations_start_delay_recorder.on_end_test()
+ self.animations_end_delay_recorder.on_end_test()
+
+ self.final_render_tree_delay_recorder.on_end_test()
if self.video_delay_recorder:
self.video_delay_recorder.on_end_test()
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py b/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
index 53ccbd4..6f9981c 100755
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
@@ -114,6 +114,9 @@
def __init__(self, platform, executable, devkit_name, command_line_args,
default_url, log_file_path):
global _default_url
+ if default_url is not None:
+ _default_url = default_url
+
self.selenium_webdriver_module = tv_testcase_util.import_selenium_module(
"webdriver")
@@ -133,10 +136,7 @@
args.append("--enable_webdriver")
args.append("--null_savegame")
args.append("--debug_console=off")
- args.append("--url=about:blank")
-
- if default_url is not None:
- _default_url = default_url
+ args.append("--url=" + _default_url)
self.launcher.SetArgs(args)
self.launcher.SetOutputCallback(self._HandleLine)
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_util.py b/src/cobalt/webdriver_benchmarks/tv_testcase_util.py
index eb1154d..5d9f87f 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_util.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_util.py
@@ -14,6 +14,11 @@
import container_util
+try:
+ import custom_query_param_constants as query_param_constants
+except ImportError:
+ import default_query_param_constants as query_param_constants
+
# These are watched for in webdriver_benchmark_test.py
TEST_RESULT = "webdriver_benchmark TEST RESULT"
TEST_COMPLETE = "webdriver_benchmark TEST COMPLETE"
@@ -56,12 +61,24 @@
return module
-def generate_url(default_url, query_params):
- """Returns the URL indicated by the path and query parameters."""
- if not query_params:
- return default_url
+def generate_url(default_url, query_params_override=None):
+ """Returns the URL indicated by the path and query parameters.
+ Args:
+ default_url: the default url to use; its query params may be overridden
+ query_params_override: optional query params that override the ones
+ contained within the default URL
+ Returns:
+ the url generated from the parameters
+ """
parsed_url = list(urlparse.urlparse(default_url))
+
+ query_params = query_param_constants.BASE_QUERY_PARAMS
+ if query_params_override:
+ query_params.update(query_params_override)
+ else:
+ query_params.update(urlparse.parse_qsl(parsed_url[4]))
+
parsed_url[4] = urlencode(query_params, doseq=True)
return urlparse.urlunparse(parsed_url)
@@ -157,6 +174,22 @@
container_util.percentile(values, self.percentile))
+class RecordStrategyStandardDeviation(object):
+ """Records the standard deviation of an array of values."""
+
+ def run(self, name, values):
+ """Records the standard deviation of an array of values.
+
+ Args:
+ name: string name of test case
+ values: must be array of JSON encodable scalar
+ Raises:
+ RuntimeError: Raised on invalid args.
+ """
+ record_test_result("{}StdDev".format(name),
+ container_util.sample_standard_deviation(values))
+
+
class ResultsRecorder(object):
""""Collects values and records results after a benchmark test ends."""
diff --git a/src/cobalt/websocket/close_event.h b/src/cobalt/websocket/close_event.h
index 43b7a79..97a499d 100644
--- a/src/cobalt/websocket/close_event.h
+++ b/src/cobalt/websocket/close_event.h
@@ -30,13 +30,25 @@
: Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {}
explicit CloseEvent(const std::string& type)
: Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {}
- CloseEvent(const base::Token type, const CloseEventInit& eventInitDict)
- : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {
- InitializeFromCloseEventInit(eventInitDict);
+ CloseEvent(const base::Token type, const CloseEventInit& init_dict)
+ : Event(type, init_dict),
+ was_clean_(true),
+ code_(net::kWebSocketNormalClosure) {
+ InitializeFromCloseEventInit(init_dict);
}
- CloseEvent(const std::string& type, const CloseEventInit& eventInitDict)
- : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {
- InitializeFromCloseEventInit(eventInitDict);
+ CloseEvent(const std::string& type, const CloseEventInit& init_dict)
+ : Event(type, init_dict),
+ was_clean_(true),
+ code_(net::kWebSocketNormalClosure) {
+ InitializeFromCloseEventInit(init_dict);
+ }
+
+ void InitCloseEvent(const std::string& type, bool can_bubble, bool cancelable,
+ bool was_clean, uint16 code, const std::string& reason) {
+ InitEvent(type, can_bubble, cancelable);
+ was_clean_ = was_clean;
+ code_ = code;
+ reason_ = reason;
}
// Readonly Attributes.
@@ -47,15 +59,15 @@
DEFINE_WRAPPABLE_TYPE(CloseEvent)
private:
- void InitializeFromCloseEventInit(const CloseEventInit& eventInitDict) {
- if (eventInitDict.has_was_clean()) {
- was_clean_ = eventInitDict.was_clean();
+ void InitializeFromCloseEventInit(const CloseEventInit& init_dict) {
+ if (init_dict.has_was_clean()) {
+ was_clean_ = init_dict.was_clean();
}
- if (eventInitDict.has_code()) {
- code_ = eventInitDict.code();
+ if (init_dict.has_code()) {
+ code_ = init_dict.code();
}
- if (eventInitDict.has_reason()) {
- reason_ = eventInitDict.reason();
+ if (init_dict.has_reason()) {
+ reason_ = init_dict.reason();
}
}
bool was_clean_;
diff --git a/src/cobalt/websocket/close_event.idl b/src/cobalt/websocket/close_event.idl
index 425e0d2..f99dade 100644
--- a/src/cobalt/websocket/close_event.idl
+++ b/src/cobalt/websocket/close_event.idl
@@ -19,5 +19,12 @@
readonly attribute boolean wasClean;
readonly attribute unsigned short code;
readonly attribute DOMString reason;
+
+ void initCloseEvent(DOMString type,
+ boolean canBubble,
+ boolean cancelable,
+ boolean wasClean,
+ unsigned short code,
+ DOMString reason);
};
diff --git a/src/cobalt/websocket/websocket.gyp b/src/cobalt/websocket/websocket.gyp
index 1c377ef..1fe61a7 100644
--- a/src/cobalt/websocket/websocket.gyp
+++ b/src/cobalt/websocket/websocket.gyp
@@ -14,7 +14,7 @@
{
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
{
diff --git a/src/cobalt/xhr/xhr.gyp b/src/cobalt/xhr/xhr.gyp
index 8d4d574..34203b8 100644
--- a/src/cobalt/xhr/xhr.gyp
+++ b/src/cobalt/xhr/xhr.gyp
@@ -14,7 +14,7 @@
{
'variables': {
- 'cobalt_code': 1,
+ 'sb_pedantic_warnings': 1,
},
'targets': [
{
@@ -33,6 +33,24 @@
'<(DEPTH)/cobalt/speech/speech.gyp:speech',
'<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:dom_parser',
],
+ 'conditions': [
+ ['enable_xhr_header_filtering == 1', {
+ 'sources': [
+ 'xhr_modify_headers.h',
+ ],
+ 'dependencies': [
+ '<@(cobalt_platform_dependencies)',
+ ],
+ 'defines': [
+ 'COBALT_ENABLE_XHR_HEADER_FILTERING',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'COBALT_ENABLE_XHR_HEADER_FILTERING',
+ ],
+ },
+ }],
+ ],
},
{
'target_name': 'xhr_test',
diff --git a/src/cobalt/xhr/xhr_modify_headers.h b/src/cobalt/xhr/xhr_modify_headers.h
new file mode 100644
index 0000000..a1a1afc
--- /dev/null
+++ b/src/cobalt/xhr/xhr_modify_headers.h
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_XHR_XHR_MODIFY_HEADERS_H_
+#define COBALT_XHR_XHR_MODIFY_HEADERS_H_
+
+#include <net/http/http_request_headers.h>
+
+namespace cobalt {
+namespace xhr {
+
+void CobaltXhrModifyHeader(net::HttpRequestHeaders* headers);
+
+} // namespace xhr
+} // namespace cobalt
+
+#endif // COBALT_XHR_XHR_MODIFY_HEADERS_H_
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc
index 3a9c8e3..fb2b97d 100644
--- a/src/cobalt/xhr/xml_http_request.cc
+++ b/src/cobalt/xhr/xml_http_request.cc
@@ -33,6 +33,7 @@
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/script/global_environment.h"
#include "cobalt/script/javascript_engine.h"
+#include "cobalt/xhr/xhr_modify_headers.h"
#include "nb/memory_scope.h"
#include "net/http/http_util.h"
@@ -324,6 +325,10 @@
error_ = false;
upload_complete_ = false;
+#if defined(COBALT_ENABLE_XHR_HEADER_FILTERING)
+ CobaltXhrModifyHeader(&request_headers_);
+#endif
+
std::string request_body_text;
// Add request body, if appropriate.
if ((method_ == net::URLFetcher::POST || method_ == net::URLFetcher::PUT) &&
diff --git a/src/media/base/video_resolution.h b/src/media/base/video_resolution.h
index 714cca1..29ae65f 100644
--- a/src/media/base/video_resolution.h
+++ b/src/media/base/video_resolution.h
@@ -17,6 +17,7 @@
#ifndef MEDIA_BASE_VIDEO_RESOLUTION_H_
#define MEDIA_BASE_VIDEO_RESOLUTION_H_
+#include "base/logging.h"
#include "media/base/media_export.h"
#include "ui/gfx/size.h"
@@ -24,11 +25,15 @@
// Enumerates the various representations of the resolution of videos. Note
// that except |kVideoResolutionInvalid|, all other values are guaranteed to be
-// in the same order as its (width, height) pair.
+// in the same order as its (width, height) pair. Note, unlike the other valid
+// resolution levels, |kVideoResolutionHighRes| is not a 16:9 resolution.
enum VideoResolution {
- kVideoResolution1080p, // 1920 x 1080
- kVideoResolution2k, // 2560 x 1440
- kVideoResolution4k, // 3840 x 2160
+ kVideoResolution1080p, // 1920 x 1080
+ kVideoResolution2k, // 2560 x 1440
+ kVideoResolution4k, // 3840 x 2160
+ kVideoResolution5k, // 5120 × 2880
+ kVideoResolution8k, // 7680 x 4320
+ kVideoResolutionHighRes, // 8192 x 8192
kVideoResolutionInvalid
};
@@ -42,6 +47,17 @@
if (width <= 3840 && height <= 2160) {
return kVideoResolution4k;
}
+ if (width <= 5120 && height <= 2880) {
+ return kVideoResolution5k;
+ }
+ if (width <= 7680 && height <= 4320) {
+ return kVideoResolution8k;
+ }
+ if (width <= 8192 && height <= 8192) {
+ return kVideoResolutionHighRes;
+ }
+ DLOG(FATAL) << "Invalid VideoResolution: width: " << width
+ << " height: " << height;
return kVideoResolutionInvalid;
}
diff --git a/src/nb/bidirectional_fit_reuse_allocator.cc b/src/nb/bidirectional_fit_reuse_allocator.cc
new file mode 100644
index 0000000..6e0ad7a
--- /dev/null
+++ b/src/nb/bidirectional_fit_reuse_allocator.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "nb/bidirectional_fit_reuse_allocator.h"
+
+#include <algorithm>
+
+#include "nb/pointer_arithmetic.h"
+#include "starboard/log.h"
+#include "starboard/types.h"
+
+namespace nb {
+
+BidirectionalFitReuseAllocator::BidirectionalFitReuseAllocator(
+ Allocator* fallback_allocator,
+ std::size_t initial_capacity,
+ std::size_t small_allocation_threshold,
+ std::size_t allocation_increment /*= 0*/
+ )
+ : ReuseAllocatorBase(fallback_allocator,
+ initial_capacity,
+ allocation_increment),
+ small_allocation_threshold_(small_allocation_threshold) {}
+
+ReuseAllocatorBase::FreeBlockSet::iterator
+BidirectionalFitReuseAllocator::FindFreeBlock(std::size_t size,
+ std::size_t alignment,
+ FreeBlockSet::iterator begin,
+ FreeBlockSet::iterator end,
+ bool* allocate_from_front) {
+ SB_DCHECK(allocate_from_front);
+
+ *allocate_from_front = size > small_allocation_threshold_;
+
+ if (*allocate_from_front) {
+ // Start looking through the free list from the front.
+ for (FreeBlockSet::iterator it = begin; it != end; ++it) {
+ if (it->CanFullfill(size, alignment)) {
+ return it;
+ }
+ }
+ }
+
+ // Start looking through the free list from the back.
+ FreeBlockSet::reverse_iterator rbegin(end);
+ FreeBlockSet::reverse_iterator rend(begin);
+ for (FreeBlockSet::reverse_iterator it = rbegin; it != rend; ++it) {
+ if (it->CanFullfill(size, alignment)) {
+ return --it.base();
+ }
+ }
+
+ return end;
+}
+
+} // namespace nb
diff --git a/src/nb/bidirectional_fit_reuse_allocator.h b/src/nb/bidirectional_fit_reuse_allocator.h
new file mode 100644
index 0000000..29662a3
--- /dev/null
+++ b/src/nb/bidirectional_fit_reuse_allocator.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NB_BIDIRECTIONAL_FIT_REUSE_ALLOCATOR_H_
+#define NB_BIDIRECTIONAL_FIT_REUSE_ALLOCATOR_H_
+
+#include "nb/reuse_allocator_base.h"
+#include "starboard/configuration.h"
+
+namespace nb {
+
+// This class uses first-fit allocation strategy to allocate memory block whose
+// size is greater than the |small_allocation_threshold|. It uses last-fit
+// strategy to allocate memory block whose size is less than or equal to the
+// |small_allocation_threshold|. This allows better fragmentation management.
+// If fragmentation is not an issue, FirstFitReuseAllocator might be a simpler
+// alternative.
+// Note that when using this class a significant |initial_capacity| should be
+// set as otherwise new allocations will almost always allocate from the front
+// of the fallback allocator.
+class BidirectionalFitReuseAllocator : public ReuseAllocatorBase {
+ public:
+ BidirectionalFitReuseAllocator(Allocator* fallback_allocator,
+ std::size_t initial_capacity,
+ std::size_t small_allocation_threshold,
+ std::size_t allocation_increment = 0);
+
+ FreeBlockSet::iterator FindFreeBlock(std::size_t size,
+ std::size_t alignment,
+ FreeBlockSet::iterator begin,
+ FreeBlockSet::iterator end,
+ bool* allocate_from_front) SB_OVERRIDE;
+
+ private:
+ std::size_t small_allocation_threshold_;
+};
+
+} // namespace nb
+
+#endif // NB_BIDIRECTIONAL_FIT_REUSE_ALLOCATOR_H_
diff --git a/src/nb/bidirectional_fit_reuse_allocator_test.cc b/src/nb/bidirectional_fit_reuse_allocator_test.cc
new file mode 100644
index 0000000..6c08b9b
--- /dev/null
+++ b/src/nb/bidirectional_fit_reuse_allocator_test.cc
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nb/bidirectional_fit_reuse_allocator.h"
+
+#include "nb/fixed_no_free_allocator.h"
+#include "nb/scoped_ptr.h"
+#include "starboard/configuration.h"
+#include "starboard/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+inline bool IsAligned(void* ptr, std::size_t boundary) {
+ uintptr_t ptr_as_int = reinterpret_cast<uintptr_t>(ptr);
+ return ptr_as_int % boundary == 0;
+}
+
+class BidirectionalFitReuseAllocatorTest : public ::testing::Test {
+ public:
+ static const int kBufferSize = 1 * 1024 * 1024;
+
+ BidirectionalFitReuseAllocatorTest() { ResetAllocator(); }
+
+ protected:
+ void ResetAllocator(std::size_t initial_capacity = 0,
+ std::size_t small_allocation_threshold = 0,
+ std::size_t allocation_increment = 0) {
+ nb::scoped_array<uint8_t> buffer(new uint8_t[kBufferSize]);
+ nb::scoped_ptr<nb::FixedNoFreeAllocator> fallback_allocator(
+ new nb::FixedNoFreeAllocator(buffer.get(), kBufferSize));
+ allocator_.reset(new nb::BidirectionalFitReuseAllocator(
+ fallback_allocator.get(), initial_capacity, small_allocation_threshold,
+ allocation_increment));
+
+ fallback_allocator_.swap(fallback_allocator);
+ buffer_.swap(buffer);
+ }
+
+ nb::scoped_array<uint8_t> buffer_;
+ nb::scoped_ptr<nb::FixedNoFreeAllocator> fallback_allocator_;
+ nb::scoped_ptr<nb::BidirectionalFitReuseAllocator> allocator_;
+};
+
+} // namespace
+
+TEST_F(BidirectionalFitReuseAllocatorTest, AlignmentCheck) {
+ const std::size_t kAlignments[] = {4, 16, 256, 32768};
+ const std::size_t kBlockSizes[] = {4, 97, 256, 65201};
+ for (int i = 0; i < SB_ARRAY_SIZE(kAlignments); ++i) {
+ for (int j = 0; j < SB_ARRAY_SIZE(kBlockSizes); ++j) {
+ void* p = allocator_->Allocate(kBlockSizes[j], kAlignments[i]);
+ EXPECT_TRUE(p != NULL);
+ EXPECT_EQ(IsAligned(p, kAlignments[i]), true);
+ allocator_->Free(p);
+ }
+ }
+}
+
+// Check that the reuse allocator actually merges adjacent free blocks.
+TEST_F(BidirectionalFitReuseAllocatorTest, FreeBlockMergingLeft) {
+ const std::size_t kBlockSizes[] = {156, 16475};
+ const std::size_t kAlignment = 4;
+ void* blocks[] = {NULL, NULL};
+ blocks[0] = allocator_->Allocate(kBlockSizes[0], kAlignment);
+ blocks[1] = allocator_->Allocate(kBlockSizes[1], kAlignment);
+ // In an empty allocator we expect first alloc to be < second.
+ EXPECT_LT(reinterpret_cast<uintptr_t>(blocks[0]),
+ reinterpret_cast<uintptr_t>(blocks[1]));
+ allocator_->Free(blocks[0]);
+ allocator_->Free(blocks[1]);
+ // Should have merged blocks 1 with block 0.
+ void* test_p =
+ allocator_->Allocate(kBlockSizes[0] + kBlockSizes[1], kAlignment);
+ EXPECT_EQ(test_p, blocks[0]);
+ allocator_->Free(test_p);
+}
+
+TEST_F(BidirectionalFitReuseAllocatorTest, FreeBlockMergingRight) {
+ const std::size_t kBlockSizes[] = {156, 202, 354};
+ const std::size_t kAlignment = 4;
+ void* blocks[] = {NULL, NULL, NULL};
+ blocks[0] = allocator_->Allocate(kBlockSizes[0], kAlignment);
+ blocks[1] = allocator_->Allocate(kBlockSizes[1], kAlignment);
+ blocks[2] = allocator_->Allocate(kBlockSizes[2], kAlignment);
+ // In an empty allocator we expect first alloc to be < second.
+ EXPECT_LT(reinterpret_cast<uintptr_t>(blocks[1]),
+ reinterpret_cast<uintptr_t>(blocks[2]));
+ allocator_->Free(blocks[2]);
+ allocator_->Free(blocks[1]);
+ // Should have merged block 1 with block 2.
+ void* test_p =
+ allocator_->Allocate(kBlockSizes[1] + kBlockSizes[2], kAlignment);
+ EXPECT_EQ(test_p, blocks[1]);
+ allocator_->Free(test_p);
+ allocator_->Free(blocks[0]);
+}
+
+TEST_F(BidirectionalFitReuseAllocatorTest, InitialCapacity) {
+ const std::size_t kInitialCapacity = kBufferSize / 2;
+ ResetAllocator(kInitialCapacity);
+ EXPECT_GE(fallback_allocator_->GetAllocated(), kInitialCapacity);
+}
+
+TEST_F(BidirectionalFitReuseAllocatorTest, AllocationIncrement) {
+ const std::size_t kAllocationIncrement = kBufferSize / 2;
+ ResetAllocator(0, 0, kAllocationIncrement);
+ void* p = allocator_->Allocate(1, 1);
+ EXPECT_TRUE(p != NULL);
+ allocator_->Free(p);
+ EXPECT_GE(fallback_allocator_->GetAllocated(), kAllocationIncrement);
+}
+
+TEST_F(BidirectionalFitReuseAllocatorTest, FallbackBlockMerge) {
+ void* p = allocator_->Allocate(kBufferSize, 1);
+ EXPECT_TRUE(p != NULL);
+ allocator_->Free(p);
+
+ ResetAllocator();
+
+ p = allocator_->Allocate(kBufferSize / 2, 1);
+ EXPECT_TRUE(p != NULL);
+ allocator_->Free(p);
+
+ p = allocator_->Allocate(kBufferSize, 1);
+ EXPECT_TRUE(p != NULL);
+ allocator_->Free(p);
+}
+
+TEST_F(BidirectionalFitReuseAllocatorTest, AllocationsWithThreshold) {
+ const std::size_t kSmallAllocationThreshold = 1024;
+
+ ResetAllocator(kBufferSize, kSmallAllocationThreshold, 0);
+
+ void* small_allocation_1 =
+ allocator_->Allocate(kSmallAllocationThreshold - 1, 1);
+ EXPECT_TRUE(small_allocation_1 != NULL);
+
+ void* large_allocation_1 =
+ allocator_->Allocate(kSmallAllocationThreshold + 1, 1);
+ EXPECT_TRUE(large_allocation_1 != NULL);
+
+ // According to the spec of BidirectionalFitReuseAllocator, any memory block
+ // whose size is equal to the threshold is allocated from the back.
+ void* small_allocation_2 = allocator_->Allocate(kSmallAllocationThreshold, 1);
+ EXPECT_TRUE(small_allocation_2 != NULL);
+
+ void* small_allocation_3 = allocator_->Allocate(1, 1);
+ EXPECT_TRUE(small_allocation_3 != NULL);
+
+ // Large allocations are allocated from the front, small allocations are
+ // allocated from the back.
+ EXPECT_LT(reinterpret_cast<uintptr_t>(large_allocation_1),
+ reinterpret_cast<uintptr_t>(small_allocation_3));
+ EXPECT_LT(reinterpret_cast<uintptr_t>(small_allocation_3),
+ reinterpret_cast<uintptr_t>(small_allocation_2));
+ EXPECT_LT(reinterpret_cast<uintptr_t>(small_allocation_2),
+ reinterpret_cast<uintptr_t>(small_allocation_1));
+
+ allocator_->Free(small_allocation_1);
+ allocator_->Free(small_allocation_2);
+ allocator_->Free(large_allocation_1);
+}
diff --git a/src/nb/first_fit_reuse_allocator.cc b/src/nb/first_fit_reuse_allocator.cc
new file mode 100644
index 0000000..29f93a6
--- /dev/null
+++ b/src/nb/first_fit_reuse_allocator.cc
@@ -0,0 +1,53 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "nb/first_fit_reuse_allocator.h"
+
+#include <algorithm>
+
+#include "nb/pointer_arithmetic.h"
+#include "starboard/log.h"
+#include "starboard/types.h"
+
+namespace nb {
+
+FirstFitReuseAllocator::FirstFitReuseAllocator(
+ Allocator* fallback_allocator,
+ std::size_t initial_capacity,
+ std::size_t allocation_increment /*= 0*/)
+ : ReuseAllocatorBase(fallback_allocator,
+ initial_capacity,
+ allocation_increment) {}
+
+ReuseAllocatorBase::FreeBlockSet::iterator
+FirstFitReuseAllocator::FindFreeBlock(std::size_t size,
+ std::size_t alignment,
+ FreeBlockSet::iterator begin,
+ FreeBlockSet::iterator end,
+ bool* allocate_from_front) {
+ SB_DCHECK(allocate_from_front);
+
+ *allocate_from_front = true;
+
+ // Start looking through the free list from the front.
+ for (FreeBlockSet::iterator it = begin; it != end; ++it) {
+ if (it->CanFullfill(size, alignment)) {
+ return it;
+ }
+ }
+
+ return end;
+}
+
+} // namespace nb
diff --git a/src/nb/first_fit_reuse_allocator.h b/src/nb/first_fit_reuse_allocator.h
new file mode 100644
index 0000000..ae4b599
--- /dev/null
+++ b/src/nb/first_fit_reuse_allocator.h
@@ -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.
+ */
+
+#ifndef NB_FIRST_FIT_REUSE_ALLOCATOR_H_
+#define NB_FIRST_FIT_REUSE_ALLOCATOR_H_
+
+#include "nb/reuse_allocator_base.h"
+#include "starboard/configuration.h"
+
+namespace nb {
+
+// This class uses first-fit allocation strategy to allocate memory.
+class FirstFitReuseAllocator : public ReuseAllocatorBase {
+ public:
+ FirstFitReuseAllocator(Allocator* fallback_allocator,
+ std::size_t initial_capacity,
+ std::size_t allocation_increment = 0);
+
+ FreeBlockSet::iterator FindFreeBlock(std::size_t size,
+ std::size_t alignment,
+ FreeBlockSet::iterator begin,
+ FreeBlockSet::iterator end,
+ bool* allocate_from_front) SB_OVERRIDE;
+};
+
+} // namespace nb
+
+#endif // NB_FIRST_FIT_REUSE_ALLOCATOR_H_
diff --git a/src/nb/first_fit_reuse_allocator_test.cc b/src/nb/first_fit_reuse_allocator_test.cc
new file mode 100644
index 0000000..2e51bd2
--- /dev/null
+++ b/src/nb/first_fit_reuse_allocator_test.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nb/first_fit_reuse_allocator.h"
+
+#include "nb/fixed_no_free_allocator.h"
+#include "nb/scoped_ptr.h"
+#include "starboard/configuration.h"
+#include "starboard/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+inline bool IsAligned(void* ptr, std::size_t boundary) {
+ uintptr_t ptr_as_int = reinterpret_cast<uintptr_t>(ptr);
+ return ptr_as_int % boundary == 0;
+}
+
+class FirstFitReuseAllocatorTest : public ::testing::Test {
+ public:
+ static const int kBufferSize = 1 * 1024 * 1024;
+
+ FirstFitReuseAllocatorTest() { ResetAllocator(); }
+
+ protected:
+ void ResetAllocator(std::size_t initial_capacity = 0,
+ std::size_t allocation_increment = 0) {
+ nb::scoped_array<uint8_t> buffer(new uint8_t[kBufferSize]);
+ nb::scoped_ptr<nb::FixedNoFreeAllocator> fallback_allocator(
+ new nb::FixedNoFreeAllocator(buffer.get(), kBufferSize));
+ allocator_.reset(new nb::FirstFitReuseAllocator(
+ fallback_allocator.get(), initial_capacity, allocation_increment));
+
+ fallback_allocator_.swap(fallback_allocator);
+ buffer_.swap(buffer);
+ }
+
+ nb::scoped_array<uint8_t> buffer_;
+ nb::scoped_ptr<nb::FixedNoFreeAllocator> fallback_allocator_;
+ nb::scoped_ptr<nb::FirstFitReuseAllocator> allocator_;
+};
+
+} // namespace
+
+TEST_F(FirstFitReuseAllocatorTest, AlignmentCheck) {
+ const std::size_t kAlignments[] = {4, 16, 256, 32768};
+ const std::size_t kBlockSizes[] = {4, 97, 256, 65201};
+ for (int i = 0; i < SB_ARRAY_SIZE(kAlignments); ++i) {
+ for (int j = 0; j < SB_ARRAY_SIZE(kBlockSizes); ++j) {
+ void* p = allocator_->Allocate(kBlockSizes[j], kAlignments[i]);
+ EXPECT_TRUE(p != NULL);
+ EXPECT_EQ(IsAligned(p, kAlignments[i]), true);
+ allocator_->Free(p);
+ }
+ }
+}
+
+// Check that the reuse allocator actually merges adjacent free blocks.
+TEST_F(FirstFitReuseAllocatorTest, FreeBlockMergingLeft) {
+ const std::size_t kBlockSizes[] = {156, 202};
+ const std::size_t kAlignment = 4;
+ void* blocks[] = {NULL, NULL};
+ blocks[0] = allocator_->Allocate(kBlockSizes[0], kAlignment);
+ blocks[1] = allocator_->Allocate(kBlockSizes[1], kAlignment);
+ // In an empty allocator we expect first alloc to be < second.
+ EXPECT_LT(reinterpret_cast<uintptr_t>(blocks[0]),
+ reinterpret_cast<uintptr_t>(blocks[1]));
+ allocator_->Free(blocks[0]);
+ allocator_->Free(blocks[1]);
+ // Should have merged blocks 1 with block 0.
+ void* test_p =
+ allocator_->Allocate(kBlockSizes[0] + kBlockSizes[1], kAlignment);
+ EXPECT_EQ(test_p, blocks[0]);
+ allocator_->Free(test_p);
+}
+
+TEST_F(FirstFitReuseAllocatorTest, FreeBlockMergingRight) {
+ const std::size_t kBlockSizes[] = {156, 202, 354};
+ const std::size_t kAlignment = 4;
+ void* blocks[] = {NULL, NULL, NULL};
+ blocks[0] = allocator_->Allocate(kBlockSizes[0], kAlignment);
+ blocks[1] = allocator_->Allocate(kBlockSizes[1], kAlignment);
+ blocks[2] = allocator_->Allocate(kBlockSizes[2], kAlignment);
+ // In an empty allocator we expect first alloc to be < second.
+ EXPECT_LT(reinterpret_cast<uintptr_t>(blocks[1]),
+ reinterpret_cast<uintptr_t>(blocks[2]));
+ allocator_->Free(blocks[2]);
+ allocator_->Free(blocks[1]);
+ // Should have merged block 1 with block 2.
+ void* test_p =
+ allocator_->Allocate(kBlockSizes[1] + kBlockSizes[2], kAlignment);
+ EXPECT_EQ(test_p, blocks[1]);
+ allocator_->Free(test_p);
+ allocator_->Free(blocks[0]);
+}
+
+TEST_F(FirstFitReuseAllocatorTest, InitialCapacity) {
+ const std::size_t kInitialCapacity = kBufferSize / 2;
+ ResetAllocator(kInitialCapacity);
+ EXPECT_GE(fallback_allocator_->GetAllocated(), kInitialCapacity);
+}
+
+TEST_F(FirstFitReuseAllocatorTest, AllocationIncrement) {
+ const std::size_t kAllocationIncrement = kBufferSize / 2;
+ ResetAllocator(0, kAllocationIncrement);
+ void* p = allocator_->Allocate(1, 1);
+ EXPECT_TRUE(p != NULL);
+ allocator_->Free(p);
+ EXPECT_GE(fallback_allocator_->GetAllocated(), kAllocationIncrement);
+}
+
+TEST_F(FirstFitReuseAllocatorTest, FallbackBlockMerge) {
+ void* p = allocator_->Allocate(kBufferSize, 1);
+ EXPECT_TRUE(p != NULL);
+ allocator_->Free(p);
+
+ ResetAllocator();
+
+ p = allocator_->Allocate(kBufferSize / 2, 1);
+ EXPECT_TRUE(p != NULL);
+ allocator_->Free(p);
+
+ p = allocator_->Allocate(kBufferSize, 1);
+ EXPECT_TRUE(p != NULL);
+ allocator_->Free(p);
+}
diff --git a/src/nb/memory_pool.cc b/src/nb/memory_pool.cc
deleted file mode 100644
index 6cdef60..0000000
--- a/src/nb/memory_pool.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "nb/memory_pool.h"
-
-#include "starboard/log.h"
-
-namespace nb {
-
-MemoryPool::MemoryPool(void* buffer,
- std::size_t size,
- bool verify_full_capacity,
- std::size_t small_allocation_threshold)
- : no_free_allocator_(buffer, size),
- reuse_allocator_(&no_free_allocator_, size, small_allocation_threshold) {
- SB_DCHECK(buffer);
- SB_DCHECK(size != 0U);
-
- // This is redundant if ReuseAllocator::Allcator() can allocate the difference
- // between the requested size and the last free block from the fallback
- // allocator and combine the blocks.
- if (verify_full_capacity && small_allocation_threshold == 0) {
- void* p = Allocate(size);
- SB_DCHECK(p);
- Free(p);
- }
-}
-
-void MemoryPool::PrintAllocations() const {
- reuse_allocator_.PrintAllocations();
-}
-
-} // namespace nb
diff --git a/src/nb/memory_pool.h b/src/nb/memory_pool.h
index 8dff09e..b42f4e5 100644
--- a/src/nb/memory_pool.h
+++ b/src/nb/memory_pool.h
@@ -18,20 +18,33 @@
#define NB_MEMORY_POOL_H_
#include "nb/allocator.h"
+#include "nb/bidirectional_fit_reuse_allocator.h"
+#include "nb/first_fit_reuse_allocator.h"
#include "nb/fixed_no_free_allocator.h"
-#include "nb/reuse_allocator.h"
+#include "starboard/log.h"
namespace nb {
// The MemoryPool class can be used to wrap a range of memory with allocators
-// such that the memory can be allocated out of and free'd memory re-used
-// as necessary.
+// such that the memory can be allocated out of and free'd memory re-used as
+// necessary.
+template <typename ReuseAllocator>
class MemoryPool : public Allocator {
public:
- MemoryPool(void* buffer,
- std::size_t size,
- bool verify_full_capacity = false,
- std::size_t small_allocation_threshold = 0);
+ MemoryPool(void* buffer, std::size_t size)
+ : no_free_allocator_(buffer, size),
+ reuse_allocator_(&no_free_allocator_, size) {
+ SB_DCHECK(buffer);
+ SB_DCHECK(size > 0U);
+ }
+
+ template <typename ParameterType>
+ MemoryPool(void* buffer, std::size_t size, ParameterType parameter1)
+ : no_free_allocator_(buffer, size),
+ reuse_allocator_(&no_free_allocator_, size, parameter1) {
+ SB_DCHECK(buffer);
+ SB_DCHECK(size > 0U);
+ }
void* Allocate(std::size_t size) { return reuse_allocator_.Allocate(size); }
void* Allocate(std::size_t size, std::size_t alignment) {
@@ -45,7 +58,7 @@
return no_free_allocator_.GetAllocated();
}
- void PrintAllocations() const;
+ void PrintAllocations() const { reuse_allocator_.PrintAllocations(); }
private:
// A budget of memory to be used by the memory pool.
@@ -57,6 +70,9 @@
ReuseAllocator reuse_allocator_;
};
+typedef MemoryPool<BidirectionalFitReuseAllocator> BidirectionalFitMemoryPool;
+typedef MemoryPool<FirstFitReuseAllocator> FirstFitMemoryPool;
+
} // namespace nb
#endif // NB_MEMORY_POOL_H_
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index db80e43..f778a3c 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -32,13 +32,16 @@
'analytics/memory_tracker_helpers.h',
'atomic.h',
'bit_cast.h',
+ 'bidirectional_fit_reuse_allocator.h',
+ 'bidirectional_fit_reuse_allocator.cc',
'concurrent_map.h',
+ 'first_fit_reuse_allocator.h',
+ 'first_fit_reuse_allocator.cc',
'fixed_no_free_allocator.cc',
'fixed_no_free_allocator.h',
'hash.cc',
'hash.h',
'lexical_cast.h',
- 'memory_pool.cc',
'memory_pool.h',
'memory_scope.cc',
'memory_scope.h',
@@ -47,8 +50,8 @@
'rect.h',
'ref_counted.cc',
'ref_counted.h',
- 'reuse_allocator.cc',
- 'reuse_allocator.h',
+ 'reuse_allocator_base.cc',
+ 'reuse_allocator_base.h',
'rewindable_vector.h',
'scoped_ptr.h',
'simple_thread.cc',
@@ -87,11 +90,12 @@
'analytics/memory_tracker_impl_test.cc',
'analytics/memory_tracker_test.cc',
'atomic_test.cc',
+ 'bidirectional_fit_reuse_allocator_test.cc',
'concurrent_map_test.cc',
+ 'first_fit_reuse_allocator_test.cc',
'fixed_no_free_allocator_test.cc',
'lexical_cast_test.cc',
'memory_scope_test.cc',
- 'reuse_allocator_test.cc',
'rewindable_vector_test.cc',
'run_all_unittests.cc',
'std_allocator_test.cc',
diff --git a/src/nb/reuse_allocator.h b/src/nb/reuse_allocator.h
deleted file mode 100644
index 71d43d8..0000000
--- a/src/nb/reuse_allocator.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef NB_REUSE_ALLOCATOR_H_
-#define NB_REUSE_ALLOCATOR_H_
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "nb/allocator.h"
-
-namespace nb {
-
-// An allocator designed to accomodate cases where the memory allocated may not
-// be efficient or safe to access via the CPU. It solves this problem by
-// maintaining all allocation meta data is outside of the allocated memory.
-// It is passed a fallback allocator that it can request additional memory
-// from as needed.
-// The default allocation strategy for the allocator is first-fit, i.e. it will
-// scan for free blocks sorted by addresses and allocate from the first free
-// block that can fulfill the allocation. However, in some situations the
-// majority of the allocations can be small ones with some large allocations.
-// This may cause serious fragmentations and the failure of large allocations.
-// If |small_allocation_threshold| in the ctor is set to a non-zero value, the
-// class will allocate small allocations whose sizes are less than or equal to
-// the threshold using last-fit, i.e. it will scan from the back to the front
-// for free blocks. This way the allocation for large blocks and small blocks
-// are separated thus cause much less fragmentations.
-class ReuseAllocator : public Allocator {
- public:
- explicit ReuseAllocator(Allocator* fallback_allocator);
- // When |small_allocation_threshold| is non-zero, this class will allocate
- // its full capacity from the |fallback_allocator| in the ctor so it is
- // possible for the class to use the last-fit allocation strategy. See the
- // class comment above for more details.
- ReuseAllocator(Allocator* fallback_allocator,
- std::size_t capacity,
- std::size_t small_allocation_threshold);
- virtual ~ReuseAllocator();
-
- // Search free memory blocks for an existing one, and if none are large
- // enough, allocate a new one from no-free memory and return that.
- void* Allocate(std::size_t size);
- void* Allocate(std::size_t size, std::size_t alignment);
-
- // Marks the memory block as being free and it will then become recyclable
- void Free(void* memory);
-
- std::size_t GetCapacity() const { return capacity_; }
- std::size_t GetAllocated() const { return total_allocated_; }
-
- void PrintAllocations() const;
-
- bool TryFree(void* memory);
-
- private:
- class MemoryBlock {
- public:
- MemoryBlock() : address_(0), size_(0) {}
- MemoryBlock(void* address, std::size_t size)
- : address_(address), size_(size) {}
-
- void* address() const { return address_; }
- std::size_t size() const { return size_; }
-
- void set_address(void* address) { address_ = address; }
- void set_size(std::size_t size) { size_ = size; }
-
- bool operator<(const MemoryBlock& other) const {
- return address_ < other.address_;
- }
- // If the current block and |other| can be combined into a continuous memory
- // block, store the conmbined block in the current block and return true.
- // Otherwise return false.
- bool Merge(const MemoryBlock& other);
- // Return true if the current block can be used to fulfill an allocation
- // with the given size and alignment.
- bool CanFullfill(std::size_t request_size, std::size_t alignment) const;
- // Allocate a block from this block with the given size and alignment.
- // Store the allocated block in |allocated|. If the rest space is large
- // enough to form a block, it will be stored into |free|. Otherwise the
- // whole block is stored into |allocated|.
- // Note that the call of this function has to ensure that CanFulfill() is
- // already called on this block and returns true.
- void Allocate(std::size_t request_size,
- std::size_t alignment,
- bool allocate_from_front,
- MemoryBlock* allocated,
- MemoryBlock* free) const;
-
- private:
- void* address_;
- std::size_t size_;
- };
-
- // Freelist sorted by address.
- typedef std::set<MemoryBlock> FreeBlockSet;
- // Map from pointers we returned to the user, back to memory blocks.
- typedef std::map<void*, MemoryBlock> AllocatedBlockMap;
-
- FreeBlockSet::iterator AddFreeBlock(MemoryBlock block_to_add);
- void RemoveFreeBlock(FreeBlockSet::iterator it);
-
- FreeBlockSet free_blocks_;
- AllocatedBlockMap allocated_blocks_;
-
- // We will allocate from the given allocator whenever we can't find pre-used
- // memory to allocate.
- Allocator* fallback_allocator_;
-
- // Any allocations with size less than or equal to the following threshold
- // will be allocated from the back of the pool. See the comment of the class
- // for more details.
- std::size_t small_allocation_threshold_;
-
- // A list of allocations made from the fallback allocator. We keep track of
- // this so that we can free them all upon our destruction.
- std::vector<void*> fallback_allocations_;
-
- // How much we have allocated from the fallback allocator.
- std::size_t capacity_;
-
- // How much has been allocated from us.
- std::size_t total_allocated_;
-};
-
-} // namespace nb
-
-#endif // NB_REUSE_ALLOCATOR_H_
diff --git a/src/nb/reuse_allocator.cc b/src/nb/reuse_allocator_base.cc
similarity index 64%
rename from src/nb/reuse_allocator.cc
rename to src/nb/reuse_allocator_base.cc
index c12375d..75c0394 100644
--- a/src/nb/reuse_allocator.cc
+++ b/src/nb/reuse_allocator_base.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "nb/reuse_allocator.h"
+#include "nb/reuse_allocator_base.h"
#include <algorithm>
@@ -28,7 +28,7 @@
// to ensure that a zero sized allocation will return a non-zero sized block.
const std::size_t kMinBlockSizeBytes = 16;
-bool ReuseAllocator::MemoryBlock::Merge(const MemoryBlock& other) {
+bool ReuseAllocatorBase::MemoryBlock::Merge(const MemoryBlock& other) {
if (AsInteger(address_) + size_ == AsInteger(other.address_)) {
size_ += other.size_;
return true;
@@ -41,19 +41,19 @@
return false;
}
-bool ReuseAllocator::MemoryBlock::CanFullfill(std::size_t request_size,
- std::size_t alignment) const {
+bool ReuseAllocatorBase::MemoryBlock::CanFullfill(std::size_t request_size,
+ std::size_t alignment) const {
const std::size_t extra_bytes_for_alignment =
AlignUp(AsInteger(address_), alignment) - AsInteger(address_);
const std::size_t aligned_size = request_size + extra_bytes_for_alignment;
return size_ >= aligned_size;
}
-void ReuseAllocator::MemoryBlock::Allocate(std::size_t request_size,
- std::size_t alignment,
- bool allocate_from_front,
- MemoryBlock* allocated,
- MemoryBlock* free) const {
+void ReuseAllocatorBase::MemoryBlock::Allocate(std::size_t request_size,
+ std::size_t alignment,
+ bool allocate_from_front,
+ MemoryBlock* allocated,
+ MemoryBlock* free) const {
SB_DCHECK(allocated);
SB_DCHECK(free);
SB_DCHECK(CanFullfill(request_size, alignment));
@@ -101,30 +101,107 @@
}
}
-ReuseAllocator::ReuseAllocator(Allocator* fallback_allocator)
- : fallback_allocator_(fallback_allocator),
- small_allocation_threshold_(0),
- capacity_(0),
- total_allocated_(0) {}
+void* ReuseAllocatorBase::Allocate(std::size_t size) {
+ return Allocate(size, 1);
+}
-ReuseAllocator::ReuseAllocator(Allocator* fallback_allocator,
- std::size_t capacity,
- std::size_t small_allocation_threshold)
+void* ReuseAllocatorBase::Allocate(std::size_t size, std::size_t alignment) {
+ // Keeping things rounded and aligned will help us avoid creating tiny and/or
+ // badly misaligned free blocks. Also ensure even for a 0-byte request we
+ // return a unique block.
+ const std::size_t kMinAlignment = 16;
+ size = AlignUp(std::max(size, kMinAlignment), kMinAlignment);
+ alignment = AlignUp(std::max<std::size_t>(alignment, 1), kMinAlignment);
+
+ MemoryBlock allocated_block;
+ bool allocate_from_front;
+ FreeBlockSet::iterator free_block_iter =
+ FindFreeBlock(size, alignment, free_blocks_.begin(), free_blocks_.end(),
+ &allocate_from_front);
+
+ if (free_block_iter == free_blocks_.end()) {
+ free_block_iter = ExpandToFit(size, alignment);
+ if (free_block_iter == free_blocks_.end()) {
+ return NULL;
+ }
+ }
+
+ MemoryBlock block = *free_block_iter;
+ // The block is big enough. We may waste some space due to alignment.
+ RemoveFreeBlock(free_block_iter);
+
+ MemoryBlock free_block;
+ block.Allocate(size, alignment, allocate_from_front, &allocated_block,
+ &free_block);
+ if (free_block.size() > 0) {
+ SB_DCHECK(free_block.address());
+ AddFreeBlock(free_block);
+ }
+ void* user_address = AlignUp(allocated_block.address(), alignment);
+ AddAllocatedBlock(user_address, allocated_block);
+
+ return user_address;
+}
+
+void ReuseAllocatorBase::Free(void* memory) {
+ bool result = TryFree(memory);
+ SB_DCHECK(result);
+}
+
+void ReuseAllocatorBase::PrintAllocations() const {
+ typedef std::map<std::size_t, std::size_t> SizesHistogram;
+ SizesHistogram sizes_histogram;
+ for (AllocatedBlockMap::const_iterator iter = allocated_blocks_.begin();
+ iter != allocated_blocks_.end(); ++iter) {
+ std::size_t block_size = iter->second.size();
+ if (sizes_histogram.find(block_size) == sizes_histogram.end()) {
+ sizes_histogram[block_size] = 0;
+ }
+ sizes_histogram[block_size] = sizes_histogram[block_size] + 1;
+ }
+
+ for (SizesHistogram::const_iterator iter = sizes_histogram.begin();
+ iter != sizes_histogram.end(); ++iter) {
+ SB_LOG(INFO) << iter->first << " : " << iter->second;
+ }
+ SB_LOG(INFO) << "Total allocations: " << allocated_blocks_.size();
+}
+
+bool ReuseAllocatorBase::TryFree(void* memory) {
+ if (!memory) {
+ return true;
+ }
+
+ AllocatedBlockMap::iterator it = allocated_blocks_.find(memory);
+ if (it == allocated_blocks_.end()) {
+ return false;
+ }
+
+ // Mark this block as free and remove it from the allocated set.
+ const MemoryBlock& block = (*it).second;
+ AddFreeBlock(block);
+
+ SB_DCHECK(block.size() <= total_allocated_);
+ total_allocated_ -= block.size();
+
+ allocated_blocks_.erase(it);
+ return true;
+}
+
+ReuseAllocatorBase::ReuseAllocatorBase(Allocator* fallback_allocator,
+ std::size_t initial_capacity,
+ std::size_t allocation_increment)
: fallback_allocator_(fallback_allocator),
- small_allocation_threshold_(small_allocation_threshold),
+ allocation_increment_(allocation_increment),
capacity_(0),
total_allocated_(0) {
- // If |small_allocation_threshold_| is non-zero, this class will use last-fit
- // strategy to fulfill small allocations. Pre-allocator full capacity so
- // last-fit makes sense.
- if (small_allocation_threshold_ > 0) {
- void* p = Allocate(capacity, 1);
- SB_DCHECK(p);
- Free(p);
+ if (initial_capacity > 0) {
+ FreeBlockSet::iterator iter = ExpandToFit(initial_capacity, 1);
+ SB_DCHECK(iter != free_blocks_.end());
}
}
-ReuseAllocator::~ReuseAllocator() {
+ReuseAllocatorBase::~ReuseAllocatorBase() {
// Assert that everything was freed.
// Note that in some unit tests this may
// not be the case.
@@ -138,7 +215,55 @@
}
}
-ReuseAllocator::FreeBlockSet::iterator ReuseAllocator::AddFreeBlock(
+ReuseAllocatorBase::FreeBlockSet::iterator ReuseAllocatorBase::ExpandToFit(
+ std::size_t size,
+ std::size_t alignment) {
+ void* ptr = NULL;
+ std::size_t size_to_try = 0;
+ if (allocation_increment_ > size) {
+ size_to_try = std::max(size, allocation_increment_);
+ ptr = fallback_allocator_->AllocateForAlignment(&size_to_try, alignment);
+ }
+ if (ptr == NULL) {
+ size_to_try = size;
+ ptr = fallback_allocator_->AllocateForAlignment(&size_to_try, alignment);
+ }
+ if (ptr != NULL) {
+ fallback_allocations_.push_back(ptr);
+ capacity_ += size_to_try;
+ return AddFreeBlock(MemoryBlock(ptr, size_to_try));
+ }
+
+ if (free_blocks_.empty()) {
+ return free_blocks_.end();
+ }
+
+ // We failed to allocate for |size| from the fallback allocator, try to
+ // allocate the difference between |size| and the size of the right most block
+ // in the hope that they are continuous and can be connect to a block that is
+ // large enough to fulfill |size|.
+ size_t size_difference = size - free_blocks_.rbegin()->size();
+ ptr = fallback_allocator_->AllocateForAlignment(&size_difference, alignment);
+ if (ptr == NULL) {
+ return free_blocks_.end();
+ }
+
+ fallback_allocations_.push_back(ptr);
+ capacity_ += size_difference;
+ AddFreeBlock(MemoryBlock(ptr, size_difference));
+ FreeBlockSet::iterator iter = free_blocks_.end();
+ --iter;
+ return iter->CanFullfill(size, alignment) ? iter : free_blocks_.end();
+}
+
+void ReuseAllocatorBase::AddAllocatedBlock(void* address,
+ const MemoryBlock& block) {
+ SB_DCHECK(allocated_blocks_.find(address) == allocated_blocks_.end());
+ allocated_blocks_[address] = block;
+ total_allocated_ += block.size();
+}
+
+ReuseAllocatorBase::FreeBlockSet::iterator ReuseAllocatorBase::AddFreeBlock(
MemoryBlock block_to_add) {
if (free_blocks_.size() == 0) {
return free_blocks_.insert(block_to_add).first;
@@ -178,136 +303,8 @@
return free_blocks_.insert(block_to_add).first;
}
-void ReuseAllocator::RemoveFreeBlock(FreeBlockSet::iterator it) {
+void ReuseAllocatorBase::RemoveFreeBlock(FreeBlockSet::iterator it) {
free_blocks_.erase(it);
}
-void* ReuseAllocator::Allocate(std::size_t size) {
- return Allocate(size, 1);
-}
-
-void* ReuseAllocator::Allocate(std::size_t size, std::size_t alignment) {
- if (alignment == 0) {
- alignment = 1;
- }
-
- // Try to satisfy request from free list.
- // First look for a block that is appropriately aligned.
- // If we can't, look for a block that is big enough that we can carve out an
- // aligned block.
- // If there is no such block, allocate more from our fallback allocator.
- void* user_address = 0;
-
- // Keeping things rounded and aligned will help us avoid creating tiny and/or
- // badly misaligned free blocks. Also ensure even for a 0-byte request we
- // return a unique block.
- const std::size_t kMinAlignment = 16;
- size = std::max(size, kMinBlockSizeBytes);
- size = AlignUp(size, kMinBlockSizeBytes);
- alignment = AlignUp(alignment, kMinAlignment);
-
- // Worst case how much memory we need.
- MemoryBlock allocated_block;
-
- bool scan_from_front = size > small_allocation_threshold_;
- FreeBlockSet::iterator free_block_iter = free_blocks_.end();
-
- if (scan_from_front) {
- // Start looking through the free list from the front.
- for (FreeBlockSet::iterator it = free_blocks_.begin();
- it != free_blocks_.end(); ++it) {
- if (it->CanFullfill(size, alignment)) {
- free_block_iter = it;
- break;
- }
- }
- } else {
- // Start looking through the free list from the back.
- for (FreeBlockSet::reverse_iterator it = free_blocks_.rbegin();
- it != free_blocks_.rend(); ++it) {
- if (it->CanFullfill(size, alignment)) {
- free_block_iter = it.base();
- --free_block_iter;
- break;
- }
- }
- }
-
- if (free_block_iter == free_blocks_.end()) {
- // No free blocks found, allocate one from the fallback allocator.
- std::size_t block_size = size;
- void* ptr =
- fallback_allocator_->AllocateForAlignment(&block_size, alignment);
- if (ptr == NULL) {
- return NULL;
- }
- free_block_iter = AddFreeBlock(MemoryBlock(ptr, block_size));
- capacity_ += block_size;
- fallback_allocations_.push_back(ptr);
- }
-
- MemoryBlock block = *free_block_iter;
- // The block is big enough. We may waste some space due to alignment.
- RemoveFreeBlock(free_block_iter);
-
- MemoryBlock free_block;
- block.Allocate(size, alignment, scan_from_front, &allocated_block,
- &free_block);
- if (free_block.size() > 0) {
- SB_DCHECK(free_block.address());
- AddFreeBlock(free_block);
- }
- user_address = AlignUp(allocated_block.address(), alignment);
-
- SB_DCHECK(allocated_blocks_.find(user_address) == allocated_blocks_.end());
- allocated_blocks_[user_address] = allocated_block;
- total_allocated_ += allocated_block.size();
- return user_address;
-}
-
-void ReuseAllocator::Free(void* memory) {
- bool result = TryFree(memory);
- SB_DCHECK(result);
-}
-
-void ReuseAllocator::PrintAllocations() const {
- typedef std::map<std::size_t, std::size_t> SizesHistogram;
- SizesHistogram sizes_histogram;
- for (AllocatedBlockMap::const_iterator iter = allocated_blocks_.begin();
- iter != allocated_blocks_.end(); ++iter) {
- std::size_t block_size = iter->second.size();
- if (sizes_histogram.find(block_size) == sizes_histogram.end()) {
- sizes_histogram[block_size] = 0;
- }
- sizes_histogram[block_size] = sizes_histogram[block_size] + 1;
- }
-
- for (SizesHistogram::const_iterator iter = sizes_histogram.begin();
- iter != sizes_histogram.end(); ++iter) {
- SB_LOG(INFO) << iter->first << " : " << iter->second;
- }
- SB_LOG(INFO) << "Total allocations: " << allocated_blocks_.size();
-}
-
-bool ReuseAllocator::TryFree(void* memory) {
- if (!memory) {
- return true;
- }
-
- AllocatedBlockMap::iterator it = allocated_blocks_.find(memory);
- if (it == allocated_blocks_.end()) {
- return false;
- }
-
- // Mark this block as free and remove it from the allocated set.
- const MemoryBlock& block = (*it).second;
- AddFreeBlock(block);
-
- SB_DCHECK(block.size() <= total_allocated_);
- total_allocated_ -= block.size();
-
- allocated_blocks_.erase(it);
- return true;
-}
-
} // namespace nb
diff --git a/src/nb/reuse_allocator_base.h b/src/nb/reuse_allocator_base.h
new file mode 100644
index 0000000..e5782fd
--- /dev/null
+++ b/src/nb/reuse_allocator_base.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NB_REUSE_ALLOCATOR_BASE_H_
+#define NB_REUSE_ALLOCATOR_BASE_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "nb/allocator.h"
+#include "starboard/configuration.h"
+
+namespace nb {
+
+// The base class of allocators designed to accommodate cases where the memory
+// allocated may not be efficient or safe to access via the CPU. It solves
+// this problem by maintaining all allocation meta data is outside of the
+// allocated memory. It is passed a fallback allocator that it can request
+// additional memory from as needed.
+class ReuseAllocatorBase : public Allocator {
+ public:
+ void* Allocate(std::size_t size) SB_OVERRIDE;
+ void* Allocate(std::size_t size, std::size_t alignment) SB_OVERRIDE;
+
+ // Marks the memory block as being free and it will then become recyclable
+ void Free(void* memory) SB_OVERRIDE;
+
+ std::size_t GetCapacity() const SB_OVERRIDE { return capacity_; }
+ std::size_t GetAllocated() const SB_OVERRIDE { return total_allocated_; }
+
+ void PrintAllocations() const SB_OVERRIDE;
+
+ bool TryFree(void* memory);
+
+ protected:
+ class MemoryBlock {
+ public:
+ MemoryBlock() : address_(0), size_(0) {}
+ MemoryBlock(void* address, std::size_t size)
+ : address_(address), size_(size) {}
+
+ void* address() const { return address_; }
+ std::size_t size() const { return size_; }
+
+ void set_address(void* address) { address_ = address; }
+ void set_size(std::size_t size) { size_ = size; }
+
+ bool operator<(const MemoryBlock& other) const {
+ return address_ < other.address_;
+ }
+ // If the current block and |other| can be combined into a continuous memory
+ // block, store the conmbined block in the current block and return true.
+ // Otherwise return false.
+ bool Merge(const MemoryBlock& other);
+ // Return true if the current block can be used to fulfill an allocation
+ // with the given size and alignment.
+ bool CanFullfill(std::size_t request_size, std::size_t alignment) const;
+ // Allocate a block from this block with the given size and alignment.
+ // Store the allocated block in |allocated|. If the rest space is large
+ // enough to form a block, it will be stored into |free|. Otherwise the
+ // whole block is stored into |allocated|.
+ // Note that the call of this function has to ensure that CanFulfill() is
+ // already called on this block and returns true.
+ void Allocate(std::size_t request_size,
+ std::size_t alignment,
+ bool allocate_from_front,
+ MemoryBlock* allocated,
+ MemoryBlock* free) const;
+
+ private:
+ void* address_;
+ std::size_t size_;
+ };
+
+ // Freelist sorted by address.
+ typedef std::set<MemoryBlock> FreeBlockSet;
+
+ ReuseAllocatorBase(Allocator* fallback_allocator,
+ std::size_t initial_capacity,
+ std::size_t allocation_increment);
+ ~ReuseAllocatorBase() SB_OVERRIDE;
+
+ // The inherited class should implement this function to inform the base
+ // class which free block to take. It returns |end| if no suitable free
+ // block is found. When |allocate_from_front| is set to true, the allocation
+ // will take place in the front of a free block if the free block is big
+ // enough to fulfill this allocation and produce another free block.
+ // Otherwise the allocation will take place from the back.
+ virtual FreeBlockSet::iterator FindFreeBlock(std::size_t size,
+ std::size_t alignment,
+ FreeBlockSet::iterator begin,
+ FreeBlockSet::iterator end,
+ bool* allocate_from_front) = 0;
+
+ private:
+ // Map from pointers we returned to the user, back to memory blocks.
+ typedef std::map<void*, MemoryBlock> AllocatedBlockMap;
+
+ FreeBlockSet::iterator ExpandToFit(std::size_t size, std::size_t alignment);
+
+ void AddAllocatedBlock(void* address, const MemoryBlock& block);
+ FreeBlockSet::iterator AddFreeBlock(MemoryBlock block_to_add);
+ void RemoveFreeBlock(FreeBlockSet::iterator it);
+
+ AllocatedBlockMap allocated_blocks_;
+ FreeBlockSet free_blocks_;
+
+ // We will allocate from the given allocator whenever we can't find pre-used
+ // memory to allocate.
+ Allocator* fallback_allocator_;
+ std::size_t allocation_increment_;
+
+ // A list of allocations made from the fallback allocator. We keep track of
+ // this so that we can free them all upon our destruction.
+ std::vector<void*> fallback_allocations_;
+
+ // How much we have allocated from the fallback allocator.
+ std::size_t capacity_;
+
+ // How much has been allocated from us.
+ std::size_t total_allocated_;
+};
+
+} // namespace nb
+
+#endif // NB_REUSE_ALLOCATOR_BASE_H_
diff --git a/src/nb/reuse_allocator_benchmark.cc b/src/nb/reuse_allocator_benchmark.cc
index 7fe94d4..786e400 100644
--- a/src/nb/reuse_allocator_benchmark.cc
+++ b/src/nb/reuse_allocator_benchmark.cc
@@ -21,8 +21,8 @@
#include <vector>
#include "nb/allocator.h"
+#include "nb/first_fit_reuse_allocator.h"
#include "nb/fixed_no_free_allocator.h"
-#include "nb/reuse_allocator.h"
#include "starboard/client_porting/wrap_main/wrap_main.h"
#include "starboard/event.h"
#include "starboard/file.h"
@@ -187,12 +187,13 @@
void PrintAllocations() const SB_OVERRIDE {}
};
+// TODO: Make this work with other ReuseAllocator types.
void MemoryPlaybackTest(const std::string& filename) {
const std::size_t kFixedNoFreeMemorySize = 512 * 1024 * 1024;
void* fixed_no_free_memory = SbMemoryAllocate(kFixedNoFreeMemorySize);
nb::FixedNoFreeAllocator fallback_allocator(fixed_no_free_memory,
kFixedNoFreeMemorySize);
- nb::ReuseAllocator reuse_allocator(&fallback_allocator);
+ nb::FirstFitReuseAllocator reuse_allocator(&fallback_allocator, 0);
std::vector<AllocationCommand> commands;
LoadAllocationPlayback(&commands, filename);
diff --git a/src/nb/reuse_allocator_test.cc b/src/nb/reuse_allocator_test.cc
deleted file mode 100644
index 4d42361..0000000
--- a/src/nb/reuse_allocator_test.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "nb/reuse_allocator.h"
-
-#include "nb/fixed_no_free_allocator.h"
-#include "nb/scoped_ptr.h"
-#include "starboard/configuration.h"
-#include "starboard/types.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-inline bool IsAligned(void* ptr, std::size_t boundary) {
- uintptr_t ptr_as_int = reinterpret_cast<uintptr_t>(ptr);
- return ptr_as_int % boundary == 0;
-}
-
-class ReuseAllocatorTest : public ::testing::Test {
- public:
- static const int kBufferSize = 1 * 1024 * 1024;
-
- ReuseAllocatorTest() { ResetAllocator(0); }
-
- protected:
- void ResetAllocator(size_t small_allocation_threshold) {
- buffer_.reset(new uint8_t[kBufferSize]);
- fallback_allocator_.reset(
- new nb::FixedNoFreeAllocator(buffer_.get(), kBufferSize));
- if (small_allocation_threshold == 0) {
- allocator_.reset(new nb::ReuseAllocator(fallback_allocator_.get()));
- } else {
- allocator_.reset(new nb::ReuseAllocator(
- fallback_allocator_.get(), kBufferSize, small_allocation_threshold));
- }
- }
-
- nb::scoped_array<uint8_t> buffer_;
- nb::scoped_ptr<nb::FixedNoFreeAllocator> fallback_allocator_;
- nb::scoped_ptr<nb::ReuseAllocator> allocator_;
-};
-
-} // namespace
-
-TEST_F(ReuseAllocatorTest, AlignmentCheck) {
- const std::size_t kAlignments[] = {4, 16, 256, 32768};
- const std::size_t kBlockSizes[] = {4, 97, 256, 65201};
- for (int i = 0; i < SB_ARRAY_SIZE(kAlignments); ++i) {
- for (int j = 0; j < SB_ARRAY_SIZE(kBlockSizes); ++j) {
- void* p = allocator_->Allocate(kBlockSizes[j], kAlignments[i]);
- // NOTE: Don't dereference p- this doesn't point anywhere valid.
- EXPECT_TRUE(p != NULL);
- EXPECT_EQ(IsAligned(p, kAlignments[i]), true);
- allocator_->Free(p);
- }
- }
-}
-
-// Check that the reuse allocator actually merges adjacent free blocks.
-TEST_F(ReuseAllocatorTest, FreeBlockMergingLeft) {
- const std::size_t kBlockSizes[] = {156, 202};
- const std::size_t kAlignment = 4;
- void* blocks[] = {NULL, NULL};
- blocks[0] = allocator_->Allocate(kBlockSizes[0], kAlignment);
- blocks[1] = allocator_->Allocate(kBlockSizes[1], kAlignment);
- // In an empty allocator we expect first alloc to be < second.
- EXPECT_LT(reinterpret_cast<uintptr_t>(blocks[0]),
- reinterpret_cast<uintptr_t>(blocks[1]));
- allocator_->Free(blocks[0]);
- allocator_->Free(blocks[1]);
- // Should have merged blocks 1 with block 0.
- void* test_p =
- allocator_->Allocate(kBlockSizes[0] + kBlockSizes[1], kAlignment);
- EXPECT_EQ(test_p, blocks[0]);
- allocator_->Free(test_p);
-}
-
-TEST_F(ReuseAllocatorTest, FreeBlockMergingRight) {
- const std::size_t kBlockSizes[] = {156, 202, 354};
- const std::size_t kAlignment = 4;
- void* blocks[] = {NULL, NULL, NULL};
- blocks[0] = allocator_->Allocate(kBlockSizes[0], kAlignment);
- blocks[1] = allocator_->Allocate(kBlockSizes[1], kAlignment);
- blocks[2] = allocator_->Allocate(kBlockSizes[2], kAlignment);
- // In an empty allocator we expect first alloc to be < second.
- EXPECT_LT(reinterpret_cast<uintptr_t>(blocks[1]),
- reinterpret_cast<uintptr_t>(blocks[2]));
- allocator_->Free(blocks[2]);
- allocator_->Free(blocks[1]);
- // Should have merged block 1 with block 2.
- void* test_p =
- allocator_->Allocate(kBlockSizes[1] + kBlockSizes[2], kAlignment);
- EXPECT_EQ(test_p, blocks[1]);
- allocator_->Free(test_p);
- allocator_->Free(blocks[0]);
-}
-
-TEST_F(ReuseAllocatorTest, SmallAlloc) {
- // Recreate allocator with small allocation threshold to 256.
- ResetAllocator(256);
-
- const std::size_t kBlockSizes[] = {117, 193, 509, 1111};
- const std::size_t kAlignment = 16;
- void* blocks[] = {NULL, NULL, NULL, NULL};
- for (int i = 0; i < SB_ARRAY_SIZE(kBlockSizes); ++i) {
- blocks[i] = allocator_->Allocate(kBlockSizes[i], kAlignment);
- }
- // The two small allocs should be in the back in reverse order.
- EXPECT_GT(reinterpret_cast<uintptr_t>(blocks[0]),
- reinterpret_cast<uintptr_t>(blocks[1]));
- // Small allocs should has higher address than other allocs.
- EXPECT_GT(reinterpret_cast<uintptr_t>(blocks[1]),
- reinterpret_cast<uintptr_t>(blocks[3]));
- // Non-small allocs are allocated from the front and the first one has the
- // lowest address.
- EXPECT_LT(reinterpret_cast<uintptr_t>(blocks[2]),
- reinterpret_cast<uintptr_t>(blocks[3]));
- for (int i = 0; i < SB_ARRAY_SIZE(kBlockSizes); ++i) {
- allocator_->Free(blocks[i]);
- }
- // Should have one single free block equals to the capacity.
- void* test_p = allocator_->Allocate(allocator_->GetCapacity());
- EXPECT_TRUE(test_p != NULL);
- allocator_->Free(test_p);
-}
diff --git a/src/net/quic/test_tools/test_task_runner.h b/src/net/quic/test_tools/test_task_runner.h
index 47e4685..80e9b51 100644
--- a/src/net/quic/test_tools/test_task_runner.h
+++ b/src/net/quic/test_tools/test_task_runner.h
@@ -11,9 +11,9 @@
#include <vector>
-#include "base/time.h"
-
+#include "base/logging.h"
#include "base/stl_util.h"
+#include "base/time.h"
#include "net/base/net_errors.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/quic_test_utils.h"
@@ -46,6 +46,11 @@
virtual bool PostDelayedTask(const tracked_objects::Location& location,
const base::Closure& closure,
base::TimeDelta delta) OVERRIDE;
+ virtual bool PostBlockingTask(const tracked_objects::Location& from_here,
+ const base::Closure& task) OVERRIDE {
+ NOTREACHED() << "Unsupported.";
+ return false;
+ }
std::vector<PostedTask>::iterator FindNextTask();
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index 1446709..7ce54d9 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -6,6 +6,56 @@
this file describes the changes made to the Starboard interface since the
version previous to it.
+## Version 6
+
+### Introduce pointer (mouse) input support.
+
+This extends the `SbInput` interface with some enum values and data members to
+allow mouse, wheel, and more generic pointer input.
+
+### Flexible audio specific config.
+
+`SbMediaAudioHeader::audio_specific_config` will be a pointer instead of an
+array.
+
+### Time Zone API Cleanup
+
+Removes `SbTimeZoneGetDstName()` -- The Daylight Savings Time version of the
+time zone.
+
+Changes `SbTimeZoneGetName()` to be more flexible in what it is allowed to
+return.
+
+### SbDecodeTargetNumberOfPlanesForFormat
+
+Adds the convenience inline function, SbDecodeTargetNumberOfPlanesForFormat() to
+`starboard/decode_target.h`.
+
+### Preload Support
+
+Adds the `kSbEventTypePreload` event, and modifies the application state machine
+to utilize it.
+
+### Platform Error Cleanup
+
+Removes `SbSystemPlatformErrorType` values specific to user status.
+
+### SbDecodeTarget support for the UYVY (i.e. YUV 422) format
+
+Add support for UYVY decode targets (e.g. YUV 422) via the
+`kSbDecodeTargetFormat1PlaneUYVY` enum.
+
+### Add Color Remote Keys
+
+This adds SbKey codes for the colored keys found on most contemporary TV
+remotes.
+
+### kSbEventTypeLowMemory
+
+Adds a new event type -- `kSbEventTypeLowMemory` -- to allow a platform to
+signal that the application may soon be terminated due to low memory
+availability.
+
## Version 5
### Add Speech Recognizer API
diff --git a/src/starboard/atomic.h b/src/starboard/atomic.h
index 447958b..f0fa6d8 100644
--- a/src/starboard/atomic.h
+++ b/src/starboard/atomic.h
@@ -370,4 +370,342 @@
// as inlined. This macro is defined on the command line by gyp.
#include STARBOARD_ATOMIC_INCLUDE
+#ifdef __cplusplus
+
+#include "starboard/mutex.h"
+
+namespace starboard {
+
+// Provides atomic types like integer and float. Some types like atomic_int32_t
+// are likely to be hardware accelerated for your platform.
+//
+// Never use the parent types like atomic_base<T>, atomic_number<T> or
+// atomic_integral<T> and instead use the fully qualified classes like
+// atomic_int32_t, atomic_pointer<T*>, etc.
+//
+// Note on template instantiation, avoid using the parent type and instead
+// use the fully qualified type.
+// BAD:
+// template<typename T>
+// void Foo(const atomic_base<T>& value);
+// GOOD:
+// template<typename atomic_t>
+// void Foo(const atomic_t& vlaue);
+
+// Atomic Pointer class. Instantiate as atomic_pointer<void*>
+// for void* pointer types.
+template <typename T>
+class atomic_pointer;
+
+// Atomic bool class.
+class atomic_bool;
+
+// Atomic int32 class
+class atomic_int32_t;
+
+// Atomic int64 class.
+class atomic_int64_t;
+
+// Atomic float class.
+class atomic_float;
+
+// Atomic double class.
+class atomic_double;
+
+///////////////////////////////////////////////////////////////////////////////
+// Class hiearchy.
+///////////////////////////////////////////////////////////////////////////////
+
+// Base functionality for atomic types. Defines exchange(), load(),
+// store(), compare_exhange_weak(), compare_exchange_strong()
+template <typename T>
+class atomic_base;
+
+// Subtype of atomic_base<T> for numbers likes float and integer types but not
+// bool. Adds fetch_add() and fetch_sub().
+template <typename T>
+class atomic_number;
+
+// Subtype of atomic_number<T> for integer types like int32 and int64. Adds
+// increment and decrement.
+template <typename T>
+class atomic_integral;
+
+///////////////////////////////////////////////////////////////////////////////
+// Implimentation.
+///////////////////////////////////////////////////////////////////////////////
+
+// Similar to C++11 std::atomic<T>.
+// atomic_base<T> may be instantiated with any TriviallyCopyable type T.
+// atomic_base<T> is neither copyable nor movable.
+template <typename T>
+class atomic_base {
+ public:
+ typedef T ValueType;
+
+ // C++11 forbids a copy constructor for std::atomic<T>, it also forbids
+ // a move operation.
+ atomic_base() : value_() {}
+ explicit atomic_base(T v) : value_(v) {}
+
+ // Checks whether the atomic operations on all objects of this type
+ // are lock-free.
+ // Returns true if the atomic operations on the objects of this type
+ // are lock-free, false otherwise.
+ //
+ // All atomic types may be implemented using mutexes or other locking
+ // operations, rather than using the lock-free atomic CPU instructions.
+ // atomic types are also allowed to be sometimes lock-free, e.g. if only
+ // aligned memory accesses are naturally atomic on a given architecture,
+ // misaligned objects of the same type have to use locks.
+ //
+ // See also std::atomic<T>::is_lock_free().
+ bool is_lock_free() const { return false; }
+ bool is_lock_free() const volatile { return false; }
+
+ // Atomically replaces the value of the atomic object and returns the value
+ // held previously.
+ // See also std::atomic<T>::exchange().
+ T exchange(T new_val) {
+ T old_value;
+ {
+ starboard::ScopedLock lock(mutex_);
+ old_value = value_;
+ value_ = new_val;
+ }
+ return old_value;
+ }
+
+ // Atomically obtains the value of the atomic object.
+ // See also std::atomic<T>::load().
+ T load() const {
+ starboard::ScopedLock lock(mutex_);
+ return value_;
+ }
+
+ // Stores the value. See std::atomic<T>::store(...)
+ void store(T val) {
+ starboard::ScopedLock lock(mutex_);
+ value_ = val;
+ }
+
+ // compare_exchange_strong(...) sets the new value if and only if
+ // *expected_value matches what is stored internally.
+ // If this succeeds then true is returned and *expected_value == new_value.
+ // Otherwise If there is a mismatch then false is returned and
+ // *expected_value is set to the internal value.
+ // Inputs:
+ // new_value: Attempt to set the value to this new value.
+ // expected_value: A test condition for success. If the actual value
+ // matches the expected_value then the swap will succeed.
+ //
+ // See also std::atomic<T>::compare_exchange_strong(...).
+ bool compare_exchange_strong(T* expected_value, T new_value) {
+ // Save original value so that its local. This hints to the compiler
+ // that test_val doesn't have aliasing issues and should result in
+ // more optimal code inside of the lock.
+ const T test_val = *expected_value;
+ starboard::ScopedLock lock(mutex_);
+ if (test_val == value_) {
+ value_ = new_value;
+ return true;
+ } else {
+ *expected_value = value_;
+ return false;
+ }
+ }
+
+ // Weak version of this function is documented to be faster, but has allows
+ // weaker memory ordering and therefore will sometimes have a false negative:
+ // The value compared will actually be equal but the return value from this
+ // function indicates otherwise.
+ // By default, the function delegates to compare_exchange_strong(...).
+ //
+ // See also std::atomic<T>::compare_exchange_weak(...).
+ bool compare_exchange_weak(T* expected_value, T new_value) {
+ return compare_exchange_strong(expected_value, new_value);
+ }
+
+ protected:
+ T value_;
+ starboard::Mutex mutex_;
+};
+
+// A subclass of atomic_base<T> that adds fetch_add(...) and fetch_sub(...).
+template <typename T>
+class atomic_number : public atomic_base<T> {
+ public:
+ typedef atomic_base<T> Super;
+ typedef T ValueType;
+
+ atomic_number() : Super() {}
+ explicit atomic_number(T v) : Super(v) {}
+
+ // Returns the previous value before the input value was added.
+ // See also std::atomic<T>::fetch_add(...).
+ T fetch_add(T val) {
+ T old_val;
+ {
+ starboard::ScopedLock lock(this->mutex_);
+ old_val = this->value_;
+ this->value_ += val;
+ }
+ return old_val;
+ }
+
+ // Returns the value before the operation was applied.
+ // See also std::atomic<T>::fetch_sub(...).
+ T fetch_sub(T val) {
+ T old_val;
+ {
+ starboard::ScopedLock lock(this->mutex_);
+ old_val = this->value_;
+ this->value_ -= val;
+ }
+ return old_val;
+ }
+};
+
+// A subclass to classify Atomic Integers. Adds increment and decrement
+// functions.
+template <typename T>
+class atomic_integral : public atomic_number<T> {
+ public:
+ typedef atomic_number<T> Super;
+
+ atomic_integral() : Super() {}
+ explicit atomic_integral(T v) : Super(v) {}
+
+ T increment() { return this->fetch_add(T(1)); }
+ T decrement() { return this->fetch_sub(T(1)); }
+};
+
+// atomic_pointer class.
+template <typename T>
+class atomic_pointer : public atomic_base<T> {
+ public:
+ typedef atomic_base<T> Super;
+ atomic_pointer() : Super() {}
+ explicit atomic_pointer(T initial_val) : Super(initial_val) {}
+};
+
+// Simple atomic bool class.
+class atomic_bool {
+ public:
+ typedef bool ValueType;
+
+ // C++11 forbids a copy constructor for std::atomic<T>, it also forbids
+ // a move operation.
+ atomic_bool() : value_(false) {}
+ explicit atomic_bool(bool v) : value_(v) {}
+
+ bool is_lock_free() const { return true; }
+ bool is_lock_free() const volatile { return true; }
+
+ bool exchange(bool new_val) {
+ return SbAtomicNoBarrier_Exchange(volatile_ptr(), new_val);
+ }
+
+ bool load() const { return SbAtomicAcquire_Load(volatile_const_ptr()) != 0; }
+
+ void store(bool val) { SbAtomicRelease_Store(volatile_ptr(), val); }
+
+ private:
+ volatile int32_t* volatile_ptr() { return &value_; }
+ volatile const int32_t* volatile_const_ptr() const { return &value_; }
+ int32_t value_;
+};
+
+// Lockfree atomic int class.
+class atomic_int32_t {
+ public:
+ typedef int32_t ValueType;
+ atomic_int32_t() : value_(0) {}
+ explicit atomic_int32_t(SbAtomic32 value) : value_(value) {}
+
+ bool is_lock_free() const { return true; }
+ bool is_lock_free() const volatile { return true; }
+
+ int32_t increment() { return fetch_add(1); }
+ int32_t decrement() { return fetch_add(-1); }
+
+ int32_t fetch_add(int32_t val) {
+ // fetch_add is a post-increment operation, while SbAtomicBarrier_Increment
+ // is a pre-increment operation. Therefore subtract the value to match
+ // the expected interface.
+ return SbAtomicBarrier_Increment(volatile_ptr(), val) - val;
+ }
+
+ int32_t fetch_sub(int32_t val) { return fetch_add(-val); }
+
+ // Atomically replaces the value of the atomic object
+ // and returns the value held previously.
+ // See also std::atomic<T>::exchange().
+ int32_t exchange(int32_t new_val) {
+ return SbAtomicNoBarrier_Exchange(volatile_ptr(), new_val);
+ }
+
+ // Atomically obtains the value of the atomic object.
+ // See also std::atomic<T>::load().
+ int32_t load() const { return SbAtomicAcquire_Load(volatile_const_ptr()); }
+
+ // Stores the value. See std::atomic<T>::store(...)
+ void store(int32_t val) { SbAtomicRelease_Store(volatile_ptr(), val); }
+
+ bool compare_exchange_strong(int32_t* expected_value, int32_t new_value) {
+ int32_t prev_value = *expected_value;
+ SbAtomicMemoryBarrier();
+ int32_t value_written =
+ SbAtomicRelease_CompareAndSwap(volatile_ptr(), prev_value, new_value);
+ const bool write_ok = (prev_value == value_written);
+ if (!write_ok) {
+ *expected_value = value_written;
+ }
+ return write_ok;
+ }
+
+ // Weak version of this function is documented to be faster, but has allows
+ // weaker memory ordering and therefore will sometimes have a false negative:
+ // The value compared will actually be equal but the return value from this
+ // function indicates otherwise.
+ // By default, the function delegates to compare_exchange_strong(...).
+ //
+ // See also std::atomic<T>::compare_exchange_weak(...).
+ bool compare_exchange_weak(int32_t* expected_value, int32_t new_value) {
+ return compare_exchange_strong(expected_value, new_value);
+ }
+
+ private:
+ volatile int32_t* volatile_ptr() { return &value_; }
+ volatile const int32_t* volatile_const_ptr() const { return &value_; }
+ int32_t value_;
+};
+
+// Simple atomic int class. This could be optimized for speed using
+// compiler intrinsics for concurrent integer modification.
+class atomic_int64_t : public atomic_integral<int64_t> {
+ public:
+ typedef atomic_integral<int64_t> Super;
+ atomic_int64_t() : Super() {}
+ explicit atomic_int64_t(int64_t initial_val) : Super(initial_val) {}
+};
+
+class atomic_float : public atomic_number<float> {
+ public:
+ typedef atomic_number<float> Super;
+ atomic_float() : Super() {}
+ explicit atomic_float(float initial_val) : Super(initial_val) {}
+};
+
+class atomic_double : public atomic_number<double> {
+ public:
+ typedef atomic_number<double> Super;
+ atomic_double() : Super() {}
+ explicit atomic_double(double initial_val) : Super(initial_val) {}
+};
+
+} // namespace starboard
+
+#endif // __cplusplus
+
#endif // STARBOARD_ATOMIC_H_
diff --git a/src/starboard/build/default_no_deploy.gypi b/src/starboard/build/default_no_deploy.gypi
new file mode 100644
index 0000000..da58d6b
--- /dev/null
+++ b/src/starboard/build/default_no_deploy.gypi
@@ -0,0 +1,25 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file is meant to be included into a target to provide a rule
+# to deploy a target on a target platform.
+#
+# The platform_deploy target should be defined in
+# starboard/<port_path>/platform_deploy.gyp. This target should perform
+# any per-executable logic that is specific to the platform. For example,
+# copying per-executable metadata files to the output directory.
+
+{
+ # This space intentionally left blank.
+}
diff --git a/src/starboard/build/deploy.gypi b/src/starboard/build/deploy.gypi
index ce7c9ce..048591b 100644
--- a/src/starboard/build/deploy.gypi
+++ b/src/starboard/build/deploy.gypi
@@ -22,33 +22,48 @@
#
# To use this, create a gyp target with the following form:
# 'targets': [
-# {
-# 'target_name': 'target_deploy',
-# 'type': 'none',
-# 'dependencies': [
-# 'target',
-# ],
-# 'variables': {
-# 'executable_name': 'target',
-# },
-# 'includes': [
-# '../build/deploy.gypi',
-# ],
-# },
+# {
+# 'target_name': 'target_deploy',
+# 'type': 'none',
+# 'dependencies': [
+# 'target',
+# ],
+# 'variables': {
+# 'executable_name': 'target',
+# },
+# 'includes': [ '../build/deploy.gypi' ],
+# },
+# ...
#
+# For example:
+# 'targets': [
+# {
+# 'target_name': 'nplb',
+# 'type': '<(gtest_target_type)',
+# 'sources': [...]
+# }
+# {
+# 'target_name': 'nplb_deploy',
+# 'type': 'none',
+# 'dependencies': [
+# 'nplb',
+# ],
+# 'variables': {
+# 'executable_name': 'nplb',
+# },
+# 'includes': [ '../build/deploy.gypi' ],
+# },
{
+
# Flag that will instruct gyp to create a special target in IDEs such as
# Visual Studio that can be used for launching a target.
'variables' : {
'ide_deploy_target': 1,
},
- 'conditions': [
- ['OS=="starboard" and sb_has_deploy_step==1', {
- 'dependencies': [
- '<(DEPTH)/<(starboard_path)/platform_deploy.gyp:platform_deploy',
- ],
- }],
- ],
+ # Include the platform specific gypi file include. Note that the
+ # expanded value will default to
+ # "starboard/build/default_no_deploy.gypi"
+ 'includes': [ '<(DEPTH)/<(include_path_platform_deploy_gypi)' ],
}
diff --git a/src/starboard/common/flat_map.h b/src/starboard/common/flat_map.h
index a232b24..6d3a3a9 100644
--- a/src/starboard/common/flat_map.h
+++ b/src/starboard/common/flat_map.h
@@ -197,6 +197,8 @@
}
}
+ void erase(iterator it) { vector_.erase(it); }
+
void erase(iterator begin_it, iterator end_it) {
vector_.erase(begin_it, end_it);
}
diff --git a/src/starboard/common/locked_ptr.h b/src/starboard/common/locked_ptr.h
index 6f308a0..d1821c2 100644
--- a/src/starboard/common/locked_ptr.h
+++ b/src/starboard/common/locked_ptr.h
@@ -37,16 +37,16 @@
}
}
- ImplType* operator->() { return ptr_; }
+ ImplType* operator->() {
+ SB_DCHECK(ptr_);
+ return ptr_;
+ }
private:
friend class LockedPtr<Type>;
Impl(Mutex* mutex, ImplType* ptr) : mutex_(mutex), ptr_(ptr) {
SB_DCHECK(mutex);
- SB_DCHECK(ptr);
-
- mutex_->Acquire();
}
// The copy ctor transfers the ownership. So only the last one holds the
@@ -78,17 +78,24 @@
ptr_ = ptr.Pass();
}
+ void reset() {
+ starboard::ScopedLock scoped_lock(mutex_);
+ ptr_.reset();
+ }
+
bool is_valid() const {
starboard::ScopedLock scoped_lock(mutex_);
return ptr_ != NULL;
}
Impl<Type> operator->() {
+ mutex_.Acquire();
Impl<Type> impl(&mutex_, ptr_.get());
return impl;
}
Impl<const Type> operator->() const {
+ mutex_.Acquire();
Impl<const Type> impl(&mutex_, ptr_.get());
return impl;
}
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index dcfe06f..00dc565 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -39,19 +39,18 @@
// The maximum API version allowed by this version of the Starboard headers,
// inclusive.
-#define SB_MAXIMUM_API_VERSION 6
+#define SB_MAXIMUM_API_VERSION 7
// The API version that is currently open for changes, and therefore is not
// stable or frozen. Production-oriented ports should avoid declaring that they
// implement the experimental Starboard API version.
-#define SB_EXPERIMENTAL_API_VERSION 6
+#define SB_EXPERIMENTAL_API_VERSION 7
// The next API version to be frozen, but is still subject to emergency
// changes. It is reasonable to base a port on the Release Candidate API
// version, but be aware that small incompatible changes may still be made to
// it.
-// #undef SB_RELEASE_CANDIDATE_API_VERSION
-#define SB_RELEASE_CANDIDATE_API_VERSION 5
+#define SB_RELEASE_CANDIDATE_API_VERSION 6
// --- Experimental Feature Defines ------------------------------------------
@@ -65,29 +64,18 @@
// // exposes functionality for my new feature.
// #define SB_MY_EXPERIMENTAL_FEATURE_VERSION SB_EXPERIMENTAL_API_VERSION
-// Introduce pointer (mouse) input support. This extends the SbInput interface
-// with some enum values and data members to allow mouse, wheel, and more
-// generic pointer input.
-#define SB_POINTER_INPUT_API_VERSION SB_EXPERIMENTAL_API_VERSION
-
-// SbMediaAudioHeader::audio_specific_config will be a pointer instead of an
-// array.
-#define SB_AUDIO_SPECIFIC_CONFIG_AS_POINTER SB_EXPERIMENTAL_API_VERSION
-
-// Removes SbTimeZoneGetDstName() - Daylight saving time version of time zone.
-// Changes SbTimeZoneGetName() is more flexible now in what it is allowed to
-// return.
-#define SB_TIME_ZONE_FLEXIBLE_API_VERSION SB_EXPERIMENTAL_API_VERSION
-
-// Adds the convenience inline function, SbDecodeTargetNumberOfPlanesForFormat()
-// to starboard/decode_target.h.
-#define SB_DECODE_TARGET_PLANES_FOR_FORMAT SB_EXPERIMENTAL_API_VERSION
-
// --- Release Candidate Feature Defines -------------------------------------
-#define SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION \
+#define SB_POINTER_INPUT_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_AUDIO_SPECIFIC_CONFIG_AS_POINTER SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_TIME_ZONE_FLEXIBLE_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_DECODE_TARGET_PLANES_FOR_FORMAT SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_PRELOAD_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_PLATFORM_ERROR_CLEANUP_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_DECODE_TARGET_UYVY_SUPPORT_API_VERSION \
SB_RELEASE_CANDIDATE_API_VERSION
-#define SB_SPEECH_RECOGNIZER_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_COLOR_KEYCODES_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
+#define SB_LOW_MEMORY_EVENT_API_VERSION SB_RELEASE_CANDIDATE_API_VERSION
// --- Common Detected Features ----------------------------------------------
@@ -117,6 +105,7 @@
// Determines at compile-time if this platform implements a given Starboard API
// version number (or above).
+// This macro is deprecated, please instead use the expanded form directly.
#define SB_VERSION(SB_API) (SB_API_VERSION >= SB_API)
// A constant expression that evaluates to the size_t size of a statically-sized
@@ -450,11 +439,11 @@
#error "Your platform must define SB_MAX_THREAD_NAME_LENGTH."
#endif
-#if SB_VERSION(2) && !defined(SB_HAS_MICROPHONE)
+#if SB_API_VERSION >= 2 && !defined(SB_HAS_MICROPHONE)
#error "Your platform must define SB_HAS_MICROPHONE in API versions 2 or later."
#endif
-#if SB_VERSION(3) && !defined(SB_HAS_TIME_THREAD_NOW)
+#if SB_API_VERSION >= 3 && !defined(SB_HAS_TIME_THREAD_NOW)
#error "Your platform must define SB_HAS_TIME_THREAD_NOW in API 3 or later."
#endif
@@ -574,11 +563,11 @@
#endif // SB_API_VERSION >= 4
-#if SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_API_VERSION >= 5
#if !defined(SB_HAS_SPEECH_RECOGNIZER)
#error "Your platform must define SB_HAS_SPEECH_RECOGNIZER."
#endif // !defined(SB_HAS_SPEECH_RECOGNIZER)
-#endif // SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_API_VERSION >= 5
// --- Derived Configuration -------------------------------------------------
diff --git a/src/starboard/creator/shared/gyp_configuration.gypi b/src/starboard/creator/shared/gyp_configuration.gypi
index bdf65af..2d7e40d 100644
--- a/src/starboard/creator/shared/gyp_configuration.gypi
+++ b/src/starboard/creator/shared/gyp_configuration.gypi
@@ -133,7 +133,7 @@
'-Wno-literal-suffix',
],
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h
index afe2e46..e109b6a 100644
--- a/src/starboard/decode_target.h
+++ b/src/starboard/decode_target.h
@@ -142,6 +142,19 @@
// A decoder target format consisting of Y, U, and V planes, in that order.
kSbDecodeTargetFormat3PlaneYUVI420,
+#if SB_API_VERSION >= SB_DECODE_TARGET_UYVY_SUPPORT_API_VERSION
+ // A decoder target format consisting of a single plane with pixels layed out
+ // in the format UYVY. Since there are two Y values per sample, but only one
+ // U value and only one V value, horizontally the Y resolution is twice the
+ // size of both the U and V resolutions. Vertically, they Y, U and V all
+ // have the same resolution. This is a YUV 422 format. When using this
+ // format with GL platforms, it is expected that the underlying texture will
+ // be set to the GL_RGBA format, and the width of the texture will be equal to
+ // the number of UYVY tuples per row (e.g. the u/v width resolution).
+ // Content region left/right should be specified in u/v width resolution.
+ kSbDecodeTargetFormat1PlaneUYVY,
+#endif // SB_DECODE_TARGET_UYVY_SUPPORT_API_VERSION
+
// An invalid decode target format.
kSbDecodeTargetFormatInvalid,
} SbDecodeTargetFormat;
@@ -344,6 +357,9 @@
SbDecodeTargetFormat format) {
switch (format) {
case kSbDecodeTargetFormat1PlaneRGBA:
+#if SB_API_VERSION >= SB_DECODE_TARGET_UYVY_SUPPORT_API_VERSION
+ case kSbDecodeTargetFormat1PlaneUYVY:
+#endif // SB_API_VERSION >= SB_DECODE_TARGET_UYVY_SUPPORT_API_VERSION
return 1;
case kSbDecodeTargetFormat1PlaneBGRA:
return 1;
diff --git a/src/starboard/doc/howto_decode_to_texture.md b/src/starboard/doc/howto_decode_to_texture.md
index 093deb4..3bb46bc 100644
--- a/src/starboard/doc/howto_decode_to_texture.md
+++ b/src/starboard/doc/howto_decode_to_texture.md
@@ -27,7 +27,6 @@
From [`starboard/decode_target.h`](../decode_target.h),
-* `SbDecodeTargetCreate()`
* `SbDecodeTargetRelease()`
* `SbDecodeTargetGetInfo()`
diff --git a/src/starboard/event.h b/src/starboard/event.h
index 9ab88c7..e854b27 100644
--- a/src/starboard/event.h
+++ b/src/starboard/event.h
@@ -18,23 +18,48 @@
//
// ## The Starboard Application life cycle
//
-// | *
-// | | _________________________
-// | Start | |
-// | | | Resume
-// | V V |
-// | [ STARTED ] --Pause--> [ PAUSED ] --Suspend--> [ SUSPENDED ]
-// | ^ | |
-// | | Unpause Stop
-// | |_____________________| |
-// | V
-// | [ STOPPED ]
+// | ---------- *
+// | | |
+// | | Preload
+// | | |
+// | | V
+// | Start [ PRELOADING ] ------------
+// | | | |
+// | | Start |
+// | | | |
+// | | V |
+// | ----> [ STARTED ] <---- |
+// | | | |
+// | Pause Unpause |
+// | | | Suspend
+// | V | |
+// | -----> [ PAUSED ] ----- |
+// | | | |
+// | Resume Suspend |
+// | | | |
+// | | V |
+// | ---- [ SUSPENDED ] <------------
+// | |
+// | Stop
+// | |
+// | V
+// | [ STOPPED ]
//
-// The first event that a Starboard application receives is Start
-// (kSbEventTypeStart). This puts the application in the |STARTED| state.
-// The application is in the foreground and can expect to do all of the normal
-// things it might want to do. Once in the |STARTED| state, it may receive a
-// |Pause| event, putting the application into the |PAUSED| state.
+// The first event that a Starboard application receives is either |Start|
+// (kSbEventTypeStart) or |Preload| (kSbEventTypePreload). |Start| puts the
+// application application in the |STARTED| state, whereas |Preload| puts the
+// application in the |PRELOADING| state.
+//
+// |PRELOADING| can only happen as the first application state. In this state,
+// the application should start and run as normal, but will not receive any
+// input, and should not try to initialize graphics resources (via GL or
+// SbBlitter). In |PRELOADING|, the application can receive |Start| or |Suspend|
+// events. |Start| will receive the same data that was passed into |Preload|.
+//
+// In the |STARTED| state, the application is in the foreground and can expect
+// to do all of the normal things it might want to do. Once in the |STARTED|
+// state, it may receive a |Pause| event, putting the application into the
+// |PAUSED| state.
//
// In the |PAUSED| state, the application is still visible, but has lost
// focus, or it is partially obscured by a modal dialog, or it is on its way
@@ -70,11 +95,33 @@
// system. Each event is accompanied by a void* data argument, and each event
// must define the type of the value pointed to by that data argument, if any.
typedef enum SbEventType {
- // The first event that an application receives on startup. Applications
- // should perform initialization and prepare to react to subsequent events.
- // Applications that wish to run and then exit must call SbSystemRequestStop()
- // to terminate. This event will only be sent once for a given process launch.
+#if SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ // Applications should perform initialization and prepare to react to
+ // subsequent events, but must not initialize any graphics resources (through
+ // GL or SbBlitter). The intent of this event is to allow the application to
+ // do as much work as possible ahead of time, so that when the application is
+ // first brought to the foreground, it's as fast as a resume.
+ //
+ // The |kSbEventTypeStart| event may be sent at any time, regardless of
+ // initialization state. Input events will not be sent in the |PRELOADING|
+ // state. This event will only be sent once for a given process launch.
// SbEventStartData is passed as the data argument.
+ //
+ // The system may send |kSbEventTypeSuspend| in |PRELOADING| if it wants to
+ // push the app into a lower resource consumption state. Applications can alo
+ // call SbSystemRequestSuspend() when they are done preloading to request
+ // this.
+ kSbEventTypePreload,
+#endif // SB_API_VERSION >= SB_PRELOAD_API_VERSION
+
+ // The first event that an application receives on startup when starting
+ // normally (i.e. not being preloaded). Applications should perform
+ // initialization, start running, and prepare to react to subsequent
+ // events. Applications that wish to run and then exit must call
+ // |SbSystemRequestStop()| to terminate. This event will only be sent once for
+ // a given process launch. |SbEventStartData| is passed as the data
+ // argument. In case of preload, the |SbEventStartData| will be the same as
+ // what was passed to |kSbEventTypePreload|.
kSbEventTypeStart,
// A dialog will be raised or the application will otherwise be put into a
@@ -158,6 +205,15 @@
// new settings.
kSbEventTypeAccessiblitySettingsChanged,
#endif
+
+#if SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION
+ // An optional event that platforms may send to indicate that the application
+ // may soon be terminated (or crash) due to low memory availability. The
+ // application may respond by reducing memory consumption by running a Garbage
+ // Collection, flushing caches, or something similar. There is no requirement
+ // to respond to or handle this event, it is only advisory.
+ kSbEventTypeLowMemory,
+#endif // SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION
} SbEventType;
// Structure representing a Starboard event and its data.
diff --git a/src/starboard/examples/window/main.cc b/src/starboard/examples/window/main.cc
index fb47cdb..c944564 100644
--- a/src/starboard/examples/window/main.cc
+++ b/src/starboard/examples/window/main.cc
@@ -32,9 +32,18 @@
void SbEventHandle(const SbEvent* event) {
switch (event->type) {
+#if SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ case kSbEventTypePreload: {
+ SB_LOG(INFO) << "PRELOAD";
+ SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
+ SB_DCHECK(data);
+ break;
+ }
+#endif
case kSbEventTypeStart: {
SB_LOG(INFO) << "START";
SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
+ SB_DCHECK(data);
g_window = SbWindowCreate(NULL);
SB_CHECK(SbWindowIsValid(g_window));
diff --git a/src/starboard/image.h b/src/starboard/image.h
index 82d93be..721067c 100644
--- a/src/starboard/image.h
+++ b/src/starboard/image.h
@@ -52,7 +52,7 @@
#include "starboard/export.h"
#include "starboard/types.h"
-#if SB_VERSION(3)
+#if SB_API_VERSION >= 3
#ifdef __cplusplus
extern "C" {
@@ -102,6 +102,6 @@
} // extern "C"
#endif
-#endif // SB_VERSION(3)
+#endif // SB_API_VERSION >= 3
#endif // STARBOARD_IMAGE_H_
diff --git a/src/starboard/key.h b/src/starboard/key.h
index 8191acd..e44b05f 100644
--- a/src/starboard/key.h
+++ b/src/starboard/key.h
@@ -202,12 +202,20 @@
kSbKeyPlay = 0xFA,
// Beyond this point are non-Windows key codes that are provided as
- // extensions, as they otherwise have no analogs
+ // extensions, as they otherwise have no analogs.
// Some media keys that are used around the industry.
kSbKeyMediaRewind = 0xE3,
kSbKeyMediaFastForward = 0xE4,
+#if SB_API_VESRION >= SB_COLOR_KEYCODES_API_VERSION
+ // The colored keys found on most contemporary TV remotes.
+ kSbKeyRed = 0x193,
+ kSbKeyGreen = 0x194,
+ kSbKeyYellow = 0x195,
+ kSbKeyBlue = 0x196,
+#endif // SB_API_VESRION >= SB_COLOR_KEYCODES_API_VERSION
+
// Mouse buttons, starting with the left mouse button.
kSbKeyMouse1 = 0x7000,
kSbKeyMouse2 = 0x7001,
diff --git a/src/starboard/linux/shared/compiler_flags.gypi b/src/starboard/linux/shared/compiler_flags.gypi
index 762dfa7..2223b30 100644
--- a/src/starboard/linux/shared/compiler_flags.gypi
+++ b/src/starboard/linux/shared/compiler_flags.gypi
@@ -108,7 +108,7 @@
'-std=gnu++11',
],
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index b7c9e2f..6688b18 100644
--- a/src/starboard/linux/shared/configuration_public.h
+++ b/src/starboard/linux/shared/configuration_public.h
@@ -24,7 +24,7 @@
#define STARBOARD_LINUX_SHARED_CONFIGURATION_PUBLIC_H_
#ifndef SB_API_VERSION
-#define SB_API_VERSION 4
+#define SB_API_VERSION 6
#endif
// --- System Header Configuration -------------------------------------------
diff --git a/src/starboard/linux/shared/decode_target_get_info.cc b/src/starboard/linux/shared/decode_target_get_info.cc
new file mode 100644
index 0000000..c63f587
--- /dev/null
+++ b/src/starboard/linux/shared/decode_target_get_info.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/decode_target.h"
+#include "starboard/linux/shared/decode_target_internal.h"
+#include "starboard/memory.h"
+
+// TODO: Consider unifying this with that of raspi/open_max and android, since
+// the only part that changes is the info struct size.
+bool SbDecodeTargetGetInfo(SbDecodeTarget decode_target,
+ SbDecodeTargetInfo* out_info) {
+ if (!SbMemoryIsZero(out_info, sizeof(*out_info))) {
+ SB_DCHECK(false) << "out_info must be zeroed out.";
+ return false;
+ }
+
+ SbMemoryCopy(out_info, &decode_target->data->info, sizeof(*out_info));
+
+ return true;
+}
diff --git a/src/starboard/linux/shared/decode_target_internal.cc b/src/starboard/linux/shared/decode_target_internal.cc
new file mode 100644
index 0000000..1b2613b
--- /dev/null
+++ b/src/starboard/linux/shared/decode_target_internal.cc
@@ -0,0 +1,157 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/linux/shared/decode_target_internal.h"
+
+#if SB_HAS(GLES2)
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include "starboard/shared/gles/gl_call.h"
+#endif
+
+#include "starboard/decode_target.h"
+
+SbDecodeTargetPrivate::Data::~Data() {
+#if SB_HAS(GLES2)
+ glDeleteTextures(1, &info.planes[0].texture);
+ SB_DCHECK(glGetError() == GL_NO_ERROR);
+#endif
+}
+
+namespace starboard {
+namespace shared {
+
+namespace {
+
+struct CreateParams {
+ SbDecodeTarget decode_target_out;
+ scoped_refptr<starboard::player::VideoFrame> frame;
+};
+
+#if SB_HAS(GLES2)
+void CreateWithContextRunner(void* context) {
+ CreateParams* params = static_cast<CreateParams*>(context);
+
+ SB_DCHECK(params->frame);
+ SB_DCHECK(params->frame->format() == starboard::player::VideoFrame::kYV12);
+ static const SbDecodeTargetFormat format = kSbDecodeTargetFormat3PlaneYUVI420;
+ static const int plane_count = 3;
+
+ if (!SbDecodeTargetIsValid(params->decode_target_out)) {
+ params->decode_target_out = new SbDecodeTargetPrivate;
+ params->decode_target_out->data = new SbDecodeTargetPrivate::Data;
+ params->decode_target_out->data->info.format = format;
+
+ GLuint textures[3];
+ GL_CALL(glGenTextures(plane_count, textures));
+ for (int plane_index = 0; plane_index < plane_count; plane_index++) {
+ params->decode_target_out->data->info.is_opaque = true;
+
+ SbDecodeTargetInfoPlane& plane =
+ params->decode_target_out->data->info.planes[plane_index];
+ plane.texture = textures[plane_index];
+ plane.gl_texture_target = GL_TEXTURE_2D;
+ plane.content_region.left = 0.0f;
+ plane.content_region.top = 0.0f;
+ plane.width = 0;
+ plane.height = 0;
+ }
+ }
+
+ SbDecodeTargetInfo& target_info = params->decode_target_out->data->info;
+
+ for (int plane_index = 0; plane_index < plane_count; plane_index++) {
+ const starboard::player::VideoFrame::Plane& video_frame_plane =
+ params->frame->GetPlane(plane_index);
+
+ GL_CALL(glBindTexture(
+ GL_TEXTURE_2D,
+ params->decode_target_out->data->info.planes[plane_index].texture));
+ GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT,
+ video_frame_plane.pitch_in_bytes));
+ if (target_info.planes[plane_index].width == video_frame_plane.width &&
+ target_info.planes[plane_index].height == video_frame_plane.height) {
+ // No need to reallocate texture object, only update pixels.
+ GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_frame_plane.width,
+ video_frame_plane.height, GL_ALPHA,
+ GL_UNSIGNED_BYTE, video_frame_plane.data));
+
+ } else {
+ // As part of texture initialization, explicitly specify all parameters.
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ GL_CALL(
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GL_CALL(
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+
+ GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, video_frame_plane.width,
+ video_frame_plane.height, 0, GL_ALPHA,
+ GL_UNSIGNED_BYTE, video_frame_plane.data));
+ }
+
+ GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0));
+ GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
+
+ // Set sizes and regions.
+ target_info.planes[plane_index].content_region.right =
+ static_cast<float>(video_frame_plane.width);
+ target_info.planes[plane_index].content_region.bottom =
+ static_cast<float>(video_frame_plane.height);
+ target_info.planes[plane_index].width = video_frame_plane.width;
+ target_info.planes[plane_index].height = video_frame_plane.height;
+ }
+
+ target_info.width = params->frame->width();
+ target_info.height = params->frame->height();
+}
+#endif
+
+} // namespace
+
+SbDecodeTarget DecodeTargetCreate(
+ SbDecodeTargetGraphicsContextProvider* provider,
+ scoped_refptr<starboard::player::VideoFrame> frame,
+ SbDecodeTarget decode_target) {
+ CreateParams params;
+ params.decode_target_out = decode_target;
+ params.frame = frame;
+
+#if SB_HAS(GLES2)
+ SbDecodeTargetRunInGlesContext(provider, &CreateWithContextRunner, ¶ms);
+#endif
+
+ return params.decode_target_out;
+}
+
+void DecodeTargetRelease(SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider,
+ SbDecodeTarget decode_target) {
+#if SB_HAS(GLES2)
+ SbDecodeTargetReleaseInGlesContext(decode_target_graphics_context_provider,
+ decode_target);
+#endif
+}
+
+SbDecodeTarget DecodeTargetCopy(SbDecodeTarget decode_target) {
+ SbDecodeTarget out_decode_target = new SbDecodeTargetPrivate;
+ out_decode_target->data = decode_target->data;
+
+ return out_decode_target;
+}
+
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/linux/shared/decode_target_internal.h b/src/starboard/linux/shared/decode_target_internal.h
new file mode 100644
index 0000000..2b087a3
--- /dev/null
+++ b/src/starboard/linux/shared/decode_target_internal.h
@@ -0,0 +1,56 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_LINUX_SHARED_DECODE_TARGET_INTERNAL_H_
+#define STARBOARD_LINUX_SHARED_DECODE_TARGET_INTERNAL_H_
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/decode_target.h"
+#include "starboard/shared/starboard/player/video_frame_internal.h"
+
+struct SbDecodeTargetPrivate {
+ class Data : public starboard::RefCounted<Data> {
+ public:
+ Data() {}
+
+ SbDecodeTargetInfo info;
+
+ private:
+ friend class starboard::RefCounted<Data>;
+ ~Data();
+ };
+
+ starboard::scoped_refptr<Data> data;
+};
+
+namespace starboard {
+namespace shared {
+
+// Outputs a video frame into a SbDecodeTarget.
+SbDecodeTarget DecodeTargetCreate(
+ SbDecodeTargetGraphicsContextProvider* provider,
+ scoped_refptr<starboard::player::VideoFrame> frame,
+ // Possibly valid structure to reuse, instead of allocating a new object.
+ SbDecodeTarget decode_target);
+
+void DecodeTargetRelease(SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider,
+ SbDecodeTarget decode_target);
+
+SbDecodeTarget DecodeTargetCopy(SbDecodeTarget decode_target);
+
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_LINUX_SHARED_DECODE_TARGET_INTERNAL_H_
diff --git a/src/starboard/linux/shared/decode_target_release.cc b/src/starboard/linux/shared/decode_target_release.cc
new file mode 100644
index 0000000..52c6f74
--- /dev/null
+++ b/src/starboard/linux/shared/decode_target_release.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/decode_target.h"
+#include "starboard/linux/shared/decode_target_internal.h"
+
+void SbDecodeTargetRelease(SbDecodeTarget decode_target) {
+ // Most of the actual data within |decode_target| is stored in the reference
+ // counted decode_target->data, so deleting |decode_target| here may not
+ // actually release any resources, if there are other references to
+ // decode_target->data.
+ delete decode_target;
+}
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index 9ac2033..b20fee6 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -16,6 +16,10 @@
'starboard_platform_sources': [
'<(DEPTH)/starboard/linux/shared/atomic_public.h',
'<(DEPTH)/starboard/linux/shared/configuration_public.h',
+ '<(DEPTH)/starboard/linux/shared/decode_target_get_info.cc',
+ '<(DEPTH)/starboard/linux/shared/decode_target_internal.cc',
+ '<(DEPTH)/starboard/linux/shared/decode_target_internal.h',
+ '<(DEPTH)/starboard/linux/shared/decode_target_release.cc',
'<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc',
'<(DEPTH)/starboard/linux/shared/system_get_device_type.cc',
'<(DEPTH)/starboard/linux/shared/system_get_path.cc',
@@ -32,6 +36,8 @@
'<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc',
@@ -227,9 +233,14 @@
'<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
@@ -238,6 +249,8 @@
'<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
@@ -280,8 +293,6 @@
'<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
'<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
'<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
'<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
'<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
diff --git a/src/starboard/linux/x64x11/clang/3.3/compiler_flags.gypi b/src/starboard/linux/x64x11/clang/3.3/compiler_flags.gypi
index a6629b0..22d5331 100644
--- a/src/starboard/linux/x64x11/clang/3.3/compiler_flags.gypi
+++ b/src/starboard/linux/x64x11/clang/3.3/compiler_flags.gypi
@@ -109,7 +109,7 @@
'-std=gnu++98',
],
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi b/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi
index 5cde2f7..d700cb9 100644
--- a/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi
+++ b/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi
@@ -126,7 +126,7 @@
'-std=gnu++11',
],
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/linux/x64x11/cpp11/compiler_flags.gypi b/src/starboard/linux/x64x11/cpp11/compiler_flags.gypi
index a59e9c6..56dae63 100644
--- a/src/starboard/linux/x64x11/cpp11/compiler_flags.gypi
+++ b/src/starboard/linux/x64x11/cpp11/compiler_flags.gypi
@@ -105,7 +105,7 @@
'-Wno-inline-new-delete',
],
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/linux/x64x11/directgles/gyp_configuration.gypi b/src/starboard/linux/x64x11/directgles/gyp_configuration.gypi
index 66e6d55..3cfea95 100644
--- a/src/starboard/linux/x64x11/directgles/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/directgles/gyp_configuration.gypi
@@ -17,8 +17,8 @@
# Use the direct-to-GLES rasterizer.
# This rasterizer falls back to the hardware skia rasterizer in certain
# situations.
- # NOTE: This rasterizer requires a 16-bit depth buffer and a full frame
- # scratch surface (without depth buffer).
+ # NOTE: This rasterizer allocates offscreen render targets upfront,
+ # including a full screen scratch surface.
'rasterizer_type': 'direct-gles',
# Accommodate the direct-to-GLES rasterizer's additional memory overhead
@@ -31,12 +31,17 @@
# target atlases. One will be used as a cache and the other used as a
# working scratch. It is recommended to allot enough memory for two
# atlases that are roughly a quarter of the framebuffer. (The render
- # target atlases use power-of-2 dimenions.)
+ # target atlases use power-of-2 dimenions.) If the target web app frequently
+ # uses effects which require offscreen targets, then more memory should be
+ # reserved for optimal performance.
'surface_cache_size_in_bytes': 2 * (1024 * 512 * 4),
# The rasterizer does not benefit much from rendering only the dirty
# region. Disable this option since it costs GPU time.
'render_dirty_region_only': 0,
+
+ # Map to mesh is not yet supported with the DirectGLES renderer.
+ 'enable_map_to_mesh': 0,
},
'target_defaults': {
'default_configuration': 'linux-x64x11-directgles_debug',
diff --git a/src/starboard/linux/x64x11/gcc/4.2/compiler_flags.gypi b/src/starboard/linux/x64x11/gcc/4.2/compiler_flags.gypi
index cbc3aeb..49a5317 100644
--- a/src/starboard/linux/x64x11/gcc/4.2/compiler_flags.gypi
+++ b/src/starboard/linux/x64x11/gcc/4.2/compiler_flags.gypi
@@ -100,7 +100,7 @@
'-Wno-deprecated',
],
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/linux/x64x11/gcc/4.4/compiler_flags.gypi b/src/starboard/linux/x64x11/gcc/4.4/compiler_flags.gypi
index 7af753c..7c5b9c9 100644
--- a/src/starboard/linux/x64x11/gcc/4.4/compiler_flags.gypi
+++ b/src/starboard/linux/x64x11/gcc/4.4/compiler_flags.gypi
@@ -96,7 +96,7 @@
'-Wno-deprecated',
],
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/linux/x64x11/gcc/6.3/compiler_flags.gypi b/src/starboard/linux/x64x11/gcc/6.3/compiler_flags.gypi
index e0c0bce..fd25c54 100644
--- a/src/starboard/linux/x64x11/gcc/6.3/compiler_flags.gypi
+++ b/src/starboard/linux/x64x11/gcc/6.3/compiler_flags.gypi
@@ -101,7 +101,7 @@
'-Wno-deprecated',
],
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/linux/x64x11/gyp_configuration.gypi b/src/starboard/linux/x64x11/gyp_configuration.gypi
index 948e2b7..0521206 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/gyp_configuration.gypi
@@ -18,6 +18,7 @@
# there for acceptable values for this variable.
'javascript_engine': 'mozjs-45',
'cobalt_enable_jit': 0,
+ 'enable_map_to_mesh%': 1,
},
'target_defaults': {
'default_configuration': 'linux-x64x11_debug',
diff --git a/src/starboard/linux/x64x11/mock/gyp_configuration.gypi b/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
index 24a0f14..659c47d 100644
--- a/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/mock/gyp_configuration.gypi
@@ -145,7 +145,7 @@
},
}, # end of configurations
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/linux/x64x11/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/starboard_platform_tests.gyp
index 0250125..461529f 100644
--- a/src/starboard/linux/x64x11/starboard_platform_tests.gyp
+++ b/src/starboard/linux/x64x11/starboard_platform_tests.gyp
@@ -16,9 +16,18 @@
{
'target_name': 'starboard_platform_tests',
'type': '<(gtest_target_type)',
+ 'includes': [
+ '<(DEPTH)/starboard/shared/starboard/media/media_tests.gypi',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/filter_tests.gypi',
+ ],
'sources': [
'<(DEPTH)/starboard/common/test_main.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_type_test.cc',
+ '<@(filter_tests_sources)',
+ '<@(media_tests_sources)',
+ ],
+ 'defines': [
+ # This allows the tests to include internal only header files.
+ 'STARBOARD_IMPLEMENTATION',
],
'dependencies': [
'<(DEPTH)/starboard/starboard.gyp:starboard',
diff --git a/src/starboard/microphone.h b/src/starboard/microphone.h
index 52464c3..ae6a02f 100644
--- a/src/starboard/microphone.h
+++ b/src/starboard/microphone.h
@@ -43,7 +43,7 @@
#include "starboard/export.h"
#include "starboard/types.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#ifdef __cplusplus
extern "C" {
@@ -194,6 +194,6 @@
} // extern "C"
#endif
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#endif // STARBOARD_MICROPHONE_H_
diff --git a/src/starboard/nplb/audio_sink_create_test.cc b/src/starboard/nplb/audio_sink_create_test.cc
index d4702cf..590ada8 100644
--- a/src/starboard/nplb/audio_sink_create_test.cc
+++ b/src/starboard/nplb/audio_sink_create_test.cc
@@ -28,7 +28,13 @@
int* offset_in_frames,
bool* is_playing,
bool* is_eos_reached,
- void* context) {}
+ void* context) {
+ *frames_in_buffer = 0;
+ *offset_in_frames = 0;
+ *is_playing = false;
+ *is_eos_reached = false;
+}
+
void ConsumeFramesFuncStub(int frames_consumed, void* context) {}
} // namespace
diff --git a/src/starboard/nplb/flat_map_test.cc b/src/starboard/nplb/flat_map_test.cc
index fd1d5c1..b5817f4 100644
--- a/src/starboard/nplb/flat_map_test.cc
+++ b/src/starboard/nplb/flat_map_test.cc
@@ -89,7 +89,7 @@
}
SbTimeMonotonic GetThreadTimeMonotonicNow() {
-#if SB_VERSION(3) && SB_HAS(TIME_THREAD_NOW)
+#if SB_API_VERSION >= 3 && SB_HAS(TIME_THREAD_NOW)
return SbTimeGetMonotonicThreadNow();
#else
return SbTimeGetMonotonicNow();
diff --git a/src/starboard/nplb/microphone_close_test.cc b/src/starboard/nplb/microphone_close_test.cc
index 662eb0a..b18508a 100644
--- a/src/starboard/nplb/microphone_close_test.cc
+++ b/src/starboard/nplb/microphone_close_test.cc
@@ -19,7 +19,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
TEST(SbMicrophoneCloseTest, SunnyDayCloseAreCalledMultipleTimes) {
SbMicrophoneInfo info_array[kMaxNumberOfMicrophone];
@@ -73,7 +73,7 @@
EXPECT_FALSE(success);
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/microphone_create_test.cc b/src/starboard/nplb/microphone_create_test.cc
index 9ce1042..4fbd015 100644
--- a/src/starboard/nplb/microphone_create_test.cc
+++ b/src/starboard/nplb/microphone_create_test.cc
@@ -19,7 +19,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
TEST(SbMicrophoneCreateTest, SunnyDayOnlyOneMicrophone) {
SbMicrophoneInfo info_array[kMaxNumberOfMicrophone];
@@ -177,7 +177,7 @@
}
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/microphone_destroy_test.cc b/src/starboard/nplb/microphone_destroy_test.cc
index 707a3f1..8f7a69e 100644
--- a/src/starboard/nplb/microphone_destroy_test.cc
+++ b/src/starboard/nplb/microphone_destroy_test.cc
@@ -18,13 +18,13 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
TEST(SbMicrophoneDestroyTest, DestroyInvalidMicrophone) {
SbMicrophoneDestroy(kSbMicrophoneInvalid);
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/microphone_get_available_test.cc b/src/starboard/nplb/microphone_get_available_test.cc
index 232995b..5324be0 100644
--- a/src/starboard/nplb/microphone_get_available_test.cc
+++ b/src/starboard/nplb/microphone_get_available_test.cc
@@ -19,7 +19,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
TEST(SbMicrophoneGetAvailableTest, SunnyDay) {
SbMicrophoneInfo info_array[kMaxNumberOfMicrophone];
@@ -53,7 +53,7 @@
}
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/microphone_helpers.h b/src/starboard/nplb/microphone_helpers.h
index 06d4c37..b413f02 100644
--- a/src/starboard/nplb/microphone_helpers.h
+++ b/src/starboard/nplb/microphone_helpers.h
@@ -17,7 +17,7 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
namespace starboard {
namespace nplb {
@@ -27,6 +27,6 @@
} // namespace nplb
} // namespace starboard
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#endif // STARBOARD_NPLB_MICROPHONE_HELPERS_H_
diff --git a/src/starboard/nplb/microphone_is_sample_rate_supported_test.cc b/src/starboard/nplb/microphone_is_sample_rate_supported_test.cc
index 1016b8d..7b86992 100644
--- a/src/starboard/nplb/microphone_is_sample_rate_supported_test.cc
+++ b/src/starboard/nplb/microphone_is_sample_rate_supported_test.cc
@@ -19,7 +19,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
TEST(SbMicrophoneIsSampleRateSupportedTest, SunnyDay) {
SbMicrophoneInfo info_array[kMaxNumberOfMicrophone];
@@ -49,7 +49,7 @@
kNormallyUsedSampleRateInHz));
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/microphone_open_test.cc b/src/starboard/nplb/microphone_open_test.cc
index 2853457..273f081 100644
--- a/src/starboard/nplb/microphone_open_test.cc
+++ b/src/starboard/nplb/microphone_open_test.cc
@@ -19,7 +19,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
TEST(SbMicrophoneOpenTest, SunnyDay) {
SbMicrophoneInfo info_array[kMaxNumberOfMicrophone];
@@ -92,7 +92,7 @@
EXPECT_FALSE(success);
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/microphone_read_test.cc b/src/starboard/nplb/microphone_read_test.cc
index 7df0260..805f035 100644
--- a/src/starboard/nplb/microphone_read_test.cc
+++ b/src/starboard/nplb/microphone_read_test.cc
@@ -20,7 +20,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
TEST(SbMicrophoneReadTest, SunnyDay) {
SbMicrophoneInfo info_array[kMaxNumberOfMicrophone];
@@ -235,7 +235,7 @@
EXPECT_LT(read_bytes, 0);
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 174b062..ef605f5 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -18,6 +18,7 @@
{
'targets': [
{
+ 'msvs_disabled_warnings': [4100, 4189, 4456],
'target_name': 'nplb',
'type': '<(gtest_target_type)',
'sources': [
diff --git a/src/starboard/nplb/speech_recognizer_cancel_test.cc b/src/starboard/nplb/speech_recognizer_cancel_test.cc
index 471275e..883f2e0 100644
--- a/src/starboard/nplb/speech_recognizer_cancel_test.cc
+++ b/src/starboard/nplb/speech_recognizer_cancel_test.cc
@@ -20,8 +20,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
TEST_F(SpeechRecognizerTest, CancelTestSunnyDay) {
SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler());
@@ -66,8 +65,7 @@
SbSpeechRecognizerCancel(kSbSpeechRecognizerInvalid);
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/speech_recognizer_create_test.cc b/src/starboard/nplb/speech_recognizer_create_test.cc
index ddbc93f..7abdd53 100644
--- a/src/starboard/nplb/speech_recognizer_create_test.cc
+++ b/src/starboard/nplb/speech_recognizer_create_test.cc
@@ -19,8 +19,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
TEST_F(SpeechRecognizerTest, CreateTestSunnyDay) {
SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler());
@@ -28,8 +27,7 @@
SbSpeechRecognizerDestroy(recognizer);
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/speech_recognizer_destroy_test.cc b/src/starboard/nplb/speech_recognizer_destroy_test.cc
index ffd14de..e240d5b 100644
--- a/src/starboard/nplb/speech_recognizer_destroy_test.cc
+++ b/src/starboard/nplb/speech_recognizer_destroy_test.cc
@@ -19,8 +19,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
TEST_F(SpeechRecognizerTest, DestroyInvalidSpeechRecognizer) {
SbSpeechRecognizerDestroy(kSbSpeechRecognizerInvalid);
@@ -34,8 +33,7 @@
SbSpeechRecognizerDestroy(recognizer);
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/speech_recognizer_helper.h b/src/starboard/nplb/speech_recognizer_helper.h
index 727a993..242053b 100644
--- a/src/starboard/nplb/speech_recognizer_helper.h
+++ b/src/starboard/nplb/speech_recognizer_helper.h
@@ -25,8 +25,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
class EventMock : public RefCounted<EventMock> {
public:
@@ -94,8 +93,7 @@
const scoped_refptr<EventMock> test_mock_;
};
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/speech_recognizer_start_test.cc b/src/starboard/nplb/speech_recognizer_start_test.cc
index 65fb03d..4515379 100644
--- a/src/starboard/nplb/speech_recognizer_start_test.cc
+++ b/src/starboard/nplb/speech_recognizer_start_test.cc
@@ -19,8 +19,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
TEST_F(SpeechRecognizerTest, StartTestSunnyDay) {
SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler());
@@ -112,8 +111,7 @@
EXPECT_FALSE(success);
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/speech_recognizer_stop_test.cc b/src/starboard/nplb/speech_recognizer_stop_test.cc
index 0663a50..7aeb105 100644
--- a/src/starboard/nplb/speech_recognizer_stop_test.cc
+++ b/src/starboard/nplb/speech_recognizer_stop_test.cc
@@ -19,8 +19,7 @@
namespace starboard {
namespace nplb {
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
TEST_F(SpeechRecognizerTest, StopIsCalledMultipleTimes) {
SbSpeechRecognizer recognizer = SbSpeechRecognizerCreate(handler());
@@ -38,8 +37,7 @@
SbSpeechRecognizerDestroy(recognizer);
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/speech_synthesis_basic_test.cc b/src/starboard/nplb/speech_synthesis_basic_test.cc
index d2e67c7..7dd636f 100644
--- a/src/starboard/nplb/speech_synthesis_basic_test.cc
+++ b/src/starboard/nplb/speech_synthesis_basic_test.cc
@@ -15,11 +15,11 @@
#include "starboard/speech_synthesis.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
+#if SB_HAS(SPEECH_SYNTHESIS) && SB_API_VERSION >= 3
TEST(SbSpeechSynthesisBasicTest, Basic) {
SbSpeechSynthesisSpeak("Hello");
SbSpeechSynthesisCancel();
}
-#endif // SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
+#endif // SB_HAS(SPEECH_SYNTHESIS) && SB_API_VERSION >= 3
diff --git a/src/starboard/nplb/system_get_property_test.cc b/src/starboard/nplb/system_get_property_test.cc
index e27b2ce..cebf148 100644
--- a/src/starboard/nplb/system_get_property_test.cc
+++ b/src/starboard/nplb/system_get_property_test.cc
@@ -70,9 +70,9 @@
BasicTest(kSbSystemPropertyChipsetModelNumber, false, true, __LINE__);
BasicTest(kSbSystemPropertyFirmwareVersion, false, true, __LINE__);
BasicTest(kSbSystemPropertyNetworkOperatorName, false, true, __LINE__);
-#if SB_VERSION(2)
+#if SB_API_VERSION >= 2
BasicTest(kSbSystemPropertySpeechApiKey, false, true, __LINE__);
-#endif // SB_VERSION(2)
+#endif // SB_API_VERSION >= 2
if (IsCEDevice(SbSystemGetDeviceType())) {
BasicTest(kSbSystemPropertyBrandName, true, true, __LINE__);
diff --git a/src/starboard/raspi/2/directgles/gyp_configuration.gypi b/src/starboard/raspi/2/directgles/gyp_configuration.gypi
index fbac27e..98d9129 100644
--- a/src/starboard/raspi/2/directgles/gyp_configuration.gypi
+++ b/src/starboard/raspi/2/directgles/gyp_configuration.gypi
@@ -17,8 +17,8 @@
# Use the direct-to-GLES rasterizer.
# This rasterizer falls back to the hardware skia rasterizer in certain
# situations.
- # NOTE: This rasterizer requires a 16-bit depth buffer and a full frame
- # scratch surface (without depth buffer).
+ # NOTE: This rasterizer allocates offscreen render targets upfront,
+ # including a full screen scratch surface.
'rasterizer_type': 'direct-gles',
# Accommodate the direct-to-GLES rasterizer's additional memory overhead
@@ -31,7 +31,9 @@
# target atlases. One will be used as a cache and the other used as a
# working scratch. It is recommended to allot enough memory for two
# atlases that are roughly a quarter of the framebuffer. (The render
- # target atlases use power-of-2 dimenions.)
+ # target atlases use power-of-2 dimenions.) If the target web app frequently
+ # uses effects which require offscreen targets, then more memory should be
+ # reserved for optimal performance.
'surface_cache_size_in_bytes': 2 * (1024 * 512 * 4),
# The rasterizer does not benefit much from rendering only the dirty
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index f89c9a5..dc0b878 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -18,7 +18,7 @@
#define STARBOARD_RASPI_SHARED_CONFIGURATION_PUBLIC_H_
// The API version implemented by this platform.
-#define SB_API_VERSION 4
+#define SB_API_VERSION 6
// --- System Header Configuration -------------------------------------------
diff --git a/src/starboard/raspi/shared/gyp_configuration.gypi b/src/starboard/raspi/shared/gyp_configuration.gypi
index 96dca33..1e1f4cf 100644
--- a/src/starboard/raspi/shared/gyp_configuration.gypi
+++ b/src/starboard/raspi/shared/gyp_configuration.gypi
@@ -30,7 +30,7 @@
# This should have a default value in cobalt/base.gypi. See the comment
# there for acceptable values for this variable.
- 'javascript_engine': 'mozjs',
+ 'javascript_engine': 'mozjs-45',
'cobalt_enable_jit': 0,
'cobalt_media_source_2016': 1,
@@ -161,7 +161,7 @@
'-Wno-literal-suffix',
],
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/raspi/shared/player_components_impl.cc b/src/starboard/raspi/shared/player_components_impl.cc
index 5644922..4d5c678 100644
--- a/src/starboard/raspi/shared/player_components_impl.cc
+++ b/src/starboard/raspi/shared/player_components_impl.cc
@@ -43,8 +43,7 @@
video_parameters.video_codec, video_parameters.job_queue);
AudioRendererImpl* audio_renderer =
- new AudioRendererImpl(audio_parameters.job_queue,
- scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
+ new AudioRendererImpl(scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
audio_parameters.audio_header);
VideoRendererImpl* video_renderer = new VideoRendererImpl(
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index dd195e1..1170664 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -81,6 +81,8 @@
'<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h',
'<(DEPTH)/starboard/shared/gcc/atomic_gcc.h',
@@ -275,9 +277,14 @@
'<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/player_components.h',
@@ -285,6 +292,8 @@
'<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
diff --git a/src/starboard/shared/directfb/application_directfb.h b/src/starboard/shared/directfb/application_directfb.h
index 49feff7..65f96c2 100644
--- a/src/starboard/shared/directfb/application_directfb.h
+++ b/src/starboard/shared/directfb/application_directfb.h
@@ -44,6 +44,11 @@
IDirectFB* GetDirectFB();
SbWindow GetWindow();
+#if SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ bool IsStartImmediate() SB_OVERRIDE { return !HasPreloadSwitch(); }
+ bool IsPreloadImmediate() SB_OVERRIDE { return HasPreloadSwitch(); }
+#endif // SB_API_VERSION >= SB_PRELOAD_API_VERSION
+
protected:
// --- Application overrides ---
void Initialize() SB_OVERRIDE;
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
index 8951eaf..16bc1c5 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -17,6 +17,7 @@
#include "starboard/audio_sink.h"
#include "starboard/log.h"
#include "starboard/memory.h"
+#include "starboard/shared/starboard/media/media_util.h"
namespace starboard {
namespace shared {
@@ -31,39 +32,6 @@
return kSbMediaAudioSampleTypeInt16;
}
-// The required output format and the output of the ffmpeg decoder can be
-// different. In this case libavresample is used to convert the ffmpeg output
-// into the required format.
-void ConvertSamples(int source_sample_format,
- int target_sample_format,
- int channel_layout,
- int sample_rate,
- int samples_per_channel,
- uint8_t** input_buffer,
- uint8_t* output_buffer) {
- AVAudioResampleContext* context = avresample_alloc_context();
- SB_DCHECK(context != NULL);
-
- av_opt_set_int(context, "in_channel_layout", channel_layout, 0);
- av_opt_set_int(context, "out_channel_layout", channel_layout, 0);
- av_opt_set_int(context, "in_sample_rate", sample_rate, 0);
- av_opt_set_int(context, "out_sample_rate", sample_rate, 0);
- av_opt_set_int(context, "in_sample_fmt", source_sample_format, 0);
- av_opt_set_int(context, "out_sample_fmt", target_sample_format, 0);
- av_opt_set_int(context, "internal_sample_fmt", source_sample_format, 0);
-
- int result = avresample_open(context);
- SB_DCHECK(!result);
-
- int samples_resampled =
- avresample_convert(context, &output_buffer, 1024, samples_per_channel,
- input_buffer, 0, samples_per_channel);
- SB_DCHECK(samples_resampled == samples_per_channel);
-
- avresample_close(context);
- av_free(context);
-}
-
AVCodecID GetFfmpegCodecIdByMediaCodec(SbMediaAudioCodec audio_codec) {
switch (audio_codec) {
case kSbMediaAudioCodecAac:
@@ -80,7 +48,6 @@
AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
const SbMediaAudioHeader& audio_header)
: audio_codec_(audio_codec),
- sample_type_(GetSupportedSampleType()),
codec_context_(NULL),
av_frame_(NULL),
stream_ended_(false),
@@ -95,9 +62,22 @@
TeardownCodec();
}
-void AudioDecoder::Decode(const InputBuffer& input_buffer) {
+void AudioDecoder::Initialize(const Closure& output_cb) {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(output_cb.is_valid());
+ SB_DCHECK(!output_cb_.is_valid());
+
+ output_cb_ = output_cb;
+}
+
+void AudioDecoder::Decode(const InputBuffer& input_buffer,
+ const Closure& consumed_cb) {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(output_cb_.is_valid());
SB_CHECK(codec_context_ != NULL);
+ Schedule(consumed_cb);
+
if (stream_ended_) {
SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
return;
@@ -127,20 +107,24 @@
if (decoded_audio_size > 0) {
scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
+ codec_context_->channels, GetSampleType(), GetStorageType(),
input_buffer.pts(),
codec_context_->channels * av_frame_->nb_samples *
- (sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4));
- if (codec_context_->sample_fmt == codec_context_->request_sample_fmt) {
+ starboard::media::GetBytesPerSample(GetSampleType()));
+ if (GetStorageType() == kSbMediaAudioFrameStorageTypeInterleaved) {
SbMemoryCopy(decoded_audio->buffer(), *av_frame_->extended_data,
decoded_audio->size());
} else {
- ConvertSamples(codec_context_->sample_fmt,
- codec_context_->request_sample_fmt,
- codec_context_->channel_layout,
- audio_header_.samples_per_second, av_frame_->nb_samples,
- av_frame_->extended_data, decoded_audio->buffer());
+ SB_DCHECK(GetStorageType() == kSbMediaAudioFrameStorageTypePlanar);
+ const int per_channel_size_in_bytes =
+ decoded_audio->size() / decoded_audio->channels();
+ for (int i = 0; i < decoded_audio->channels(); ++i) {
+ SbMemoryCopy(decoded_audio->buffer() + per_channel_size_in_bytes * i,
+ av_frame_->extended_data[i], per_channel_size_in_bytes);
+ }
}
decoded_audios_.push(decoded_audio);
+ Schedule(output_cb_);
} else {
// TODO: Consider fill it with silence.
SB_LOG(ERROR) << "Decoded audio frame is empty.";
@@ -148,14 +132,23 @@
}
void AudioDecoder::WriteEndOfStream() {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(output_cb_.is_valid());
+
// AAC has no dependent frames so we needn't flush the decoder. Set the flag
// to ensure that Decode() is not called when the stream is ended.
stream_ended_ = true;
// Put EOS into the queue.
decoded_audios_.push(new DecodedAudio);
+
+ Schedule(output_cb_);
}
scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read() {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(output_cb_.is_valid());
+ SB_DCHECK(!decoded_audios_.empty());
+
scoped_refptr<DecodedAudio> result;
if (!decoded_audios_.empty()) {
result = decoded_audios_.front();
@@ -165,17 +158,51 @@
}
void AudioDecoder::Reset() {
+ SB_DCHECK(BelongsToCurrentThread());
+
stream_ended_ = false;
while (!decoded_audios_.empty()) {
decoded_audios_.pop();
}
+
+ CancelPendingJobs();
}
SbMediaAudioSampleType AudioDecoder::GetSampleType() const {
- return sample_type_;
+ SB_DCHECK(BelongsToCurrentThread());
+
+ if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 ||
+ codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) {
+ return kSbMediaAudioSampleTypeInt16;
+ } else if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT ||
+ codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) {
+ return kSbMediaAudioSampleTypeFloat32;
+ }
+
+ SB_NOTREACHED();
+
+ return kSbMediaAudioSampleTypeFloat32;
+}
+
+SbMediaAudioFrameStorageType AudioDecoder::GetStorageType() const {
+ SB_DCHECK(BelongsToCurrentThread());
+
+ if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 ||
+ codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) {
+ return kSbMediaAudioFrameStorageTypeInterleaved;
+ }
+ if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P ||
+ codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) {
+ return kSbMediaAudioFrameStorageTypePlanar;
+ }
+
+ SB_NOTREACHED();
+ return kSbMediaAudioFrameStorageTypeInterleaved;
}
int AudioDecoder::GetSamplesPerSecond() const {
+ SB_DCHECK(BelongsToCurrentThread());
+
return audio_header_.samples_per_second;
}
@@ -192,7 +219,7 @@
codec_context_->codec_type = AVMEDIA_TYPE_AUDIO;
codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(audio_codec_);
// Request_sample_fmt is set by us, but sample_fmt is set by the decoder.
- if (sample_type_ == kSbMediaAudioSampleTypeInt16) {
+ if (GetSupportedSampleType() == kSbMediaAudioSampleTypeInt16) {
codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
} else {
codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT;
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
index 893d2a0..40bd9c8 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -20,31 +20,31 @@
#include "starboard/media.h"
#include "starboard/shared/ffmpeg/ffmpeg_common.h"
#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/closure.h"
#include "starboard/shared/starboard/player/decoded_audio_internal.h"
#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
namespace starboard {
namespace shared {
namespace ffmpeg {
-class AudioDecoder : public starboard::player::filter::AudioDecoder {
+class AudioDecoder : public starboard::player::filter::AudioDecoder,
+ private starboard::player::JobQueue::JobOwner {
public:
- typedef starboard::player::DecodedAudio DecodedAudio;
- typedef starboard::player::InputBuffer InputBuffer;
-
AudioDecoder(SbMediaAudioCodec audio_codec,
const SbMediaAudioHeader& audio_header);
~AudioDecoder() SB_OVERRIDE;
- void Decode(const InputBuffer& input_buffer) SB_OVERRIDE;
+ void Initialize(const Closure& output_cb) SB_OVERRIDE;
+ void Decode(const InputBuffer& input_buffer,
+ const Closure& consumed_cb) SB_OVERRIDE;
void WriteEndOfStream() SB_OVERRIDE;
scoped_refptr<DecodedAudio> Read() SB_OVERRIDE;
void Reset() SB_OVERRIDE;
SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE;
+ SbMediaAudioFrameStorageType GetStorageType() const SB_OVERRIDE;
int GetSamplesPerSecond() const SB_OVERRIDE;
- bool CanAcceptMoreData() const SB_OVERRIDE {
- return !stream_ended_ && decoded_audios_.size() <= kMaxDecodedAudiosSize;
- }
bool is_valid() const { return codec_context_ != NULL; }
@@ -54,8 +54,8 @@
static const int kMaxDecodedAudiosSize = 64;
+ Closure output_cb_;
SbMediaAudioCodec audio_codec_;
- SbMediaAudioSampleType sample_type_;
AVCodecContext* codec_context_;
AVFrame* av_frame_;
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc
new file mode 100644
index 0000000..3892b04
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc
@@ -0,0 +1,226 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/ffmpeg/ffmpeg_audio_resampler.h"
+
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/starboard/media/media_util.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+namespace {
+
+const int kMaxChannels = 8;
+const int kMaxCachedSamples = 65536;
+
+int GetChannelLayoutFromChannels(int channels) {
+ if (channels == 1) {
+ return AV_CH_LAYOUT_MONO;
+ }
+ if (channels == 2) {
+ return AV_CH_LAYOUT_STEREO;
+ }
+ if (channels == 6) {
+ return AV_CH_LAYOUT_5POINT1;
+ }
+ if (channels == 8) {
+ return AV_CH_LAYOUT_7POINT1;
+ }
+ SB_NOTREACHED() << "Unsupported channel count: " << channels;
+ return AV_CH_LAYOUT_STEREO;
+}
+
+int GetSampleFormatBySampleTypeAndStorageType(
+ SbMediaAudioSampleType sample_type,
+ SbMediaAudioFrameStorageType storage_type) {
+ if (sample_type == kSbMediaAudioSampleTypeInt16 &&
+ storage_type == kSbMediaAudioFrameStorageTypeInterleaved) {
+ return AV_SAMPLE_FMT_S16;
+ }
+ if (sample_type == kSbMediaAudioSampleTypeFloat32 &&
+ storage_type == kSbMediaAudioFrameStorageTypeInterleaved) {
+ return AV_SAMPLE_FMT_FLT;
+ }
+ if (sample_type == kSbMediaAudioSampleTypeInt16 &&
+ storage_type == kSbMediaAudioFrameStorageTypePlanar) {
+ return AV_SAMPLE_FMT_S16P;
+ }
+ if (sample_type == kSbMediaAudioSampleTypeFloat32 &&
+ storage_type == kSbMediaAudioFrameStorageTypePlanar) {
+ return AV_SAMPLE_FMT_FLTP;
+ }
+ SB_NOTREACHED() << "Unsupported sample type (" << sample_type << ") and "
+ << " storage type (" << storage_type << ") combination";
+ return AV_SAMPLE_FMT_FLT;
+}
+
+} // namespace
+
+AudioResampler::AudioResampler(
+ SbMediaAudioSampleType source_sample_type,
+ SbMediaAudioFrameStorageType source_storage_type,
+ int source_sample_rate,
+ SbMediaAudioSampleType destination_sample_type,
+ SbMediaAudioFrameStorageType destination_storage_type,
+ int destination_sample_rate,
+ int channels)
+ : source_sample_type_(source_sample_type),
+ source_storage_type_(source_storage_type),
+ destination_sample_type_(destination_sample_type),
+ destination_storage_type_(destination_storage_type),
+ destination_sample_rate_(destination_sample_rate),
+ channels_(channels),
+ start_pts_(-1),
+ samples_returned_(0),
+ eos_reached_(false) {
+ SB_DCHECK(channels_ <= kMaxChannels);
+
+ context_ = avresample_alloc_context();
+ SB_DCHECK(context_ != NULL);
+
+ av_opt_set_int(context_, "in_channel_layout",
+ GetChannelLayoutFromChannels(channels_), 0);
+ av_opt_set_int(context_, "out_channel_layout",
+ GetChannelLayoutFromChannels(channels_), 0);
+ av_opt_set_int(context_, "in_sample_rate", source_sample_rate, 0);
+ av_opt_set_int(context_, "out_sample_rate", destination_sample_rate, 0);
+ av_opt_set_int(context_, "in_sample_fmt",
+ GetSampleFormatBySampleTypeAndStorageType(source_sample_type,
+ source_storage_type),
+ 0);
+ av_opt_set_int(context_, "out_sample_fmt",
+ GetSampleFormatBySampleTypeAndStorageType(
+ destination_sample_type, destination_storage_type),
+ 0);
+ av_opt_set_int(context_, "internal_sample_fmt", AV_SAMPLE_FMT_S16P, 0);
+
+ int result = avresample_open(context_);
+ SB_DCHECK(!result);
+}
+
+AudioResampler::~AudioResampler() {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+ avresample_close(context_);
+ av_free(context_);
+}
+
+scoped_refptr<AudioResampler::DecodedAudio> AudioResampler::Resample(
+ const scoped_refptr<DecodedAudio>& audio_data) {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(audio_data);
+ SB_DCHECK(audio_data->sample_type() == source_sample_type_);
+ SB_DCHECK(audio_data->storage_type() == source_storage_type_);
+ SB_DCHECK(audio_data->channels() == channels_);
+ SB_DCHECK(!eos_reached_);
+
+ if (channels_ > kMaxChannels) {
+ return new DecodedAudio;
+ }
+
+ if (eos_reached_) {
+ return new DecodedAudio;
+ }
+
+ if (start_pts_ < 0) {
+ start_pts_ = audio_data->pts();
+ }
+
+ uint8_t* input_buffers[kMaxChannels] = {
+ const_cast<uint8_t*>(audio_data->buffer())};
+ if (source_storage_type_ == kSbMediaAudioFrameStorageTypePlanar) {
+ for (int i = 1; i < channels_; ++i) {
+ input_buffers[i] = const_cast<uint8_t*>(
+ audio_data->buffer() + audio_data->size() / channels_ * i);
+ }
+ }
+
+ int result = avresample_convert(context_, NULL, 0, 0, input_buffers, 0,
+ audio_data->frames());
+ SB_DCHECK(result == 0);
+
+ return RetrieveOutput();
+}
+
+scoped_refptr<AudioResampler::DecodedAudio> AudioResampler::WriteEndOfStream() {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(!eos_reached_);
+
+ eos_reached_ = true;
+ int result = avresample_convert(context_, NULL, 0, 0, NULL, 0, 0);
+ SB_DCHECK(result == 0);
+
+ return RetrieveOutput();
+}
+
+scoped_refptr<AudioResampler::DecodedAudio> AudioResampler::RetrieveOutput() {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+
+ int frames_in_buffer = avresample_available(context_);
+ SbMediaTime pts = start_pts_ + samples_returned_ * kSbMediaTimeSecond /
+ destination_sample_rate_;
+ int bytes_per_sample =
+ starboard::media::GetBytesPerSample(destination_sample_type_);
+ scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
+ channels_, destination_sample_type_, destination_storage_type_, pts,
+ frames_in_buffer * bytes_per_sample * channels_);
+ samples_returned_ += frames_in_buffer;
+
+ if (frames_in_buffer > 0) {
+ uint8_t* output_buffers[kMaxChannels] = {
+ const_cast<uint8_t*>(decoded_audio->buffer())};
+ if (destination_storage_type_ == kSbMediaAudioFrameStorageTypePlanar) {
+ for (int i = 1; i < channels_; ++i) {
+ output_buffers[i] = const_cast<uint8_t*>(
+ decoded_audio->buffer() + decoded_audio->size() / channels_ * i);
+ }
+ }
+
+ int frames_read =
+ avresample_read(context_, output_buffers, frames_in_buffer);
+ SB_DCHECK(frames_read == frames_in_buffer);
+ }
+
+ return decoded_audio;
+}
+
+} // namespace ffmpeg
+
+namespace starboard {
+namespace player {
+namespace filter {
+
+// static
+scoped_ptr<AudioResampler> AudioResampler::Create(
+ SbMediaAudioSampleType source_sample_type,
+ SbMediaAudioFrameStorageType source_storage_type,
+ int source_sample_rate,
+ SbMediaAudioSampleType destination_sample_type,
+ SbMediaAudioFrameStorageType destination_storage_type,
+ int destination_sample_rate,
+ int channels) {
+ return scoped_ptr<AudioResampler>(new ffmpeg::AudioResampler(
+ source_sample_type, source_storage_type, source_sample_rate,
+ destination_sample_type, destination_storage_type,
+ destination_sample_rate, channels));
+}
+
+} // namespace filter
+} // namespace player
+} // namespace starboard
+
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h
new file mode 100644
index 0000000..5ebb3e8
--- /dev/null
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h
@@ -0,0 +1,67 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_RESAMPLER_H_
+#define STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_RESAMPLER_H_
+
+#include <queue>
+
+#include "starboard/media.h"
+#include "starboard/shared/ffmpeg/ffmpeg_common.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_resampler.h"
+#include "starboard/shared/starboard/thread_checker.h"
+
+namespace starboard {
+namespace shared {
+namespace ffmpeg {
+
+class AudioResampler : public starboard::player::filter::AudioResampler {
+ public:
+ AudioResampler(SbMediaAudioSampleType source_sample_type,
+ SbMediaAudioFrameStorageType source_storage_type,
+ int source_sample_rate,
+ SbMediaAudioSampleType destination_sample_type,
+ SbMediaAudioFrameStorageType destination_storage_type,
+ int destination_sample_rate,
+ int channels);
+ ~AudioResampler() SB_OVERRIDE;
+
+ scoped_refptr<DecodedAudio> Resample(
+ const scoped_refptr<DecodedAudio>& audio_data) SB_OVERRIDE;
+ scoped_refptr<DecodedAudio> WriteEndOfStream() SB_OVERRIDE;
+
+ private:
+ scoped_refptr<DecodedAudio> RetrieveOutput();
+
+ shared::starboard::ThreadChecker thread_checker_;
+
+ SbMediaAudioSampleType source_sample_type_;
+ SbMediaAudioFrameStorageType source_storage_type_;
+ SbMediaAudioSampleType destination_sample_type_;
+ SbMediaAudioFrameStorageType destination_storage_type_;
+ int destination_sample_rate_; // Used for timestamp adjustment.
+ int channels_;
+ SbMediaTime start_pts_;
+ int samples_returned_;
+ bool eos_reached_;
+ AVAudioResampleContext* context_;
+};
+
+} // namespace ffmpeg
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_RESAMPLER_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
index 2d4e659..111d974 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -14,7 +14,10 @@
#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h"
+#include "starboard/linux/shared/decode_target_internal.h"
+
#include "starboard/memory.h"
+#include "starboard/thread.h"
namespace starboard {
namespace shared {
@@ -93,14 +96,21 @@
} // namespace
-VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec)
+VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec,
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider)
: video_codec_(video_codec),
host_(NULL),
codec_context_(NULL),
av_frame_(NULL),
stream_ended_(false),
error_occured_(false),
- decoder_thread_(kSbThreadInvalid) {
+ decoder_thread_(kSbThreadInvalid),
+ output_mode_(output_mode),
+ decode_target_graphics_context_provider_(
+ decode_target_graphics_context_provider),
+ decode_target_(kSbDecodeTargetInvalid) {
InitializeCodec();
}
@@ -156,6 +166,11 @@
decoder_thread_ = kSbThreadInvalid;
stream_ended_ = false;
+
+ if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
+ TeardownCodec();
+ InitializeCodec();
+ }
}
// static
@@ -227,6 +242,27 @@
codec_context_->reordered_opaque, av_frame_->data[0], av_frame_->data[1],
av_frame_->data[2]);
host_->OnDecoderStatusUpdate(kBufferFull, frame);
+
+ if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
+ return UpdateDecodeTarget(frame);
+ }
+
+ return true;
+}
+
+bool VideoDecoder::UpdateDecodeTarget(const scoped_refptr<VideoFrame>& frame) {
+ SbDecodeTarget decode_target = DecodeTargetCreate(
+ decode_target_graphics_context_provider_, frame, decode_target_);
+
+ // Lock only after the post to the renderer thread, to prevent deadlock.
+ ScopedLock lock(decode_target_mutex_);
+ decode_target_ = decode_target;
+
+ if (!SbDecodeTargetIsValid(decode_target)) {
+ SB_LOG(ERROR) << "Could not acquire a decode target from provider.";
+ return false;
+ }
+
return true;
}
@@ -289,6 +325,31 @@
av_free(av_frame_);
av_frame_ = NULL;
}
+
+ if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
+ ScopedLock lock(decode_target_mutex_);
+ if (SbDecodeTargetIsValid(decode_target_)) {
+ DecodeTargetRelease(decode_target_graphics_context_provider_,
+ decode_target_);
+ decode_target_ = kSbDecodeTargetInvalid;
+ }
+ }
+}
+
+// When in decode-to-texture mode, this returns the current decoded video frame.
+SbDecodeTarget VideoDecoder::GetCurrentDecodeTarget() {
+ SB_DCHECK(output_mode_ == kSbPlayerOutputModeDecodeToTexture);
+
+ // We must take a lock here since this function can be called from a
+ // separate thread.
+ ScopedLock lock(decode_target_mutex_);
+ if (SbDecodeTargetIsValid(decode_target_)) {
+ // Make a disposable copy, since the state is internally reused by this
+ // class (to avoid recreating GL objects).
+ return DecodeTargetCopy(decode_target_);
+ } else {
+ return kSbDecodeTargetInvalid;
+ }
}
} // namespace ffmpeg
@@ -305,7 +366,12 @@
SB_UNREFERENCED_PARAMETER(codec);
SB_UNREFERENCED_PARAMETER(drm_system);
- return output_mode == kSbPlayerOutputModePunchOut;
+ if (output_mode == kSbPlayerOutputModePunchOut ||
+ output_mode == kSbPlayerOutputModeDecodeToTexture) {
+ return true;
+ }
+
+ return false;
}
#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
index 03639ed..3daf19e 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
@@ -34,7 +34,10 @@
typedef starboard::player::InputBuffer InputBuffer;
typedef starboard::player::VideoFrame VideoFrame;
- explicit VideoDecoder(SbMediaVideoCodec video_codec);
+ VideoDecoder(SbMediaVideoCodec video_codec,
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider);
~VideoDecoder() SB_OVERRIDE;
void SetHost(Host* host) SB_OVERRIDE;
@@ -71,6 +74,9 @@
bool DecodePacket(AVPacket* packet);
void InitializeCodec();
void TeardownCodec();
+ SbDecodeTarget GetCurrentDecodeTarget() SB_OVERRIDE;
+
+ bool UpdateDecodeTarget(const scoped_refptr<VideoFrame>& frame);
// These variables will be initialized inside ctor or SetHost() and will not
// be changed during the life time of this class.
@@ -89,6 +95,26 @@
// Working thread to avoid lengthy decoding work block the player thread.
SbThread decoder_thread_;
+
+ // Decode-to-texture related state.
+ SbPlayerOutputMode output_mode_;
+
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider_;
+
+ // If decode-to-texture is enabled, then we store the decode target texture
+ // inside of this |decode_target_| member.
+ SbDecodeTarget decode_target_;
+
+ // GetCurrentDecodeTarget() needs to be called from an arbitrary thread
+ // to obtain the current decode target (which ultimately ends up being a
+ // copy of |decode_target_|), we need to safe-guard access to |decode_target_|
+ // and we do so through this mutex.
+ Mutex decode_target_mutex_;
+ // Mutex frame_mutex_;
+
+ // int frame_last_rendered_pts_;
+ // scoped_refptr<VideoFrame> frame_;
};
} // namespace ffmpeg
diff --git a/src/starboard/shared/msvc/uwp/toolchain.py b/src/starboard/shared/msvc/uwp/toolchain.py
new file mode 100644
index 0000000..e0d9d3d
--- /dev/null
+++ b/src/starboard/shared/msvc/uwp/toolchain.py
@@ -0,0 +1,931 @@
+# Copyright 2017 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""This module implements starboard's abstract toolchain for MSVC UWP."""
+
+from difflib import get_close_matches
+import os
+import re
+import subprocess
+import sys
+
+_COBALT_SRC = os.path.abspath(os.path.join(*([__file__] + 5 * [os.pardir])))
+sys.path.append(os.path.join(_COBALT_SRC, 'starboard', 'tools'))
+
+import gyp.MSVSVersion # pylint: disable=g-import-not-at-top
+# This tool_chain is starboard/build/toolchain.py
+from starboard.tools.toolchain import CompilerSettings
+from starboard.tools.toolchain import PrecompiledHeader
+from starboard.tools.toolchain import Toolchain
+
+def _LanguageMatchesForPch(source_ext, pch_source_ext):
+ c_exts = ('.c',)
+ cc_exts = ('.cc', '.cxx', '.cpp')
+ return ((source_ext in c_exts and pch_source_ext in c_exts) or
+ (source_ext in cc_exts and pch_source_ext in cc_exts))
+
+
+class MSVCPrecompiledHeader(PrecompiledHeader):
+ """MSVC implementation of PrecompiledHeader interface."""
+
+ def __init__(self, **kwargs):
+ self.settings = kwargs['settings']
+ self.config = kwargs['config']
+ gyp_path_to_ninja = kwargs['gyp_path_to_ninja']
+ gyp_path_to_unique_output = kwargs['gyp_path_to_unique_output']
+ obj_ext = kwargs['obj_ext']
+ pch_source = self.settings.msvs_precompiled_source[self.config]
+ self.pch_source = gyp_path_to_ninja(pch_source)
+ filename, _ = os.path.splitext(pch_source)
+ self.output_obj = gyp_path_to_unique_output(filename + obj_ext).lower()
+
+ def GetFlagsModifications(self, input_flags, output, implicit, command,
+ cflags_c, cflags_cc, expand_special):
+ """Get the modified cflags and implicit dependencies."""
+ if self.settings.UsesComponentExtensions(self.config):
+ # No-op if component extensions are used.
+ return [], output, []
+
+ if input_flags == self.pch_source:
+ pch_output = ['/Yc' + self._PchHeader()]
+ if command == 'cxx':
+ return ([('cflags_cc', map(expand_special, cflags_cc + pch_output))],
+ self.output_obj, [])
+ elif command == 'cc':
+ return ([('cflags_c', map(expand_special, cflags_c + pch_output))],
+ self.output_obj, [])
+ return [], output, implicit
+
+ def GetInclude(self):
+ pass
+
+ def GetPchBuildCommands(self):
+ """Not used on Windows as there are no additional build steps required
+ (instead, existing steps are modified in GetFlagsModifications below)."""
+ return []
+
+ def _PchHeader(self):
+ """Get the header that will appear in an #include line for all source
+ files."""
+ return os.path.split(self.settings.msvs_precompiled_header[self.config])[1]
+
+ def GetObjDependencies(self, sources, output):
+ """Given a list of sources files and the corresponding object files,
+ returns a list of the pch files that should be depended upon. The
+ additional wrapping in the return value is for interface compatability
+ with make.py on Mac, and xcode_emulation.py."""
+ if not self._PchHeader():
+ return []
+ pch_ext = os.path.splitext(self.pch_source)[1]
+ for source in sources:
+ if _LanguageMatchesForPch(os.path.splitext(source)[1], pch_ext):
+ return [(None, None, self.output_obj)]
+ return []
+
+
+vs_version = None
+
+
+def GetVSVersion(generator_flags):
+ global vs_version
+ if not vs_version:
+ vs_version = gyp.MSVSVersion.SelectVisualStudioVersion(
+ generator_flags.get('msvs_version', 'auto'))
+ return vs_version
+
+
+def _GenericRetrieve(root, default, path):
+ """Given a list of dictionary keys |path| and a tree of dicts |root|, find
+ value at path, or return |default| if any of the path doesn't exist."""
+ if not root:
+ return default
+ if not path:
+ return root
+ return _GenericRetrieve(root.get(path[0]), default, path[1:])
+
+
+def _AddPrefix(element, prefix):
+ """Add |prefix| to |element| or each subelement if element is iterable."""
+ if element is None:
+ return element
+ # Note, not Iterable because we don't want to handle strings like that.
+ if isinstance(element, list) or isinstance(element, tuple):
+ return [prefix + e for e in element]
+ else:
+ return prefix + element
+
+
+def _CallMap(map, element):
+ e = map(element)
+ if e is None:
+ raise Exception('Invalid element %s' % element)
+ return e
+
+
+def _DoRemapping(element, map):
+ """If |element| then remap it through |map|. If |element| is iterable then
+ each item will be remapped. Any elements not found will be removed."""
+ if map is not None and element is not None:
+ if not callable(map):
+ map = map.get # Assume it's a dict, otherwise a callable to do the remap.
+ if isinstance(element, list) or isinstance(element, tuple):
+ element = [_CallMap(map, elem) for elem in element]
+ else:
+ element = _CallMap(map, element)
+
+ return element
+
+
+def _AppendOrReturn(append, element):
+ """If |append| is None, simply return |element|. If |append| is not None,
+ then add |element| to it, adding each item in |element| if it's a list or
+ tuple."""
+ if append is not None and element is not None:
+ if isinstance(element, list) or isinstance(element, tuple):
+ append.extend(element)
+ else:
+ append.append(element)
+ else:
+ return element
+
+
+class MsvsSettings(CompilerSettings):
+ """A class that understands the gyp 'msvs_...' values (especially the
+ msvs_settings field). They largely correpond to the VS2008 IDE DOM. This
+ class helps map those settings to command line options."""
+
+ def __init__(self, spec, generator_flags):
+ self.spec = spec
+ self.vs_version = GetVSVersion(generator_flags)
+ # Try to find an installation location for the Windows DDK by checking
+ # the WDK_DIR environment variable, may be None.
+ self.wdk_dir = os.environ.get('WDK_DIR')
+
+ supported_fields = [
+ ('msvs_configuration_attributes', dict),
+ ('msvs_settings', dict),
+ ('msvs_system_include_dirs', list),
+ ('msvs_disabled_warnings', list),
+ ('msvs_precompiled_header', str),
+ ('msvs_precompiled_source', str),
+ ('msvs_configuration_platform', str),
+ ('msvs_target_platform', str),
+ ]
+ validators = {
+ 'msvs_settings': self._SettingsValidator,
+ }
+ configs = spec['configurations']
+ for field, default in supported_fields:
+ setattr(self, field, {})
+ for configname, config in configs.iteritems():
+ getattr(self, field)[configname] = config.get(field, default())
+ if field in validators:
+ validators[field](configname)
+
+ self.msvs_cygwin_dirs = spec.get('msvs_cygwin_dirs', ['.'])
+
+ def GetVSMacroEnv(self, base_to_build=None, config=None):
+ """Get a dict of variables mapping internal VS macro names to their gyp
+ equivalents."""
+ vs_path = self.vs_version.Path()
+ # Absolute paths in cygwin the path will start with /cygdrive/c/
+ # The MS compiler tools need
+ # TODO: this is getting generated from the vs install path
+ # need to pass this in, or fix earlier in generation
+ if sys.platform == 'cygwin':
+ vs_path = cygpath.to_nt(vs_path)
+
+ target_platform = 'Win32' if self.GetArch(config) == 'x86' else 'x64'
+
+ replacements = {
+ '$(VSInstallDir)': vs_path,
+ '$(VCInstallDir)': os.path.join(vs_path, 'VC') + '\\',
+ '$(OutDir)\\': base_to_build + '\\' if base_to_build else '',
+ '$(IntDir)': '$!INTERMEDIATE_DIR',
+ '$(InputPath)': '${source}',
+ '$(InputName)': '${root}',
+ '$(ProjectName)': self.spec['target_name'],
+ '$(PlatformName)': target_platform,
+ '$(ProjectDir)\\': '',
+ }
+
+ replacements['$(WDK_DIR)'] = self.wdk_dir if self.wdk_dir else ''
+ return replacements
+
+ def ConvertVSMacros(self, s, base_to_build=None, config=None):
+ """Convert from VS macro names to something equivalent."""
+ env = self.GetVSMacroEnv(base_to_build, config=config)
+ return ToolchainImpl().ExpandEnvVars(s, env)
+
+ def ProcessLibraries(self, libraries):
+ """Strip -l from library if it's specified with that."""
+ return [lib[2:] if lib.startswith('-l') else lib for lib in libraries]
+
+ def _GetAndMunge(self, field, path, default, prefix, append, map):
+ """Retrieve a value from |field| at |path| or return |default|. If
+ |append| is specified, and the item is found, it will be appended to that
+ object instead of returned. If |map| is specified, results will be
+ remapped through |map| before being returned or appended."""
+ result = _GenericRetrieve(field, default, path)
+ result = _DoRemapping(result, map)
+ result = _AddPrefix(result, prefix)
+ return _AppendOrReturn(append, result)
+
+ class _GetWrapper(object):
+
+ def __init__(self, parent, field, base_path, append=None):
+ self.parent = parent
+ self.field = field
+ self.base_path = [base_path]
+ self.append = append
+
+ def __call__(self, name, map=None, prefix='', default=None):
+ return self.parent._GetAndMunge(
+ self.field,
+ self.base_path + [name],
+ default=default,
+ prefix=prefix,
+ append=self.append,
+ map=map)
+
+ def GetArch(self, config):
+ """Get architecture based on msvs_configuration_platform and
+ msvs_target_platform. Returns either 'x86' or 'x64'."""
+ configuration_platform = self.msvs_configuration_platform.get(config, '')
+ platform = self.msvs_target_platform.get(config, '')
+ if not platform: # If no specific override, use the configuration's.
+ platform = configuration_platform
+ # Map from platform to architecture.
+ return {'Win32': 'x86', 'x64': 'x64'}.get(platform, 'x86')
+
+ def _TargetConfig(self, config):
+ """Returns the target-specific configuration."""
+ # On Cobalt, we're not using any suffix on config names like Win_Debug_x64.
+ # Cobalt on Windows is x64 only.
+ return config
+
+ def _Setting(self,
+ path,
+ config,
+ default=None,
+ prefix='',
+ append=None,
+ map=None):
+ """_GetAndMunge for msvs_settings."""
+ return self._GetAndMunge(self.msvs_settings[config], path, default, prefix,
+ append, map)
+
+ def _SettingsValidator(self, configname):
+ """Validate msvs_settings."""
+ valid_fields = [
+ 'VCCLCompilerTool',
+ 'VCLinkerTool',
+ 'VCLibrarianTool',
+ 'VCMIDLTool',
+ 'VCResourceCompilerTool',
+ 'VCManifestTool',
+ ]
+ for field in self.msvs_settings[configname]:
+ if field not in valid_fields:
+ message = ('Invalid msvs_settings field: "%s", '
+ 'config: "%s"' % (field, configname))
+ close_match = get_close_matches(field, valid_fields, 1)
+ if close_match:
+ message += '\nDid you mean %s?' % tuple(close_match)
+ raise Exception(message)
+
+ def _ConfigAttrib(self,
+ path,
+ config,
+ default=None,
+ prefix='',
+ append=None,
+ map=None):
+ """_GetAndMunge for msvs_configuration_attributes."""
+ return self._GetAndMunge(self.msvs_configuration_attributes[config], path,
+ default, prefix, append, map)
+
+ def ProcessIncludeDirs(self, include_dirs, config):
+ """Updates include_dirs to expand VS specific paths, and adds the system
+ include dirs used for platform SDK and similar."""
+ config = self._TargetConfig(config)
+ includes = include_dirs + self.msvs_system_include_dirs[config]
+ includes.extend(
+ self._Setting(
+ ('VCCLCompilerTool', 'AdditionalIncludeDirectories'),
+ config,
+ default=[]))
+ return [self.ConvertVSMacros(p, config=config) for p in includes]
+
+ def GetDefines(self, config):
+ """Returns the set of defines that are injected to the defines list based
+ on other VS settings."""
+ config = self._TargetConfig(config)
+ defines = []
+ if self._ConfigAttrib(['CharacterSet'], config) == '1':
+ defines.extend(('_UNICODE', 'UNICODE'))
+ if self._ConfigAttrib(['CharacterSet'], config) == '2':
+ defines.append('_MBCS')
+ defines.extend(
+ self._Setting(
+ ('VCCLCompilerTool', 'PreprocessorDefinitions'), config,
+ default=[]))
+ return defines
+
+ def GetOutputName(self, config, expand_special):
+ """Gets the explicitly overridden output name for a target or returns None
+ if it's not overridden."""
+ config = self._TargetConfig(config)
+ type = self.spec['type']
+ root = 'VCLibrarianTool' if type == 'static_library' else 'VCLinkerTool'
+ # TODO: Handle OutputDirectory without OutputFile.
+ output_file = self._Setting((root, 'OutputFile'), config)
+ if output_file:
+ output_file = expand_special(
+ self.ConvertVSMacros(output_file, config=config))
+ return output_file
+
+ def GetPDBName(self, config, expand_special):
+ """Gets the explicitly overridden pdb name for a target or returns None
+ if it's not overridden."""
+ config = self._TargetConfig(config)
+ output_file = self._Setting(('VCLinkerTool', 'ProgramDatabaseFile'), config)
+ if output_file:
+ output_file = expand_special(
+ self.ConvertVSMacros(output_file, config=config))
+ return output_file
+
+ def GetCflags(self, config):
+ """Returns the flags that need to be added to .c and .cc compilations."""
+ config = self._TargetConfig(config)
+ cflags = []
+ cflags.extend(['/wd' + w for w in self.msvs_disabled_warnings[config]])
+ cl = self._GetWrapper(
+ self, self.msvs_settings[config], 'VCCLCompilerTool', append=cflags)
+ cl('Optimization',
+ map={'0': 'd',
+ '1': '1',
+ '2': '2',
+ '3': 'x'},
+ prefix='/O')
+ cl('InlineFunctionExpansion', prefix='/Ob')
+ cl('OmitFramePointers', map={'false': '-', 'true': ''}, prefix='/Oy')
+ cl('FavorSizeOrSpeed', map={'1': 't', '2': 's'}, prefix='/O')
+ cl('WholeProgramOptimization', map={'true': '/GL'})
+ cl('WarningLevel', prefix='/W')
+ cl('WarnAsError', map={'false': '', 'true': '/WX'})
+ cl('DebugInformationFormat',
+ map={'1': '7',
+ '3': 'i',
+ '4': 'I'},
+ prefix='/Z')
+ cl('RuntimeTypeInfo', map={'true': '/GR', 'false': '/GR-'})
+ cl('EnableFunctionLevelLinking', map={'true': '/Gy', 'false': '/Gy-'})
+ cl('MinimalRebuild', map={'true': '/Gm', 'false': '/Gm-'})
+ cl('BufferSecurityCheck', map={'true': '/GS', 'false': '/GS-'})
+ cl('BasicRuntimeChecks', map={'1': 's', '2': 'u', '3': '1'}, prefix='/RTC')
+ cl('RuntimeLibrary',
+ map={'0': 'T',
+ '1': 'Td',
+ '2': 'D',
+ '3': 'Dd'},
+ prefix='/M')
+ cl('ExceptionHandling', map={'1': 'sc', '2': 'a'}, prefix='/EH')
+ cl('EnablePREfast', map={'true': '/analyze'})
+ cl('AdditionalOptions', prefix='')
+ cflags.extend([
+ '/FI' + f
+ for f in self._Setting(
+ ('VCCLCompilerTool', 'ForcedIncludeFiles'), config, default=[])
+ ])
+ cflags.extend([
+ '/Zc:' + f
+ for f in self._Setting(
+ ('VCCLCompilerTool', 'Conformance'), config, default=[])
+ ])
+
+ # ninja handles parallelism by itself, don't have the compiler do it too.
+ cflags = filter(lambda x: not x.startswith('/MP'), cflags)
+ return cflags
+
+ def GetCflagsObjectiveC(self):
+ pass
+
+ def GetCflagsObjectiveCC(self):
+ pass
+
+ def _GetPchFlags(self, config, extension):
+ """Get the flags to be added to the cflags for precompiled header support.
+ """
+ config = self._TargetConfig(config)
+ # The PCH is only built once by a particular source file. Usage of PCH must
+ # only be for the same language (i.e. C vs. C++), so only include the pch
+ # flags when the language matches.
+ if self.msvs_precompiled_header[config]:
+ source_ext = os.path.splitext(self.msvs_precompiled_source[config])[1]
+ if _LanguageMatchesForPch(source_ext, extension):
+ pch = os.path.split(self.msvs_precompiled_header[config])[1]
+ return ['/Yu' + pch, '/FI' + pch, '/Fp${pchprefix}.' + pch + '.pch']
+ return []
+
+ def UsesComponentExtensions(self, config):
+ return self._Setting(
+ ('VCCLCompilerTool', 'ComponentExtensions'), config, default=[])
+
+ def GetCflagsC(self, config):
+ """Returns the flags that need to be added to .c compilations."""
+ config = self._TargetConfig(config)
+ return self._GetPchFlags(config, '.c')
+
+ def GetCflagsCC(self, config):
+ """Returns the flags that need to be added to .cc compilations."""
+ config = self._TargetConfig(config)
+ ccflags = []
+ cl = self._GetWrapper(
+ self, self.msvs_settings[config], 'VCCLCompilerTool', append=ccflags)
+ cl('ComponentExtensions', map={'true': '/ZW'})
+
+ if self.UsesComponentExtensions(config):
+ # Disable PCH for libs compiled with /ZW, even if it was requested.
+ # Causes a fatal compiler error.
+ return ['/TP'] + ccflags
+ else:
+ return ['/TP'] + self._GetPchFlags(config, '.cc') + ccflags
+
+ def _GetAdditionalLibraryDirectories(self, root, config, gyp_path_to_ninja):
+ """Get and normalize the list of paths in AdditionalLibraryDirectories
+ setting."""
+ config = self._TargetConfig(config)
+ libpaths = self._Setting(
+ (root, 'AdditionalLibraryDirectories'), config, default=[])
+ libpaths = [
+ os.path.normpath(
+ gyp_path_to_ninja(self.ConvertVSMacros(p, config=config)))
+ for p in libpaths
+ ]
+ return ['/LIBPATH:"' + p + '"' for p in libpaths]
+
+ def GetLibFlags(self, config, gyp_path_to_ninja):
+ """Returns the flags that need to be added to lib commands."""
+ config = self._TargetConfig(config)
+ libflags = []
+ lib = self._GetWrapper(
+ self, self.msvs_settings[config], 'VCLibrarianTool', append=libflags)
+ libflags.extend(
+ self._GetAdditionalLibraryDirectories('VCLibrarianTool', config,
+ gyp_path_to_ninja))
+ lib('AdditionalOptions')
+ return libflags
+
+ def _GetDefFileAsLdflags(self, spec, ldflags, gyp_path_to_ninja):
+ """.def files get implicitly converted to a ModuleDefinitionFile for the
+ linker in the VS generator. Emulate that behaviour here."""
+ def_file = ''
+ if spec['type'] in ('shared_library', 'loadable_module', 'executable'):
+ def_files = [s for s in spec.get('sources', []) if s.endswith('.def')]
+ if len(def_files) == 1:
+ ldflags.append('/DEF:"%s"' % gyp_path_to_ninja(def_files[0]))
+ elif len(def_files) > 1:
+ raise Exception('Multiple .def files')
+
+ def GetLdFlags(self, config, **kwargs):
+ """Returns the flags that need to be added to link commands, and the
+ manifest files."""
+
+ gyp_path_to_ninja = kwargs['gyp_path_to_ninja']
+ expand_special = kwargs['expand_special']
+ manifest_name = kwargs['manifest_name']
+ is_executable = kwargs['is_executable']
+
+ config = self._TargetConfig(config)
+ ldflags = []
+ ld = self._GetWrapper(
+ self, self.msvs_settings[config], 'VCLinkerTool', append=ldflags)
+ self._GetDefFileAsLdflags(self.spec, ldflags, gyp_path_to_ninja)
+ ld('GenerateDebugInformation', map={'true': '/DEBUG'})
+ ld('TargetMachine', map={'1': 'X86', '17': 'X64'}, prefix='/MACHINE:')
+ ldflags.extend(
+ self._GetAdditionalLibraryDirectories('VCLinkerTool', config,
+ gyp_path_to_ninja))
+ ld('DelayLoadDLLs', prefix='/DELAYLOAD:')
+ out = self.GetOutputName(config, expand_special)
+ if out:
+ ldflags.append('/OUT:' + out)
+ pdb = self.GetPDBName(config, expand_special)
+ if pdb:
+ ldflags.append('/PDB:' + pdb)
+ ld('AdditionalOptions', prefix='')
+ ld('SubSystem', map={'1': 'CONSOLE', '2': 'WINDOWS'}, prefix='/SUBSYSTEM:')
+ ld('LinkIncremental', map={'1': ':NO', '2': ''}, prefix='/INCREMENTAL')
+ ld('FixedBaseAddress', map={'1': ':NO', '2': ''}, prefix='/FIXED')
+ ld('RandomizedBaseAddress',
+ map={'1': ':NO',
+ '2': ''},
+ prefix='/DYNAMICBASE')
+ ld('DataExecutionPrevention', map={'1': ':NO', '2': ''}, prefix='/NXCOMPAT')
+ ld('OptimizeReferences', map={'1': 'NOREF', '2': 'REF'}, prefix='/OPT:')
+ ld('EnableCOMDATFolding', map={'1': 'NOICF', '2': 'ICF'}, prefix='/OPT:')
+ ld('LinkTimeCodeGeneration', map={'1': '/LTCG'})
+ ld('IgnoreDefaultLibraryNames', prefix='/NODEFAULTLIB:')
+ ld('ResourceOnlyDLL', map={'true': '/NOENTRY'})
+ ld('EntryPointSymbol', prefix='/ENTRY:')
+ ld('Profile', map={'true': '/PROFILE'})
+ # TODO: This should sort of be somewhere else (not really a flag).
+ ld('AdditionalDependencies', prefix='')
+
+ # If the base address is not specifically controlled, DYNAMICBASE should
+ # be on by default.
+ base_flags = filter(lambda x: 'DYNAMICBASE' in x or x == '/FIXED',
+ ldflags)
+ if not base_flags:
+ ldflags.append('/DYNAMICBASE')
+
+ # If the NXCOMPAT flag has not been specified, default to on. Despite the
+ # documentation that says this only defaults to on when the subsystem is
+ # Vista or greater (which applies to the linker), the IDE defaults it on
+ # unless it's explicitly off.
+ if not filter(lambda x: 'NXCOMPAT' in x, ldflags):
+ ldflags.append('/NXCOMPAT')
+
+ have_def_file = filter(lambda x: x.startswith('/DEF:'), ldflags)
+ manifest_flags, intermediate_manifest_file = self._GetLdManifestFlags(
+ config, manifest_name, is_executable and not have_def_file)
+ ldflags.extend(manifest_flags)
+ manifest_files = self._GetAdditionalManifestFiles(config, gyp_path_to_ninja)
+ manifest_files.append(intermediate_manifest_file)
+
+ return ldflags, manifest_files
+
+ def _GetLdManifestFlags(self, config, name, allow_isolation):
+ """Returns the set of flags that need to be added to the link to generate
+ a default manifest, as well as the name of the generated file."""
+ output_name = name + '.intermediate.manifest'
+ # Manifests are off for UWP. If needed by another target,
+ # please find a way to configure them per target.
+ flags = [
+ '/MANIFEST:NO',
+ ]
+ return flags, output_name
+
+ def _GetAdditionalManifestFiles(self, config, gyp_path_to_ninja):
+ """Gets additional manifest files that are added to the default one
+ generated by the linker."""
+ files = self._Setting(
+ ('VCManifestTool', 'AdditionalManifestFiles'), config, default=[])
+ if (self._Setting(('VCManifestTool', 'EmbedManifest'), config,
+ default='') == 'true'):
+ print 'gyp/msvs_emulation.py: "EmbedManifest: true" not yet supported.'
+ if isinstance(files, str):
+ files = files.split(';')
+ return [
+ os.path.normpath(
+ gyp_path_to_ninja(self.ConvertVSMacros(f, config=config)))
+ for f in files
+ ]
+
+ def IsUseLibraryDependencyInputs(self, config):
+ """Returns whether the target should be linked via Use Library Dependency
+ Inputs (using component .objs of a given .lib)."""
+ config = self._TargetConfig(config)
+ uldi = self._Setting(('VCLinkerTool', 'UseLibraryDependencyInputs'), config)
+ return uldi == 'true'
+
+ def GetRcFlags(self, config, gyp_to_ninja_path):
+ """Returns the flags that need to be added to invocations of the resource
+ compiler."""
+ config = self._TargetConfig(config)
+ rcflags = []
+ rc = self._GetWrapper(
+ self,
+ self.msvs_settings[config],
+ 'VCResourceCompilerTool',
+ append=rcflags)
+ rc('AdditionalIncludeDirectories', map=gyp_to_ninja_path, prefix='/I')
+ rcflags.append('/I' + gyp_to_ninja_path('.'))
+ rc('PreprocessorDefinitions', prefix='/d')
+ # /l arg must be in hex without leading '0x'
+ rc('Culture', prefix='/l', map=lambda x: hex(int(x))[2:])
+ return rcflags
+
+ def BuildCygwinBashCommandLine(self, args, path_to_base):
+ """Build a command line that runs args via cygwin bash. We assume that all
+ incoming paths are in Windows normpath'd form, so they need to be
+ converted to posix style for the part of the command line that's passed to
+ bash. We also have to do some Visual Studio macro emulation here because
+ various rules use magic VS names for things. Also note that rules that
+ contain ninja variables cannot be fixed here (for example ${source}), so
+ the outer generator needs to make sure that the paths that are written out
+ are in posix style, if the command line will be used here."""
+ cygwin_dir = os.path.normpath(
+ os.path.join(path_to_base, self.msvs_cygwin_dirs[0]))
+ cd = ('cd %s' % path_to_base).replace('\\', '/')
+ args = [a.replace('\\', '/').replace('"', '\\"') for a in args]
+ args = ["'%s'" % a.replace("'", "'\\''") for a in args]
+ bash_cmd = ' '.join(args)
+ cmd = ('call "%s\\setup_env.bat" && set CYGWIN=nontsec && ' % cygwin_dir +
+ 'bash -c "%s ; %s"' % (cd, bash_cmd))
+ return cmd
+
+ def IsRuleRunUnderCygwin(self, rule):
+ """Determine if an action should be run under cygwin. If the variable is
+ unset, or set to 1 we use cygwin."""
+ return int(
+ rule.get('msvs_cygwin_shell', self.spec.get('msvs_cygwin_shell',
+ 1))) != 0
+
+ def _HasExplicitRuleForExtension(self, spec, extension):
+ """Determine if there's an explicit rule for a particular extension."""
+ for rule in spec.get('rules', []):
+ if rule['extension'] == extension:
+ return True
+ return False
+
+ def HasExplicitIdlRules(self, spec):
+ """Determine if there's an explicit rule for idl files. When there isn't we
+ need to generate implicit rules to build MIDL .idl files."""
+ return self._HasExplicitRuleForExtension(spec, 'idl')
+
+ def HasExplicitAsmRules(self, spec):
+ """Determine if there's an explicit rule for asm files. When there isn't we
+ need to generate implicit rules to assemble .asm files."""
+ return self._HasExplicitRuleForExtension(spec, 'asm')
+
+ def GetIdlBuildData(self, source, config):
+ """Determine the implicit outputs for an idl file. Returns output
+ directory, outputs, and variables and flags that are required."""
+ config = self._TargetConfig(config)
+ midl_get = self._GetWrapper(self, self.msvs_settings[config], 'VCMIDLTool')
+
+ def midl(name, default=None):
+ return self.ConvertVSMacros(
+ midl_get(name, default=default), config=config)
+
+ # TODO: remove references to xb1 below.
+ if config.startswith('xb1'):
+ tlb = ''
+ header = midl('HeaderFileName', default='${root}.h')
+ dlldata = ''
+ iid = ''
+ proxy = ''
+ outdir = midl('OutputDirectory', default='')
+ else:
+ tlb = midl('TypeLibraryName', default='${root}.tlb')
+ header = midl('HeaderFileName', default='${root}.h')
+ dlldata = midl('DLLDataFileName', default='dlldata.c')
+ iid = midl('InterfaceIdentifierFileName', default='${root}_i.c')
+ proxy = midl('ProxyFileName', default='${root}_p.c')
+ # Note that .tlb is not included in the outputs as it is not always
+ # generated depending on the content of the input idl file.
+ outdir = midl('OutputDirectory', default='')
+ if config.startswith('xb1'):
+ output = [header]
+ else:
+ output = [header, dlldata, iid, proxy]
+ variables = [('tlb', tlb), ('h', header), ('dlldata', dlldata),
+ ('iid', iid), ('proxy', proxy)]
+ if config.startswith('xb1'):
+ metadata_dir = '"%s%s"' % ('C:\\Program Files (x86)\\Windows Kits\\10\\',
+ 'UnionMetadata')
+ flags = [
+ '/env', 'x64', '/W1', '/char', 'signed', '/enum_class',
+ '/metadata_dir', metadata_dir, '/notlb', '/winrt'
+ ]
+ else:
+ # TODO: Are there configuration settings to set these flags?
+ flags = ['/char', 'signed', '/env', 'win32', '/Oicf']
+ return outdir, output, variables, flags
+
+
+def _ExtractImportantEnvironment(output_of_set):
+ """Extracts environment variables required for the toolchain to run from
+ a textual dump output by the cmd.exe 'set' command."""
+ envvars_to_save = (
+ 'durangoxdk',
+ 'goma_.*', # TODO: This is ugly, but needed for goma.
+ 'include',
+ 'lib',
+ 'libpath',
+ 'path',
+ 'pathext',
+ 'systemroot',
+ 'temp',
+ 'tmp',
+ 'xedk',
+ 'cell_.*',
+ 'sn_.*',
+ 'sce_.*',)
+ env = {}
+ for line in output_of_set.splitlines():
+ for envvar in envvars_to_save:
+ if re.match(envvar + '=', line.lower()):
+ var, setting = line.split('=', 1)
+ if envvar == 'path':
+ # Our own rules (for running gyp-win-tool) and other actions in
+ # Chromium rely on python being in the path. Add the path to this
+ # python here so that if it's not in the path when ninja is run
+ # later, python will still be found.
+ setting = os.path.dirname(sys.executable) + ';' + setting
+ env[var.upper()] = setting
+ break
+ for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
+ if required not in env:
+ raise Exception('Environment variable "%s" '
+ 'required to be set to valid path' % required)
+ return env
+
+
+def _FormatAsEnvironmentBlock(envvar_dict):
+ """Format as an 'environment block' directly suitable for CreateProcess.
+ Briefly this is a list of key=value\0, terminated by an additional \0. See
+ CreateProcess documentation for more details."""
+ block = ''
+ nul = '\0'
+ for key, value in envvar_dict.iteritems():
+ block += key + '=' + value + nul
+ block += nul
+ return block
+
+
+class MSVCUWPToolchain(Toolchain):
+
+ def __init__(self):
+ self.compiler_settings = None
+
+ def _escape(self, string):
+ """Escape a string such that it can be embedded into a Ninja file without
+ further interpretation."""
+ assert '\n' not in string, 'Ninja syntax does not allow newlines'
+ # We only have one special metacharacter: '$'.
+ return string.replace('$', '$$')
+
+ def Define(self, d):
+ # cl.exe replaces literal # characters with = in preprocesor definitions for
+ # some reason. Octal-encode to work around that.
+ d = d.replace('#', '\\%03o' % ord('#'))
+ return '/D' + self.QuoteForRspFile(self._escape(d))
+
+ def EncodeRspFileList(self, args):
+ """Process a list of arguments using QuoteForRspFile."""
+ # Note that the first argument is assumed to be the command. Don't add
+ # quotes around it because then built-ins like 'echo', etc. won't work.
+ # Take care to normpath only the path in the case of 'call ../x.bat' because
+ # otherwise the whole thing is incorrectly interpreted as a path and not
+ # normalized correctly.
+ if not args:
+ return ''
+ if args[0].startswith('call '):
+ call, program = args[0].split(' ', 1)
+ program = call + ' ' + os.path.normpath(program)
+ else:
+ program = os.path.normpath(args[0])
+ return program + ' ' + ' '.join(
+ self.QuoteForRspFile(arg) for arg in args[1:])
+
+ def ExpandEnvVars(self, string, expansions):
+ """Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv
+ for the canonical way to retrieve a suitable dict."""
+ if '$' in string:
+ for old, new in expansions.iteritems():
+ assert '$(' not in new, new
+ string = string.replace(old, new)
+ return string
+
+ def ExpandRuleVariables(self, path, root, dirname, source, ext, name):
+ path = self.compiler_settings.ConvertVSMacros(path, config=self.config_name)
+ path = path.replace(generator_default_variables['RULE_INPUT_ROOT'], root)
+ path = path.replace(generator_default_variables['RULE_INPUT_DIRNAME'],
+ dirname)
+ path = path.replace(generator_default_variables['RULE_INPUT_PATH'], source)
+ path = path.replace(generator_default_variables['RULE_INPUT_EXT'], ext)
+ path = path.replace(generator_default_variables['RULE_INPUT_NAME'], name)
+ return path
+
+ def GenerateEnvironmentFiles(self, toplevel_build_dir, generator_flags,
+ open_out):
+ """It's not sufficient to have the absolute path to the compiler, linker,
+
+ etc. on Windows, as those tools rely on .dlls being in the PATH. Different
+ architectures require a different compiler binary, and different supporting
+ environment variables (INCLUDE, LIB, LIBPATH). So, we extract the
+ environment
+ here, wrap all invocations of compiler tools (cl, link, lib, rc, midl, etc.)
+ via win_tool.py which sets up the environment, and then we do not prefix the
+ compiler with an absolute path, instead preferring something like "cl.exe"
+ in
+ the rule which will then run whichever the environment setup has put in the
+ path.
+ """
+ arch = 'x64'
+
+ # Get the dos environment via set:
+ # Use cmd /c to execute under native windows command
+ args = 'set'
+
+ popen = subprocess.Popen(
+ args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ variables, _ = popen.communicate()
+ env = _ExtractImportantEnvironment(variables)
+ env_block = _FormatAsEnvironmentBlock(env)
+ f = open_out(os.path.join(toplevel_build_dir, 'environment.' + arch), 'wb')
+ f.write(env_block)
+ f.close()
+
+ def GetCompilerSettings(self):
+ return self.compiler_settings
+
+ def GetPrecompiledHeader(self, **kwargs):
+ return MSVCPrecompiledHeader(**kwargs)
+
+ def InitCompilerSettings(self, spec, **kwargs):
+ self.compiler_settings = MsvsSettings(spec, kwargs['generator_flags'])
+
+ def SetAdditionalGypVariables(self, default_variables, **kwargs):
+ """Calculate additional variables for use in the build (called by gyp)."""
+ default_variables.setdefault('OS', 'win')
+ default_variables['EXECUTABLE_SUFFIX'] = '.exe'
+ default_variables['STATIC_LIB_PREFIX'] = ''
+ default_variables['STATIC_LIB_SUFFIX'] = '.lib'
+ default_variables['SHARED_LIB_PREFIX'] = ''
+ default_variables['SHARED_LIB_SUFFIX'] = '.dll'
+ generator_flags = {}
+
+ # Copy additional generator configuration data from VS, which is shared
+ # by the Windows Ninja generator.
+ import gyp.generator.msvs as msvs_generator
+ generator_additional_non_configuration_keys = getattr(
+ msvs_generator, 'generator_additional_non_configuration_keys', [])
+ generator_additional_path_sections = getattr(
+ msvs_generator, 'generator_additional_path_sections', [])
+
+ # Set a variable so conditions can be based on msvs_version.
+ msvs_version = gyp.msvs_emulation.GetVSVersion(generator_flags)
+ default_variables['MSVS_VERSION'] = msvs_version.ShortName()
+
+ # To determine processor word size on Windows, in addition to checking
+ # PROCESSOR_ARCHITECTURE (which reflects the word size of the current
+ # process), it is also necessary to check PROCESSOR_ARCHITEW6432 (which
+ # contains the actual word size of the system when running thru WOW64).
+ if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or
+ '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')):
+ default_variables['MSVS_OS_BITS'] = 64
+ else:
+ default_variables['MSVS_OS_BITS'] = 32
+ return
+
+ def VerifyMissingSources(self, sources, **kwargs):
+ """Emulate behavior of msvs_error_on_missing_sources present in the msvs
+
+ generator: Check that all regular source files, i.e. not created at run
+ time,
+ exist on disk. Missing files cause needless recompilation when building via
+ VS, and we want this check to match for people/bots that build using ninja,
+ so they're not surprised when the VS build fails.
+ """
+ build_dir = kwargs['build_dir']
+ generator_flags = kwargs['generator_flags']
+ gyp_path_to_ninja = kwargs['gyp_path_to_ninja']
+ if int(generator_flags.get('msvs_error_on_missing_sources', 0)):
+ no_specials = filter(lambda x: '$' not in x, sources)
+ relative = [
+ os.path.join(build_dir, gyp_path_to_ninja(s)) for s in no_specials
+ ]
+ missing = filter(lambda x: not os.path.exists(x), relative)
+ if missing:
+ # They'll look like out\Release\..\..\stuff\things.cc, so normalize the
+ # path for a slightly less crazy looking output.
+ cleaned_up = [os.path.normpath(x) for x in missing]
+ raise Exception('Missing input files:\n%s' % '\n'.join(cleaned_up))
+
+ def QuoteForRspFile(self, arg):
+ """Quote a command line argument so that it appears as one argument when
+ processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for
+ Windows programs)."""
+ # See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment
+ # threads. This is actually the quoting rules for CommandLineToArgvW, not
+ # for the shell, because the shell doesn't do anything in Windows. This
+ # works more or less because most programs (including the compiler, etc.)
+ # use that function to handle command line arguments.
+
+ # For a literal quote, CommandLineToArgvW requires 2n+1 backslashes
+ # preceding it, and results in n backslashes + the quote. So we substitute
+ # in 2* what we match, +1 more, plus the quote.
+ windows_quoter_regex = re.compile(r'(\\*)"')
+ arg = windows_quoter_regex.sub(lambda mo: 2 * mo.group(1) + '\\"', arg)
+
+ # %'s also need to be doubled otherwise they're interpreted as batch
+ # positional arguments. Also make sure to escape the % so that they're
+ # passed literally through escaping so they can be singled to just the
+ # original %. Otherwise, trying to pass the literal representation that
+ # looks like an environment variable to the shell (e.g. %PATH%) would fail.
+ arg = arg.replace('%', '%%')
+
+ # These commands are used in rsp files, so no escaping for the shell (via ^)
+ # is necessary.
+
+ # Finally, wrap the whole thing in quotes so that the above quote rule
+ # applies and whitespace isn't a word break.
+ return '"' + arg + '"'
+
+
+def ToolchainImpl():
+ return MSVCUWPToolchain()
\ No newline at end of file
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc
index 1402d12..1a4bf89 100644
--- a/src/starboard/shared/starboard/application.cc
+++ b/src/starboard/shared/starboard/application.cc
@@ -15,8 +15,10 @@
#include "starboard/shared/starboard/application.h"
#include "starboard/atomic.h"
+#include "starboard/common/scoped_ptr.h"
#include "starboard/condition_variable.h"
#include "starboard/event.h"
+#include "starboard/log.h"
#include "starboard/memory.h"
#include "starboard/string.h"
@@ -28,6 +30,8 @@
namespace {
+const char kPreloadSwitch[] = "preload";
+
// Dispatches an event of |type| with |data| to the system event handler,
// calling |destructor| on |data| when finished dispatching. Does all
// appropriate NULL checks so you don't have to.
@@ -75,7 +79,9 @@
int Application::Run(int argc, char** argv) {
Initialize();
command_line_.reset(new CommandLine(argc, argv));
- if (IsStartImmediate()) {
+ if (IsPreloadImmediate()) {
+ DispatchPreload();
+ } else if (IsStartImmediate()) {
DispatchStart();
}
@@ -129,7 +135,6 @@
}
#if SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
-
void Application::HandleFrame(SbPlayer player,
const scoped_refptr<VideoFrame>& frame,
int x,
@@ -141,6 +146,7 @@
#endif // SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
void Application::SetStartLink(const char* start_link) {
+ SB_DCHECK(IsCurrentThread());
SbMemoryDeallocate(start_link_);
if (start_link) {
start_link_ = SbStringDuplicate(start_link);
@@ -150,60 +156,90 @@
}
void Application::DispatchStart() {
+ SB_DCHECK(IsCurrentThread());
+ SB_DCHECK(state_ == kStateUnstarted || state_ == kStatePreloading);
+ DispatchAndDelete(CreateInitialEvent(kSbEventTypeStart));
+}
+
+void Application::DispatchPreload() {
+ SB_DCHECK(IsCurrentThread());
+#if SB_API_VERSION >= SB_PRELOAD_API_VERSION
SB_DCHECK(state_ == kStateUnstarted);
- SbEventStartData start_data;
- start_data.argument_values =
- const_cast<char**>(command_line_->GetOriginalArgv());
- start_data.argument_count = command_line_->GetOriginalArgc();
- start_data.link = start_link_;
- Dispatch(kSbEventTypeStart, &start_data, NULL);
- state_ = kStateStarted;
+ DispatchAndDelete(CreateInitialEvent(kSbEventTypePreload));
+#else // SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ SB_NOTREACHED();
+#endif // SB_API_VERSION >= SB_PRELOAD_API_VERSION
+}
+
+bool Application::HasPreloadSwitch() {
+ return command_line_->HasSwitch(kPreloadSwitch);
}
bool Application::DispatchAndDelete(Application::Event* event) {
+ SB_DCHECK(IsCurrentThread());
if (!event) {
return true;
}
- // DispatchStart() must be called first
- SB_DCHECK(state_ != kStateUnstarted);
+ // Ensure the event is deleted unless it is released.
+ scoped_ptr<Event> scoped_event(event);
// Ensure that we go through the the appropriate lifecycle events based on the
// current state.
- switch (event->event->type) {
+ switch (scoped_event->event->type) {
+#if SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ case kSbEventTypePreload:
+ if (state() != kStateUnstarted) {
+ return true;
+ }
+ break;
+#endif // SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ case kSbEventTypeStart:
+ if (state() != kStatePreloading && state() != kStateUnstarted) {
+ return true;
+ }
+ break;
case kSbEventTypePause:
if (state() != kStateStarted) {
- delete event;
return true;
}
break;
case kSbEventTypeUnpause:
if (state() == kStateStarted) {
- delete event;
+ return true;
+ }
+
+ if (state() == kStatePreloading) {
+ // Convert to Start event and consume.
+ DispatchStart();
return true;
}
if (state() == kStateSuspended) {
Inject(new Event(kSbEventTypeResume, NULL, NULL));
- Inject(event);
+ Inject(scoped_event.release());
return true;
}
break;
case kSbEventTypeSuspend:
if (state() == kStateSuspended) {
- delete event;
return true;
}
+ if (state() == kStatePreloading) {
+ // If Preloading, we can jump straight to Suspended, so we don't try to
+ // do anything fancy here.
+ break;
+ }
+
if (state() == kStateStarted) {
Inject(new Event(kSbEventTypePause, NULL, NULL));
- Inject(event);
+ Inject(scoped_event.release());
return true;
}
break;
case kSbEventTypeResume:
if (state() == kStateStarted || state() == kStatePaused) {
- delete event;
return true;
}
if (state() == kStateSuspended) {
@@ -214,32 +250,40 @@
if (state() == kStateStarted) {
Inject(new Event(kSbEventTypePause, NULL, NULL));
Inject(new Event(kSbEventTypeSuspend, NULL, NULL));
- Inject(event);
+ Inject(scoped_event.release());
return true;
}
if (state() == kStatePaused) {
Inject(new Event(kSbEventTypeSuspend, NULL, NULL));
- Inject(event);
+ Inject(scoped_event.release());
return true;
}
- error_level_ = event->error_level;
+ error_level_ = scoped_event->error_level;
break;
case kSbEventTypeScheduled: {
TimedEvent* timed_event =
- reinterpret_cast<TimedEvent*>(event->event->data);
+ reinterpret_cast<TimedEvent*>(scoped_event->event->data);
timed_event->callback(timed_event->context);
- delete event;
return true;
}
default:
break;
}
- SbEventHandle(event->event);
+ SbEventHandle(scoped_event->event);
- bool should_continue = true;
- switch (event->event->type) {
+ switch (scoped_event->event->type) {
+#if SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ case kSbEventTypePreload:
+ SB_DCHECK(state() == kStateUnstarted);
+ state_ = kStatePreloading;
+ break;
+#endif // SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ case kSbEventTypeStart:
+ SB_DCHECK(state() == kStatePreloading || state() == kStateUnstarted);
+ state_ = kStateStarted;
+ break;
case kSbEventTypePause:
SB_DCHECK(state() == kStateStarted);
state_ = kStatePaused;
@@ -249,7 +293,7 @@
state_ = kStateStarted;
break;
case kSbEventTypeSuspend:
- SB_DCHECK(state() == kStatePaused);
+ SB_DCHECK(state() == kStatePreloading || state() == kStatePaused);
state_ = kStateSuspended;
OnSuspend();
break;
@@ -260,14 +304,14 @@
case kSbEventTypeStop:
SB_DCHECK(state() == kStateSuspended);
state_ = kStateStopped;
- should_continue = false;
- break;
+ return false;
default:
break;
}
- delete event;
- return should_continue;
+ // Should not be unstarted after the first event.
+ SB_DCHECK(state() != kStateUnstarted);
+ return true;
}
void Application::CallTeardownCallbacks() {
@@ -277,6 +321,21 @@
}
}
+Application::Event* Application::CreateInitialEvent(SbEventType type) {
+#if SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ SB_DCHECK(type == kSbEventTypePreload || type == kSbEventTypeStart);
+#else // SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ SB_DCHECK(type == kSbEventTypeStart);
+#endif // SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ SbEventStartData* start_data = new SbEventStartData();
+ SbMemorySet(start_data, 0, sizeof(SbEventStartData));
+ start_data->argument_values =
+ const_cast<char**>(command_line_->GetOriginalArgv());
+ start_data->argument_count = command_line_->GetOriginalArgc();
+ start_data->link = start_link_;
+ return new Event(type, start_data, &DeleteDestructor<SbEventStartData>);
+}
+
} // namespace starboard
} // namespace shared
} // namespace starboard
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h
index 1137712..c70bcfb 100644
--- a/src/starboard/shared/starboard/application.h
+++ b/src/starboard/shared/starboard/application.h
@@ -55,6 +55,11 @@
// The initial Unstarted state.
kStateUnstarted,
+ // The preloading state, where the application gets as much work done as
+ // possible to launch, but is not visible. You see exits to kStateStarted
+ // and kStateSuspended.
+ kStatePreloading,
+
// The normal foreground, fully-visible state after receiving the initial
// START event or after UNPAUSE from Paused.
kStateStarted,
@@ -248,12 +253,12 @@
#if SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
// Subclasses may override this method to accept video frames from the media
// system. Will be called from an external thread.
- virtual void AcceptFrame(SbPlayer player,
- const scoped_refptr<VideoFrame>& frame,
- int x,
- int y,
- int width,
- int height) {}
+ virtual void AcceptFrame(SbPlayer /* player */,
+ const scoped_refptr<VideoFrame>& /* frame */,
+ int /* x */,
+ int /* y */,
+ int /* width */,
+ int /* height */) {}
#endif // SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
// Blocks until the next event is available. Subclasses must implement this
@@ -314,13 +319,29 @@
// means "success" or at least "no error."
int error_level() const { return error_level_; }
- // Returns true if the Start event should be sent in |Run| before entering the
- // event loop. Derived classes that return false must call |DispatchStart|.
+ // Returns whether the Start event should be sent in |Run| before entering the
+ // event loop. Derived classes that return false must call |DispatchStart| at
+ // some point.
virtual bool IsStartImmediate() { return true; }
- // Dispatches a Start event to the system event handler.
+ // Synchronously dispatches a Start event to the system event handler. Must be
+ // called on the main dispatch thread.
void DispatchStart();
+ // Returns whether the Preload event should be sent in |Run| before entering
+ // the event loop. Derived classes that return true must call |Unpause| or
+ // |DispatchStart| at some point.
+ //
+ // |IsPreloadImmediate|, if true, takes precedence over |IsStartImmediate|.
+ virtual bool IsPreloadImmediate() { return false; }
+
+ // Synchronously dispatches a Preload event to the system event handler. Must
+ // be called on the main dispatch thread.
+ void DispatchPreload();
+
+ // Returns whether the '--preload' command-line argument is specified.
+ bool HasPreloadSwitch();
+
// Dispatches |event| to the system event handler, taking ownership of the
// event. Checks for consistency with the current application state when state
// events are dispatched. Returns whether to keep servicing the event queue,
@@ -332,6 +353,10 @@
void CallTeardownCallbacks();
private:
+ // Creates an initial event type of either Start or Preload with the original
+ // command line and deep link.
+ Event* CreateInitialEvent(SbEventType type);
+
// The single application instance.
static Application* g_instance;
diff --git a/src/starboard/shared/starboard/media/media_tests.gypi b/src/starboard/shared/starboard/media/media_tests.gypi
new file mode 100644
index 0000000..24e641a
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_tests.gypi
@@ -0,0 +1,23 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'variables': {
+ # This will be included by 'starboard_platform_tests.gyp' so full path names
+ # have to be used here.
+ 'media_tests_sources': [
+ '<(DEPTH)/starboard/shared/starboard/media/mime_type_test.cc',
+ ],
+ },
+}
diff --git a/src/starboard/shared/starboard/media/media_util.cc b/src/starboard/shared/starboard/media/media_util.cc
index bac646d..550dd52 100644
--- a/src/starboard/shared/starboard/media/media_util.cc
+++ b/src/starboard/shared/starboard/media/media_util.cc
@@ -14,6 +14,8 @@
#include "starboard/shared/starboard/media/media_util.h"
+#include "starboard/log.h"
+
namespace starboard {
namespace shared {
namespace starboard {
@@ -48,6 +50,18 @@
return kSbMediaTransferIdUnknown;
}
+int GetBytesPerSample(SbMediaAudioSampleType sample_type) {
+ switch (sample_type) {
+ case kSbMediaAudioSampleTypeInt16:
+ return 2;
+ case kSbMediaAudioSampleTypeFloat32:
+ return 4;
+ }
+
+ SB_NOTREACHED();
+ return 4;
+}
+
} // namespace media
} // namespace starboard
} // namespace shared
diff --git a/src/starboard/shared/starboard/media/media_util.h b/src/starboard/shared/starboard/media/media_util.h
index 51c793c..e778b78 100644
--- a/src/starboard/shared/starboard/media/media_util.h
+++ b/src/starboard/shared/starboard/media/media_util.h
@@ -33,6 +33,8 @@
// Requirements (2018).
SbMediaTransferId GetTransferIdFromString(const std::string& eotf);
+int GetBytesPerSample(SbMediaAudioSampleType sample_type);
+
} // namespace media
} // namespace starboard
} // namespace shared
diff --git a/src/starboard/shared/starboard/media/mime_type_test.cc b/src/starboard/shared/starboard/media/mime_type_test.cc
index cf18f64..e8847d7 100644
--- a/src/starboard/shared/starboard/media/mime_type_test.cc
+++ b/src/starboard/shared/starboard/media/mime_type_test.cc
@@ -91,32 +91,8 @@
}
TEST(MimeTypeTest, TypeNotAtBeginning) {
- {
- MimeType mime_type(";video/mp4");
- EXPECT_FALSE(mime_type.is_valid());
- }
-
- {
- MimeType mime_type("codecs=\"abc\"; audio/mp4");
- EXPECT_FALSE(mime_type.is_valid());
- }
-}
-
-TEST(MimeTypeTest, EmptyComponent) {
- {
- MimeType mime_type("video/mp4;");
- EXPECT_FALSE(mime_type.is_valid());
- }
-
- {
- MimeType mime_type("video/mp4;;");
- EXPECT_FALSE(mime_type.is_valid());
- }
-
- {
- MimeType mime_type("audio/mp4; codecs=\"abc\";");
- EXPECT_FALSE(mime_type.is_valid());
- }
+ MimeType mime_type("codecs=\"abc\"; audio/mp4");
+ EXPECT_FALSE(mime_type.is_valid());
}
TEST(MimeTypeTest, ValidContentTypeWithParams) {
@@ -221,11 +197,9 @@
}
TEST(MimeTypeTest, GetParamFloatValueWithIndex) {
- {
- MimeType mime_type("video/mp4; name0=123; name1=123.4");
- EXPECT_FLOAT_EQ(123., mime_type.GetParamFloatValue(0));
- EXPECT_FLOAT_EQ(123.4, mime_type.GetParamFloatValue(1));
- }
+ MimeType mime_type("video/mp4; name0=123; name1=123.4");
+ EXPECT_FLOAT_EQ(123., mime_type.GetParamFloatValue(0));
+ EXPECT_FLOAT_EQ(123.4, mime_type.GetParamFloatValue(1));
}
TEST(MimeTypeTest, GetParamStringValueWithIndex) {
@@ -242,6 +216,13 @@
}
}
+TEST(MimeTypeTest, GetParamValueInInvalidType) {
+ MimeType mime_type("video/mp4; name0=abc; name1=123.4");
+ EXPECT_FLOAT_EQ(0, mime_type.GetParamIntValue(0));
+ EXPECT_FLOAT_EQ(0.f, mime_type.GetParamFloatValue(0));
+ EXPECT_FLOAT_EQ(0, mime_type.GetParamIntValue(1));
+}
+
TEST(MimeTypeTest, GetParamIntValueWithName) {
{
MimeType mime_type("video/mp4; name=123");
diff --git a/src/starboard/shared/starboard/microphone/microphone_close.cc b/src/starboard/shared/starboard/microphone/microphone_close.cc
index 60c1a98..9519189 100644
--- a/src/starboard/shared/starboard/microphone/microphone_close.cc
+++ b/src/starboard/shared/starboard/microphone/microphone_close.cc
@@ -14,7 +14,7 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#include "starboard/shared/starboard/microphone/microphone_internal.h"
@@ -22,4 +22,4 @@
return SbMicrophoneIsValid(microphone) ? microphone->Close() : false;
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/starboard/microphone/microphone_create.cc b/src/starboard/shared/starboard/microphone/microphone_create.cc
index 3eee5d4..962af50 100644
--- a/src/starboard/shared/starboard/microphone/microphone_create.cc
+++ b/src/starboard/shared/starboard/microphone/microphone_create.cc
@@ -14,7 +14,7 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#include "starboard/shared/starboard/microphone/microphone_internal.h"
@@ -25,4 +25,4 @@
buffer_size);
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/starboard/microphone/microphone_destroy.cc b/src/starboard/shared/starboard/microphone/microphone_destroy.cc
index df7891f..59ec025 100644
--- a/src/starboard/shared/starboard/microphone/microphone_destroy.cc
+++ b/src/starboard/shared/starboard/microphone/microphone_destroy.cc
@@ -14,7 +14,7 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#include "starboard/shared/starboard/microphone/microphone_internal.h"
@@ -22,4 +22,4 @@
SbMicrophonePrivate::DestroyMicrophone(microphone);
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/starboard/microphone/microphone_get_available.cc b/src/starboard/shared/starboard/microphone/microphone_get_available.cc
index 2cb666e..58fe474 100644
--- a/src/starboard/shared/starboard/microphone/microphone_get_available.cc
+++ b/src/starboard/shared/starboard/microphone/microphone_get_available.cc
@@ -14,7 +14,7 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#include "starboard/shared/starboard/microphone/microphone_internal.h"
@@ -24,4 +24,4 @@
info_array_size);
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/starboard/microphone/microphone_internal.h b/src/starboard/shared/starboard/microphone/microphone_internal.h
index f14e734..4895da5 100644
--- a/src/starboard/shared/starboard/microphone/microphone_internal.h
+++ b/src/starboard/shared/starboard/microphone/microphone_internal.h
@@ -18,7 +18,7 @@
#include "starboard/microphone.h"
#include "starboard/shared/internal_only.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
struct SbMicrophonePrivate {
virtual ~SbMicrophonePrivate() {}
@@ -36,6 +36,6 @@
static void DestroyMicrophone(SbMicrophone microphone);
};
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#endif // STARBOARD_SHARED_STARBOARD_MICROPHONE_MICROPHONE_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/microphone/microphone_is_sample_rate_supported.cc b/src/starboard/shared/starboard/microphone/microphone_is_sample_rate_supported.cc
index 9f32f2b..2154a0b 100644
--- a/src/starboard/shared/starboard/microphone/microphone_is_sample_rate_supported.cc
+++ b/src/starboard/shared/starboard/microphone/microphone_is_sample_rate_supported.cc
@@ -14,7 +14,7 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#include "starboard/shared/starboard/microphone/microphone_internal.h"
@@ -24,4 +24,4 @@
id, sample_rate_in_hz);
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/starboard/microphone/microphone_open.cc b/src/starboard/shared/starboard/microphone/microphone_open.cc
index a7ebb6c..4042c76 100644
--- a/src/starboard/shared/starboard/microphone/microphone_open.cc
+++ b/src/starboard/shared/starboard/microphone/microphone_open.cc
@@ -14,7 +14,7 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#include "starboard/shared/starboard/microphone/microphone_internal.h"
@@ -22,4 +22,4 @@
return SbMicrophoneIsValid(microphone) ? microphone->Open() : false;
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/starboard/microphone/microphone_read.cc b/src/starboard/shared/starboard/microphone/microphone_read.cc
index 137c854..1fbfce6 100644
--- a/src/starboard/shared/starboard/microphone/microphone_read.cc
+++ b/src/starboard/shared/starboard/microphone/microphone_read.cc
@@ -14,7 +14,7 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
#include "starboard/shared/starboard/microphone/microphone_internal.h"
@@ -26,4 +26,4 @@
: -1;
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.cc b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
index 1d53da4..5ee70b0 100644
--- a/src/starboard/shared/starboard/player/decoded_audio_internal.cc
+++ b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
@@ -14,6 +14,8 @@
#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include <algorithm>
+
#include "starboard/log.h"
namespace starboard {
@@ -21,11 +23,100 @@
namespace starboard {
namespace player {
-DecodedAudio::DecodedAudio() : pts_(0), size_(0) {}
+DecodedAudio::DecodedAudio()
+ : channels_(0),
+ sample_type_(kSbMediaAudioSampleTypeInt16),
+ storage_type_(kSbMediaAudioFrameStorageTypeInterleaved),
+ pts_(0),
+ size_(0) {}
-DecodedAudio::DecodedAudio(SbMediaTime pts, size_t size)
- : pts_(pts), buffer_(new uint8_t[size]), size_(size) {
- SB_DCHECK(size > 0) << size;
+DecodedAudio::DecodedAudio(int channels,
+ SbMediaAudioSampleType sample_type,
+ SbMediaAudioFrameStorageType storage_type,
+ SbMediaTime pts,
+ size_t size)
+ : channels_(channels),
+ sample_type_(sample_type),
+ storage_type_(storage_type),
+ pts_(pts),
+ buffer_(new uint8_t[size]),
+ size_(size) {}
+
+int DecodedAudio::frames() const {
+ int bytes_per_sample;
+ if (sample_type_ == kSbMediaAudioSampleTypeInt16) {
+ bytes_per_sample = 2;
+ } else {
+ SB_DCHECK(sample_type_ == kSbMediaAudioSampleTypeFloat32);
+ bytes_per_sample = 4;
+ }
+ SB_DCHECK(size_ % (bytes_per_sample * channels_) == 0);
+ return size_ / bytes_per_sample / channels_;
+}
+
+void DecodedAudio::ShrinkTo(size_t new_size) {
+ SB_DCHECK(new_size <= size_);
+ size_ = new_size;
+}
+
+void DecodedAudio::SwitchFormatTo(
+ SbMediaAudioSampleType new_sample_type,
+ SbMediaAudioFrameStorageType new_storage_type) {
+ if (new_sample_type == sample_type_ && new_storage_type == storage_type_) {
+ return;
+ }
+
+ if (storage_type_ != kSbMediaAudioFrameStorageTypeInterleaved ||
+ new_storage_type != kSbMediaAudioFrameStorageTypeInterleaved) {
+ SB_NOTREACHED();
+ // TODO: Implement switching between other storage type pairs.
+ return;
+ }
+
+ if (sample_type_ == kSbMediaAudioSampleTypeInt16 &&
+ new_sample_type == kSbMediaAudioSampleTypeFloat32 &&
+ storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved &&
+ new_storage_type == kSbMediaAudioFrameStorageTypeInterleaved) {
+ size_t new_size = sizeof(float) * frames() * channels();
+ scoped_array<uint8_t> new_buffer(new uint8_t[new_size]);
+ float* new_samples = reinterpret_cast<float*>(new_buffer.get());
+ int16_t* old_samples = reinterpret_cast<int16_t*>(buffer_.get());
+
+ for (int i = 0; i < frames() * channels(); ++i) {
+ new_samples[i] = static_cast<float>(old_samples[i]) / 32768.f;
+ }
+
+ buffer_.swap(new_buffer);
+ sample_type_ = new_sample_type;
+ size_ = new_size;
+
+ return;
+ }
+
+ if (sample_type_ == kSbMediaAudioSampleTypeFloat32 &&
+ new_sample_type == kSbMediaAudioSampleTypeInt16 &&
+ storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved &&
+ new_storage_type == kSbMediaAudioFrameStorageTypeInterleaved) {
+ size_t new_size = sizeof(int16_t) * frames() * channels();
+ scoped_array<uint8_t> new_buffer(new uint8_t[new_size]);
+ int16_t* new_samples = reinterpret_cast<int16_t*>(new_buffer.get());
+ float* old_samples = reinterpret_cast<float*>(buffer_.get());
+
+ for (int i = 0; i < frames() * channels(); ++i) {
+ float sample = std::max(old_samples[i], -1.f);
+ sample = std::min(sample, 1.f);
+ new_samples[i] = static_cast<int16_t>(sample * 32767.f);
+ }
+
+ buffer_.swap(new_buffer);
+ sample_type_ = new_sample_type;
+ size_ = new_size;
+
+ return;
+ }
+
+ // TODO: Implement switching between other sample and storage types.
+ SB_NOTREACHED();
}
} // namespace player
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.h b/src/starboard/shared/starboard/player/decoded_audio_internal.h
index 40a12fe..366f412 100644
--- a/src/starboard/shared/starboard/player/decoded_audio_internal.h
+++ b/src/starboard/shared/starboard/player/decoded_audio_internal.h
@@ -33,7 +33,15 @@
class DecodedAudio : public RefCountedThreadSafe<DecodedAudio> {
public:
DecodedAudio(); // Signal an EOS.
- DecodedAudio(SbMediaTime pts, size_t size);
+ DecodedAudio(int channels,
+ SbMediaAudioSampleType sample_type,
+ SbMediaAudioFrameStorageType storage_type,
+ SbMediaTime pts,
+ size_t size);
+
+ int channels() const { return channels_; }
+ SbMediaAudioSampleType sample_type() const { return sample_type_; }
+ SbMediaAudioFrameStorageType storage_type() const { return storage_type_; }
bool is_end_of_stream() const { return buffer_ == NULL; }
SbMediaTime pts() const { return pts_; }
@@ -41,8 +49,16 @@
size_t size() const { return size_; }
uint8_t* buffer() { return buffer_.get(); }
+ int frames() const;
+
+ void SwitchFormatTo(SbMediaAudioSampleType new_sample_type,
+ SbMediaAudioFrameStorageType new_storage_type);
+ void ShrinkTo(size_t new_size);
private:
+ int channels_;
+ SbMediaAudioSampleType sample_type_;
+ SbMediaAudioFrameStorageType storage_type_;
// The timestamp of the first audio frame.
SbMediaTime pts_;
// Use scoped_array<uint8_t> instead of std::vector<uint8_t> to avoid wasting
diff --git a/src/starboard/shared/starboard/player/filter/audio_buffer_queue.cc b/src/starboard/shared/starboard/player/filter/audio_buffer_queue.cc
deleted file mode 100644
index c051ee8..0000000
--- a/src/starboard/shared/starboard/player/filter/audio_buffer_queue.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cobalt/media/base/audio_buffer_queue.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "cobalt/media/base/audio_bus.h"
-
-namespace cobalt {
-namespace media {
-
-AudioBufferQueue::AudioBufferQueue() {
- Clear();
-}
-AudioBufferQueue::~AudioBufferQueue() {}
-
-void AudioBufferQueue::Clear() {
- buffers_.clear();
- current_buffer_ = buffers_.begin();
- current_buffer_offset_ = 0;
- frames_ = 0;
-}
-
-void AudioBufferQueue::Append(const scoped_refptr<AudioBuffer>& buffer_in) {
- // Add the buffer to the queue. Inserting into deque invalidates all
- // iterators, so point to the first buffer.
- buffers_.push_back(buffer_in);
- current_buffer_ = buffers_.begin();
-
- // Update the |frames_| counter since we have added frames.
- frames_ += buffer_in->frame_count();
- CHECK_GT(frames_, 0); // make sure it doesn't overflow.
-}
-
-int AudioBufferQueue::ReadFrames(int frames,
- int dest_frame_offset,
- AudioBus* dest) {
- DCHECK_GE(dest->frames(), frames + dest_frame_offset);
- return InternalRead(frames, true, 0, dest_frame_offset, dest);
-}
-
-int AudioBufferQueue::PeekFrames(int frames,
- int source_frame_offset,
- int dest_frame_offset,
- AudioBus* dest) {
- DCHECK_GE(dest->frames(), frames);
- return InternalRead(frames, false, source_frame_offset, dest_frame_offset,
- dest);
-}
-
-void AudioBufferQueue::SeekFrames(int frames) {
- // Perform seek only if we have enough bytes in the queue.
- CHECK_LE(frames, frames_);
- int taken = InternalRead(frames, true, 0, 0, NULL);
- DCHECK_EQ(taken, frames);
-}
-
-int AudioBufferQueue::InternalRead(int frames,
- bool advance_position,
- int source_frame_offset,
- int dest_frame_offset,
- AudioBus* dest) {
- // Counts how many frames are actually read from the buffer queue.
- int taken = 0;
- BufferQueue::iterator current_buffer = current_buffer_;
- int current_buffer_offset = current_buffer_offset_;
-
- int frames_to_skip = source_frame_offset;
- while (taken < frames) {
- // |current_buffer| is valid since the first time this buffer is appended
- // with data. Make sure there is data to be processed.
- if (current_buffer == buffers_.end())
- break;
-
- scoped_refptr<AudioBuffer> buffer = *current_buffer;
-
- int remaining_frames_in_buffer =
- buffer->frame_count() - current_buffer_offset;
-
- if (frames_to_skip > 0) {
- // If there are frames to skip, do it first. May need to skip into
- // subsequent buffers.
- int skipped = std::min(remaining_frames_in_buffer, frames_to_skip);
- current_buffer_offset += skipped;
- frames_to_skip -= skipped;
- } else {
- // Find the right amount to copy from the current buffer. We shall copy no
- // more than |frames| frames in total and each single step copies no more
- // than the current buffer size.
- int copied = std::min(frames - taken, remaining_frames_in_buffer);
-
- // if |dest| is NULL, there's no need to copy.
- if (dest) {
- buffer->ReadFrames(copied, current_buffer_offset,
- dest_frame_offset + taken, dest);
- }
-
- // Increase total number of frames copied, which regulates when to end
- // this loop.
- taken += copied;
-
- // We have read |copied| frames from the current buffer. Advance the
- // offset.
- current_buffer_offset += copied;
- }
-
- // Has the buffer has been consumed?
- if (current_buffer_offset == buffer->frame_count()) {
- // If we are at the last buffer, no more data to be copied, so stop.
- BufferQueue::iterator next = current_buffer + 1;
- if (next == buffers_.end())
- break;
-
- // Advances the iterator.
- current_buffer = next;
- current_buffer_offset = 0;
- }
- }
-
- if (advance_position) {
- // Update the appropriate values since |taken| frames have been copied out.
- frames_ -= taken;
- DCHECK_GE(frames_, 0);
- DCHECK(current_buffer_ != buffers_.end() || frames_ == 0);
-
- // Remove any buffers before the current buffer as there is no going
- // backwards.
- buffers_.erase(buffers_.begin(), current_buffer);
- current_buffer_ = buffers_.begin();
- current_buffer_offset_ = current_buffer_offset;
- }
-
- return taken;
-}
-
-} // namespace media
-} // namespace cobalt
diff --git a/src/starboard/shared/starboard/player/filter/audio_buffer_queue.h b/src/starboard/shared/starboard/player/filter/audio_buffer_queue.h
deleted file mode 100644
index 64162a4..0000000
--- a/src/starboard/shared/starboard/player/filter/audio_buffer_queue.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COBALT_MEDIA_BASE_AUDIO_BUFFER_QUEUE_H_
-#define COBALT_MEDIA_BASE_AUDIO_BUFFER_QUEUE_H_
-
-#include <deque>
-
-#include "base/basictypes.h"
-#include "cobalt/media/base/audio_buffer.h"
-#include "cobalt/media/base/media_export.h"
-
-namespace cobalt {
-namespace media {
-
-class AudioBus;
-
-// A queue of AudioBuffers to support reading of arbitrary chunks of a media
-// data source. Audio data can be copied into an AudioBus for output. The
-// current position can be forwarded to anywhere in the buffered data.
-//
-// This class is not inherently thread-safe. Concurrent access must be
-// externally serialized.
-class MEDIA_EXPORT AudioBufferQueue {
- public:
- AudioBufferQueue();
- ~AudioBufferQueue();
-
- // Clears the buffer queue.
- void Clear();
-
- // Appends |buffer_in| to this queue.
- void Append(const scoped_refptr<AudioBuffer>& buffer_in);
-
- // Reads a maximum of |frames| frames into |dest| from the current position.
- // Returns the number of frames read. The current position will advance by the
- // amount of frames read. |dest_frame_offset| specifies a starting offset into
- // |dest|. On each call, the frames are converted from their source format
- // into the destination AudioBus.
- int ReadFrames(int frames, int dest_frame_offset, AudioBus* dest);
-
- // Copies up to |frames| frames from current position to |dest|. Returns
- // number of frames copied. Doesn't advance current position. Starts at
- // |source_frame_offset| from current position. |dest_frame_offset| specifies
- // a starting offset into |dest|. On each call, the frames are converted from
- // their source format into the destination AudioBus.
- int PeekFrames(int frames,
- int source_frame_offset,
- int dest_frame_offset,
- AudioBus* dest);
-
- // Moves the current position forward by |frames| frames. If |frames| exceeds
- // frames available, the seek operation will fail.
- void SeekFrames(int frames);
-
- // Returns the number of frames buffered beyond the current position.
- int frames() const { return frames_; }
-
- private:
- // Definition of the buffer queue.
- typedef std::deque<scoped_refptr<AudioBuffer> > BufferQueue;
-
- // An internal method shared by ReadFrames() and SeekFrames() that actually
- // does reading. It reads a maximum of |frames| frames into |dest|. Returns
- // the number of frames read. The current position will be moved forward by
- // the number of frames read if |advance_position| is set. If |dest| is NULL,
- // only the current position will advance but no data will be copied.
- // |source_frame_offset| can be used to skip frames before reading.
- // |dest_frame_offset| specifies a starting offset into |dest|.
- int InternalRead(int frames,
- bool advance_position,
- int source_frame_offset,
- int dest_frame_offset,
- AudioBus* dest);
-
- BufferQueue::iterator current_buffer_;
- BufferQueue buffers_;
- int current_buffer_offset_;
-
- // Number of frames available to be read in the buffer.
- int frames_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioBufferQueue);
-};
-
-} // namespace media
-} // namespace cobalt
-
-#endif // COBALT_MEDIA_BASE_AUDIO_BUFFER_QUEUE_H_
diff --git a/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h b/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
index bb501f1..dadbe57 100644
--- a/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
@@ -19,9 +19,9 @@
#include "starboard/media.h"
#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/closure.h"
#include "starboard/shared/starboard/player/decoded_audio_internal.h"
#include "starboard/shared/starboard/player/input_buffer_internal.h"
-#include "starboard/shared/starboard/player/job_queue.h"
#include "starboard/types.h"
namespace starboard {
@@ -33,34 +33,58 @@
// This class decodes encoded audio stream into playable audio data.
class AudioDecoder {
public:
+ typedef ::starboard::shared::starboard::player::Closure Closure;
+ typedef ::starboard::shared::starboard::player::DecodedAudio DecodedAudio;
+ typedef ::starboard::shared::starboard::player::InputBuffer InputBuffer;
+
virtual ~AudioDecoder() {}
- // Decode the encoded audio data stored in |input_buffer|.
- virtual void Decode(const InputBuffer& input_buffer) = 0;
+ // Whenever the decoder produces a new output, it calls |output_cb| once and
+ // exactly once. This notify the user to call Read() to acquire the next
+ // output. The user is free to not call Read() immediately but it can expect
+ // that a further call of Read() returns valid output until Reset() is called.
+ // Note that |output_cb| is always called asynchronously on the calling job
+ // queue.
+ virtual void Initialize(const Closure& output_cb) = 0;
- // Note that there won't be more input data unless Reset() is called.
+ // Decode the encoded audio data stored in |input_buffer|. Whenever the input
+ // is consumed and the decoder is ready to accept a new input, it calls
+ // |consumed_cb|.
+ // Note that |consumed_cb| is always called asynchronously on the calling job
+ // queue.
+ virtual void Decode(const InputBuffer& input_buffer,
+ const Closure& consumed_cb) = 0;
+
+ // Notice the object that there is no more input data unless Reset() is
+ // called.
virtual void WriteEndOfStream() = 0;
- // Try to read the next decoded audio buffer. If there is no decoded audio
- // available currently, it returns NULL. If the audio stream reaches EOS and
- // there is no more decoded audio available, it returns an EOS buffer.
+ // Try to read the next decoded audio buffer. If the audio stream reaches EOS
+ // and there is no more decoded audio available, it returns an EOS buffer. It
+ // should only be called when |output_cb| is called and will always return a
+ // valid buffer containing audio data or signals and EOS.
+ // Note that there may not be a one-to-one relationship between the decoded
+ // audio and the input data passed in via Decode(). The decoder may break or
+ // combine multiple decoded audio access units into one. The implementation
+ // has to ensure that the particular resampler can handle such combined access
+ // units as input.
virtual scoped_refptr<DecodedAudio> Read() = 0;
- // Clear any cached buffer of the codec and reset the state of the codec.
- // This function will be called during seek to ensure that the left over
- // data from previous buffers are cleared.
+ // Clear any cached buffer of the codec and reset the state of the codec. This
+ // function will be called during seek to ensure that the left over data from
+ // from previous buffers are cleared.
virtual void Reset() = 0;
// Return the sample type of the decoded pcm data.
virtual SbMediaAudioSampleType GetSampleType() const = 0;
+ // Return the storage type of the decoded pcm data.
+ virtual SbMediaAudioFrameStorageType GetStorageType() const = 0;
+
// Return the sample rate of the incoming audio. This should be used by the
// audio renderer as the sample rate of the underlying audio stream can be
// different than the sample rate stored in the meta data.
virtual int GetSamplesPerSecond() const = 0;
-
- // Return whether the decoder can accept more data or not.
- virtual bool CanAcceptMoreData() const = 0;
};
} // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/audio_frame_tracker.h b/src/starboard/shared/starboard/player/filter/audio_frame_tracker.h
new file mode 100644
index 0000000..97bae5b
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/audio_frame_tracker.h
@@ -0,0 +1,112 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_FRAME_TRACKER_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_FRAME_TRACKER_H_
+
+#include <queue>
+
+#include "starboard/log.h"
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/thread_checker.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+// This class helps on tracking how many audio frames have been played with
+// playback rate taking into account.
+//
+// For example, when playback rate is set to 2.0 and 20 frames have been played,
+// the adjusted played frame will be 40.
+class AudioFrameTracker {
+ public:
+ AudioFrameTracker() { Reset(); }
+
+ // Reset the class to its initial state. In this state there is no frames
+ // tracked and the playback frames is 0.
+ void Reset() {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+
+ while (!frame_records_.empty()) {
+ frame_records_.pop();
+ }
+ frames_played_adjusted_to_playback_rate_ = 0;
+ }
+
+ void AddFrames(int number_of_frames, double playback_rate) {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (number_of_frames == 0) {
+ return;
+ }
+ SB_DCHECK(playback_rate > 0);
+
+ if (frame_records_.empty() ||
+ frame_records_.back().playback_rate != playback_rate) {
+ FrameRecord record = {number_of_frames, playback_rate};
+ frame_records_.push(record);
+ } else {
+ frame_records_.back().number_of_frames += number_of_frames;
+ }
+ }
+
+ void RecordPlayedFrames(int number_of_frames) {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+
+ while (number_of_frames > 0 && !frame_records_.empty()) {
+ FrameRecord& record = frame_records_.front();
+ if (record.number_of_frames > number_of_frames) {
+ frames_played_adjusted_to_playback_rate_ +=
+ static_cast<int>(number_of_frames * record.playback_rate);
+ record.number_of_frames -= number_of_frames;
+ number_of_frames = 0;
+ } else {
+ number_of_frames -= record.number_of_frames;
+ frames_played_adjusted_to_playback_rate_ +=
+ static_cast<int>(record.number_of_frames * record.playback_rate);
+ frame_records_.pop();
+ }
+ }
+ SB_DCHECK(number_of_frames == 0)
+ << number_of_frames << " " << frame_records_.size();
+ }
+
+ SbMediaTime GetFramePlayedAdjustedToPlaybackRate() const {
+ SB_DCHECK(thread_checker_.CalledOnValidThread());
+
+ return frames_played_adjusted_to_playback_rate_;
+ }
+
+ private:
+ struct FrameRecord {
+ int number_of_frames;
+ double playback_rate;
+ };
+
+ ThreadChecker thread_checker_;
+ std::queue<FrameRecord> frame_records_;
+ int frames_played_adjusted_to_playback_rate_;
+};
+
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_FRAME_TRACKER_H_
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
index dac6b06..1251d5d 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
@@ -18,7 +18,7 @@
#include "starboard/memory.h"
#include "starboard/shared/starboard/audio_sink/audio_sink_internal.h"
-#include "starboard/shared/starboard/player/closure.h"
+#include "starboard/shared/starboard/media/media_util.h"
namespace starboard {
namespace shared {
@@ -28,41 +28,69 @@
namespace {
-const SbTime kEndOfStreamWrittenUpdateInterval = 5 * kSbTimeMillisecond;
+// This class works only when the input format and output format are the same.
+// It allows for a simplified AudioRendererImpl implementation by always using a
+// resampler.
+class IdentityAudioResampler : public AudioResampler {
+ public:
+ IdentityAudioResampler() : eos_reached_(false) {}
+ scoped_refptr<DecodedAudio> Resample(
+ const scoped_refptr<DecodedAudio>& audio_data) SB_OVERRIDE {
+ SB_DCHECK(!eos_reached_);
+
+ return audio_data;
+ }
+ scoped_refptr<DecodedAudio> WriteEndOfStream() SB_OVERRIDE {
+ SB_DCHECK(!eos_reached_);
+ eos_reached_ = true;
+ return new DecodedAudio();
+ }
+
+ private:
+ bool eos_reached_;
+};
+
+// AudioRendererImpl uses AudioTimeStretcher internally to adjust to playback
+// rate and AudioTimeStretcher can only process float32 samples. So we try to
+// use kSbMediaAudioSampleTypeFloat32 and only use kSbMediaAudioSampleTypeInt16
+// when float32 is not supported. To use kSbMediaAudioSampleTypeFloat32 will
+// cause an extra conversion from float32 to int16 before the samples are sent
+// to the audio sink.
+SbMediaAudioSampleType GetSinkAudioSampleType() {
+ return SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)
+ ? kSbMediaAudioSampleTypeFloat32
+ : kSbMediaAudioSampleTypeInt16;
+}
} // namespace
-AudioRendererImpl::AudioRendererImpl(JobQueue* job_queue,
- scoped_ptr<AudioDecoder> decoder,
+AudioRendererImpl::AudioRendererImpl(scoped_ptr<AudioDecoder> decoder,
const SbMediaAudioHeader& audio_header)
- : job_queue_(job_queue),
+ : eos_state_(kEOSNotReceived),
channels_(audio_header.number_of_channels),
- bytes_per_frame_(
- (decoder->GetSampleType() == kSbMediaAudioSampleTypeInt16 ? 2 : 4) *
- channels_),
+ sink_sample_type_(GetSinkAudioSampleType()),
+ bytes_per_frame_(media::GetBytesPerSample(sink_sample_type_) * channels_),
playback_rate_(1.0),
paused_(true),
seeking_(false),
seeking_to_pts_(0),
frame_buffer_(kMaxCachedFrames * bytes_per_frame_),
- frames_in_buffer_(0),
- offset_in_frames_(0),
- frames_consumed_(0),
+ frames_sent_to_sink_(0),
+ pending_decoder_outputs_(0),
+ frames_consumed_by_sink_(0),
frames_consumed_set_at_(SbTimeGetMonotonicNow()),
- end_of_stream_written_(false),
- end_of_stream_decoded_(false),
decoder_(decoder.Pass()),
audio_sink_(kSbAudioSinkInvalid),
+ can_accept_more_data_(true),
+ process_audio_data_scheduled_(false),
decoder_needs_full_reset_(false) {
- SB_DCHECK(job_queue != NULL);
SB_DCHECK(decoder_ != NULL);
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
frame_buffers_[0] = &frame_buffer_[0];
-// TODO: The audio sink on Android is currently broken on certain devices,
-// which causes all of playback to hang. Log it for now, so we can tell
-// when it happens, but this should be removed once the sink is fixed.
+// TODO: The audio sink on Android is currently broken on certain devices, which
+// causes all of playback to hang. Log it for now, so we can tell when it
+// happens, but this should be removed once the sink is fixed.
#if defined(NDEBUG)
const bool kLogFramesConsumed = false;
#else
@@ -71,298 +99,384 @@
if (kLogFramesConsumed) {
log_frames_consumed_closure_ =
Bind(&AudioRendererImpl::LogFramesConsumed, this);
- job_queue_->Schedule(log_frames_consumed_closure_, kSbTimeSecond);
+ Schedule(log_frames_consumed_closure_, kSbTimeSecond);
}
+
+ decoder_->Initialize(Bind(&AudioRendererImpl::OnDecoderOutput, this));
+
+ int source_sample_rate = decoder_->GetSamplesPerSecond();
+ int destination_sample_rate =
+ SbAudioSinkGetNearestSupportedSampleFrequency(source_sample_rate);
+ time_stretcher_.Initialize(channels_, destination_sample_rate);
}
AudioRendererImpl::~AudioRendererImpl() {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
if (audio_sink_ != kSbAudioSinkInvalid) {
SbAudioSinkDestroy(audio_sink_);
}
-
- if (read_from_decoder_closure_.is_valid()) {
- job_queue_->Remove(read_from_decoder_closure_);
- }
-
- if (log_frames_consumed_closure_.is_valid()) {
- job_queue_->Remove(log_frames_consumed_closure_);
- }
}
void AudioRendererImpl::WriteSample(const InputBuffer& input_buffer) {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(can_accept_more_data_);
- if (end_of_stream_written_) {
+ if (eos_state_.load() >= kEOSWrittenToDecoder) {
SB_LOG(ERROR) << "Appending audio sample at " << input_buffer.pts()
<< " after EOS reached.";
return;
}
- decoder_->Decode(input_buffer);
+ can_accept_more_data_ = false;
- ScopedLock lock(mutex_);
+ decoder_->Decode(input_buffer,
+ Bind(&AudioRendererImpl::OnDecoderConsumed, this));
decoder_needs_full_reset_ = true;
- if (!read_from_decoder_closure_.is_valid()) {
- read_from_decoder_closure_ =
- Bind(&AudioRendererImpl::ReadFromDecoder, this);
- job_queue_->Schedule(read_from_decoder_closure_);
- }
}
void AudioRendererImpl::WriteEndOfStream() {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
+ // TODO: Check |can_accept_more_data_| and make WriteEndOfStream() depend on
+ // CanAcceptMoreData() or callback.
+ // SB_DCHECK(can_accept_more_data_);
+ // can_accept_more_data_ = false;
- SB_LOG_IF(WARNING, end_of_stream_written_)
- << "Try to write EOS after EOS is reached";
- if (end_of_stream_written_) {
+ if (eos_state_.load() >= kEOSWrittenToDecoder) {
+ SB_LOG(ERROR) << "Try to write EOS after EOS is reached";
return;
}
decoder_->WriteEndOfStream();
- ScopedLock lock(mutex_);
- end_of_stream_written_ = true;
+ eos_state_.store(kEOSWrittenToDecoder);
decoder_needs_full_reset_ = true;
- // If we are seeking, we consider the seek is finished if end of stream is
- // reached as there won't be any audio data in future.
- if (seeking_) {
- seeking_ = false;
- }
}
void AudioRendererImpl::Play() {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
- ScopedLock lock(mutex_);
- paused_ = false;
+ paused_.store(false);
}
void AudioRendererImpl::Pause() {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
- ScopedLock lock(mutex_);
- paused_ = true;
+ paused_.store(true);
}
#if SB_API_VERSION >= 4
void AudioRendererImpl::SetPlaybackRate(double playback_rate) {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
playback_rate_ = playback_rate;
if (audio_sink_) {
- audio_sink_->SetPlaybackRate(playback_rate);
+ // TODO: Remove SetPlaybackRate() support from audio sink as it only need to
+ // support play/pause.
+ audio_sink_->SetPlaybackRate(playback_rate_ > 0.0 ? 1.0 : 0.0);
}
}
#endif // SB_API_VERSION >= 4
void AudioRendererImpl::Seek(SbMediaTime seek_to_pts) {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(seek_to_pts >= 0);
SbAudioSinkDestroy(audio_sink_);
+
// Now the sink is destroyed and the callbacks will no longer be called, so
// the following modifications are safe without lock.
audio_sink_ = kSbAudioSinkInvalid;
+ if (resampler_) {
+ resampler_.reset();
+ time_stretcher_.FlushBuffers();
+ }
+
+ eos_state_.store(kEOSNotReceived);
seeking_to_pts_ = std::max<SbMediaTime>(seek_to_pts, 0);
- seeking_ = true;
- frames_in_buffer_ = 0;
- offset_in_frames_ = 0;
- frames_consumed_ = 0;
- frames_consumed_set_at_ = SbTimeGetMonotonicNow();
- end_of_stream_written_ = false;
- end_of_stream_decoded_ = false;
- pending_decoded_audio_ = NULL;
+ seeking_.store(true);
+ frames_sent_to_sink_.store(0);
+ frames_consumed_by_sink_.store(0);
+ frames_consumed_by_sink_since_last_get_current_time_.store(0);
+ pending_decoder_outputs_ = 0;
+ audio_frame_tracker_.Reset();
+ frames_consumed_set_at_.store(SbTimeGetMonotonicNow());
+ can_accept_more_data_ = true;
+ process_audio_data_scheduled_ = false;
if (decoder_needs_full_reset_) {
decoder_->Reset();
decoder_needs_full_reset_ = false;
}
+
+ CancelPendingJobs();
+
+ if (log_frames_consumed_closure_.is_valid()) {
+ Schedule(log_frames_consumed_closure_, kSbTimeSecond);
+ }
}
bool AudioRendererImpl::IsEndOfStreamPlayed() const {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
- ScopedLock lock(mutex_);
- return end_of_stream_decoded_ && frames_in_buffer_ == 0;
+ return eos_state_.load() >= kEOSSentToSink &&
+ frames_sent_to_sink_.load() == frames_consumed_by_sink_.load();
}
bool AudioRendererImpl::CanAcceptMoreData() const {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
- {
- ScopedLock lock(mutex_);
- if (end_of_stream_written_) {
- return false;
- }
- }
- return decoder_->CanAcceptMoreData();
+ return eos_state_.load() == kEOSNotReceived && can_accept_more_data_ &&
+ !time_stretcher_.IsQueueFull();
}
bool AudioRendererImpl::IsSeekingInProgress() const {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
- return seeking_;
+ SB_DCHECK(BelongsToCurrentThread());
+ return seeking_.load();
}
SbMediaTime AudioRendererImpl::GetCurrentTime() {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
- if (seeking_) {
+ if (seeking_.load()) {
return seeking_to_pts_;
}
+
+ audio_frame_tracker_.RecordPlayedFrames(
+ frames_consumed_by_sink_since_last_get_current_time_.exchange(0));
+
return seeking_to_pts_ +
- frames_consumed_ * kSbMediaTimeSecond /
- decoder_->GetSamplesPerSecond();
+ audio_frame_tracker_.GetFramePlayedAdjustedToPlaybackRate() *
+ kSbMediaTimeSecond / decoder_->GetSamplesPerSecond();
+}
+
+void AudioRendererImpl::CreateAudioSinkAndResampler() {
+ int source_sample_rate = decoder_->GetSamplesPerSecond();
+ SbMediaAudioSampleType source_sample_type = decoder_->GetSampleType();
+ SbMediaAudioFrameStorageType source_storage_type = decoder_->GetStorageType();
+
+ int destination_sample_rate =
+ SbAudioSinkGetNearestSupportedSampleFrequency(source_sample_rate);
+
+ // AudioTimeStretcher only supports interleaved float32 samples.
+ if (source_sample_rate != destination_sample_rate ||
+ source_sample_type != kSbMediaAudioSampleTypeFloat32 ||
+ source_storage_type != kSbMediaAudioFrameStorageTypeInterleaved) {
+ resampler_ = AudioResampler::Create(
+ decoder_->GetSampleType(), decoder_->GetStorageType(),
+ source_sample_rate, kSbMediaAudioSampleTypeFloat32,
+ kSbMediaAudioFrameStorageTypeInterleaved, destination_sample_rate,
+ channels_);
+ SB_DCHECK(resampler_);
+ } else {
+ resampler_.reset(new IdentityAudioResampler);
+ }
+
+ // TODO: Support planar only audio sink.
+ audio_sink_ = SbAudioSinkCreate(
+ channels_, destination_sample_rate, sink_sample_type_,
+ kSbMediaAudioFrameStorageTypeInterleaved,
+ reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_),
+ kMaxCachedFrames, &AudioRendererImpl::UpdateSourceStatusFunc,
+ &AudioRendererImpl::ConsumeFramesFunc, this);
+ SB_DCHECK(SbAudioSinkIsValid(audio_sink_));
+
+#if SB_API_VERSION >= 4
+ // TODO: Remove SetPlaybackRate() support from audio sink as it only need to
+ // support play/pause.
+ audio_sink_->SetPlaybackRate(playback_rate_ > 0.0 ? 1.0 : 0.0);
+#endif // SB_API_VERSION >= 4
}
void AudioRendererImpl::UpdateSourceStatus(int* frames_in_buffer,
int* offset_in_frames,
bool* is_playing,
bool* is_eos_reached) {
- ScopedLock lock(mutex_);
+ *is_eos_reached = eos_state_.load() >= kEOSSentToSink;
- *is_eos_reached = end_of_stream_decoded_;
+ *is_playing = !paused_.load() && !seeking_.load();
- if (!end_of_stream_decoded_ && !read_from_decoder_closure_.is_valid()) {
- read_from_decoder_closure_ =
- Bind(&AudioRendererImpl::ReadFromDecoder, this);
- if (paused_ || seeking_) {
- job_queue_->Schedule(read_from_decoder_closure_, 10 * kSbTimeMillisecond);
- } else {
- job_queue_->Schedule(read_from_decoder_closure_);
- }
- }
-
- if (paused_ || seeking_) {
- *is_playing = false;
+ if (*is_playing) {
+ *frames_in_buffer =
+ frames_sent_to_sink_.load() - frames_consumed_by_sink_.load();
+ *offset_in_frames = frames_consumed_by_sink_.load() % kMaxCachedFrames;
+ } else {
*frames_in_buffer = *offset_in_frames = 0;
- return;
}
-
- *is_playing = true;
- *frames_in_buffer = frames_in_buffer_;
- *offset_in_frames = offset_in_frames_;
}
void AudioRendererImpl::ConsumeFrames(int frames_consumed) {
- ScopedLock lock(mutex_);
-
- SB_DCHECK(frames_consumed <= frames_in_buffer_);
- offset_in_frames_ += frames_consumed;
- offset_in_frames_ %= kMaxCachedFrames;
- frames_in_buffer_ -= frames_consumed;
- frames_consumed_ += frames_consumed;
- frames_consumed_set_at_ = SbTimeGetMonotonicNow();
+ frames_consumed_by_sink_.fetch_add(frames_consumed);
+ SB_DCHECK(frames_consumed_by_sink_.load() <= frames_sent_to_sink_.load());
+ frames_consumed_by_sink_since_last_get_current_time_.fetch_add(
+ frames_consumed);
+ frames_consumed_set_at_.store(SbTimeGetMonotonicNow());
}
void AudioRendererImpl::LogFramesConsumed() {
SbTimeMonotonic time_since =
- SbTimeGetMonotonicNow() - frames_consumed_set_at_;
+ SbTimeGetMonotonicNow() - frames_consumed_set_at_.load();
if (time_since > kSbTimeSecond) {
SB_DLOG(WARNING) << "|frames_consumed_| has not been updated for "
<< (time_since / kSbTimeSecond) << "."
<< ((time_since / (kSbTimeSecond / 10)) % 10)
- << " seconds, and |pending_decoded_audio_| is "
- << (!!pending_decoded_audio_ ? "" : "not ") << "ready.";
+ << " seconds";
}
- job_queue_->Schedule(log_frames_consumed_closure_, kSbTimeSecond);
+ Schedule(log_frames_consumed_closure_, kSbTimeSecond);
}
-// Try to read some audio data from the decoder. Note that this operation is
-// valid across seeking. If a seek happens immediately after a ReadFromDecoder
-// request is scheduled, the seek will reset the decoder. So the
-// ReadFromDecoder request will not read stale data.
-void AudioRendererImpl::ReadFromDecoder() {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+void AudioRendererImpl::OnDecoderConsumed() {
+ SB_DCHECK(BelongsToCurrentThread());
- ScopedLock lock(mutex_);
- SB_DCHECK(read_from_decoder_closure_.is_valid());
- read_from_decoder_closure_.reset();
+ // TODO: Unify EOS and non EOS request once WriteEndOfStream() depends on
+ // CanAcceptMoreData().
+ if (eos_state_.load() == kEOSNotReceived) {
+ SB_DCHECK(!can_accept_more_data_);
- // Create the audio sink if it is the first incoming AU after seeking.
- if (audio_sink_ == kSbAudioSinkInvalid) {
- int sample_rate = decoder_->GetSamplesPerSecond();
- // TODO: Implement resampler.
- SB_DCHECK(sample_rate ==
- SbAudioSinkGetNearestSupportedSampleFrequency(sample_rate));
- // TODO: Handle sink creation failure.
- audio_sink_ = SbAudioSinkCreate(
- channels_, sample_rate, decoder_->GetSampleType(),
- kSbMediaAudioFrameStorageTypeInterleaved,
- reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_),
- kMaxCachedFrames, &AudioRendererImpl::UpdateSourceStatusFunc,
- &AudioRendererImpl::ConsumeFramesFunc, this);
-#if SB_API_VERSION >= 4
- audio_sink_->SetPlaybackRate(playback_rate_);
-#endif // SB_API_VERSION >= 4
+ can_accept_more_data_ = true;
}
+}
- scoped_refptr<DecodedAudio> decoded_audio;
- if (pending_decoded_audio_) {
- decoded_audio = pending_decoded_audio_;
- } else {
- decoded_audio = decoder_->Read();
- }
- pending_decoded_audio_ = NULL;
- if (!decoded_audio) {
+void AudioRendererImpl::OnDecoderOutput() {
+ SB_DCHECK(BelongsToCurrentThread());
+
+ ++pending_decoder_outputs_;
+
+ if (process_audio_data_scheduled_) {
+ // A ProcessAudioData() callback has been scheduled and we should let it
+ // process the output.
return;
}
- if (decoded_audio->is_end_of_stream()) {
- SB_DCHECK(end_of_stream_written_);
- end_of_stream_decoded_ = true;
- return;
+ process_audio_data_scheduled_ = true;
+ ProcessAudioData();
+}
+
+void AudioRendererImpl::ProcessAudioData() {
+ process_audio_data_scheduled_ = false;
+
+ if (!SbAudioSinkIsValid(audio_sink_)) {
+ CreateAudioSinkAndResampler();
}
- if (seeking_) {
- if (decoded_audio->pts() < seeking_to_pts_) {
- // Discard any audio data before the seeking target.
+ // Loop until no audio is appended, i.e. AppendAudioToFrameBuffer() returns
+ // false.
+ while (AppendAudioToFrameBuffer()) {
+ }
+
+ while (pending_decoder_outputs_ > 0) {
+ if (time_stretcher_.IsQueueFull()) {
+ // There is no room to do any further processing, schedule the function
+ // again for a later time. The delay time is 1/4 of the buffer size.
+ const SbTimeMonotonic delay = kMaxCachedFrames * kSbTimeSecond /
+ decoder_->GetSamplesPerSecond() / 4;
+ process_audio_data_scheduled_ = true;
+ Schedule(Bind(&AudioRendererImpl::ProcessAudioData, this), delay);
return;
}
+
+ scoped_refptr<DecodedAudio> resampled_audio;
+ scoped_refptr<DecodedAudio> decoded_audio = decoder_->Read();
+
+ SB_DCHECK(decoded_audio);
+ --pending_decoder_outputs_;
+
+ if (decoded_audio->is_end_of_stream()) {
+ SB_DCHECK(eos_state_.load() == kEOSWrittenToDecoder) << eos_state_.load();
+ eos_state_.store(kEOSDecoded);
+ seeking_.store(false);
+
+ resampled_audio = resampler_->WriteEndOfStream();
+ } else {
+ // Discard any audio data before the seeking target.
+ if (seeking_.load() && decoded_audio->pts() < seeking_to_pts_) {
+ continue;
+ }
+
+ resampled_audio = resampler_->Resample(decoded_audio);
+ }
+
+ if (resampled_audio->size() > 0) {
+ time_stretcher_.EnqueueBuffer(resampled_audio);
+ }
+
+ // Loop until no audio is appended, i.e. AppendAudioToFrameBuffer() returns
+ // false.
+ while (AppendAudioToFrameBuffer()) {
+ }
}
- if (!AppendDecodedAudio_Locked(decoded_audio)) {
- pending_decoded_audio_ = decoded_audio;
- return;
- }
-
- if (seeking_ && frame_buffer_.size() > kPrerollFrames * bytes_per_frame_) {
- seeking_ = false;
+ int64_t frames_in_buffer =
+ frames_sent_to_sink_.load() - frames_consumed_by_sink_.load();
+ if (kMaxCachedFrames - frames_in_buffer < kFrameAppendUnit &&
+ eos_state_.load() < kEOSSentToSink) {
+ // There are still audio data not appended so schedule a callback later.
+ SbTimeMonotonic delay = 0;
+ if (kMaxCachedFrames - frames_in_buffer < kMaxCachedFrames / 4) {
+ int frames_to_delay =
+ kMaxCachedFrames / 4 - (kMaxCachedFrames - frames_in_buffer);
+ delay = frames_to_delay * kSbTimeSecond / decoder_->GetSamplesPerSecond();
+ }
+ process_audio_data_scheduled_ = true;
+ Schedule(Bind(&AudioRendererImpl::ProcessAudioData, this), delay);
}
}
-// TODO: This function should be executed when lock is not acquired as it copies
-// relatively large amount of data.
-bool AudioRendererImpl::AppendDecodedAudio_Locked(
- const scoped_refptr<DecodedAudio>& decoded_audio) {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+bool AudioRendererImpl::AppendAudioToFrameBuffer() {
+ SB_DCHECK(BelongsToCurrentThread());
- const uint8_t* source_buffer = decoded_audio->buffer();
- int frames_to_append = decoded_audio->size() / bytes_per_frame_;
+ int frames_in_buffer =
+ frames_sent_to_sink_.load() - frames_consumed_by_sink_.load();
- if (frames_in_buffer_ + frames_to_append > kMaxCachedFrames) {
+ if (kMaxCachedFrames - frames_in_buffer < kFrameAppendUnit) {
return false;
}
- int offset_to_append =
- (offset_in_frames_ + frames_in_buffer_) % kMaxCachedFrames;
+ int offset_to_append = frames_sent_to_sink_.load() % kMaxCachedFrames;
+
+ // When |playback_rate_| is 0, try to fill the buffer with playback rate as 1.
+ // Otherwise the preroll will never finish.
+ float playback_rate_to_fill = playback_rate_ == 0.f ? 1.f : playback_rate_;
+ scoped_refptr<DecodedAudio> decoded_audio =
+ time_stretcher_.Read(kFrameAppendUnit, playback_rate_to_fill);
+ SB_DCHECK(decoded_audio);
+ if (decoded_audio->frames() == 0 && eos_state_.load() == kEOSDecoded) {
+ eos_state_.store(kEOSSentToSink);
+ }
+ audio_frame_tracker_.AddFrames(decoded_audio->frames(),
+ playback_rate_to_fill);
+ // TODO: Support kSbMediaAudioFrameStorageTypePlanar.
+ decoded_audio->SwitchFormatTo(sink_sample_type_,
+ kSbMediaAudioFrameStorageTypeInterleaved);
+ const uint8_t* source_buffer = decoded_audio->buffer();
+ int frames_to_append = decoded_audio->frames();
+ int frames_appended = 0;
+
if (frames_to_append > kMaxCachedFrames - offset_to_append) {
SbMemoryCopy(&frame_buffer_[offset_to_append * bytes_per_frame_],
source_buffer,
(kMaxCachedFrames - offset_to_append) * bytes_per_frame_);
source_buffer += (kMaxCachedFrames - offset_to_append) * bytes_per_frame_;
frames_to_append -= kMaxCachedFrames - offset_to_append;
- frames_in_buffer_ += kMaxCachedFrames - offset_to_append;
+ frames_appended += kMaxCachedFrames - offset_to_append;
offset_to_append = 0;
}
+
SbMemoryCopy(&frame_buffer_[offset_to_append * bytes_per_frame_],
source_buffer, frames_to_append * bytes_per_frame_);
- frames_in_buffer_ += frames_to_append;
+ frames_appended += frames_to_append;
- return true;
+ frames_sent_to_sink_.fetch_add(frames_appended);
+
+ int64_t preroll_frames =
+ decoder_->GetSamplesPerSecond() * kPrerollTime / kSbTimeSecond;
+ if (seeking_.load() && frames_sent_to_sink_.load() > preroll_frames) {
+ seeking_.store(false);
+ }
+
+ return frames_appended > 0;
}
// static
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
index ac90e0a..f908fd8 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
@@ -17,16 +17,19 @@
#include <vector>
+#include "starboard/atomic.h"
#include "starboard/audio_sink.h"
#include "starboard/common/scoped_ptr.h"
#include "starboard/log.h"
#include "starboard/media.h"
-#include "starboard/mutex.h"
#include "starboard/shared/internal_only.h"
#include "starboard/shared/starboard/player/closure.h"
#include "starboard/shared/starboard/player/decoded_audio_internal.h"
#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_frame_tracker.h"
#include "starboard/shared/starboard/player/filter/audio_renderer_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_resampler.h"
+#include "starboard/shared/starboard/player/filter/audio_time_stretcher.h"
#include "starboard/shared/starboard/player/input_buffer_internal.h"
#include "starboard/shared/starboard/player/job_queue.h"
#include "starboard/types.h"
@@ -39,10 +42,13 @@
// A default implementation of |AudioRenderer| that only depends on the
// |AudioDecoder| interface, rather than a platform specific implementation.
-class AudioRendererImpl : public AudioRenderer {
+class AudioRendererImpl : public AudioRenderer, private JobQueue::JobOwner {
public:
- AudioRendererImpl(JobQueue* job_queue,
- scoped_ptr<AudioDecoder> decoder,
+ // Preroll is considered as finished after either the amount of audio caches
+ // exceeds kPrerollTime or if EOS is reached.
+ static const size_t kPrerollTime = kSbTimeSecond / 4;
+
+ AudioRendererImpl(scoped_ptr<AudioDecoder> decoder,
const SbMediaAudioHeader& audio_header);
~AudioRendererImpl() SB_OVERRIDE;
@@ -57,7 +63,7 @@
void Seek(SbMediaTime seek_to_pts) SB_OVERRIDE;
bool IsEndOfStreamWritten() const SB_OVERRIDE {
- return end_of_stream_written_;
+ return eos_state_.load() >= kEOSWrittenToDecoder;
};
bool IsEndOfStreamPlayed() const SB_OVERRIDE;
bool CanAcceptMoreData() const SB_OVERRIDE;
@@ -65,15 +71,23 @@
SbMediaTime GetCurrentTime() SB_OVERRIDE;
private:
- // Preroll considered finished after either kPrerollFrames is cached or EOS
- // is reached.
- static const size_t kPrerollFrames = 64 * 1024;
+ enum EOSState {
+ kEOSNotReceived,
+ kEOSWrittenToDecoder,
+ kEOSDecoded,
+ kEOSSentToSink
+ };
+
// Set a soft limit for the max audio frames we can cache so we can:
// 1. Avoid using too much memory.
- // 2. Have the audio cache full to simulate the state that the renderer can
- // no longer accept more data.
+ // 2. Have the audio cache full to simulate the state that the renderer can no
+ // longer accept more data.
static const size_t kMaxCachedFrames = 256 * 1024;
+ // The audio renderer tries to append |kAppendFrameUnit| frames every time to
+ // the sink buffer.
+ static const size_t kFrameAppendUnit = 16384;
+ void CreateAudioSinkAndResampler();
void UpdateSourceStatus(int* frames_in_buffer,
int* offset_in_frames,
bool* is_playing,
@@ -81,9 +95,11 @@
void ConsumeFrames(int frames_consumed);
void LogFramesConsumed();
- void ReadFromDecoder();
- bool AppendDecodedAudio_Locked(
- const scoped_refptr<DecodedAudio>& decoded_audio);
+ void OnDecoderConsumed();
+ void OnDecoderOutput();
+ void ProcessAudioData();
+ void FillResamplerAndTimeStretcher();
+ bool AppendAudioToFrameBuffer();
// SbAudioSink callbacks
static void UpdateSourceStatusFunc(int* frames_in_buffer,
@@ -93,32 +109,35 @@
void* context);
static void ConsumeFramesFunc(int frames_consumed, void* context);
- JobQueue* job_queue_;
+ atomic_int32_t eos_state_;
const int channels_;
+ const SbMediaAudioSampleType sink_sample_type_;
const int bytes_per_frame_;
+ scoped_ptr<AudioResampler> resampler_;
+ AudioTimeStretcher time_stretcher_;
double playback_rate_;
- Mutex mutex_;
- bool paused_;
- bool seeking_;
+ atomic_bool paused_;
+ atomic_bool seeking_;
SbMediaTime seeking_to_pts_;
std::vector<uint8_t> frame_buffer_;
uint8_t* frame_buffers_[1];
- int frames_in_buffer_;
- int offset_in_frames_;
+ atomic_int64_t frames_sent_to_sink_;
+ atomic_int64_t frames_consumed_by_sink_;
+ atomic_int32_t frames_consumed_by_sink_since_last_get_current_time_;
- int frames_consumed_;
- SbTimeMonotonic frames_consumed_set_at_;
- bool end_of_stream_written_;
- bool end_of_stream_decoded_;
+ int32_t pending_decoder_outputs_;
+ AudioFrameTracker audio_frame_tracker_;
+ atomic_int64_t frames_consumed_set_at_;
Closure log_frames_consumed_closure_;
scoped_ptr<AudioDecoder> decoder_;
SbAudioSink audio_sink_;
- scoped_refptr<DecodedAudio> pending_decoded_audio_;
- Closure read_from_decoder_closure_;
+
+ bool can_accept_more_data_;
+ bool process_audio_data_scheduled_;
// Our owner will attempt to seek to pts 0 when playback begins. In
// general, seeking could require a full reset of the underlying decoder on
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal_test.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal_test.cc
new file mode 100644
index 0000000..5737d57
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal_test.cc
@@ -0,0 +1,439 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h"
+
+#include <set>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/media.h"
+#include "starboard/memory.h"
+#include "starboard/shared/starboard/media/media_util.h"
+#include "starboard/shared/starboard/player/filter/mock_audio_decoder.h"
+#include "starboard/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+namespace testing {
+namespace {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SaveArg;
+
+// TODO: Inject and test the renderer using SbAudioSink mock. Otherwise the
+// tests rely on a correctly implemented audio sink and may fail on other audio
+// sinks.
+
+class AudioRendererImplTest : public ::testing::Test {
+ protected:
+ static const int kDefaultNumberOfChannels = 2;
+ static const int kDefaultSamplesPerSecond = 100000;
+ static const SbMediaAudioSampleType kDefaultAudioSampleType =
+ kSbMediaAudioSampleTypeFloat32;
+ static const SbMediaAudioFrameStorageType kDefaultAudioFrameStorageType =
+ kSbMediaAudioFrameStorageTypeInterleaved;
+
+ AudioRendererImplTest() {
+ ResetToFormat(kSbMediaAudioSampleTypeFloat32,
+ kSbMediaAudioFrameStorageTypeInterleaved);
+ }
+
+ // This function should be called in the fixture before any other functions
+ // to set the desired format of the decoder.
+ void ResetToFormat(SbMediaAudioSampleType sample_type,
+ SbMediaAudioFrameStorageType storage_type) {
+ audio_renderer_.reset(NULL);
+ sample_type_ = sample_type;
+ storage_type_ = storage_type;
+ audio_decoder_ = new MockAudioDecoder(sample_type_, storage_type_,
+ kDefaultSamplesPerSecond);
+ EXPECT_CALL(*audio_decoder_, Initialize(_))
+ .WillOnce(SaveArg<0>(&output_cb_));
+ audio_renderer_.reset(
+ new AudioRendererImpl(make_scoped_ptr<AudioDecoder>(audio_decoder_),
+ GetDefaultAudioHeader()));
+ }
+
+ void WriteSample(InputBuffer input_buffer) {
+ ASSERT_TRUE(input_buffer.is_valid());
+ ASSERT_FALSE(consumed_cb_.is_valid());
+
+ buffers_in_decoder_.insert(input_buffer.data());
+ EXPECT_CALL(*audio_decoder_, Decode(input_buffer, _))
+ .WillOnce(SaveArg<1>(&consumed_cb_));
+ audio_renderer_->WriteSample(input_buffer);
+ job_queue_.RunUntilIdle();
+
+ ASSERT_TRUE(consumed_cb_.is_valid());
+ }
+
+ void WriteEndOfStream() {
+ EXPECT_CALL(*audio_decoder_, WriteEndOfStream());
+ audio_renderer_->WriteEndOfStream();
+ job_queue_.RunUntilIdle();
+ }
+
+ void Seek(SbMediaTime seek_to_pts) {
+ audio_renderer_->Seek(seek_to_pts);
+ job_queue_.RunUntilIdle();
+ EXPECT_TRUE(audio_renderer_->IsSeekingInProgress());
+ }
+
+ void CallConsumedCB() {
+ ASSERT_TRUE(consumed_cb_.is_valid());
+ consumed_cb_.Run();
+ consumed_cb_.reset();
+ job_queue_.RunUntilIdle();
+ }
+
+ void SendDecoderOutput(const scoped_refptr<DecodedAudio>& decoded_audio) {
+ ASSERT_TRUE(output_cb_.is_valid());
+
+ EXPECT_CALL(*audio_decoder_, Read()).WillOnce(Return(decoded_audio));
+ output_cb_.Run();
+ job_queue_.RunUntilIdle();
+ }
+
+ InputBuffer CreateInputBuffer(SbMediaTime pts) {
+ const int kInputBufferSize = 4;
+ return InputBuffer(kSbMediaTypeAudio, DeallocateSampleCB, NULL, this,
+ SbMemoryAllocate(kInputBufferSize), kInputBufferSize,
+ pts, NULL, NULL);
+ }
+
+ scoped_refptr<DecodedAudio> CreateDecodedAudio(SbMediaTime pts, int frames) {
+ scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
+ kDefaultNumberOfChannels, sample_type_, storage_type_, pts,
+ frames * kDefaultNumberOfChannels *
+ media::GetBytesPerSample(sample_type_));
+ SbMemorySet(decoded_audio->buffer(), 0, decoded_audio->size());
+ return decoded_audio;
+ }
+
+ SbMediaAudioSampleType sample_type_;
+ SbMediaAudioFrameStorageType storage_type_;
+
+ JobQueue job_queue_;
+ std::set<const void*> buffers_in_decoder_;
+
+ Closure output_cb_;
+ Closure consumed_cb_;
+
+ scoped_ptr<AudioRenderer> audio_renderer_;
+ MockAudioDecoder* audio_decoder_;
+
+ private:
+ void OnDeallocateSample(const void* sample_buffer) {
+ ASSERT_TRUE(buffers_in_decoder_.find(sample_buffer) !=
+ buffers_in_decoder_.end());
+ buffers_in_decoder_.erase(buffers_in_decoder_.find(sample_buffer));
+ SbMemoryDeallocate(const_cast<void*>(sample_buffer));
+ }
+
+ static SbMediaAudioHeader GetDefaultAudioHeader() {
+ SbMediaAudioHeader audio_header = {};
+
+ audio_header.number_of_channels = kDefaultNumberOfChannels;
+ audio_header.samples_per_second = kDefaultSamplesPerSecond;
+ audio_header.bits_per_sample = 32;
+ audio_header.average_bytes_per_second = audio_header.samples_per_second *
+ audio_header.number_of_channels *
+ audio_header.bits_per_sample / 8;
+ audio_header.block_alignment = 4;
+ audio_header.audio_specific_config_size = 0;
+
+ return audio_header;
+ }
+
+ static void DeallocateSampleCB(SbPlayer player,
+ void* context,
+ const void* sample_buffer) {
+ AudioRendererImplTest* test = static_cast<AudioRendererImplTest*>(context);
+ EXPECT_TRUE(test != NULL);
+ test->OnDeallocateSample(sample_buffer);
+ }
+};
+
+TEST_F(AudioRendererImplTest, StateAfterConstructed) {
+ EXPECT_FALSE(audio_renderer_->IsEndOfStreamWritten());
+ EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
+ EXPECT_TRUE(audio_renderer_->CanAcceptMoreData());
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+ EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
+}
+
+TEST_F(AudioRendererImplTest, SunnyDay) {
+ Seek(0);
+
+ const int kFramesPerBuffer = 1024;
+
+ int frames_written = 0;
+ int preroll_frames = kDefaultSamplesPerSecond *
+ AudioRendererImpl::kPrerollTime / kSbTimeSecond;
+
+ while (frames_written <= preroll_frames) {
+ SbMediaTime pts = frames_written / kDefaultSamplesPerSecond;
+ WriteSample(CreateInputBuffer(pts));
+ CallConsumedCB();
+ SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer));
+ frames_written += kFramesPerBuffer;
+ }
+
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+
+ WriteEndOfStream();
+
+ EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+
+ audio_renderer_->Play();
+
+ SendDecoderOutput(new DecodedAudio);
+
+ SbMediaTime media_time = audio_renderer_->GetCurrentTime();
+
+ while (!audio_renderer_->IsEndOfStreamPlayed()) {
+ SbThreadSleep(AudioRendererImpl::kPrerollTime);
+ SbMediaTime new_media_time = audio_renderer_->GetCurrentTime();
+ EXPECT_GT(new_media_time, media_time);
+ media_time = new_media_time;
+ }
+}
+
+TEST_F(AudioRendererImplTest, SunnyDayWithDoublePlaybackRateAndInt16Samples) {
+ const int kPlaybackRate = 2;
+
+ ResetToFormat(kSbMediaAudioSampleTypeInt16,
+ kSbMediaAudioFrameStorageTypeInterleaved);
+ audio_renderer_->SetPlaybackRate(static_cast<float>(kPlaybackRate));
+
+ Seek(0);
+
+ const int kFramesPerBuffer = 1024;
+
+ int frames_written = 0;
+ int preroll_frames = kDefaultSamplesPerSecond *
+ AudioRendererImpl::kPrerollTime / kSbTimeSecond *
+ kPlaybackRate * 2;
+
+ while (frames_written <= preroll_frames) {
+ SbMediaTime pts = frames_written / kDefaultSamplesPerSecond;
+ WriteSample(CreateInputBuffer(pts));
+ CallConsumedCB();
+ SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer));
+ frames_written += kFramesPerBuffer;
+
+ if (!audio_renderer_->IsSeekingInProgress()) {
+ break;
+ }
+ }
+
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+
+ WriteEndOfStream();
+
+ EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+
+ audio_renderer_->Play();
+
+ SendDecoderOutput(new DecodedAudio);
+
+ SbMediaTime media_time = audio_renderer_->GetCurrentTime();
+
+ while (!audio_renderer_->IsEndOfStreamPlayed()) {
+ SbThreadSleep(AudioRendererImpl::kPrerollTime);
+ SbMediaTime new_media_time = audio_renderer_->GetCurrentTime();
+ EXPECT_GT(new_media_time, media_time);
+ media_time = new_media_time;
+ }
+}
+
+TEST_F(AudioRendererImplTest, StartPlayBeforePreroll) {
+ Seek(0);
+
+ const int kFramesPerBuffer = 1024;
+
+ int frames_written = 0;
+ int preroll_frames = kDefaultSamplesPerSecond *
+ AudioRendererImpl::kPrerollTime / kSbTimeSecond;
+
+ audio_renderer_->Play();
+
+ while (frames_written <= preroll_frames) {
+ SbMediaTime pts = frames_written / kDefaultSamplesPerSecond;
+ WriteSample(CreateInputBuffer(pts));
+ CallConsumedCB();
+ SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer));
+ frames_written += kFramesPerBuffer;
+ }
+
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+
+ WriteEndOfStream();
+ SendDecoderOutput(new DecodedAudio);
+
+ SbMediaTime media_time = audio_renderer_->GetCurrentTime();
+
+ while (!audio_renderer_->IsEndOfStreamPlayed()) {
+ SbThreadSleep(AudioRendererImpl::kPrerollTime);
+ SbMediaTime new_media_time = audio_renderer_->GetCurrentTime();
+ EXPECT_GT(new_media_time, media_time);
+ media_time = new_media_time;
+ }
+}
+
+TEST_F(AudioRendererImplTest, DecoderReturnsEOSWithoutAnyData) {
+ Seek(0);
+
+ WriteSample(CreateInputBuffer(0));
+ CallConsumedCB();
+
+ WriteEndOfStream();
+
+ EXPECT_TRUE(audio_renderer_->IsEndOfStreamWritten());
+ EXPECT_FALSE(audio_renderer_->CanAcceptMoreData());
+ EXPECT_TRUE(audio_renderer_->IsSeekingInProgress());
+
+ // Return EOS from decoder without sending any audio data, which is valid.
+ SendDecoderOutput(new DecodedAudio);
+
+ EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+ EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
+}
+
+// Test decoders that take many input samples before returning any output.
+TEST_F(AudioRendererImplTest, DecoderConsumeAllInputBeforeReturningData) {
+ Seek(0);
+
+ for (int i = 0; i < 128; ++i) {
+ WriteSample(CreateInputBuffer(i));
+ CallConsumedCB();
+
+ if (!audio_renderer_->CanAcceptMoreData()) {
+ break;
+ }
+ }
+
+ // Send an EOS to "force" the decoder return data.
+ WriteEndOfStream();
+
+ EXPECT_TRUE(audio_renderer_->IsEndOfStreamWritten());
+ EXPECT_FALSE(audio_renderer_->CanAcceptMoreData());
+ EXPECT_TRUE(audio_renderer_->IsSeekingInProgress());
+
+ // Return EOS from decoder without sending any audio data, which is valid.
+ SendDecoderOutput(new DecodedAudio);
+
+ EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+ EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
+}
+
+TEST_F(AudioRendererImplTest, MoreNumberOfOuputBuffersThanInputBuffers) {
+ Seek(0);
+
+ const int kFramesPerBuffer = 1024;
+
+ int frames_written = 0;
+ int preroll_frames = kDefaultSamplesPerSecond *
+ AudioRendererImpl::kPrerollTime / kSbTimeSecond;
+
+ while (frames_written <= preroll_frames) {
+ SbMediaTime pts = frames_written / kDefaultSamplesPerSecond;
+ WriteSample(CreateInputBuffer(pts));
+ CallConsumedCB();
+ SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer / 2));
+ frames_written += kFramesPerBuffer / 2;
+ pts = frames_written / kDefaultSamplesPerSecond;
+ SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer / 2));
+ frames_written += kFramesPerBuffer / 2;
+ }
+
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+
+ WriteEndOfStream();
+
+ EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+
+ audio_renderer_->Play();
+
+ SendDecoderOutput(new DecodedAudio);
+
+ SbMediaTime media_time = audio_renderer_->GetCurrentTime();
+
+ while (!audio_renderer_->IsEndOfStreamPlayed()) {
+ SbThreadSleep(AudioRendererImpl::kPrerollTime);
+ SbMediaTime new_media_time = audio_renderer_->GetCurrentTime();
+ EXPECT_GT(new_media_time, media_time);
+ media_time = new_media_time;
+ }
+}
+
+TEST_F(AudioRendererImplTest, LessNumberOfOuputBuffersThanInputBuffers) {
+ Seek(0);
+
+ const int kFramesPerBuffer = 1024;
+
+ int frames_written = 0;
+ int preroll_frames = kDefaultSamplesPerSecond *
+ AudioRendererImpl::kPrerollTime / kSbTimeSecond;
+
+ while (frames_written <= preroll_frames) {
+ SbMediaTime pts = frames_written / kDefaultSamplesPerSecond;
+ WriteSample(CreateInputBuffer(pts));
+ CallConsumedCB();
+ frames_written += kFramesPerBuffer;
+ }
+
+ SendDecoderOutput(CreateDecodedAudio(0, frames_written));
+
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+
+ WriteEndOfStream();
+
+ EXPECT_EQ(audio_renderer_->GetCurrentTime(), 0);
+ EXPECT_FALSE(audio_renderer_->IsSeekingInProgress());
+
+ audio_renderer_->Play();
+
+ SendDecoderOutput(new DecodedAudio);
+
+ SbMediaTime media_time = audio_renderer_->GetCurrentTime();
+
+ while (!audio_renderer_->IsEndOfStreamPlayed()) {
+ SbThreadSleep(AudioRendererImpl::kPrerollTime);
+ SbMediaTime new_media_time = audio_renderer_->GetCurrentTime();
+ EXPECT_GT(new_media_time, media_time);
+ media_time = new_media_time;
+ }
+}
+
+TEST_F(AudioRendererImplTest, Seek) {}
+
+} // namespace
+} // namespace testing
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
index 816c959..ccb12a7 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -42,6 +42,7 @@
virtual void Seek(SbMediaTime seek_to_pts) = 0;
virtual bool IsEndOfStreamWritten() const = 0;
virtual bool IsEndOfStreamPlayed() const = 0;
+ // TODO: Replace polling with callbacks.
virtual bool CanAcceptMoreData() const = 0;
virtual bool IsSeekingInProgress() const = 0;
virtual SbMediaTime GetCurrentTime() = 0;
diff --git a/src/starboard/shared/starboard/player/filter/audio_resampler.h b/src/starboard/shared/starboard/player/filter/audio_resampler.h
new file mode 100644
index 0000000..c994fac
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/audio_resampler.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 STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RESAMPLER_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RESAMPLER_H_
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+// Classes inherited from this interface can convert input audio samples in one
+// sample and storage type into another sample and storage type. For example,
+// it can convert planar int16 samples into interleaved float32 samples.
+// All functions (including Create() and the dtor) of the class should be called
+// on the same thread as the JobQueue passed in the Create() function.
+// It doesn't have a function to reset its internal state so during a seek the
+// user of this class shoudld destroy and recreate it.
+class AudioResampler {
+ public:
+ typedef ::starboard::shared::starboard::player::DecodedAudio DecodedAudio;
+
+ virtual ~AudioResampler() {}
+
+ // Write frames to the AudioResampler. The format of the frames is determined
+ // by the input formats passed to Create().
+ virtual scoped_refptr<DecodedAudio> Resample(
+ const scoped_refptr<DecodedAudio>& audio_data) = 0;
+
+ // Signal that the last audio input frame has been written. The resampler
+ // should allow for reading of any audio data inside its internal cache. The
+ // caller should continue call Read() after calling this function until Read()
+ // returns EOS.
+ virtual scoped_refptr<DecodedAudio> WriteEndOfStream() = 0;
+
+ // Create an AudioResampler that takes input specified by |source_*| and
+ // produce output specified by |destination_*|. The input and output have to
+ // have the same number of channels which is specified in |channels|.
+ // The |output_cb| will be called whenever there is resampled data ready. It
+ // is always called asynchronously on |job_queue| and then the user of
+ // AudioResampler can call Read() to read the next chunk of resampled data.
+ static scoped_ptr<AudioResampler> Create(
+ SbMediaAudioSampleType source_sample_type,
+ SbMediaAudioFrameStorageType source_storage_type,
+ int source_sample_rate,
+ SbMediaAudioSampleType destination_sample_type,
+ SbMediaAudioFrameStorageType destination_storage_type,
+ int destination_sample_rate,
+ int channels);
+};
+
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_RESAMPLER_H_
diff --git a/src/starboard/shared/starboard/player/filter/audio_resampler_impl.cc b/src/starboard/shared/starboard/player/filter/audio_resampler_impl.cc
new file mode 100644
index 0000000..4ad3330
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/audio_resampler_impl.cc
@@ -0,0 +1,86 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+#include "starboard/shared/starboard/player/filter/audio_resampler.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+namespace {
+
+// CPU based simple AudioResampler implementation. Note that it currently only
+// supports resample audio between same storage type and same channels but with
+// different sample types.
+class AudioResamplerImpl : public AudioResampler {
+ public:
+ AudioResamplerImpl(SbMediaAudioSampleType source_sample_type,
+ SbMediaAudioFrameStorageType source_storage_type,
+ SbMediaAudioSampleType destination_sample_type,
+ SbMediaAudioFrameStorageType destination_storage_type,
+ int channels)
+ : source_sample_type_(source_sample_type),
+ source_storage_type_(source_storage_type),
+ destination_sample_type_(destination_sample_type),
+ destination_storage_type_(destination_storage_type),
+ channels_(channels) {}
+
+ scoped_refptr<DecodedAudio> Resample(
+ const scoped_refptr<DecodedAudio>& audio_data) SB_OVERRIDE {
+ SB_DCHECK(audio_data->sample_type() == source_sample_type_);
+ SB_DCHECK(audio_data->storage_type() == source_storage_type_);
+ SB_DCHECK(audio_data->channels() == channels_);
+ audio_data->SwitchFormatTo(destination_sample_type_,
+ destination_storage_type_);
+ return audio_data;
+ }
+
+ scoped_refptr<DecodedAudio> WriteEndOfStream() SB_OVERRIDE {
+ return new DecodedAudio;
+ }
+
+ private:
+ SbMediaAudioSampleType source_sample_type_;
+ SbMediaAudioFrameStorageType source_storage_type_;
+ SbMediaAudioSampleType destination_sample_type_;
+ SbMediaAudioFrameStorageType destination_storage_type_;
+ int channels_;
+};
+
+} // namespace
+
+// static
+scoped_ptr<AudioResampler> AudioResampler::Create(
+ SbMediaAudioSampleType source_sample_type,
+ SbMediaAudioFrameStorageType source_storage_type,
+ int source_sample_rate,
+ SbMediaAudioSampleType destination_sample_type,
+ SbMediaAudioFrameStorageType destination_storage_type,
+ int destination_sample_rate,
+ int channels) {
+ SB_DCHECK(source_sample_rate == destination_sample_rate);
+ return scoped_ptr<AudioResampler>(new AudioResamplerImpl(
+ source_sample_type, source_storage_type, destination_sample_type,
+ destination_storage_type, channels));
+}
+
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_algorithm.cc b/src/starboard/shared/starboard/player/filter/audio_time_stretcher.cc
similarity index 61%
rename from src/starboard/shared/starboard/player/filter/audio_renderer_algorithm.cc
rename to src/starboard/shared/starboard/player/filter/audio_time_stretcher.cc
index 5143d7b..5d5b589 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_algorithm.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_time_stretcher.cc
@@ -2,19 +2,34 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "cobalt/media/filters/audio_renderer_algorithm.h"
+// Modifications Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/player/filter/audio_time_stretcher.h"
#include <algorithm>
#include <cmath>
-#include "base/logging.h"
-#include "cobalt/media/base/audio_bus.h"
-#include "cobalt/media/base/limits.h"
-#include "cobalt/media/filters/wsola_internals.h"
+#include "starboard/log.h"
#include "starboard/memory.h"
+#include "starboard/shared/starboard/player/filter/wsola_internal.h"
-namespace cobalt {
-namespace media {
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
// Waveform Similarity Overlap-and-add (WSOLA).
//
@@ -65,8 +80,9 @@
// The minimum size in ms for the |audio_buffer_|. Arbitrarily determined.
static const int kStartingCapacityInMs = 200;
-AudioRendererAlgorithm::AudioRendererAlgorithm()
+AudioTimeStretcher::AudioTimeStretcher()
: channels_(0),
+ bytes_per_frame_(0),
samples_per_second_(0),
muted_partial_frame_(0),
capacity_(0),
@@ -81,16 +97,14 @@
initial_capacity_(0),
max_capacity_(0) {}
-AudioRendererAlgorithm::~AudioRendererAlgorithm() {}
+AudioTimeStretcher::~AudioTimeStretcher() {}
-void AudioRendererAlgorithm::Initialize(const AudioParameters& params) {
- CHECK(params.IsValid());
-
- channels_ = params.channels();
- samples_per_second_ = params.sample_rate();
+void AudioTimeStretcher::Initialize(int channels, int samples_per_second) {
+ channels_ = channels;
+ bytes_per_frame_ = sizeof(float) * channels_;
+ samples_per_second_ = samples_per_second;
initial_capacity_ = capacity_ =
- std::max(params.frames_per_buffer() * 2,
- ConvertMillisecondsToFrames(kStartingCapacityInMs));
+ ConvertMillisecondsToFrames(kStartingCapacityInMs);
max_capacity_ =
std::max(initial_capacity_, kMaxCapacityInSeconds * samples_per_second_);
num_candidate_blocks_ = ConvertMillisecondsToFrames(kWsolaSearchIntervalMs);
@@ -130,23 +144,40 @@
internal::GetSymmetricHanningWindow(2 * ola_window_size_,
transition_window_.get());
- wsola_output_ = AudioBus::Create(channels_, ola_window_size_ + ola_hop_size_);
- wsola_output_->Zero(); // Initialize for overlap-and-add of the first block.
+ wsola_output_ =
+ new DecodedAudio(channels_, kSbMediaAudioSampleTypeFloat32,
+ kSbMediaAudioFrameStorageTypeInterleaved, 0,
+ (ola_window_size_ + ola_hop_size_) * bytes_per_frame_);
+ // Initialize for overlap-and-add of the first block.
+ SbMemorySet(wsola_output_->buffer(), 0, wsola_output_->size());
// Auxiliary containers.
- optimal_block_ = AudioBus::Create(channels_, ola_window_size_);
- search_block_ = AudioBus::Create(
- channels_, num_candidate_blocks_ + (ola_window_size_ - 1));
- target_block_ = AudioBus::Create(channels_, ola_window_size_);
+ optimal_block_ = new DecodedAudio(channels_, kSbMediaAudioSampleTypeFloat32,
+ kSbMediaAudioFrameStorageTypeInterleaved, 0,
+ ola_window_size_ * bytes_per_frame_);
+ search_block_ = new DecodedAudio(
+ channels_, kSbMediaAudioSampleTypeFloat32,
+ kSbMediaAudioFrameStorageTypeInterleaved, 0,
+ (num_candidate_blocks_ + (ola_window_size_ - 1)) * bytes_per_frame_);
+ target_block_ = new DecodedAudio(channels_, kSbMediaAudioSampleTypeFloat32,
+ kSbMediaAudioFrameStorageTypeInterleaved, 0,
+ ola_window_size_ * bytes_per_frame_);
}
-int AudioRendererAlgorithm::FillBuffer(AudioBus* dest, int dest_offset,
- int requested_frames,
- double playback_rate) {
- if (playback_rate == 0) return 0;
+scoped_refptr<DecodedAudio> AudioTimeStretcher::Read(int requested_frames,
+ double playback_rate) {
+ SB_DCHECK(bytes_per_frame_ > 0);
+ SB_DCHECK(playback_rate >= 0);
- DCHECK_GT(playback_rate, 0);
- DCHECK_EQ(channels_, dest->channels());
+ scoped_refptr<DecodedAudio> dest =
+ new DecodedAudio(channels_, kSbMediaAudioSampleTypeFloat32,
+ kSbMediaAudioFrameStorageTypeInterleaved, 0,
+ requested_frames * bytes_per_frame_);
+
+ if (playback_rate == 0) {
+ dest->ShrinkTo(0);
+ return dest;
+ }
// Optimize the muted case to issue a single clear instead of performing
// the full crossfade and clearing each crossfaded frame.
@@ -164,7 +195,7 @@
// audio_buffer_.frames()+1.
int seek_frames = std::min(static_cast<int>(muted_partial_frame_),
audio_buffer_.frames());
- dest->ZeroFramesPartial(dest_offset, frames_to_render);
+ SbMemorySet(dest->buffer(), 0, frames_to_render * bytes_per_frame_);
audio_buffer_.SeekFrames(seek_frames);
// Determine the partial frame that remains to be skipped for next call. If
@@ -173,7 +204,8 @@
// another playback rate that mutes, the code will attempt to line up the
// frames again.
muted_partial_frame_ -= seek_frames;
- return frames_to_render;
+ dest->ShrinkTo(frames_to_render * bytes_per_frame_);
+ return dest;
}
int slower_step = ceil(ola_window_size_ * playback_rate);
@@ -184,29 +216,29 @@
if (ola_window_size_ <= faster_step && slower_step >= ola_window_size_) {
const int frames_to_copy =
std::min(audio_buffer_.frames(), requested_frames);
- const int frames_read =
- audio_buffer_.ReadFrames(frames_to_copy, dest_offset, dest);
- DCHECK_EQ(frames_read, frames_to_copy);
- return frames_read;
+ const int frames_read = audio_buffer_.ReadFrames(frames_to_copy, 0, dest);
+ SB_DCHECK(frames_read == frames_to_copy);
+ dest->ShrinkTo(frames_read * bytes_per_frame_);
+ return dest;
}
int rendered_frames = 0;
do {
- rendered_frames +=
- WriteCompletedFramesTo(requested_frames - rendered_frames,
- dest_offset + rendered_frames, dest);
+ rendered_frames += WriteCompletedFramesTo(
+ requested_frames - rendered_frames, rendered_frames, dest);
} while (rendered_frames < requested_frames &&
RunOneWsolaIteration(playback_rate));
- return rendered_frames;
+ dest->ShrinkTo(rendered_frames * bytes_per_frame_);
+ return dest;
}
-void AudioRendererAlgorithm::FlushBuffers() {
+void AudioTimeStretcher::FlushBuffers() {
// Clear the queue of decoded packets (releasing the buffers).
audio_buffer_.Clear();
output_time_ = 0.0;
search_block_index_ = 0;
target_block_index_ = 0;
- wsola_output_->Zero();
+ SbMemorySet(wsola_output_->buffer(), 0, wsola_output_->size());
num_complete_frames_ = 0;
// Reset |capacity_| so growth triggered by underflows doesn't penalize seek
@@ -214,55 +246,57 @@
capacity_ = initial_capacity_;
}
-void AudioRendererAlgorithm::EnqueueBuffer(
- const scoped_refptr<AudioBuffer>& buffer_in) {
- DCHECK(!buffer_in->end_of_stream());
- audio_buffer_.Append(buffer_in);
+void AudioTimeStretcher::EnqueueBuffer(
+ const scoped_refptr<DecodedAudio>& audio_data) {
+ SB_DCHECK(!audio_data->is_end_of_stream());
+ audio_buffer_.Append(audio_data);
}
-bool AudioRendererAlgorithm::IsQueueFull() {
+bool AudioTimeStretcher::IsQueueFull() const {
return audio_buffer_.frames() >= capacity_;
}
-void AudioRendererAlgorithm::IncreaseQueueCapacity() {
- DCHECK_LE(capacity_, max_capacity_);
- capacity_ = std::min(2 * capacity_, max_capacity_);
-}
-
-int64_t AudioRendererAlgorithm::GetMemoryUsage() const {
- return audio_buffer_.frames() * channels_ * sizeof(float);
-}
-
-bool AudioRendererAlgorithm::CanPerformWsola() const {
+// TODO: Make order of functions in .cc the same as order of functions in .h.
+bool AudioTimeStretcher::CanPerformWsola() const {
const int search_block_size = num_candidate_blocks_ + (ola_window_size_ - 1);
const int frames = audio_buffer_.frames();
return target_block_index_ + ola_window_size_ <= frames &&
search_block_index_ + search_block_size <= frames;
}
-int AudioRendererAlgorithm::ConvertMillisecondsToFrames(int ms) const {
- return ms * (samples_per_second_ /
- static_cast<double>(base::Time::kMillisecondsPerSecond));
+int AudioTimeStretcher::ConvertMillisecondsToFrames(int ms) const {
+ const double kMillsecondsPerSeconds =
+ static_cast<double>(kSbTimeSecond / kSbTimeMillisecond);
+ return ms * (samples_per_second_ / kMillsecondsPerSeconds);
}
-bool AudioRendererAlgorithm::RunOneWsolaIteration(double playback_rate) {
+bool AudioTimeStretcher::RunOneWsolaIteration(double playback_rate) {
+ SB_DCHECK(bytes_per_frame_ > 0);
+
if (!CanPerformWsola()) return false;
GetOptimalBlock();
// Overlap-and-add.
for (int k = 0; k < channels_; ++k) {
- const float* const ch_opt_frame = optimal_block_->channel(k);
- float* ch_output = wsola_output_->channel(k) + num_complete_frames_;
+ const float* const ch_opt_frame =
+ reinterpret_cast<const float*>(optimal_block_->buffer()) + k;
+ float* ch_output = reinterpret_cast<float*>(wsola_output_->buffer()) + k +
+ num_complete_frames_ * sizeof(float);
for (int n = 0; n < ola_hop_size_; ++n) {
- ch_output[n] = ch_output[n] * ola_window_[ola_hop_size_ + n] +
- ch_opt_frame[n] * ola_window_[n];
+ ch_output[n * channels_] =
+ ch_output[n * channels_] * ola_window_[ola_hop_size_ + n] +
+ ch_opt_frame[n * channels_] * ola_window_[n];
}
-
- // Copy the second half to the output.
- SbMemoryCopy(&ch_output[ola_hop_size_], &ch_opt_frame[ola_hop_size_],
- sizeof(*ch_opt_frame) * ola_hop_size_);
}
+ // Copy the second half to the output.
+ const float* const ch_opt_frame =
+ reinterpret_cast<const float*>(optimal_block_->buffer());
+ float* ch_output = reinterpret_cast<float*>(wsola_output_->buffer()) +
+ num_complete_frames_ * sizeof(float);
+ SbMemoryCopy(&ch_output[ola_hop_size_ * channels_],
+ &ch_opt_frame[ola_hop_size_ * channels_],
+ sizeof(*ch_opt_frame) * ola_hop_size_ * channels_);
num_complete_frames_ += ola_hop_size_;
UpdateOutputTime(playback_rate, ola_hop_size_);
@@ -270,8 +304,8 @@
return true;
}
-void AudioRendererAlgorithm::UpdateOutputTime(double playback_rate,
- double time_change) {
+void AudioTimeStretcher::UpdateOutputTime(double playback_rate,
+ double time_change) {
output_time_ += time_change;
// Center of the search region, in frames.
const int search_block_center_index =
@@ -279,7 +313,7 @@
search_block_index_ = search_block_center_index - search_block_center_offset_;
}
-void AudioRendererAlgorithm::RemoveOldInputFrames(double playback_rate) {
+void AudioTimeStretcher::RemoveOldInputFrames(double playback_rate) {
const int earliest_used_index =
std::min(target_block_index_, search_block_index_);
if (earliest_used_index <= 0) return; // Nothing to remove.
@@ -291,31 +325,33 @@
// Adjust output index.
double output_time_change =
static_cast<double>(earliest_used_index) / playback_rate;
- CHECK_GE(output_time_, output_time_change);
+ SB_CHECK(output_time_ >= output_time_change);
UpdateOutputTime(playback_rate, -output_time_change);
}
-int AudioRendererAlgorithm::WriteCompletedFramesTo(int requested_frames,
- int dest_offset,
- AudioBus* dest) {
+int AudioTimeStretcher::WriteCompletedFramesTo(int requested_frames,
+ int dest_offset,
+ DecodedAudio* dest) {
+ SB_DCHECK(bytes_per_frame_ > 0);
+
int rendered_frames = std::min(num_complete_frames_, requested_frames);
if (rendered_frames == 0)
return 0; // There is nothing to read from |wsola_output_|, return.
- wsola_output_->CopyPartialFramesTo(0, rendered_frames, dest_offset, dest);
+ SbMemoryCopy(dest->buffer() + bytes_per_frame_ * dest_offset,
+ wsola_output_->buffer(), rendered_frames * bytes_per_frame_);
// Remove the frames which are read.
int frames_to_move = wsola_output_->frames() - rendered_frames;
- for (int k = 0; k < channels_; ++k) {
- float* ch = wsola_output_->channel(k);
- SbMemoryMove(ch, &ch[rendered_frames], sizeof(*ch) * frames_to_move);
- }
+ SbMemoryMove(wsola_output_->buffer(),
+ wsola_output_->buffer() + rendered_frames * bytes_per_frame_,
+ frames_to_move * bytes_per_frame_);
num_complete_frames_ -= rendered_frames;
return rendered_frames;
}
-bool AudioRendererAlgorithm::TargetIsWithinSearchRegion() const {
+bool AudioTimeStretcher::TargetIsWithinSearchRegion() const {
const int search_block_size = num_candidate_blocks_ + (ola_window_size_ - 1);
return target_block_index_ >= search_block_index_ &&
@@ -323,7 +359,7 @@
search_block_index_ + search_block_size;
}
-void AudioRendererAlgorithm::GetOptimalBlock() {
+void AudioTimeStretcher::GetOptimalBlock() {
int optimal_index = 0;
// An interval around last optimal block which is excluded from the search.
@@ -345,7 +381,9 @@
// |optimal_index| is in frames and it is relative to the beginning of the
// |search_block_|.
optimal_index = internal::OptimalIndex(
- search_block_.get(), target_block_.get(), exclude_iterval);
+ search_block_.get(), target_block_.get(),
+ kSbMediaAudioSampleTypeFloat32,
+ kSbMediaAudioFrameStorageTypeInterleaved, exclude_iterval);
// Translate |index| w.r.t. the beginning of |audio_buffer_| and extract the
// optimal block.
@@ -361,11 +399,13 @@
// where target-block has higher weight close to zero (weight of 1 at index
// 0) and lower weight close the end.
for (int k = 0; k < channels_; ++k) {
- float* ch_opt = optimal_block_->channel(k);
- const float* const ch_target = target_block_->channel(k);
+ float* ch_opt = reinterpret_cast<float*>(optimal_block_->buffer()) + k;
+ const float* const ch_target =
+ reinterpret_cast<float*>(target_block_->buffer()) + k;
for (int n = 0; n < ola_window_size_; ++n) {
- ch_opt[n] = ch_opt[n] * transition_window_[n] +
- ch_target[n] * transition_window_[ola_window_size_ + n];
+ ch_opt[n * channels_] =
+ ch_opt[n * channels_] * transition_window_[n] +
+ ch_target[n * channels_] * transition_window_[ola_window_size_ + n];
}
}
}
@@ -374,9 +414,10 @@
target_block_index_ = optimal_index + ola_hop_size_;
}
-void AudioRendererAlgorithm::PeekAudioWithZeroPrepend(int read_offset_frames,
- AudioBus* dest) {
- CHECK_LE(read_offset_frames + dest->frames(), audio_buffer_.frames());
+void AudioTimeStretcher::PeekAudioWithZeroPrepend(int read_offset_frames,
+ DecodedAudio* dest) {
+ SB_DCHECK(bytes_per_frame_ > 0);
+ SB_CHECK(read_offset_frames + dest->frames() <= audio_buffer_.frames());
int write_offset = 0;
int num_frames_to_read = dest->frames();
@@ -386,11 +427,14 @@
read_offset_frames = 0;
num_frames_to_read -= num_zero_frames_appended;
write_offset = num_zero_frames_appended;
- dest->ZeroFrames(num_zero_frames_appended);
+ SbMemorySet(dest->buffer(), 0, num_zero_frames_appended * bytes_per_frame_);
}
audio_buffer_.PeekFrames(num_frames_to_read, read_offset_frames, write_offset,
dest);
}
-} // namespace media
-} // namespace cobalt
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_algorithm.h b/src/starboard/shared/starboard/player/filter/audio_time_stretcher.h
similarity index 71%
rename from src/starboard/shared/starboard/player/filter/audio_renderer_algorithm.h
rename to src/starboard/shared/starboard/player/filter/audio_time_stretcher.h
index e2becc9..8ff6033 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_algorithm.h
+++ b/src/starboard/shared/starboard/player/filter/audio_time_stretcher.h
@@ -2,46 +2,58 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// AudioRendererAlgorithm buffers and transforms audio data. The owner of
-// this object provides audio data to the object through EnqueueBuffer() and
-// requests data from the buffer via FillBuffer().
+// Modifications 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.
+
+// AudioTimeStretcher buffers and transforms audio data. The owner of this
+// object provides audio data to the object through EnqueueBuffer() and requests
+// data from the buffer via FillBuffer().
//
// This class is *not* thread-safe. Calls to enqueue and retrieve data must be
// locked if called from multiple threads.
//
-// AudioRendererAlgorithm uses the Waveform Similarity Overlap and Add (WSOLA)
+// AudioTimeStretcher uses the Waveform Similarity Overlap and Add (WSOLA)
// algorithm to stretch or compress audio data to meet playback speeds less than
// or greater than the natural playback of the audio stream. The algorithm
// preserves local properties of the audio, therefore, pitch and harmonics are
-// are preserved. See audio_renderer_algorith.cc for a more elaborate
-// description of the algorithm.
+// preserved. See audio_renderer_algorith.cc for a more elaborate description of
+// the algorithm.
//
// Audio at very low or very high playback rates are muted to preserve quality.
-#ifndef COBALT_MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_H_
-#define COBALT_MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_H_
+#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_TIME_STRETCHER_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_TIME_STRETCHER_H_
-#include <memory>
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "cobalt/media/base/audio_buffer.h"
-#include "cobalt/media/base/audio_buffer_queue.h"
-#include "cobalt/media/base/audio_parameters.h"
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/decoded_audio_queue.h"
#include "starboard/types.h"
-namespace cobalt {
-namespace media {
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
-class AudioBus;
-
-class MEDIA_EXPORT AudioRendererAlgorithm {
+class AudioTimeStretcher {
public:
- AudioRendererAlgorithm();
- ~AudioRendererAlgorithm();
+ AudioTimeStretcher();
+ ~AudioTimeStretcher();
// Initializes this object with information about the audio stream.
- void Initialize(const AudioParameters& params);
+ void Initialize(int channels, int samples_per_second);
// Tries to fill |requested_frames| frames into |dest| with possibly scaled
// data from our |audio_buffer_|. Data is scaled based on |playback_rate|,
@@ -52,36 +64,23 @@
// |dest_offset| is the offset in frames for writing into |dest|.
//
// Returns the number of frames copied into |dest|.
- int FillBuffer(AudioBus* dest, int dest_offset, int requested_frames,
- double playback_rate);
+ scoped_refptr<DecodedAudio> Read(int requested_frames, double playback_rate);
// Clears |audio_buffer_|.
void FlushBuffers();
// Enqueues a buffer. It is called from the owner of the algorithm after a
// read completes.
- void EnqueueBuffer(const scoped_refptr<AudioBuffer>& buffer_in);
+ void EnqueueBuffer(const scoped_refptr<DecodedAudio>& audio_data);
// Returns true if |audio_buffer_| is at or exceeds capacity.
- bool IsQueueFull();
-
- // Returns the capacity of |audio_buffer_| in frames.
- int QueueCapacity() const { return capacity_; }
-
- // Increase the capacity of |audio_buffer_| if possible.
- void IncreaseQueueCapacity();
-
- // Returns an estimate of the amount of memory (in bytes) used for frames.
- int64_t GetMemoryUsage() const;
+ bool IsQueueFull() const;
// Returns the number of frames left in |audio_buffer_|, which may be larger
// than QueueCapacity() in the event that EnqueueBuffer() delivered more data
// than |audio_buffer_| was intending to hold.
int frames_buffered() { return audio_buffer_.frames(); }
- // Returns the samples per second for this audio stream.
- int samples_per_second() { return samples_per_second_; }
-
private:
// Within |search_block_|, find the block of data that is most similar to
// |target_block_|, and write it in |optimal_block_|. This method assumes that
@@ -91,8 +90,9 @@
// Read a maximum of |requested_frames| frames from |wsola_output_|. Returns
// number of frames actually read.
- int WriteCompletedFramesTo(int requested_frames, int output_offset,
- AudioBus* dest);
+ int WriteCompletedFramesTo(int requested_frames,
+ int output_offset,
+ DecodedAudio* dest);
// Fill |dest| with frames from |audio_buffer_| starting from frame
// |read_offset_frames|. |dest| is expected to have the same number of
@@ -101,7 +101,7 @@
// for negative indices. This might happen for few first frames. This method
// assumes there is enough frames to fill |dest|, i.e. |read_offset_frames| +
// |dest->frames()| does not extend to future.
- void PeekAudioWithZeroPrepend(int read_offset_frames, AudioBus* dest);
+ void PeekAudioWithZeroPrepend(int read_offset_frames, DecodedAudio* dest);
// Run one iteration of WSOLA, if there are sufficient frames. This will
// overlap-and-add one block to |wsola_output_|, hence, |num_complete_frames_|
@@ -129,11 +129,14 @@
// Number of channels in audio stream.
int channels_;
+ // Bytes per audio frame.
+ int bytes_per_frame_;
+
// Sample rate of audio stream.
int samples_per_second_;
// Buffered audio data.
- AudioBufferQueue audio_buffer_;
+ DecodedAudioQueue audio_buffer_;
// If muted, keep track of partial frames that should have been skipped over.
double muted_partial_frame_;
@@ -181,38 +184,41 @@
// number of requested samples. Furthermore, due to overlap-and-add,
// the last half-window of the output is incomplete, which is stored in this
// buffer.
- std::unique_ptr<AudioBus> wsola_output_;
+ scoped_refptr<DecodedAudio> wsola_output_;
// Overlap-and-add window.
- std::unique_ptr<float[]> ola_window_;
+ scoped_array<float> ola_window_;
// Transition window, used to update |optimal_block_| by a weighted sum of
// |optimal_block_| and |target_block_|.
- std::unique_ptr<float[]> transition_window_;
+ scoped_array<float> transition_window_;
// Auxiliary variables to avoid allocation in every iteration.
// Stores the optimal block in every iteration. This is the most
// similar block to |target_block_| within |search_block_| and it is
// overlap-and-added to |wsola_output_|.
- std::unique_ptr<AudioBus> optimal_block_;
+ scoped_refptr<DecodedAudio> optimal_block_;
// A block of data that search is performed over to find the |optimal_block_|.
- std::unique_ptr<AudioBus> search_block_;
+ scoped_refptr<DecodedAudio> search_block_;
// Stores the target block, denoted as |target| above. |search_block_| is
// searched for a block (|optimal_block_|) that is most similar to
// |target_block_|.
- std::unique_ptr<AudioBus> target_block_;
+ scoped_refptr<DecodedAudio> target_block_;
// The initial and maximum capacity calculated by Initialize().
int initial_capacity_;
int max_capacity_;
- DISALLOW_COPY_AND_ASSIGN(AudioRendererAlgorithm);
+ SB_DISALLOW_COPY_AND_ASSIGN(AudioTimeStretcher);
};
-} // namespace media
-} // namespace cobalt
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
-#endif // COBALT_MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_H_
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_TIME_STRETCHER_H_
diff --git a/src/starboard/shared/starboard/player/filter/decoded_audio_queue.cc b/src/starboard/shared/starboard/player/filter/decoded_audio_queue.cc
new file mode 100644
index 0000000..509453f
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/decoded_audio_queue.cc
@@ -0,0 +1,168 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Modifications Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/player/filter/decoded_audio_queue.h"
+
+#include <algorithm>
+
+#include "starboard/log.h"
+#include "starboard/media.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+DecodedAudioQueue::DecodedAudioQueue() {
+ Clear();
+}
+DecodedAudioQueue::~DecodedAudioQueue() {}
+
+void DecodedAudioQueue::Clear() {
+ buffers_.clear();
+ current_buffer_ = buffers_.begin();
+ current_buffer_offset_ = 0;
+ frames_ = 0;
+}
+
+void DecodedAudioQueue::Append(
+ const scoped_refptr<DecodedAudio>& decoded_audio) {
+ SB_DCHECK(decoded_audio->sample_type() == kSbMediaAudioSampleTypeFloat32);
+ SB_DCHECK(decoded_audio->storage_type() ==
+ kSbMediaAudioFrameStorageTypeInterleaved)
+ << decoded_audio->storage_type();
+ // Add the buffer to the queue. Inserting into deque invalidates all
+ // iterators, so point to the first buffer.
+ buffers_.push_back(decoded_audio);
+ current_buffer_ = buffers_.begin();
+
+ // Update the |frames_| counter since we have added frames.
+ frames_ += decoded_audio->frames();
+ SB_CHECK(frames_ > 0); // make sure it doesn't overflow.
+}
+
+int DecodedAudioQueue::ReadFrames(int frames,
+ int dest_frame_offset,
+ DecodedAudio* dest) {
+ SB_DCHECK(dest->frames() >= frames + dest_frame_offset);
+ return InternalRead(frames, true, 0, dest_frame_offset, dest);
+}
+
+int DecodedAudioQueue::PeekFrames(int frames,
+ int source_frame_offset,
+ int dest_frame_offset,
+ DecodedAudio* dest) {
+ SB_DCHECK(dest->frames() >= frames);
+ return InternalRead(frames, false, source_frame_offset, dest_frame_offset,
+ dest);
+}
+
+void DecodedAudioQueue::SeekFrames(int frames) {
+ // Perform seek only if we have enough bytes in the queue.
+ SB_CHECK(frames <= frames_);
+ int taken = InternalRead(frames, true, 0, 0, NULL);
+ SB_DCHECK(taken == frames);
+}
+
+int DecodedAudioQueue::InternalRead(int frames,
+ bool advance_position,
+ int source_frame_offset,
+ int dest_frame_offset,
+ DecodedAudio* dest) {
+ // Counts how many frames are actually read from the buffer queue.
+ int taken = 0;
+ BufferQueue::iterator current_buffer = current_buffer_;
+ int current_buffer_offset = current_buffer_offset_;
+
+ int frames_to_skip = source_frame_offset;
+ while (taken < frames) {
+ // |current_buffer| is valid since the first time this buffer is appended
+ // with data. Make sure there is data to be processed.
+ if (current_buffer == buffers_.end())
+ break;
+
+ scoped_refptr<DecodedAudio> buffer = *current_buffer;
+
+ int remaining_frames_in_buffer = buffer->frames() - current_buffer_offset;
+
+ if (frames_to_skip > 0) {
+ // If there are frames to skip, do it first. May need to skip into
+ // subsequent buffers.
+ int skipped = std::min(remaining_frames_in_buffer, frames_to_skip);
+ current_buffer_offset += skipped;
+ frames_to_skip -= skipped;
+ } else {
+ // Find the right amount to copy from the current buffer. We shall copy no
+ // more than |frames| frames in total and each single step copies no more
+ // than the current buffer size.
+ int copied = std::min(frames - taken, remaining_frames_in_buffer);
+
+ // if |dest| is NULL, there's no need to copy.
+ if (dest) {
+ SB_DCHECK(buffer->channels() == dest->channels());
+ const float* source = reinterpret_cast<const float*>(buffer->buffer()) +
+ buffer->channels() * current_buffer_offset;
+ float* destination = reinterpret_cast<float*>(dest->buffer()) +
+ dest->channels() * (dest_frame_offset + taken);
+ SbMemoryCopy(destination, source, copied * dest->channels() * 4);
+ }
+
+ // Increase total number of frames copied, which regulates when to end
+ // this loop.
+ taken += copied;
+
+ // We have read |copied| frames from the current buffer. Advance the
+ // offset.
+ current_buffer_offset += copied;
+ }
+
+ // Has the buffer has been consumed?
+ if (current_buffer_offset == buffer->frames()) {
+ // If we are at the last buffer, no more data to be copied, so stop.
+ BufferQueue::iterator next = current_buffer + 1;
+ if (next == buffers_.end())
+ break;
+
+ // Advances the iterator.
+ current_buffer = next;
+ current_buffer_offset = 0;
+ }
+ }
+
+ if (advance_position) {
+ // Update the appropriate values since |taken| frames have been copied out.
+ frames_ -= taken;
+ SB_DCHECK(frames_ >= 0);
+ SB_DCHECK(current_buffer_ != buffers_.end() || frames_ == 0);
+
+ // Remove any buffers before the current buffer as there is no going
+ // backwards.
+ buffers_.erase(buffers_.begin(), current_buffer);
+ current_buffer_ = buffers_.begin();
+ current_buffer_offset_ = current_buffer_offset;
+ }
+
+ return taken;
+}
+
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/decoded_audio_queue.h b/src/starboard/shared/starboard/player/filter/decoded_audio_queue.h
new file mode 100644
index 0000000..5dce63b
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/decoded_audio_queue.h
@@ -0,0 +1,108 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Modifications Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_DECODED_AUDIO_QUEUE_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_DECODED_AUDIO_QUEUE_H_
+
+#include <deque>
+
+#include "starboard/configuration.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+// A queue of AudioBuffers to support reading of arbitrary chunks of a media
+// data source. Audio data can be copied into an DecodedAudio for output. The
+// current position can be forwarded to anywhere in the buffered data.
+//
+// This class is not inherently thread-safe. Concurrent access must be
+// externally serialized.
+class DecodedAudioQueue {
+ public:
+ DecodedAudioQueue();
+ ~DecodedAudioQueue();
+
+ // Clears the buffer queue.
+ void Clear();
+
+ // Appends |decoded_audio| to this queue.
+ void Append(const scoped_refptr<DecodedAudio>& decoded_audio);
+
+ // Reads a maximum of |frames| frames into |dest| from the current position.
+ // Returns the number of frames read. The current position will advance by the
+ // amount of frames read. |dest_frame_offset| specifies a starting offset into
+ // |dest|. On each call, the frames are converted from their source format
+ // into the destination DecodedAudio.
+ int ReadFrames(int frames, int dest_frame_offset, DecodedAudio* dest);
+
+ // Copies up to |frames| frames from current position to |dest|. Returns
+ // number of frames copied. Doesn't advance current position. Starts at
+ // |source_frame_offset| from current position. |dest_frame_offset| specifies
+ // a starting offset into |dest|. On each call, the frames are converted from
+ // their source format into the destination DecodedAudio.
+ int PeekFrames(int frames,
+ int source_frame_offset,
+ int dest_frame_offset,
+ DecodedAudio* dest);
+
+ // Moves the current position forward by |frames| frames. If |frames| exceeds
+ // frames available, the seek operation will fail.
+ void SeekFrames(int frames);
+
+ // Returns the number of frames buffered beyond the current position.
+ int frames() const { return frames_; }
+
+ private:
+ // Definition of the buffer queue.
+ typedef std::deque<scoped_refptr<DecodedAudio> > BufferQueue;
+
+ // An internal method shared by ReadFrames() and SeekFrames() that actually
+ // does reading. It reads a maximum of |frames| frames into |dest|. Returns
+ // the number of frames read. The current position will be moved forward by
+ // the number of frames read if |advance_position| is set. If |dest| is NULL,
+ // only the current position will advance but no data will be copied.
+ // |source_frame_offset| can be used to skip frames before reading.
+ // |dest_frame_offset| specifies a starting offset into |dest|.
+ int InternalRead(int frames,
+ bool advance_position,
+ int source_frame_offset,
+ int dest_frame_offset,
+ DecodedAudio* dest);
+
+ BufferQueue::iterator current_buffer_;
+ BufferQueue buffers_;
+ int current_buffer_offset_;
+
+ // Number of frames available to be read in the buffer.
+ int frames_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(DecodedAudioQueue);
+};
+
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_DECODED_AUDIO_QUEUE_H_
diff --git a/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc b/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
index 5b19b46..033b543 100644
--- a/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
@@ -48,16 +48,16 @@
return scoped_ptr<PlayerComponents>(NULL);
}
- VideoDecoderImpl* video_decoder =
- new VideoDecoderImpl(video_parameters.video_codec);
+ VideoDecoderImpl* video_decoder = new VideoDecoderImpl(
+ video_parameters.video_codec, video_parameters.output_mode,
+ video_parameters.decode_target_graphics_context_provider);
if (!video_decoder->is_valid()) {
delete video_decoder;
return scoped_ptr<PlayerComponents>(NULL);
}
AudioRendererImpl* audio_renderer =
- new AudioRendererImpl(audio_parameters.job_queue,
- scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
+ new AudioRendererImpl(scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
audio_parameters.audio_header);
VideoRendererImpl* video_renderer = new VideoRendererImpl(
scoped_ptr<HostedVideoDecoder>(video_decoder).Pass());
diff --git a/src/starboard/shared/starboard/player/filter/filter_tests.gypi b/src/starboard/shared/starboard/player/filter/filter_tests.gypi
new file mode 100644
index 0000000..12ef856
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/filter_tests.gypi
@@ -0,0 +1,24 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'variables': {
+ # This will be included by 'starboard_platform_tests.gyp' on platforms that
+ # uses the filter graph based SbPlayer implementation. So full path names
+ # have to be used here.
+ 'filter_tests_sources': [
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_impl_internal_test.cc',
+ ],
+ },
+}
diff --git a/src/starboard/shared/starboard/player/filter/mock_audio_decoder.h b/src/starboard/shared/starboard/player/filter/mock_audio_decoder.h
new file mode 100644
index 0000000..d1726c2
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/mock_audio_decoder.h
@@ -0,0 +1,72 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_MOCK_AUDIO_DECODER_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_MOCK_AUDIO_DECODER_H_
+
+#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/closure.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+namespace testing {
+
+class MockAudioDecoder : public AudioDecoder {
+ public:
+ MockAudioDecoder(SbMediaAudioSampleType sample_type,
+ SbMediaAudioFrameStorageType storage_type,
+ int sample_per_second)
+ : sample_type_(sample_type),
+ storage_type_(storage_type),
+ samples_per_second_(sample_per_second) {}
+
+ MOCK_METHOD1(Initialize, void(const Closure&));
+ MOCK_METHOD2(Decode, void(const InputBuffer&, const Closure&));
+ MOCK_METHOD0(WriteEndOfStream, void());
+ MOCK_METHOD0(Read, scoped_refptr<DecodedAudio>());
+ MOCK_METHOD0(Reset, void());
+
+ SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE {
+ return sample_type_;
+ }
+ SbMediaAudioFrameStorageType GetStorageType() const SB_OVERRIDE {
+ return storage_type_;
+ }
+ int GetSamplesPerSecond() const SB_OVERRIDE { return samples_per_second_; }
+
+ private:
+ SbMediaAudioSampleType sample_type_;
+ SbMediaAudioFrameStorageType storage_type_;
+ int samples_per_second_;
+};
+
+} // namespace testing
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_MOCK_AUDIO_DECODER_H_
diff --git a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
index 8711274..0d3728d 100644
--- a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
@@ -16,8 +16,10 @@
#include <queue>
+#include "starboard/shared/starboard/player/closure.h"
#include "starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h"
#include "starboard/shared/starboard/player/filter/video_renderer_impl_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
namespace starboard {
namespace shared {
@@ -38,13 +40,17 @@
} // namespace
-class StubAudioDecoder : public AudioDecoder {
+class StubAudioDecoder : public AudioDecoder, JobQueue::JobOwner {
public:
explicit StubAudioDecoder(const SbMediaAudioHeader& audio_header)
: sample_type_(GetSupportedSampleType()),
audio_header_(audio_header),
stream_ended_(false) {}
- void Decode(const InputBuffer& input_buffer) SB_OVERRIDE {
+ void Initialize(const Closure& output_cb) SB_OVERRIDE {
+ output_cb_ = output_cb;
+ }
+ void Decode(const InputBuffer& input_buffer,
+ const Closure& consumed_cb) SB_OVERRIDE {
// Values to represent what kind of dummy audio to fill the decoded audio
// we produce with.
enum FillType {
@@ -79,6 +85,8 @@
}
}
last_input_buffer_ = input_buffer;
+ Schedule(consumed_cb);
+ Schedule(output_cb_);
}
void WriteEndOfStream() SB_OVERRIDE {
if (last_input_buffer_.is_valid()) {
@@ -89,6 +97,7 @@
}
decoded_audios_.push(new DecodedAudio());
stream_ended_ = true;
+ Schedule(output_cb_);
}
scoped_refptr<DecodedAudio> Read() SB_OVERRIDE {
scoped_refptr<DecodedAudio> result;
@@ -104,6 +113,8 @@
}
stream_ended_ = false;
last_input_buffer_ = InputBuffer();
+
+ CancelPendingJobs();
}
SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE {
return sample_type_;
@@ -118,6 +129,7 @@
private:
static const kMaxDecodedAudiosSize = 64;
+ Closure output_cb_;
SbMediaAudioSampleType sample_type_;
SbMediaAudioHeader audio_header_;
bool stream_ended_;
diff --git a/src/starboard/shared/starboard/player/filter/video_decoder_internal.h b/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
index 246ee6c..6dcce32 100644
--- a/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
@@ -83,7 +83,7 @@
const scoped_refptr<VideoFrame>& frame) = 0;
protected:
- ~Host() {}
+ virtual ~Host() {}
};
virtual void SetHost(Host* host) = 0;
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
index 2a35283..e0ca1a2 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
@@ -67,7 +67,7 @@
// Preroll considered finished after either kPrerollFrames is cached or EOS
// is reached.
- static const size_t kPrerollFrames = 8;
+ static const size_t kPrerollFrames = 1;
// Set a soft limit for the max video frames we can cache so we can:
// 1. Avoid using too much memory.
// 2. Have the frame cache full to simulate the state that the renderer can
diff --git a/src/starboard/shared/starboard/player/filter/wsola_internals.cc b/src/starboard/shared/starboard/player/filter/wsola_internal.cc
similarity index 67%
rename from src/starboard/shared/starboard/player/filter/wsola_internals.cc
rename to src/starboard/shared/starboard/player/filter/wsola_internal.cc
index 507893e..a7291cf 100644
--- a/src/starboard/shared/starboard/player/filter/wsola_internals.cc
+++ b/src/starboard/shared/starboard/player/filter/wsola_internal.cc
@@ -2,25 +2,44 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// Modifications 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.
+
// MSVC++ requires this to be set before any other includes to get M_PI.
#define _USE_MATH_DEFINES
-#include "cobalt/media/filters/wsola_internals.h"
+#include "starboard/shared/starboard/player/filter/wsola_internal.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include <memory>
-#include "base/logging.h"
-#include "cobalt/media/base/audio_bus.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/log.h"
#include "starboard/memory.h"
+#include "starboard/shared/internal_only.h"
-namespace cobalt {
-namespace media {
-
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
namespace internal {
+namespace {
+
bool InInterval(int n, Interval q) { return n >= q.first && n <= q.second; }
float MultiChannelSimilarityMeasure(const float* dot_prod_a_b,
@@ -35,43 +54,53 @@
return similarity_measure;
}
-void MultiChannelDotProduct(const AudioBus* a, int frame_offset_a,
- const AudioBus* b, int frame_offset_b,
- int num_frames, float* dot_product) {
- DCHECK_EQ(a->channels(), b->channels());
- DCHECK_GE(frame_offset_a, 0);
- DCHECK_GE(frame_offset_b, 0);
- DCHECK_LE(frame_offset_a + num_frames, a->frames());
- DCHECK_LE(frame_offset_b + num_frames, b->frames());
+void MultiChannelDotProduct(const scoped_refptr<DecodedAudio>& a,
+ int frame_offset_a,
+ const scoped_refptr<DecodedAudio>& b,
+ int frame_offset_b,
+ int num_frames,
+ float* dot_product) {
+ SB_DCHECK(a->channels() == b->channels());
+ SB_DCHECK(frame_offset_a >= 0) << frame_offset_a;
+ SB_DCHECK(frame_offset_b >= 0) << frame_offset_b;
+ SB_DCHECK(frame_offset_a + num_frames <= a->frames());
+ SB_DCHECK(frame_offset_b + num_frames <= b->frames());
SbMemorySet(dot_product, 0, sizeof(*dot_product) * a->channels());
+ const float* a_frames = reinterpret_cast<const float*>(a->buffer());
+ const float* b_frames = reinterpret_cast<const float*>(b->buffer());
for (int k = 0; k < a->channels(); ++k) {
- const float* ch_a = a->channel(k) + frame_offset_a;
- const float* ch_b = b->channel(k) + frame_offset_b;
+ const float* ch_a = a_frames + frame_offset_a * a->channels() + k;
+ const float* ch_b = b_frames + frame_offset_b * b->channels() + k;
for (int n = 0; n < num_frames; ++n) {
- dot_product[k] += *ch_a++ * *ch_b++;
+ dot_product[k] += *ch_a * *ch_b;
+ ch_a += a->channels();
+ ch_b += b->channels();
}
}
}
-void MultiChannelMovingBlockEnergies(const AudioBus* input,
- int frames_per_block, float* energy) {
+void MultiChannelMovingBlockEnergies(const scoped_refptr<DecodedAudio>& input,
+ int frames_per_block,
+ float* energy) {
int num_blocks = input->frames() - (frames_per_block - 1);
int channels = input->channels();
- for (int k = 0; k < input->channels(); ++k) {
- const float* input_channel = input->channel(k);
+ const float* input_frames = reinterpret_cast<const float*>(input->buffer());
+ for (int k = 0; k < channels; ++k) {
+ const float* input_channel = input_frames + k;
energy[k] = 0;
// First block of channel |k|.
for (int m = 0; m < frames_per_block; ++m) {
- energy[k] += input_channel[m] * input_channel[m];
+ energy[k] += input_channel[m * channels] * input_channel[m * channels];
}
const float* slide_out = input_channel;
- const float* slide_in = input_channel + frames_per_block;
- for (int n = 1; n < num_blocks; ++n, ++slide_in, ++slide_out) {
+ const float* slide_in = input_channel + frames_per_block * channels;
+ for (int n = 1; n < num_blocks;
+ ++n, slide_in += channels, slide_out += channels) {
energy[k + n * channels] = energy[k + (n - 1) * channels] -
*slide_out * *slide_out +
*slide_in * *slide_in;
@@ -100,15 +129,16 @@
}
}
-int DecimatedSearch(int decimation, Interval exclude_interval,
- const AudioBus* target_block,
- const AudioBus* search_segment,
+int DecimatedSearch(int decimation,
+ Interval exclude_interval,
+ const scoped_refptr<DecodedAudio>& target_block,
+ const scoped_refptr<DecodedAudio>& search_segment,
const float* energy_target_block,
const float* energy_candidate_blocks) {
int channels = search_segment->channels();
int block_size = target_block->frames();
int num_candidate_blocks = search_segment->frames() - (block_size - 1);
- std::unique_ptr<float[]> dot_prod(new float[channels]);
+ scoped_array<float> dot_prod(new float[channels]);
float similarity[3]; // Three elements for cubic interpolation.
int n = 0;
@@ -178,13 +208,16 @@
return optimal_index;
}
-int FullSearch(int low_limit, int high_limit, Interval exclude_interval,
- const AudioBus* target_block, const AudioBus* search_block,
+int FullSearch(int low_limit,
+ int high_limit,
+ Interval exclude_interval,
+ const scoped_refptr<DecodedAudio>& target_block,
+ const scoped_refptr<DecodedAudio>& search_block,
const float* energy_target_block,
const float* energy_candidate_blocks) {
int channels = search_block->channels();
int block_size = target_block->frames();
- std::unique_ptr<float[]> dot_prod(new float[channels]);
+ scoped_array<float> dot_prod(new float[channels]);
float best_similarity = std::numeric_limits<float>::min();
int optimal_index = 0;
@@ -209,10 +242,18 @@
return optimal_index;
}
-int OptimalIndex(const AudioBus* search_block, const AudioBus* target_block,
+} // namespace
+
+int OptimalIndex(const scoped_refptr<DecodedAudio>& search_block,
+ const scoped_refptr<DecodedAudio>& target_block,
+ SbMediaAudioSampleType sample_type,
+ SbMediaAudioFrameStorageType storage_type,
Interval exclude_interval) {
int channels = search_block->channels();
- DCHECK_EQ(channels, target_block->channels());
+ SB_DCHECK(channels == target_block->channels());
+ SB_DCHECK(sample_type == kSbMediaAudioSampleTypeFloat32);
+ SB_DCHECK(storage_type == kSbMediaAudioFrameStorageTypeInterleaved);
+
int target_size = target_block->frames();
int num_candidate_blocks = search_block->frames() - (target_size - 1);
@@ -224,8 +265,8 @@
// heuristically based on experiments.
const int kSearchDecimation = 5;
- std::unique_ptr<float[]> energy_target_block(new float[channels]);
- std::unique_ptr<float[]> energy_candidate_blocks(
+ scoped_array<float> energy_target_block(new float[channels]);
+ scoped_array<float> energy_candidate_blocks(
new float[channels * num_candidate_blocks]);
// Energy of all candid frames.
@@ -255,6 +296,8 @@
}
} // namespace internal
-
-} // namespace media
-} // namespace cobalt
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/wsola_internal.h b/src/starboard/shared/starboard/player/filter/wsola_internal.h
new file mode 100644
index 0000000..c49dc0c
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/wsola_internal.h
@@ -0,0 +1,62 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Modifications 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.
+
+// A set of utility functions to perform WSOLA (Waveform Similarity Based
+// Overlap-Add) to modify audio playback speed without changing the pitch.
+// See http://ieeexplore.ieee.org/document/319366 for more details.
+
+#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_WSOLA_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_WSOLA_INTERNAL_H_
+
+#include <utility>
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/media.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+namespace internal {
+
+typedef std::pair<int, int> Interval;
+
+// Find the index of the block, within |search_block|, that is most similar
+// to |target_block|. Obviously, the returned index is w.r.t. |search_block|.
+// |exclude_interval| is an interval that is excluded from the search.
+int OptimalIndex(const scoped_refptr<DecodedAudio>& search_block,
+ const scoped_refptr<DecodedAudio>& target_block,
+ SbMediaAudioSampleType sample_type,
+ SbMediaAudioFrameStorageType storage_type,
+ Interval exclude_interval);
+
+// Return a "periodic" Hann window(https://en.wikipedia.org/wiki/Hann_function).
+// This is the first L samples of an L+1 Hann window. It is perfect
+// reconstruction for overlap-and-add.
+void GetSymmetricHanningWindow(int window_length, float* window);
+
+} // namespace internal
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_WSOLA_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/player/filter/wsola_internals.h b/src/starboard/shared/starboard/player/filter/wsola_internals.h
deleted file mode 100644
index 4d96b39..0000000
--- a/src/starboard/shared/starboard/player/filter/wsola_internals.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// A set of utility functions to perform WSOLA.
-
-#ifndef COBALT_MEDIA_FILTERS_WSOLA_INTERNALS_H_
-#define COBALT_MEDIA_FILTERS_WSOLA_INTERNALS_H_
-
-#include <utility>
-
-#include "cobalt/media/base/media_export.h"
-
-namespace cobalt {
-namespace media {
-
-class AudioBus;
-
-namespace internal {
-
-typedef std::pair<int, int> Interval;
-
-// Dot-product of channels of two AudioBus. For each AudioBus an offset is
-// given. |dot_product[k]| is the dot-product of channel |k|. The caller should
-// allocate sufficient space for |dot_product|.
-MEDIA_EXPORT void MultiChannelDotProduct(const AudioBus* a, int frame_offset_a,
- const AudioBus* b, int frame_offset_b,
- int num_frames, float* dot_product);
-
-// Energies of sliding windows of channels are interleaved.
-// The number windows is |input->frames()| - (|frames_per_window| - 1), hence,
-// the method assumes |energy| must be, at least, of size
-// (|input->frames()| - (|frames_per_window| - 1)) * |input->channels()|.
-MEDIA_EXPORT void MultiChannelMovingBlockEnergies(const AudioBus* input,
- int frames_per_window,
- float* energy);
-
-// Fit the curve f(x) = a * x^2 + b * x + c such that
-// f(-1) = y[0]
-// f(0) = y[1]
-// f(1) = y[2]
-// and return the maximum, assuming that y[0] <= y[1] >= y[2].
-MEDIA_EXPORT void QuadraticInterpolation(const float* y_values, float* extremum,
- float* extremum_value);
-
-// Search a subset of all candid blocks. The search is performed every
-// |decimation| frames. This reduces complexity by a factor of about
-// 1 / |decimation|. A cubic interpolation is used to have a better estimate of
-// the best match.
-MEDIA_EXPORT int DecimatedSearch(int decimation, Interval exclude_interval,
- const AudioBus* target_block,
- const AudioBus* search_segment,
- const float* energy_target_block,
- const float* energy_candid_blocks);
-
-// Search [|low_limit|, |high_limit|] of |search_segment| to find a block that
-// is most similar to |target_block|. |energy_target_block| is the energy of the
-// |target_block|. |energy_candidate_blocks| is the energy of all blocks within
-// |search_block|.
-MEDIA_EXPORT int FullSearch(int low_limit, int hight_limimit,
- Interval exclude_interval,
- const AudioBus* target_block,
- const AudioBus* search_block,
- const float* energy_target_block,
- const float* energy_candidate_blocks);
-
-// Find the index of the block, within |search_block|, that is most similar
-// to |target_block|. Obviously, the returned index is w.r.t. |search_block|.
-// |exclude_interval| is an interval that is excluded from the search.
-MEDIA_EXPORT int OptimalIndex(const AudioBus* search_block,
- const AudioBus* target_block,
- Interval exclude_interval);
-
-// Return a "periodic" Hann window. This is the first L samples of an L+1
-// Hann window. It is perfect reconstruction for overlap-and-add.
-MEDIA_EXPORT void GetSymmetricHanningWindow(int window_length, float* window);
-
-} // namespace internal
-
-} // namespace media
-} // namespace cobalt
-
-#endif // COBALT_MEDIA_FILTERS_WSOLA_INTERNALS_H_
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.cc b/src/starboard/shared/starboard/player/input_buffer_internal.cc
index 9794c14..3ff537f 100644
--- a/src/starboard/shared/starboard/player/input_buffer_internal.cc
+++ b/src/starboard/shared/starboard/player/input_buffer_internal.cc
@@ -205,6 +205,20 @@
buffer_->SetDecryptedContent(buffer, size);
}
+bool operator==(const InputBuffer& lhs, const InputBuffer& rhs) {
+ if (!lhs.is_valid() && !rhs.is_valid()) {
+ return true;
+ }
+ if (lhs.is_valid() && rhs.is_valid()) {
+ return lhs.sample_type() == rhs.sample_type() && lhs.data() == rhs.data() &&
+ lhs.size() == rhs.size() && lhs.pts() == rhs.pts() &&
+ lhs.video_sample_info() == rhs.video_sample_info() &&
+ lhs.drm_info() == rhs.drm_info();
+ }
+
+ return false;
+}
+
} // namespace player
} // namespace starboard
} // namespace shared
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.h b/src/starboard/shared/starboard/player/input_buffer_internal.h
index 8f4e0ce..123beab 100644
--- a/src/starboard/shared/starboard/player/input_buffer_internal.h
+++ b/src/starboard/shared/starboard/player/input_buffer_internal.h
@@ -64,6 +64,8 @@
ReferenceCountedBuffer* buffer_;
};
+bool operator==(const InputBuffer& lhs, const InputBuffer& rhs);
+
} // namespace player
} // namespace starboard
} // namespace shared
diff --git a/src/starboard/shared/starboard/player/job_queue.cc b/src/starboard/shared/starboard/player/job_queue.cc
index 254378d..02be55c 100644
--- a/src/starboard/shared/starboard/player/job_queue.cc
+++ b/src/starboard/shared/starboard/player/job_queue.cc
@@ -16,7 +16,6 @@
#include <utility>
-#include "starboard/log.h"
#include "starboard/once.h"
#include "starboard/thread.h"
@@ -62,7 +61,8 @@
} // namespace
-JobQueue::JobQueue() : thread_id_(SbThreadGetId()), stopped_(false) {
+JobQueue::JobQueue()
+ : thread_id_(SbThreadGetId()), condition_(mutex_), stopped_(false) {
SB_DCHECK(SbThreadIsValidId(thread_id_));
SetCurrentThreadJobQueue(this);
}
@@ -73,39 +73,23 @@
ResetCurrentThreadJobQueue();
}
-void JobQueue::Schedule(Closure job, SbTimeMonotonic delay /*= 0*/) {
- SB_DCHECK(job.is_valid());
- SB_DCHECK(delay >= 0) << delay;
-
- ScopedLock scoped_lock(mutex_);
- if (stopped_) {
- return;
- }
- if (delay <= 0) {
- queue_.Put(job);
- return;
- }
- SbTimeMonotonic time_to_run_job = SbTimeGetMonotonicNow() + delay;
- time_to_job_map_.insert(std::make_pair(time_to_run_job, job));
- if (time_to_job_map_.begin()->second == job) {
- queue_.Wake();
- }
+void JobQueue::Schedule(Closure closure, SbTimeMonotonic delay /*= 0*/) {
+ Schedule(closure, NULL, delay);
}
-void JobQueue::Remove(Closure job) {
+void JobQueue::Remove(Closure closure) {
SB_DCHECK(BelongsToCurrentThread());
- SB_DCHECK(job.is_valid());
+ SB_DCHECK(closure.is_valid());
ScopedLock scoped_lock(mutex_);
- queue_.Remove(job);
// std::multimap::erase() doesn't return an iterator until C++11. So this has
- // to be done in a nested loop to delete multiple occurrences of |job|.
+ // to be done in a nested loop to delete multiple occurrences of |closure|.
bool should_keep_running = true;
while (should_keep_running) {
should_keep_running = false;
for (TimeToJobMap::iterator iter = time_to_job_map_.begin();
iter != time_to_job_map_.end(); ++iter) {
- if (iter->second == job) {
+ if (iter->second.closure == closure) {
time_to_job_map_.erase(iter);
should_keep_running = true;
break;
@@ -118,17 +102,8 @@
{
ScopedLock scoped_lock(mutex_);
stopped_ = true;
+ time_to_job_map_.clear();
}
-
- queue_.Wake();
-
- for (;;) {
- Closure job = queue_.Poll();
- if (!job.is_valid()) {
- break;
- }
- }
- time_to_job_map_.clear();
}
void JobQueue::RunUntilStopped() {
@@ -164,40 +139,87 @@
return GetCurrentThreadJobQueue();
}
+void JobQueue::Schedule(Closure closure,
+ JobOwner* owner,
+ SbTimeMonotonic delay) {
+ SB_DCHECK(closure.is_valid());
+ SB_DCHECK(delay >= 0) << delay;
+
+ Job job = {closure, owner};
+
+ ScopedLock scoped_lock(mutex_);
+ if (stopped_) {
+ return;
+ }
+ SbTimeMonotonic time_to_run_job = SbTimeGetMonotonicNow() + delay;
+ bool is_first_job = time_to_job_map_.empty() ||
+ time_to_run_job < time_to_job_map_.begin()->first;
+
+ time_to_job_map_.insert(std::make_pair(time_to_run_job, job));
+ if (is_first_job) {
+ condition_.Signal();
+ }
+}
+
+void JobQueue::RemoveJobsByToken(JobOwner* owner) {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(owner);
+
+ ScopedLock scoped_lock(mutex_);
+ // std::multimap::erase() doesn't return an iterator until C++11. So this has
+ // to be done in a nested loop to delete multiple occurrences of |closure|.
+ bool should_keep_running = true;
+ while (should_keep_running) {
+ should_keep_running = false;
+ for (TimeToJobMap::iterator iter = time_to_job_map_.begin();
+ iter != time_to_job_map_.end(); ++iter) {
+ if (iter->second.owner == owner) {
+ time_to_job_map_.erase(iter);
+ should_keep_running = true;
+ break;
+ }
+ }
+ }
+}
+
bool JobQueue::TryToRunOneJob(bool wait_for_next_job) {
SB_DCHECK(BelongsToCurrentThread());
Closure job;
- // |kSbTimeMax| makes more sense here, but |kSbTimeDay| is much safer.
- SbTimeMonotonic delay = kSbTimeDay;
{
ScopedLock scoped_lock(mutex_);
if (stopped_) {
return false;
}
- if (!time_to_job_map_.empty()) {
- TimeToJobMap::iterator first_delayed_job = time_to_job_map_.begin();
- delay = first_delayed_job->first - SbTimeGetMonotonicNow();
- if (delay <= 0) {
- job = first_delayed_job->second;
- time_to_job_map_.erase(first_delayed_job);
+ if (time_to_job_map_.empty() && wait_for_next_job) {
+ // |kSbTimeMax| makes more sense here, but |kSbTimeDay| is much safer.
+ condition_.WaitTimed(kSbTimeDay);
+ }
+ if (time_to_job_map_.empty()) {
+ return false;
+ }
+ TimeToJobMap::iterator first_delayed_job = time_to_job_map_.begin();
+ SbTimeMonotonic delay = first_delayed_job->first - SbTimeGetMonotonicNow();
+ if (delay > 0) {
+ if (wait_for_next_job) {
+ condition_.WaitTimed(delay);
+ } else {
+ return false;
}
}
- }
-
- if (!job.is_valid()) {
- if (wait_for_next_job) {
- job = queue_.GetTimed(delay);
- } else {
- job = queue_.Poll();
+ // Try to retrieve the job again as the job map can be altered during the
+ // wait.
+ first_delayed_job = time_to_job_map_.begin();
+ delay = first_delayed_job->first - SbTimeGetMonotonicNow();
+ if (delay > 0) {
+ return false;
}
+ job = first_delayed_job->second.closure;
+ time_to_job_map_.erase(first_delayed_job);
}
- if (!job.is_valid()) {
- return false;
- }
-
+ SB_DCHECK(job.is_valid());
job.Run();
return true;
}
diff --git a/src/starboard/shared/starboard/player/job_queue.h b/src/starboard/shared/starboard/player/job_queue.h
index 36100ab..361d82e 100644
--- a/src/starboard/shared/starboard/player/job_queue.h
+++ b/src/starboard/shared/starboard/player/job_queue.h
@@ -19,6 +19,7 @@
#include "starboard/common/scoped_ptr.h"
#include "starboard/condition_variable.h"
+#include "starboard/log.h"
#include "starboard/mutex.h"
#include "starboard/queue.h"
#include "starboard/shared/internal_only.h"
@@ -39,11 +40,30 @@
// A thread can only have one job queue.
class JobQueue {
public:
+ class JobOwner {
+ protected:
+ explicit JobOwner(JobQueue* job_queue = JobQueue::current())
+ : job_queue_(job_queue) {
+ SB_DCHECK(job_queue);
+ }
+ ~JobOwner() { CancelPendingJobs(); }
+ bool BelongsToCurrentThread() const {
+ return job_queue_->BelongsToCurrentThread();
+ }
+ void Schedule(Closure closure, SbTimeMonotonic delay = 0) {
+ job_queue_->Schedule(closure, this, delay);
+ }
+ void CancelPendingJobs() { job_queue_->RemoveJobsByToken(this); }
+
+ private:
+ JobQueue* job_queue_;
+ };
+
JobQueue();
~JobQueue();
- void Schedule(Closure job, SbTimeMonotonic delay = 0);
- void Remove(Closure job);
+ void Schedule(Closure closure, SbTimeMonotonic delay = 0);
+ void Remove(Closure closure);
// The processing of jobs may not be stopped when this function returns, but
// it is guaranteed that the processing will be stopped very soon. So it is
@@ -57,8 +77,14 @@
static JobQueue* current();
private:
- typedef std::multimap<SbTimeMonotonic, Closure> TimeToJobMap;
+ struct Job {
+ Closure closure;
+ JobOwner* owner;
+ };
+ typedef std::multimap<SbTimeMonotonic, Job> TimeToJobMap;
+ void Schedule(Closure closure, JobOwner* owner, SbTimeMonotonic delay);
+ void RemoveJobsByToken(JobOwner* owner);
// Return true if a job is run, otherwise return false. When there is no job
// ready to run currently and |wait_for_next_job| is true, the function will
// wait to until a job is available or if the |queue_| is woken up. Note that
@@ -68,7 +94,7 @@
SbThreadId thread_id_;
Mutex mutex_;
- Queue<Closure> queue_;
+ ConditionVariable condition_;
TimeToJobMap time_to_job_map_;
bool stopped_;
};
diff --git a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc
index e831818..5b5e8d6 100644
--- a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc
+++ b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc
@@ -14,8 +14,7 @@
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#include "starboard/log.h"
#include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h"
@@ -26,5 +25,4 @@
}
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
diff --git a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_create.cc b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_create.cc
index 875726f..d34c9b2 100644
--- a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_create.cc
+++ b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_create.cc
@@ -14,8 +14,7 @@
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h"
@@ -24,5 +23,4 @@
return SbSpeechRecognizerPrivate::CreateSpeechRecognizer(handler);
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
diff --git a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc
index 3948cc7..abf0c5a 100644
--- a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc
+++ b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc
@@ -14,8 +14,7 @@
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h"
@@ -25,5 +24,4 @@
}
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
diff --git a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h
index a69a05c..d05fccb 100644
--- a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h
+++ b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h
@@ -18,8 +18,7 @@
#include "starboard/shared/internal_only.h"
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
struct SbSpeechRecognizerPrivate {
virtual ~SbSpeechRecognizerPrivate() {}
virtual bool Start(const SbSpeechConfiguration* configuration) = 0;
@@ -29,7 +28,6 @@
const SbSpeechRecognizerHandler* handler);
static void DestroySpeechRecognizer(SbSpeechRecognizer speech_recognizer);
};
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#endif // STARBOARD_SHARED_STARBOARD_SPEECH_RECOGNIZER_SPEECH_RECOGNIZER_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_start.cc b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_start.cc
index 2fd1002..523819a 100644
--- a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_start.cc
+++ b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_start.cc
@@ -14,8 +14,7 @@
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h"
@@ -26,5 +25,4 @@
: false;
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
diff --git a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc
index 717d229..96db02a 100644
--- a/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc
+++ b/src/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc
@@ -14,8 +14,7 @@
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#include "starboard/log.h"
#include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h"
@@ -26,5 +25,4 @@
}
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
diff --git a/src/starboard/shared/stub/decode_target_is_opaque.cc b/src/starboard/shared/stub/decode_target_is_opaque.cc
index 3759b36..16301d9 100644
--- a/src/starboard/shared/stub/decode_target_is_opaque.cc
+++ b/src/starboard/shared/stub/decode_target_is_opaque.cc
@@ -17,8 +17,8 @@
#if SB_API_VERSION < 4
-#if !(SB_VERSION(3) && SB_HAS(GRAPHICS))
-#error "SbDecodeTargetIsOpaque requires SB_VERSION(3) and SB_HAS(GRAPHICS)."
+#if !(SB_API_VERSION >= 3 && SB_HAS(GRAPHICS))
+#error "Requires SB_API_VERSION >= 3 and SB_HAS(GRAPHICS)."
#endif
bool SbDecodeTargetIsOpaque(SbDecodeTarget decode_target) {
diff --git a/src/starboard/shared/stub/image_decode.cc b/src/starboard/shared/stub/image_decode.cc
index 3072b96..dd8d4d8 100644
--- a/src/starboard/shared/stub/image_decode.cc
+++ b/src/starboard/shared/stub/image_decode.cc
@@ -15,8 +15,8 @@
#include "starboard/configuration.h"
#include "starboard/image.h"
-#if !(SB_VERSION(3) && SB_HAS(GRAPHICS))
-#error "SbImageDecode requires SB_VERSION(3) and SB_HAS(GRAPHICS)."
+#if !(SB_API_VERSION >= 3 && SB_HAS(GRAPHICS))
+#error "SbImageDecode requires SB_API_VERSION >= 3 and SB_HAS(GRAPHICS)."
#endif
#if SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/image_is_decode_supported.cc b/src/starboard/shared/stub/image_is_decode_supported.cc
index 2b0b367..b620022 100644
--- a/src/starboard/shared/stub/image_is_decode_supported.cc
+++ b/src/starboard/shared/stub/image_is_decode_supported.cc
@@ -15,8 +15,8 @@
#include "starboard/configuration.h"
#include "starboard/image.h"
-#if !(SB_VERSION(3) && SB_HAS(GRAPHICS))
-#error "SbImageIsDecodeSupported requires SB_VERSION(3) and SB_HAS(GRAPHICS)."
+#if !(SB_API_VERSION >= 3 && SB_HAS(GRAPHICS))
+#error "Requires SB_API_VERSION >= 3 and SB_HAS(GRAPHICS)."
#endif
bool SbImageIsDecodeSupported(const char* mime_type,
diff --git a/src/starboard/shared/stub/microphone_close.cc b/src/starboard/shared/stub/microphone_close.cc
index fd1bd15..9246202 100644
--- a/src/starboard/shared/stub/microphone_close.cc
+++ b/src/starboard/shared/stub/microphone_close.cc
@@ -14,10 +14,10 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
bool SbMicrophoneClose(SbMicrophone microphone) {
return false;
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/stub/microphone_create.cc b/src/starboard/shared/stub/microphone_create.cc
index eeb5c13..bdc4166 100644
--- a/src/starboard/shared/stub/microphone_create.cc
+++ b/src/starboard/shared/stub/microphone_create.cc
@@ -14,7 +14,7 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
SbMicrophone SbMicrophoneCreate(SbMicrophoneId id,
int sample_rate_in_hz,
@@ -22,4 +22,4 @@
return kSbMicrophoneInvalid;
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/stub/microphone_destroy.cc b/src/starboard/shared/stub/microphone_destroy.cc
index 11b1ce7..b0bbdab 100644
--- a/src/starboard/shared/stub/microphone_destroy.cc
+++ b/src/starboard/shared/stub/microphone_destroy.cc
@@ -14,8 +14,8 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
void SbMicrophoneDestroy(SbMicrophone microphone) {}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/stub/microphone_get_available.cc b/src/starboard/shared/stub/microphone_get_available.cc
index 5fdb309..264b2d0 100644
--- a/src/starboard/shared/stub/microphone_get_available.cc
+++ b/src/starboard/shared/stub/microphone_get_available.cc
@@ -14,11 +14,11 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
int SbMicrophoneGetAvailable(SbMicrophoneInfo* out_info_array,
int info_array_size) {
return 0;
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/stub/microphone_is_sample_rate_supported.cc b/src/starboard/shared/stub/microphone_is_sample_rate_supported.cc
index d33766f..82cfbc1 100644
--- a/src/starboard/shared/stub/microphone_is_sample_rate_supported.cc
+++ b/src/starboard/shared/stub/microphone_is_sample_rate_supported.cc
@@ -14,11 +14,11 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
bool SbMicrophoneIsSampleRateSupported(SbMicrophoneId id,
int sample_rate_in_hz) {
return false;
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/stub/microphone_open.cc b/src/starboard/shared/stub/microphone_open.cc
index 0ce049b..19fedbf 100644
--- a/src/starboard/shared/stub/microphone_open.cc
+++ b/src/starboard/shared/stub/microphone_open.cc
@@ -14,10 +14,10 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
bool SbMicrophoneOpen(SbMicrophone microphone) {
return false;
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/stub/microphone_read.cc b/src/starboard/shared/stub/microphone_read.cc
index 2965831..d798d57 100644
--- a/src/starboard/shared/stub/microphone_read.cc
+++ b/src/starboard/shared/stub/microphone_read.cc
@@ -14,7 +14,7 @@
#include "starboard/microphone.h"
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
int SbMicrophoneRead(SbMicrophone microphone,
void* out_audio_data,
@@ -22,4 +22,4 @@
return -1;
}
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
diff --git a/src/starboard/shared/stub/player_create.cc b/src/starboard/shared/stub/player_create.cc
index 7d5eb89..0a0bf62 100644
--- a/src/starboard/shared/stub/player_create.cc
+++ b/src/starboard/shared/stub/player_create.cc
@@ -32,7 +32,7 @@
,
SbPlayerOutputMode output_mode,
SbDecodeTargetGraphicsContextProvider* /*provider*/
-#elif SB_VERSION(3)
+#elif SB_API_VERSION >= 3
,
SbDecodeTargetProvider* /*provider*/
#endif
diff --git a/src/starboard/shared/stub/speech_recognizer_cancel.cc b/src/starboard/shared/stub/speech_recognizer_cancel.cc
index a7c263a..479571a 100644
--- a/src/starboard/shared/stub/speech_recognizer_cancel.cc
+++ b/src/starboard/shared/stub/speech_recognizer_cancel.cc
@@ -14,10 +14,8 @@
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
void SbSpeechRecognizerCancel(SbSpeechRecognizer /*recognizer*/) {}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
diff --git a/src/starboard/shared/stub/speech_recognizer_create.cc b/src/starboard/shared/stub/speech_recognizer_create.cc
index 5acdf3a..ffa08cd 100644
--- a/src/starboard/shared/stub/speech_recognizer_create.cc
+++ b/src/starboard/shared/stub/speech_recognizer_create.cc
@@ -14,13 +14,11 @@
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
SbSpeechRecognizer SbSpeechRecognizerCreate(
const SbSpeechRecognizerHandler* /*handler*/) {
return kSbSpeechRecognizerInvalid;
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
diff --git a/src/starboard/shared/stub/speech_recognizer_destroy.cc b/src/starboard/shared/stub/speech_recognizer_destroy.cc
index 090417c..b79df26 100644
--- a/src/starboard/shared/stub/speech_recognizer_destroy.cc
+++ b/src/starboard/shared/stub/speech_recognizer_destroy.cc
@@ -14,10 +14,8 @@
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
void SbSpeechRecognizerDestroy(SbSpeechRecognizer /*recognizer*/) {}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
diff --git a/src/starboard/shared/stub/speech_recognizer_start.cc b/src/starboard/shared/stub/speech_recognizer_start.cc
index fc2d43d..0b4e4be 100644
--- a/src/starboard/shared/stub/speech_recognizer_start.cc
+++ b/src/starboard/shared/stub/speech_recognizer_start.cc
@@ -14,13 +14,11 @@
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
bool SbSpeechRecognizerStart(SbSpeechRecognizer /*recognizer*/,
const SbSpeechConfiguration* /*configuration*/) {
return false;
}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
diff --git a/src/starboard/shared/stub/speech_recognizer_stop.cc b/src/starboard/shared/stub/speech_recognizer_stop.cc
index b799661..b3b74b9 100644
--- a/src/starboard/shared/stub/speech_recognizer_stop.cc
+++ b/src/starboard/shared/stub/speech_recognizer_stop.cc
@@ -14,10 +14,8 @@
#include "starboard/speech_recognizer.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
void SbSpeechRecognizerStop(SbSpeechRecognizer /*recognizer*/) {}
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
diff --git a/src/starboard/shared/stub/speech_synthesis_cancel.cc b/src/starboard/shared/stub/speech_synthesis_cancel.cc
index 5a4cf19..5071394 100644
--- a/src/starboard/shared/stub/speech_synthesis_cancel.cc
+++ b/src/starboard/shared/stub/speech_synthesis_cancel.cc
@@ -14,7 +14,7 @@
#include "starboard/speech_synthesis.h"
-#if !SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
+#if !SB_HAS(SPEECH_SYNTHESIS) && SB_API_VERSION >= 3
#error If speech synthesis not enabled on this platform, please exclude it\
from the build
#endif
diff --git a/src/starboard/shared/stub/speech_synthesis_set_language.cc b/src/starboard/shared/stub/speech_synthesis_set_language.cc
index 6cc1b33..8cc1042 100644
--- a/src/starboard/shared/stub/speech_synthesis_set_language.cc
+++ b/src/starboard/shared/stub/speech_synthesis_set_language.cc
@@ -16,7 +16,7 @@
#include "starboard/speech_synthesis.h"
-#if !SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
+#if !SB_HAS(SPEECH_SYNTHESIS) && SB_API_VERSION >= 3
#error If speech synthesis not enabled on this platform, please exclude it\
from the build
#endif
diff --git a/src/starboard/shared/stub/speech_synthesis_speak.cc b/src/starboard/shared/stub/speech_synthesis_speak.cc
index 53cd5f5..a06c9cf 100644
--- a/src/starboard/shared/stub/speech_synthesis_speak.cc
+++ b/src/starboard/shared/stub/speech_synthesis_speak.cc
@@ -14,7 +14,7 @@
#include "starboard/speech_synthesis.h"
-#if !SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
+#if !SB_HAS(SPEECH_SYNTHESIS) && SB_API_VERSION >= 3
#error If speech synthesis not enabled on this platform, please exclude it\
from the build
#endif
diff --git a/src/starboard/shared/stub/system_raise_platform_error.cc b/src/starboard/shared/stub/system_raise_platform_error.cc
index 13303cb..aad4c25 100644
--- a/src/starboard/shared/stub/system_raise_platform_error.cc
+++ b/src/starboard/shared/stub/system_raise_platform_error.cc
@@ -27,12 +27,14 @@
case kSbSystemPlatformErrorTypeConnectionError:
message = "Connection error.";
break;
+#if SB_API_VERSION < SB_PLATFORM_ERROR_CLEANUP_API_VERSION
case kSbSystemPlatformErrorTypeUserSignedOut:
message = "User is not signed in.";
break;
case kSbSystemPlatformErrorTypeUserAgeRestricted:
message = "User is age restricted.";
break;
+#endif
default:
message = "<unknown>";
break;
diff --git a/src/starboard/shared/uwp/application_uwp.cc b/src/starboard/shared/uwp/application_uwp.cc
index 41ed103..1911a61 100644
--- a/src/starboard/shared/uwp/application_uwp.cc
+++ b/src/starboard/shared/uwp/application_uwp.cc
@@ -14,57 +14,243 @@
#include "starboard/shared/uwp/application_uwp.h"
+#include <windows.h>
+#include <WinSock2.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
#include "starboard/event.h"
#include "starboard/log.h"
#include "starboard/shared/starboard/application.h"
+#include "starboard/shared/starboard/audio_sink/audio_sink_internal.h"
#include "starboard/shared/uwp/window_internal.h"
+#include "starboard/shared/win32/thread_private.h"
+#include "starboard/shared/win32/wchar_utils.h"
+#include "starboard/string.h"
using starboard::shared::starboard::Application;
using starboard::shared::starboard::CommandLine;
using starboard::shared::uwp::ApplicationUwp;
+using starboard::shared::uwp::GetArgvZero;
+using starboard::shared::win32::wchar_tToUTF8;
+using Windows::ApplicationModel::Activation::ActivationKind;
using Windows::ApplicationModel::Activation::IActivatedEventArgs;
+using Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs;
using Windows::ApplicationModel::Core::CoreApplication;
using Windows::ApplicationModel::Core::CoreApplicationView;
using Windows::ApplicationModel::Core::IFrameworkView;
using Windows::ApplicationModel::Core::IFrameworkViewSource;
+using Windows::ApplicationModel::SuspendingEventArgs;
+using Windows::Foundation::EventHandler;
+using Windows::Foundation::TimeSpan;
using Windows::Foundation::TypedEventHandler;
-using Windows::UI::Core::CoreWindow;
+using Windows::Foundation::Uri;
+using Windows::System::Threading::TimerElapsedHandler;
+using Windows::System::Threading::ThreadPoolTimer;
+using Windows::UI::Core::CoreDispatcherPriority;
+using Windows::System::UserAuthenticationStatus;
using Windows::UI::Core::CoreProcessEventsOption;
+using Windows::UI::Core::CoreWindow;
+using Windows::UI::Core::DispatchedHandler;
+using Windows::UI::Core::KeyEventArgs;
+
+namespace sbwin32 = starboard::shared::win32;
+
+namespace {
+
+const int kWinSockVersionMajor = 2;
+const int kWinSockVersionMinor = 2;
+
+int main_return_value = 0;
+
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+
+// Parses a starboard: URI scheme by splitting args at ';' boundries.
+std::vector<std::string> ParseStarboardUri(const std::string& uri) {
+ std::vector<std::string> result;
+ result.push_back(GetArgvZero());
+
+ size_t index = uri.find(':');
+ if (index == std::string::npos) {
+ return result;
+ }
+
+ std::string args = uri.substr(index + 1);
+
+ while (!args.empty()) {
+ size_t next = args.find(';');
+ result.push_back(args.substr(0, next));
+ if (next == std::string::npos) {
+ return result;
+ }
+ args = args.substr(next + 1);
+ }
+ return result;
+}
+
+#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+
+std::unique_ptr<Application::Event> MakeDeepLinkEvent(
+ const std::string& uri_string) {
+ size_t index = uri_string.find(':');
+ SB_DCHECK(index != std::string::npos);
+
+ std::string uri_protocol_stripped = uri_string.substr(index + 1);
+ SB_LOG(INFO) << "Navigate to: [" << uri_protocol_stripped << "]";
+ const size_t kMaxDeepLinkSize = 128 * 1024;
+ const std::size_t uri_size = uri_protocol_stripped.size();
+ if (uri_size > kMaxDeepLinkSize) {
+ SB_NOTREACHED() << "App launch data too big: " << uri_size;
+ return nullptr;
+ }
+
+ const int kBufferSize = static_cast<int>(uri_protocol_stripped.size()) + 1;
+ char* deep_link = new char[kBufferSize];
+ SB_DCHECK(deep_link);
+ SbStringCopy(deep_link, uri_protocol_stripped.c_str(), kBufferSize);
+
+ return std::unique_ptr<Application::Event>(
+ new Application::Event(kSbEventTypeLink, deep_link,
+ Application::DeleteArrayDestructor<const char*>));
+}
+
+} // namespace
ref class App sealed : public IFrameworkView {
public:
- App() {}
+ App() : previously_activated_(false) {}
// IFrameworkView methods.
virtual void Initialize(
- Windows::ApplicationModel::Core::CoreApplicationView^ applicationView) {
+ CoreApplicationView^ applicationView) {
+ SbAudioSinkPrivate::Initialize();
+ CoreApplication::Suspending +=
+ ref new EventHandler<SuspendingEventArgs^>(this, &App::OnSuspending);
+ CoreApplication::Resuming +=
+ ref new EventHandler<Object^>(this, &App::OnResuming);
applicationView->Activated +=
ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(
this, &App::OnActivated);
}
- virtual void SetWindow(CoreWindow^ window) {}
+ virtual void SetWindow(CoreWindow^ window) {
+ ApplicationUwp::Get()->SetCoreWindow(window);
+ window->KeyUp += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(
+ this, &App::OnKeyUp);
+ window->KeyDown += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(
+ this, &App::OnKeyDown);
+ }
virtual void Load(Platform::String^ entryPoint) {}
virtual void Run() {
- CoreWindow ^window = CoreWindow::GetForCurrentThread();
- window->Activate();
- window->Dispatcher->ProcessEvents(
- CoreProcessEventsOption::ProcessUntilQuit);
+ args_.push_back(GetArgvZero());
+ argv_.push_back(args_.begin()->c_str());
+ main_return_value = application_.Run(
+ static_cast<int>(argv_.size()), const_cast<char**>(argv_.data()));
}
- virtual void Uninitialize() {
+ virtual void Uninitialize() { SbAudioSinkPrivate::TearDown(); }
+
+ void OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args) {
+ SB_DLOG(INFO) << "Suspending";
+ // Note if we dispatch "suspend" here before pause, application.cc
+ // will inject the "pause" which will cause us to go async which
+ // will cause us to not have completed the suspend operation before
+ // returning, which UWP requires.
+ ApplicationUwp::Get()->DispatchAndDelete(
+ new ApplicationUwp::Event(kSbEventTypePause, NULL, NULL));
+ ApplicationUwp::Get()->DispatchAndDelete(
+ new ApplicationUwp::Event(kSbEventTypeSuspend, NULL, NULL));
+ }
+
+ void OnResuming(Platform::Object^ sender, Platform::Object^ args) {
+ SB_DLOG(INFO) << "Resuming";
+ ApplicationUwp::Get()->DispatchAndDelete(
+ new ApplicationUwp::Event(kSbEventTypeResume, NULL, NULL));
+ ApplicationUwp::Get()->DispatchAndDelete(
+ new ApplicationUwp::Event(kSbEventTypeUnpause, NULL, NULL));
+ }
+
+ void OnKeyUp(CoreWindow^ sender, KeyEventArgs^ args) {
+ ApplicationUwp::Get()->OnKeyEvent(sender, args, true);
+ }
+
+ void OnKeyDown(CoreWindow^ sender, KeyEventArgs^ args) {
+ ApplicationUwp::Get()->OnKeyEvent(sender, args, false);
}
void OnActivated(
CoreApplicationView^ applicationView, IActivatedEventArgs^ args) {
- CoreWindow::GetForCurrentThread()->Activate();
- ApplicationUwp::Get()->DispatchStart();
+ // Please see application lifecyle description:
+ // https://docs.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle
+ // Note that this document was written for Xaml apps not core apps,
+ // so for us the precise API is a little different.
+ // The substance is that, while OnActiviated is definitely called the
+ // first time the application is started, it may additionally called
+ // in other cases while the process is already running. Starboard
+ // applications cannot fully restart in a process lifecycle,
+ // so we interpret the first activation and the subsequent ones differently.
+ if (args->Kind == ActivationKind::Protocol) {
+ Uri^ uri = dynamic_cast<IProtocolActivatedEventArgs^>(args)->Uri;
+
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+ // The starboard: scheme provides commandline arguments, but that's
+ // only allowed during a process's first activation.
+ if (!previously_activated_ && uri->SchemeName->Equals("starboard")) {
+ std::string uri_string = wchar_tToUTF8(uri->RawUri->Data());
+ // args_ is a vector of std::string, but argv_ is a vector of
+ // char* into args_ so as to compose a char**.
+ args_ = ParseStarboardUri(uri_string);
+ for (const std::string& arg : args_) {
+ argv_.push_back(arg.c_str());
+ }
+
+ ApplicationUwp::Get()->SetCommandLine(
+ static_cast<int>(argv_.size()), argv_.data());
+ }
+#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+ if (uri->SchemeName->Equals("youtube") ||
+ uri->SchemeName->Equals("ms-xbl-07459769")) {
+ std::string uri_string = sbwin32::platformStringToString(uri->RawUri);
+ if (previously_activated_) {
+ std::unique_ptr<Application::Event> event =
+ MakeDeepLinkEvent(uri_string);
+ SB_DCHECK(event);
+ ApplicationUwp::Get()->Inject(event.release());
+ } else {
+ SB_DCHECK(!uri_string.empty());
+ ApplicationUwp::Get()->SetStartLink(uri_string.c_str());
+ }
+ }
+ }
+ previous_activation_kind_ = args->Kind;
+
+ if (!previously_activated_) {
+ CoreWindow::GetForCurrentThread()->Activate();
+ // Call DispatchStart async so the UWP system thinks we're activated.
+ // Some tools seem to want the application to be activated before
+ // interacting with them, some things are disallowed during activation
+ // (such as exiting), and DispatchStart (for example) runs
+ // automated tests synchronously.
+ CoreWindow::GetForCurrentThread()->Dispatcher->RunAsync(
+ CoreDispatcherPriority::Normal, ref new DispatchedHandler([this]() {
+ ApplicationUwp::Get()->DispatchStart();
+ }));
+ }
+ previously_activated_ = true;
}
+ private:
+ bool previously_activated_;
+ // Only valid if previously_activated_ is true
+ ActivationKind previous_activation_kind_;
+ std::vector<std::string> args_;
+ std::vector<const char *> argv_;
+
+ starboard::shared::uwp::ApplicationUwp application_;
};
ref class Direct3DApplicationSource sealed : IFrameworkViewSource {
public:
- Direct3DApplicationSource() {
- SB_LOG(INFO) << "Direct3DApplicationSource";
- }
+ Direct3DApplicationSource() {}
virtual IFrameworkView^ CreateView() {
return ref new App();
}
@@ -74,6 +260,21 @@
namespace shared {
namespace uwp {
+// If an argv[0] is required, fill it in with the result of
+// GetModuleFileName()
+std::string GetArgvZero() {
+ const size_t kMaxModuleNameSize = 256;
+ wchar_t buffer[kMaxModuleNameSize];
+ DWORD result = GetModuleFileName(NULL, buffer, kMaxModuleNameSize);
+ std::string arg;
+ if (result == 0) {
+ arg = "unknown";
+ } else {
+ arg = wchar_tToUTF8(buffer, result).c_str();
+ }
+ return arg;
+}
+
ApplicationUwp::ApplicationUwp() : window_(kSbWindowInvalid) {}
ApplicationUwp::~ApplicationUwp() {}
@@ -87,7 +288,7 @@
return nullptr;
}
-SbWindow ApplicationUwp::CreateWindow(const SbWindowOptions* options) {
+SbWindow ApplicationUwp::CreateWindowForUWP(const SbWindowOptions* options) {
// TODO: Determine why SB_DCHECK(IsCurrentThread()) fails in nplb, fix it,
// and add back this check.
@@ -116,22 +317,61 @@
}
bool ApplicationUwp::DispatchNextEvent() {
- auto direct3DApplicationSource = ref new Direct3DApplicationSource();
- CoreApplication::Run(direct3DApplicationSource);
+ core_window_->Activate();
+ core_window_->Dispatcher->ProcessEvents(
+ CoreProcessEventsOption::ProcessUntilQuit);
return false;
}
void ApplicationUwp::Inject(Application::Event* event) {
- // TODO: Implement with CoreWindow->GetForCurrentThread->Dispatcher->RunAsync
- SB_NOTIMPLEMENTED();
+ CoreWindow::GetForCurrentThread()->Dispatcher->RunAsync(
+ CoreDispatcherPriority::Normal,
+ ref new DispatchedHandler([this, event]() {
+ bool result = DispatchAndDelete(event);
+ if (!result) {
+ CoreApplication::Exit();
+ }
+ }));
}
void ApplicationUwp::InjectTimedEvent(Application::TimedEvent* timed_event) {
- SB_NOTIMPLEMENTED();
+ SbTimeMonotonic delay_usec =
+ timed_event->target_time - SbTimeGetMonotonicNow();
+ if (delay_usec < 0) {
+ delay_usec = 0;
+ }
+
+ // TimeSpan ticks are, like FILETIME, 100ns
+ const SbTimeMonotonic kTicksPerUsec = 10;
+
+ TimeSpan timespan;
+ timespan.Duration = delay_usec * kTicksPerUsec;
+
+ ScopedLock lock(mutex_);
+ ThreadPoolTimer^ timer = ThreadPoolTimer::CreateTimer(
+ ref new TimerElapsedHandler([this, timed_event](ThreadPoolTimer^ timer) {
+ core_window_->Dispatcher->RunAsync(
+ CoreDispatcherPriority::Normal,
+ ref new DispatchedHandler([this, timed_event]() {
+ timed_event->callback(timed_event->context);
+ ScopedLock lock(mutex_);
+ auto it = timer_event_map_.find(timed_event->id);
+ if (it != timer_event_map_.end()) {
+ timer_event_map_.erase(it);
+ }
+ }));
+ }), timespan);
+ timer_event_map_.emplace(timed_event->id, timer);
}
void ApplicationUwp::CancelTimedEvent(SbEventId event_id) {
- SB_NOTIMPLEMENTED();
+ ScopedLock lock(mutex_);
+ auto it = timer_event_map_.find(event_id);
+ if (it == timer_event_map_.end()) {
+ return;
+ }
+ it->second->Cancel();
+ timer_event_map_.erase(it);
}
Application::TimedEvent* ApplicationUwp::GetNextDueTimedEvent() {
@@ -147,3 +387,35 @@
} // namespace uwp
} // namespace shared
} // namespace starboard
+
+[Platform::MTAThread]
+int main(Platform::Array<Platform::String^>^ args) {
+ if (!IsDebuggerPresent()) {
+ // By default, a Windows application will display a dialog box
+ // when it crashes. This is extremely undesirable when run offline.
+ // The following configures messages to be print to the console instead.
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ }
+
+ WSAData wsaData;
+ int init_result = WSAStartup(
+ MAKEWORD(kWinSockVersionMajor, kWinSockVersionMajor), &wsaData);
+
+ SB_CHECK(init_result == 0);
+ // WSAStartup returns the highest version that is supported up to the version
+ // we request.
+ SB_CHECK(LOBYTE(wsaData.wVersion) == kWinSockVersionMajor &&
+ HIBYTE(wsaData.wVersion) == kWinSockVersionMinor);
+
+ starboard::shared::win32::RegisterMainThread();
+
+ auto direct3DApplicationSource = ref new Direct3DApplicationSource();
+ CoreApplication::Run(direct3DApplicationSource);
+
+ WSACleanup();
+
+ return main_return_value;
+}
diff --git a/src/starboard/shared/uwp/application_uwp.h b/src/starboard/shared/uwp/application_uwp.h
index f2c9efa..8bdc00d 100644
--- a/src/starboard/shared/uwp/application_uwp.h
+++ b/src/starboard/shared/uwp/application_uwp.h
@@ -15,30 +15,27 @@
#ifndef STARBOARD_SHARED_UWP_APPLICATION_UWP_H_
#define STARBOARD_SHARED_UWP_APPLICATION_UWP_H_
+#include <agile.h>
+
+#include <string>
+#include <unordered_map>
+
#include "starboard/configuration.h"
+#include "starboard/mutex.h"
#include "starboard/shared/internal_only.h"
#include "starboard/shared/starboard/application.h"
#include "starboard/shared/starboard/command_line.h"
+#include "starboard/shared/uwp/winrt_workaround.h"
#include "starboard/types.h"
#include "starboard/window.h"
-namespace __winRT {
-// TODO: without this, we get the following error at CoreApplication::Run:
-// 'long __winRT::__getActivationFactoryByPCWSTR(i
-// void *,Platform::Guid &,void **)':
-// cannot convert argument 1 from 'const wchar_t [46]' to 'void *'
-inline long __getActivationFactoryByPCWSTR(const wchar_t* a,
- Platform::Guid& b,
- void** c) {
- return __getActivationFactoryByPCWSTR(
- static_cast<void*>(const_cast<wchar_t*>(a)), b, c);
-}
-} // namespace __winRT
-
namespace starboard {
namespace shared {
namespace uwp {
+// Returns win32's GetModuleFileName(). For cases where we'd like an argv[0].
+std::string GetArgvZero();
+
class ApplicationUwp : public shared::starboard::Application {
public:
ApplicationUwp();
@@ -48,10 +45,7 @@
return static_cast<ApplicationUwp*>(shared::starboard::Application::Get());
}
-// Do not use the macro from windows.h.
-#undef CreateWindow
-#undef CreateWindowW
- SbWindow CreateWindow(const SbWindowOptions* options);
+ SbWindow CreateWindowForUWP(const SbWindowOptions* options);
bool DestroyWindow(SbWindow window);
@@ -59,6 +53,34 @@
shared::starboard::Application::DispatchStart();
}
+ // public for IFrameworkView subclass
+ void SetCommandLine(int argc, const char** argv) {
+ shared::starboard::Application::SetCommandLine(argc, argv);
+ }
+
+ // public for IFrameworkView subclass
+ bool DispatchAndDelete(Application::Event* event) {
+ return shared::starboard::Application::DispatchAndDelete(event);
+ }
+
+ Platform::Agile<Windows::UI::Core::CoreWindow> GetCoreWindow() const {
+ return core_window_;
+ }
+
+ // public for IFrameworkView subclass
+ void SetCoreWindow(Windows::UI::Core::CoreWindow^ window) {
+ core_window_ = window;
+ }
+
+ void OnKeyEvent(Windows::UI::Core::CoreWindow^ sender,
+ Windows::UI::Core::KeyEventArgs^ args, bool up);
+
+ void Inject(Event* event) SB_OVERRIDE;
+
+ void SetStartLink(const char* link) SB_OVERRIDE {
+ shared::starboard::Application::SetStartLink(link);
+ }
+
private:
// --- Application overrides ---
bool IsStartImmediate() SB_OVERRIDE { return false; }
@@ -66,7 +88,6 @@
void Teardown() SB_OVERRIDE;
Event* GetNextEvent() SB_OVERRIDE;
bool DispatchNextEvent() SB_OVERRIDE;
- void Inject(Event* event) SB_OVERRIDE;
void InjectTimedEvent(TimedEvent* timed_event) SB_OVERRIDE;
void CancelTimedEvent(SbEventId event_id) SB_OVERRIDE;
TimedEvent* GetNextDueTimedEvent() SB_OVERRIDE;
@@ -74,6 +95,12 @@
// The single open window, if any.
SbWindow window_;
+ Platform::Agile<Windows::UI::Core::CoreWindow> core_window_;
+
+ Mutex mutex_;
+ // Locked by mutex_
+ std::unordered_map<SbEventId, Windows::System::Threading::ThreadPoolTimer^>
+ timer_event_map_;
};
} // namespace uwp
diff --git a/src/starboard/shared/uwp/application_uwp_key_event.cc b/src/starboard/shared/uwp/application_uwp_key_event.cc
new file mode 100644
index 0000000..be64071
--- /dev/null
+++ b/src/starboard/shared/uwp/application_uwp_key_event.cc
@@ -0,0 +1,300 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/uwp/application_uwp.h"
+
+#include "starboard/event.h"
+#include "starboard/input.h"
+#include "starboard/key.h"
+
+using Windows::UI::Core::CoreWindow;
+using Windows::UI::Core::KeyEventArgs;
+using Windows::UI::Core::CoreVirtualKeyStates;
+using Windows::Security::ExchangeActiveSyncProvisioning::
+ EasClientDeviceInformation;
+using Windows::System::VirtualKey;
+
+namespace {
+
+SbKey VirtualKeyToSbKey(VirtualKey key) {
+ switch (key) {
+ case VirtualKey::None:
+ case VirtualKey::NavigationView:
+ case VirtualKey::NavigationMenu:
+ case VirtualKey::NavigationUp:
+ case VirtualKey::NavigationDown:
+ case VirtualKey::NavigationLeft:
+ case VirtualKey::NavigationRight:
+ case VirtualKey::NavigationAccept:
+ case VirtualKey::NavigationCancel:
+ return kSbKeyUnknown;
+ case VirtualKey::Cancel: return kSbKeyCancel;
+ case VirtualKey::Back: return kSbKeyBack;
+ case VirtualKey::Tab: return kSbKeyTab;
+ case VirtualKey::Clear: return kSbKeyClear;
+ case VirtualKey::Enter: return kSbKeyReturn;
+ case VirtualKey::Shift: return kSbKeyShift;
+ case VirtualKey::Control: return kSbKeyControl;
+ case VirtualKey::Menu: return kSbKeyMenu;
+ case VirtualKey::Pause: return kSbKeyPause;
+ case VirtualKey::CapitalLock: return kSbKeyCapital;
+ // Hangul and Kana have the same VirtualKey constant
+ case VirtualKey::Kana: return kSbKeyKana;
+ case VirtualKey::Junja: return kSbKeyJunja;
+ case VirtualKey::Final: return kSbKeyFinal;
+ // Hanja and Kanji have the same VirtualKey constant
+ case VirtualKey::Hanja: return kSbKeyHanja;
+ case VirtualKey::Escape: return kSbKeyEscape;
+ case VirtualKey::Convert: return kSbKeyConvert;
+ case VirtualKey::NonConvert: return kSbKeyNonconvert;
+ case VirtualKey::Accept: return kSbKeyAccept;
+ case VirtualKey::ModeChange: return kSbKeyModechange;
+ case VirtualKey::Space: return kSbKeySpace;
+ case VirtualKey::PageUp: return kSbKeyPrior;
+ case VirtualKey::PageDown: return kSbKeyNext;
+ case VirtualKey::End: return kSbKeyEnd;
+ case VirtualKey::Home: return kSbKeyHome;
+ case VirtualKey::Left: return kSbKeyLeft;
+ case VirtualKey::Up: return kSbKeyUp;
+ case VirtualKey::Right: return kSbKeyRight;
+ case VirtualKey::Down: return kSbKeyDown;
+ case VirtualKey::Select: return kSbKeySelect;
+ case VirtualKey::Print: return kSbKeyPrint;
+ case VirtualKey::Execute: return kSbKeyExecute;
+ case VirtualKey::Snapshot: return kSbKeySnapshot;
+ case VirtualKey::Insert: return kSbKeyInsert;
+ case VirtualKey::Delete: return kSbKeyDelete;
+ case VirtualKey::Number0: return kSbKey0;
+ case VirtualKey::Number1: return kSbKey1;
+ case VirtualKey::Number2: return kSbKey2;
+ case VirtualKey::Number3: return kSbKey3;
+ case VirtualKey::Number4: return kSbKey4;
+ case VirtualKey::Number5: return kSbKey5;
+ case VirtualKey::Number6: return kSbKey6;
+ case VirtualKey::Number7: return kSbKey7;
+ case VirtualKey::Number8: return kSbKey8;
+ case VirtualKey::Number9: return kSbKey9;
+ case VirtualKey::A: return kSbKeyA;
+ case VirtualKey::B: return kSbKeyB;
+ case VirtualKey::C: return kSbKeyC;
+ case VirtualKey::D: return kSbKeyD;
+ case VirtualKey::E: return kSbKeyE;
+ case VirtualKey::F: return kSbKeyF;
+ case VirtualKey::G: return kSbKeyG;
+ case VirtualKey::H: return kSbKeyH;
+ case VirtualKey::I: return kSbKeyI;
+ case VirtualKey::J: return kSbKeyJ;
+ case VirtualKey::K: return kSbKeyK;
+ case VirtualKey::L: return kSbKeyL;
+ case VirtualKey::M: return kSbKeyM;
+ case VirtualKey::N: return kSbKeyN;
+ case VirtualKey::O: return kSbKeyO;
+ case VirtualKey::P: return kSbKeyP;
+ case VirtualKey::Q: return kSbKeyQ;
+ case VirtualKey::R: return kSbKeyR;
+ case VirtualKey::S: return kSbKeyS;
+ case VirtualKey::T: return kSbKeyT;
+ case VirtualKey::U: return kSbKeyU;
+ case VirtualKey::V: return kSbKeyV;
+ case VirtualKey::W: return kSbKeyW;
+ case VirtualKey::X: return kSbKeyX;
+ case VirtualKey::Y: return kSbKeyY;
+ case VirtualKey::Z: return kSbKeyZ;
+ case VirtualKey::LeftWindows: return kSbKeyLwin;
+ case VirtualKey::RightWindows: return kSbKeyRwin;
+ case VirtualKey::Application: return kSbKeyApps;
+ case VirtualKey::Sleep: return kSbKeySleep;
+ case VirtualKey::NumberPad0: return kSbKeyNumpad0;
+ case VirtualKey::NumberPad1: return kSbKeyNumpad1;
+ case VirtualKey::NumberPad2: return kSbKeyNumpad2;
+ case VirtualKey::NumberPad3: return kSbKeyNumpad3;
+ case VirtualKey::NumberPad4: return kSbKeyNumpad4;
+ case VirtualKey::NumberPad5: return kSbKeyNumpad5;
+ case VirtualKey::NumberPad6: return kSbKeyNumpad6;
+ case VirtualKey::NumberPad7: return kSbKeyNumpad7;
+ case VirtualKey::NumberPad8: return kSbKeyNumpad8;
+ case VirtualKey::NumberPad9: return kSbKeyNumpad9;
+ case VirtualKey::Multiply: return kSbKeyMultiply;
+ case VirtualKey::Add: return kSbKeyAdd;
+ case VirtualKey::Separator: return kSbKeySeparator;
+ case VirtualKey::Subtract: return kSbKeySubtract;
+ case VirtualKey::Decimal: return kSbKeyDecimal;
+ case VirtualKey::Divide: return kSbKeyDivide;
+ case VirtualKey::F1: return kSbKeyF1;
+ case VirtualKey::F2: return kSbKeyF2;
+ case VirtualKey::F3: return kSbKeyF3;
+ case VirtualKey::F4: return kSbKeyF4;
+ case VirtualKey::F5: return kSbKeyF5;
+ case VirtualKey::F6: return kSbKeyF6;
+ case VirtualKey::F7: return kSbKeyF7;
+ case VirtualKey::F8: return kSbKeyF8;
+ case VirtualKey::F9: return kSbKeyF9;
+ case VirtualKey::F10: return kSbKeyF10;
+ case VirtualKey::F11: return kSbKeyF11;
+ case VirtualKey::F12: return kSbKeyF12;
+ case VirtualKey::F13: return kSbKeyF13;
+ case VirtualKey::F14: return kSbKeyF14;
+ case VirtualKey::F15: return kSbKeyF15;
+ case VirtualKey::F16: return kSbKeyF16;
+ case VirtualKey::F17: return kSbKeyF17;
+ case VirtualKey::F18: return kSbKeyF18;
+ case VirtualKey::F19: return kSbKeyF19;
+ case VirtualKey::F20: return kSbKeyF20;
+ case VirtualKey::F21: return kSbKeyF21;
+ case VirtualKey::F22: return kSbKeyF22;
+ case VirtualKey::F23: return kSbKeyF23;
+ case VirtualKey::F24: return kSbKeyF24;
+ case VirtualKey::NumberKeyLock: return kSbKeyNumlock;
+ case VirtualKey::Scroll: return kSbKeyScroll;
+ case VirtualKey::LeftShift: return kSbKeyLshift;
+ case VirtualKey::RightShift: return kSbKeyRshift;
+ case VirtualKey::LeftControl: return kSbKeyLcontrol;
+ case VirtualKey::RightControl: return kSbKeyRcontrol;
+ case VirtualKey::LeftMenu: return kSbKeyLmenu;
+ case VirtualKey::RightMenu: return kSbKeyRmenu;
+ case VirtualKey::GoBack: return kSbKeyBrowserBack;
+ case VirtualKey::GoForward: return kSbKeyBrowserForward;
+ case VirtualKey::Refresh: return kSbKeyBrowserRefresh;
+ case VirtualKey::Stop: return kSbKeyBrowserStop;
+ case VirtualKey::Search: return kSbKeyBrowserSearch;
+ case VirtualKey::Favorites: return kSbKeyBrowserFavorites;
+ case VirtualKey::GoHome: return kSbKeyBrowserHome;
+ case VirtualKey::LeftButton: return kSbKeyMouse1;
+ case VirtualKey::RightButton: return kSbKeyMouse2;
+ case VirtualKey::MiddleButton: return kSbKeyMouse3;
+ case VirtualKey::XButton1: return kSbKeyMouse4;
+ case VirtualKey::XButton2: return kSbKeyMouse5;
+ case VirtualKey::GamepadA: return kSbKeyGamepad1;
+ case VirtualKey::GamepadB: return kSbKeyGamepad2;
+ case VirtualKey::GamepadX: return kSbKeyGamepad3;
+ case VirtualKey::GamepadY: return kSbKeyGamepad4;
+ case VirtualKey::GamepadRightShoulder: return kSbKeyGamepadRightBumper;
+ case VirtualKey::GamepadLeftShoulder: return kSbKeyGamepadLeftBumper;
+ case VirtualKey::GamepadLeftTrigger: return kSbKeyGamepadLeftTrigger;
+ case VirtualKey::GamepadRightTrigger: return kSbKeyGamepadRightTrigger;
+ case VirtualKey::GamepadDPadUp: return kSbKeyGamepadDPadUp;
+ case VirtualKey::GamepadDPadDown: return kSbKeyGamepadDPadDown;
+ case VirtualKey::GamepadDPadLeft: return kSbKeyGamepadDPadLeft;
+ case VirtualKey::GamepadDPadRight: return kSbKeyGamepadDPadRight;
+ case VirtualKey::GamepadMenu: return kSbKeyGamepad6;
+ case VirtualKey::GamepadView: return kSbKeyGamepad5;
+ case VirtualKey::GamepadLeftThumbstickButton:
+ return kSbKeyGamepadLeftStick;
+ case VirtualKey::GamepadRightThumbstickButton:
+ return kSbKeyGamepadRightStick;
+ case VirtualKey::GamepadLeftThumbstickUp:
+ return kSbKeyGamepadLeftStickUp;
+ case VirtualKey::GamepadLeftThumbstickDown:
+ return kSbKeyGamepadLeftStickDown;
+ case VirtualKey::GamepadLeftThumbstickRight:
+ return kSbKeyGamepadLeftStickRight;
+ case VirtualKey::GamepadLeftThumbstickLeft:
+ return kSbKeyGamepadLeftStickLeft;
+ case VirtualKey::GamepadRightThumbstickUp:
+ return kSbKeyGamepadRightStickUp;
+ case VirtualKey::GamepadRightThumbstickDown:
+ return kSbKeyGamepadRightStickDown;
+ case VirtualKey::GamepadRightThumbstickRight:
+ return kSbKeyGamepadRightStickRight;
+ case VirtualKey::GamepadRightThumbstickLeft:
+ return kSbKeyGamepadRightStickLeft;
+ default:
+ return kSbKeyUnknown;
+ }
+}
+
+// Returns true if a given VirtualKey is currently being held down.
+bool IsDown(CoreWindow^ sender, VirtualKey key) {
+ switch (sender->GetKeyState(key)) {
+ case CoreVirtualKeyStates::Down:
+ case CoreVirtualKeyStates::Locked:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace
+
+namespace starboard {
+namespace shared {
+namespace uwp {
+
+void ApplicationUwp::OnKeyEvent(
+ CoreWindow^ sender, KeyEventArgs^ args, bool up) {
+ args->Handled = true;
+ SbInputData* data = new SbInputData();
+ SbMemorySet(data, 0, sizeof(*data));
+
+ data->window = window_;
+ data->device_type = kSbInputDeviceTypeKeyboard;
+ // TODO: Devices might have colliding hashcodes. Some other unique int
+ // ID generation tool would be better.
+ auto device_information = ref new EasClientDeviceInformation();
+ Platform::String^ device_id_string = device_information->Id.ToString();
+ data->device_id = device_id_string->GetHashCode();
+ data->key = VirtualKeyToSbKey(args->VirtualKey);
+
+ if (up) {
+ data->type = kSbInputEventTypeUnpress;
+ } else {
+ data->type = kSbInputEventTypePress;
+ }
+
+ // Build up key_modifiers
+ if (IsDown(sender, VirtualKey::Menu) ||
+ IsDown(sender, VirtualKey::LeftMenu) ||
+ IsDown(sender, VirtualKey::RightMenu)) {
+ data->key_modifiers |= kSbKeyModifiersAlt;
+ }
+ if (IsDown(sender, VirtualKey::Control) ||
+ IsDown(sender, VirtualKey::LeftControl) ||
+ IsDown(sender, VirtualKey::RightControl)) {
+ data->key_modifiers |= kSbKeyModifiersCtrl;
+ }
+ if (IsDown(sender, VirtualKey::LeftWindows) ||
+ IsDown(sender, VirtualKey::RightWindows)) {
+ data->key_modifiers |= kSbKeyModifiersMeta;
+ }
+ if (IsDown(sender, VirtualKey::Shift) ||
+ IsDown(sender, VirtualKey::LeftShift) ||
+ IsDown(sender, VirtualKey::RightShift)) {
+ data->key_modifiers |= kSbKeyModifiersShift;
+ }
+
+ // Set key_location
+ switch (args->VirtualKey) {
+ case VirtualKey::LeftMenu:
+ case VirtualKey::LeftControl:
+ case VirtualKey::LeftWindows:
+ case VirtualKey::LeftShift:
+ data->key_location = kSbKeyLocationLeft;
+ break;
+ case VirtualKey::RightMenu:
+ case VirtualKey::RightControl:
+ case VirtualKey::RightWindows:
+ case VirtualKey::RightShift:
+ data->key_location = kSbKeyLocationRight;
+ break;
+ default:
+ break;
+ }
+
+ DispatchAndDelete(new Event(kSbEventTypeInput, data,
+ &Application::DeleteDestructor<SbInputData>));
+}
+
+} // namespace uwp
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/uwp/async_utils.h b/src/starboard/shared/uwp/async_utils.h
new file mode 100644
index 0000000..7e01f82
--- /dev/null
+++ b/src/starboard/shared/uwp/async_utils.h
@@ -0,0 +1,50 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_UWP_ASYNC_UTILS_H_
+#define STARBOARD_SHARED_UWP_ASYNC_UTILS_H_
+
+#include <windows.h>
+
+#include <ppltasks.h>
+
+#include "starboard/log.h"
+
+using Windows::Foundation::IAsyncOperation;
+
+namespace starboard {
+namespace shared {
+namespace uwp {
+
+template <typename TResult>
+TResult WaitForResult(IAsyncOperation<TResult>^ operation) {
+ using concurrency::task_continuation_context;
+ HANDLE event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+ concurrency::create_task(operation,
+ task_continuation_context::use_arbitrary())
+ .then([&event](TResult result) {
+ BOOL success = SetEvent(event);
+ SB_DCHECK(success);
+ }, task_continuation_context::use_arbitrary());
+ DWORD return_value = WaitForSingleObject(event, INFINITE);
+ SB_DCHECK(return_value == WAIT_OBJECT_0);
+ CloseHandle(event);
+ return operation->GetResults();
+}
+
+} // namespace uwp
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_UWP_ASYNC_UTILS_H_
diff --git a/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp b/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp
new file mode 100644
index 0000000..9da1070
--- /dev/null
+++ b/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp
@@ -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.
+
+{
+ 'variables': {
+ 'sb_pedantic_warnings': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'cobalt_platform',
+ 'type': 'static_library',
+ 'sources': [
+ 'xhr_modify_headers.cc',
+ ],
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'AdditionalOptions': [
+ '/ZW', # Windows Runtime
+ '/ZW:nostdlib', # Windows Runtime, no default #using
+ '/EHsx', # C++ exceptions (required with /ZW)
+ '/FU"<(visual_studio_install_path)/lib/x86/store/references/platform.winmd"',
+ '/FU"<(windows_sdk_path)/References/<(windows_sdk_version)/Windows.Foundation.FoundationContract/3.0.0.0/Windows.Foundation.FoundationContract.winmd"',
+ '/FU"<(windows_sdk_path)/References/<(windows_sdk_version)/Windows.Foundation.UniversalApiContract/4.0.0.0/Windows.Foundation.UniversalApiContract.winmd"',
+ ],
+ },
+ },
+ 'defines': [
+ # VS2017 always defines this for UWP apps
+ 'WINAPI_FAMILY=WINAPI_FAMILY_APP',
+ # VS2017 always defines this for UWP apps
+ '__WRL_NO_DEFAULT_LIB__',
+ ],
+ },
+ ],
+}
diff --git a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
new file mode 100644
index 0000000..9497a40
--- /dev/null
+++ b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
@@ -0,0 +1,162 @@
+// 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/xhr/xhr_modify_headers.h"
+
+#include <base/logging.h>
+#include "starboard/shared/uwp/async_utils.h"
+#include "starboard/shared/uwp/winrt_workaround.h"
+#include "starboard/shared/win32/wchar_utils.h"
+
+using Windows::Security::Authentication::Web::Core::
+ WebAuthenticationCoreManager;
+using Windows::Security::Authentication::Web::Core::WebTokenRequest;
+using Windows::Security::Authentication::Web::Core::WebTokenResponse;
+using Windows::Security::Authentication::Web::Core::WebTokenRequestResult;
+using Windows::Security::Authentication::Web::Core::WebTokenRequestStatus;
+using Windows::Security::Credentials::WebAccountProvider;
+using Windows::System::UserAuthenticationStatus;
+
+namespace sbwin32 = starboard::shared::win32;
+
+namespace {
+// The name of the header to send the STS token out on.
+const char kXauthHeaderName[] = "Authorization";
+
+// The prefix for the value of the Authorization header when there is an XAuth
+// token to attach to the request. The token itself should directly follow this
+// prefix.
+const char kXauthHeaderPrefix[] = "XBL3.0 x=";
+
+// The name of the header to detect on requests as a trigger to add an STS token
+// for the given RelyingPartyId (the value of the header). The header itself
+// will be removed from the outgoing request, and replaced with an Authorization
+// header with a valid STS token for the current primary user.
+const base::StringPiece kXauthTriggerHeaderName = "X-STS-RelyingPartyId";
+
+inline std::ostream& operator<<(std::ostream& os,
+ const UserAuthenticationStatus& state) {
+ switch (state) {
+ case UserAuthenticationStatus::Unauthenticated:
+ os << "Unauthenticated";
+ break;
+ case UserAuthenticationStatus::LocallyAuthenticated:
+ os << "LocallyAuthenticated";
+ break;
+ case UserAuthenticationStatus::RemotelyAuthenticated:
+ os << "RemotelyAuthenticated";
+ break;
+ default:
+ os << "Unknown";
+ }
+ return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+ const WebTokenRequestStatus& status) {
+ switch (status) {
+ case WebTokenRequestStatus::Success:
+ os << "Success";
+ break;
+ case WebTokenRequestStatus::AccountProviderNotAvailable:
+ os << "Account provider is not available.";
+ break;
+ case WebTokenRequestStatus::AccountSwitch:
+ os << "Account associated with the request was switched.";
+ break;
+ case WebTokenRequestStatus::ProviderError:
+ os << "Provider Error. See Provider's documentation.";
+ break;
+ case WebTokenRequestStatus::UserCancel:
+ os << "User Cancel";
+ break;
+ case WebTokenRequestStatus::UserInteractionRequired:
+ os << "User interaction is required. Try the request with "
+ "RequestTokenAsync";
+ break;
+ default:
+ os << "Unknown case";
+ }
+ return os;
+}
+
+bool PopulateToken(const std::string& relying_party, std::string* out) {
+ using starboard::shared::uwp::WaitForResult;
+ DCHECK(out);
+ WebAccountProvider^ xbox_provider =
+ WaitForResult(WebAuthenticationCoreManager::FindAccountProviderAsync(
+ "https://xsts.auth.xboxlive.com"));
+ WebTokenRequest^ request = ref new WebTokenRequest(xbox_provider);
+ Platform::String^ relying_party_cx =
+ sbwin32::stringToPlatformString(relying_party);
+ request->Properties->Insert("Url", relying_party_cx);
+ request->Properties->Insert("Target", "xboxlive.signin");
+ request->Properties->Insert("Policy", "DELEGATION");
+
+ WebTokenRequestResult^ token_result = WaitForResult(
+ WebAuthenticationCoreManager::GetTokenSilentlyAsync(request));
+ if (token_result->ResponseStatus ==
+ WebTokenRequestStatus::UserInteractionRequired) {
+ token_result =
+ WaitForResult(WebAuthenticationCoreManager::RequestTokenAsync(request));
+ }
+
+ if (token_result->ResponseStatus == WebTokenRequestStatus::Success) {
+ SB_DCHECK(token_result->ResponseData->Size == 1);
+ if (token_result->ResponseData->Size != 1) {
+ *out = "";
+ return false;
+ }
+ WebTokenResponse^ token_response = token_result->ResponseData->GetAt(0);
+ *out = sbwin32::platformStringToString(token_response->Token);
+ return true;
+ } else {
+ SB_DLOG(INFO) << "Response Status " << token_result->ResponseStatus;
+ if (token_result->ResponseError) {
+ unsigned int error_code = token_result->ResponseError->ErrorCode;
+ Platform::String^ message = token_result->ResponseError->ErrorMessage;
+ SB_DLOG(INFO) << "Error code: " << error_code;
+ SB_DLOG(INFO) << "Error message: "
+ << sbwin32::platformStringToString(message);
+ }
+ *out = "";
+ }
+
+ return false;
+}
+} // namespace
+
+namespace cobalt {
+namespace xhr {
+
+void CobaltXhrModifyHeader(net::HttpRequestHeaders* headers) {
+ DCHECK(headers);
+
+ std::string relying_party;
+ bool trigger_header_found = headers->GetHeader(kXauthTriggerHeaderName,
+ &relying_party);
+
+ if (!trigger_header_found) {
+ return;
+ }
+ std::string out_string;
+ if (!PopulateToken(relying_party, &out_string)) {
+ return;
+ }
+ headers->RemoveHeader(kXauthTriggerHeaderName);
+ headers->SetHeader(kXauthHeaderName, out_string);
+}
+
+} // namespace xhr
+} // namespace cobalt
diff --git a/src/starboard/shared/uwp/cobalt/xhr_modify_headers_test.cc b/src/starboard/shared/uwp/cobalt/xhr_modify_headers_test.cc
new file mode 100644
index 0000000..138579a
--- /dev/null
+++ b/src/starboard/shared/uwp/cobalt/xhr_modify_headers_test.cc
@@ -0,0 +1,74 @@
+// 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/xhr/xhr_modify_headers.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace shared {
+namespace uwp {
+namespace cobalt {
+
+using ::cobalt::xhr::CobaltXhrModifyHeader;
+using net::HttpRequestHeaders;
+
+TEST(XHRModificationTest, EmptyHeaders) {
+ HttpRequestHeaders headers;
+ CobaltXhrModifyHeader(&headers);
+ EXPECT_TRUE(headers.IsEmpty());
+}
+
+TEST(XHRModificationTest, HeaderNotFound) {
+ HttpRequestHeaders headers;
+ headers.SetHeader("Authorization", "ABC");
+ CobaltXhrModifyHeader(&headers);
+ EXPECT_FALSE(!headers.IsEmpty());
+ std::string headers_serialized = headers.ToString();
+ EXPECT_STREQ(headers_serialized.c_str(), "Authorization: ABC\r\n\r\n");
+}
+
+TEST(XHRModificationTest, HeaderReplaced) {
+ HttpRequestHeaders headers;
+ static const char* kXauthTriggerHeaderName = "X-STS-RelyingPartyId";
+ headers.SetHeader(kXauthTriggerHeaderName, "ABC");
+ EXPECT_TRUE(headers.HasHeader(kXauthTriggerHeaderName));
+ CobaltXhrModifyHeader(&headers);
+ EXPECT_FALSE(headers.HasHeader(kXauthTriggerHeaderName));
+ std::string headers_serialized = headers.ToString();
+ EXPECT_TRUE(headers_serialized.find("Authorization: XBL3.0 x=") !=
+ std::string::npos);
+}
+
+TEST(XHRModificationTest, MultipleHeaders) {
+ HttpRequestHeaders headers;
+ static const char* kXauthTriggerHeaderName = "X-STS-RelyingPartyId";
+ headers.SetHeader("H1", "h1");
+ headers.SetHeader("H2", "h2");
+ headers.SetHeader("H3", "h3");
+ EXPECT_TRUE(headers.HasHeader(kXauthTriggerHeaderName));
+ CobaltXhrModifyHeader(&headers);
+ EXPECT_TRUE(headers.HasHeader("H1"));
+ EXPECT_TRUE(headers.HasHeader("H2"));
+ EXPECT_TRUE(headers.HasHeader("H3"));
+ EXPECT_FALSE(headers.HasHeader(kXauthTriggerHeaderName));
+ std::string headers_serialized = headers.ToString();
+ EXPECT_TRUE(headers_serialized.find("Authorization: XBL3.0 x=") !=
+ std::string::npos);
+}
+
+} // namespace cobalt
+} // namespace uwp
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/uwp/starboard_platform.gypi b/src/starboard/shared/uwp/starboard_platform.gypi
new file mode 100644
index 0000000..9e107a2
--- /dev/null
+++ b/src/starboard/shared/uwp/starboard_platform.gypi
@@ -0,0 +1,36 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+ 'variables': {
+ 'starboard_platform_dependent_files': [
+ 'application_uwp.cc',
+ 'application_uwp.h',
+ 'application_uwp_key_event.cc',
+ 'async_utils.h',
+ 'system_get_property.cc',
+ 'window_create.cc',
+ 'window_destroy.cc',
+ 'window_get_platform_handle.cc',
+ 'window_get_size.cc',
+ 'window_set_default_options.cc',
+ 'window_internal.cc',
+ 'window_internal.h',
+ 'winrt_workaround.h',
+ '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
+ ]
+ }
+}
diff --git a/src/starboard/shared/uwp/system_get_property.cc b/src/starboard/shared/uwp/system_get_property.cc
new file mode 100644
index 0000000..85e85c5
--- /dev/null
+++ b/src/starboard/shared/uwp/system_get_property.cc
@@ -0,0 +1,124 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/system.h"
+
+#include <string>
+
+#include "starboard/log.h"
+#include "starboard/shared/uwp/application_uwp.h"
+#include "starboard/shared/win32/wchar_utils.h"
+#include "starboard/string.h"
+
+using Windows::Security::ExchangeActiveSyncProvisioning::
+ EasClientDeviceInformation;
+using Windows::System::Profile::AnalyticsInfo;
+using Windows::System::Profile::AnalyticsVersionInfo;
+
+namespace sbwin32 = starboard::shared::win32;
+
+namespace {
+bool CopyStringAndTestIfSuccess(char* out_value,
+ int value_length,
+ const char* from_value) {
+ if (SbStringGetLength(from_value) + 1 > value_length)
+ return false;
+ SbStringCopy(out_value, from_value, value_length);
+ return true;
+}
+} // namespace
+
+bool SbSystemGetProperty(SbSystemPropertyId property_id,
+ char* out_value,
+ int value_length) {
+ if (!out_value || !value_length) {
+ return false;
+ }
+
+ using sbwin32::platformStringToString;
+
+ switch (property_id) {
+ case kSbSystemPropertyChipsetModelNumber:
+ case kSbSystemPropertyModelYear:
+ case kSbSystemPropertyNetworkOperatorName:
+ case kSbSystemPropertySpeechApiKey:
+ return false;
+ case kSbSystemPropertyBrandName: {
+ EasClientDeviceInformation^ current_device_info =
+ ref new EasClientDeviceInformation();
+ std::string brand_name =
+ platformStringToString(current_device_info->SystemManufacturer);
+ if (brand_name.empty()) {
+ return false;
+ }
+ return CopyStringAndTestIfSuccess(out_value, value_length,
+ brand_name.c_str());
+ }
+ case kSbSystemPropertyFirmwareVersion: {
+ EasClientDeviceInformation ^ current_device_info =
+ ref new EasClientDeviceInformation();
+ std::string firmware_version =
+ platformStringToString(current_device_info->SystemFirmwareVersion);
+ if (firmware_version.empty()) {
+ return false;
+ }
+ return CopyStringAndTestIfSuccess(out_value, value_length,
+ firmware_version.c_str());
+ }
+ case kSbSystemPropertyModelName: {
+ EasClientDeviceInformation ^ current_device_info =
+ ref new EasClientDeviceInformation();
+ std::string product_name =
+ platformStringToString(current_device_info->SystemProductName);
+ product_name.erase(
+ std::remove(product_name.begin(), product_name.end(), ' '),
+ product_name.end());
+
+ return CopyStringAndTestIfSuccess(out_value, value_length,
+ product_name.c_str());
+ }
+ case kSbSystemPropertyFriendlyName: {
+ EasClientDeviceInformation^ current_device_info =
+ ref new EasClientDeviceInformation();
+ std::string friendly_name =
+ platformStringToString(current_device_info->FriendlyName);
+ if (friendly_name.empty()) {
+ return false;
+ }
+ return CopyStringAndTestIfSuccess(out_value, value_length,
+ friendly_name.c_str());
+ }
+
+ case kSbSystemPropertyPlatformName: {
+ AnalyticsVersionInfo^ version_info = AnalyticsInfo::VersionInfo;
+ std::string platform_str =
+ starboard::shared::win32::platformStringToString(
+ version_info->DeviceFamily);
+ if (platform_str.empty()) {
+ return false;
+ }
+ return CopyStringAndTestIfSuccess(out_value, value_length,
+ platform_str.c_str());
+ }
+ case kSbSystemPropertyPlatformUuid: {
+ SB_NOTIMPLEMENTED();
+ return CopyStringAndTestIfSuccess(out_value, value_length, "N/A");
+ }
+ default:
+ SB_DLOG(WARNING) << __FUNCTION__
+ << ": Unrecognized property: " << property_id;
+ break;
+ }
+ return false;
+}
diff --git a/src/starboard/shared/uwp/window_create.cc b/src/starboard/shared/uwp/window_create.cc
index 70ea0cc..9f9a0fa 100644
--- a/src/starboard/shared/uwp/window_create.cc
+++ b/src/starboard/shared/uwp/window_create.cc
@@ -17,5 +17,6 @@
#include "starboard/shared/uwp/application_uwp.h"
SbWindow SbWindowCreate(const SbWindowOptions* options) {
- return starboard::shared::uwp::ApplicationUwp::Get()->CreateWindow(options);
+ return starboard::shared::uwp::ApplicationUwp::Get()->CreateWindowForUWP(
+ options);
}
diff --git a/src/starboard/shared/uwp/window_get_platform_handle.cc b/src/starboard/shared/uwp/window_get_platform_handle.cc
index 7e19456..ba780bd 100644
--- a/src/starboard/shared/uwp/window_get_platform_handle.cc
+++ b/src/starboard/shared/uwp/window_get_platform_handle.cc
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <EGL/egl.h>
+
+#include "starboard/shared/uwp/window_internal.h"
#include "starboard/window.h"
void* SbWindowGetPlatformHandle(SbWindow window) {
@@ -19,5 +22,5 @@
return NULL;
}
- return static_cast<void*>(window);
+ return reinterpret_cast<EGLNativeWindowType>(window->egl_native_window());
}
diff --git a/src/starboard/shared/uwp/window_internal.cc b/src/starboard/shared/uwp/window_internal.cc
index 8fb0fc7..0b34b01 100644
--- a/src/starboard/shared/uwp/window_internal.cc
+++ b/src/starboard/shared/uwp/window_internal.cc
@@ -12,11 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <EGL/egl.h>
+#include <windows.h>
+
+// For __getActivationFactoryByPCWSTR custom definition.
+#include "starboard/shared/uwp/application_uwp.h"
#include "starboard/shared/uwp/window_internal.h"
+using Windows::UI::Core::CoreWindow;
+
// TODO: Make sure the width and height here behave well given that we want
// 1080 video, but perhaps 4k UI where applicable.
SbWindowPrivate::SbWindowPrivate(const SbWindowOptions* /*options*/)
- : width(1920), height(1080) {}
+ : width(1920),
+ height(1080),
+ output_width(1920),
+ output_height(1080) {
+ egl_native_window_ = reinterpret_cast<EGLNativeWindowType>(
+ starboard::shared::uwp::ApplicationUwp::Get()->GetCoreWindow().Get());
+}
SbWindowPrivate::~SbWindowPrivate() {}
diff --git a/src/starboard/shared/uwp/window_internal.h b/src/starboard/shared/uwp/window_internal.h
index 92d567b..ef0777e 100644
--- a/src/starboard/shared/uwp/window_internal.h
+++ b/src/starboard/shared/uwp/window_internal.h
@@ -15,6 +15,8 @@
#ifndef STARBOARD_SHARED_UWP_WINDOW_INTERNAL_H_
#define STARBOARD_SHARED_UWP_WINDOW_INTERNAL_H_
+#include <EGL/egl.h>
+
#include "starboard/atomic.h"
#include "starboard/time.h"
#include "starboard/window.h"
@@ -33,6 +35,8 @@
// indicates success.
bool GetRefreshRate(uint64_t* refresh_rate);
+ EGLNativeWindowType egl_native_window() const { return egl_native_window_; }
+
// The width of this window.
int width;
@@ -46,6 +50,8 @@
int output_height;
private:
+ EGLNativeWindowType egl_native_window_;
+
// Open and Initialize the video output port.
void SetupVideo();
diff --git a/src/starboard/shared/uwp/winrt_workaround.h b/src/starboard/shared/uwp/winrt_workaround.h
new file mode 100644
index 0000000..e75b766
--- /dev/null
+++ b/src/starboard/shared/uwp/winrt_workaround.h
@@ -0,0 +1,31 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_UWP_WINRT_WORKAROUND_H_
+#define STARBOARD_SHARED_UWP_WINRT_WORKAROUND_H_
+
+namespace __winRT {
+// TODO: without this, we get the following error at CoreApplication::Run:
+// 'long __winRT::__getActivationFactoryByPCWSTR(i
+// void *,Platform::Guid &,void **)':
+// cannot convert argument 1 from 'const wchar_t [46]' to 'void *'
+inline long __getActivationFactoryByPCWSTR(const wchar_t* a,
+ Platform::Guid& b,
+ void** c) {
+ return __getActivationFactoryByPCWSTR(
+ static_cast<void*>(const_cast<wchar_t*>(a)), b, c);
+}
+} // namespace __winRT
+
+#endif // STARBOARD_SHARED_UWP_WINRT_WORKAROUND_H_
diff --git a/src/starboard/shared/wayland/application_wayland.cc b/src/starboard/shared/wayland/application_wayland.cc
index e054624..06b2a28 100644
--- a/src/starboard/shared/wayland/application_wayland.cc
+++ b/src/starboard/shared/wayland/application_wayland.cc
@@ -39,15 +39,17 @@
// Tizen application engine using the generic queue and a tizen implementation.
-ApplicationWayland::ApplicationWayland()
- : seat_(NULL),
+ApplicationWayland::ApplicationWayland(float video_pixel_ratio)
+ : video_pixel_ratio_(video_pixel_ratio),
+ seat_(NULL),
keyboard_(NULL),
key_repeat_event_id_(kSbEventIdInvalid),
- key_repeat_interval_(kKeyHoldTime) {}
+ key_repeat_interval_(kKeyHoldTime),
+ key_modifiers_(0) {}
SbWindow ApplicationWayland::CreateWindow(const SbWindowOptions* options) {
SB_DLOG(INFO) << "CreateWindow";
- SbWindow window = new SbWindowPrivate(options);
+ SbWindow window = new SbWindowPrivate(options, video_pixel_ratio_);
window_ = window;
// Video Plane
@@ -256,11 +258,11 @@
SbMemorySet(data, 0, sizeof(*data));
data->window = window_;
data->type = (state == 0 ? kSbInputEventTypeUnpress : kSbInputEventTypePress);
- data->device_type = kSbInputDeviceTypeRemote; // device_type;
+ data->device_type = kSbInputDeviceTypeRemote;
data->device_id = 1; // kKeyboardDeviceId;
data->key = KeyCodeToSbKey(key);
data->key_location = KeyCodeToSbKeyLocation(key);
- data->key_modifiers = 0; // modifiers;
+ data->key_modifiers = key_modifiers_;
Inject(new Event(kSbEventTypeInput, data,
&Application::DeleteDestructor<SbInputData>));
@@ -286,16 +288,16 @@
wl_shell_surface_set_toplevel(window_->shell_surface);
}
-void ApplicationWayland::Pause() {
- Application::Pause(NULL, NULL);
+void ApplicationWayland::Pause(void* context, EventHandledCallback callback) {
+ Application::Pause(context, callback);
ScopedLock lock(observers_mutex_);
std::for_each(observers_.begin(), observers_.end(),
[](StateObserver* i) { i->OnAppPause(); });
}
-void ApplicationWayland::Unpause() {
- Application::Unpause(NULL, NULL);
+void ApplicationWayland::Unpause(void* context, EventHandledCallback callback) {
+ Application::Unpause(context, callback);
ScopedLock lock(observers_mutex_);
std::for_each(observers_.begin(), observers_.end(),
@@ -317,6 +319,14 @@
observers_.erase(it);
}
+void ApplicationWayland::Deeplink(char* payload) {
+ const size_t payload_size = strlen(payload) + 1;
+ char* copied_payload = new char[payload_size];
+ snprintf(copied_payload, payload_size, "%s", payload);
+ Inject(new Event(kSbEventTypeLink, copiedPayload,
+ [](void* data) { delete[] reinterpret_cast<char*>(data); }));
+}
+
} // namespace wayland
} // namespace shared
} // namespace starboard
diff --git a/src/starboard/shared/wayland/application_wayland.h b/src/starboard/shared/wayland/application_wayland.h
index e355c8b..c9ae756 100644
--- a/src/starboard/shared/wayland/application_wayland.h
+++ b/src/starboard/shared/wayland/application_wayland.h
@@ -36,7 +36,7 @@
class ApplicationWayland : public shared::starboard::QueueApplication {
public:
- ApplicationWayland();
+ explicit ApplicationWayland(float video_pixel_ratio);
~ApplicationWayland() SB_OVERRIDE{};
static ApplicationWayland* Get() {
@@ -54,6 +54,7 @@
void SetPolicy(tizen_policy* policy) { tz_policy_ = policy; }
tizen_policy* GetPolicy() { return tz_policy_; }
void WindowRaise();
+ wl_display* GetWLDisplay() { return display_; }
// input devices
void SetKeyboard(wl_keyboard* keyboard) { keyboard_ = keyboard; }
@@ -62,13 +63,16 @@
wl_seat* GetSeat() { return seat_; }
// key event
+ void UpdateKeyModifiers(unsigned int modifiers) {
+ key_modifiers_ = modifiers;
+ }
void CreateRepeatKey();
void DeleteRepeatKey();
void CreateKey(int key, int state, bool is_repeat);
// state change
- void Pause() SB_OVERRIDE;
- void Unpause() SB_OVERRIDE;
+ void Pause(void* context, EventHandledCallback callback) SB_OVERRIDE;
+ void Unpause(void* context, EventHandledCallback callback) SB_OVERRIDE;
// state change observer
class StateObserver {
@@ -81,6 +85,9 @@
void RegisterObserver(StateObserver* observer);
void UnregisterObserver(StateObserver* observer);
+ // deeplink
+ void Deeplink(char* payload);
+
protected:
// --- Application overrides ---
void Initialize() SB_OVERRIDE;
@@ -100,6 +107,7 @@
// window
SbWindow window_;
+ float video_pixel_ratio_;
wl_display* display_;
wl_compositor* compositor_;
wl_shell* shell_;
@@ -112,6 +120,7 @@
int key_repeat_state_;
SbEventId key_repeat_event_id_;
SbTime key_repeat_interval_;
+ unsigned int key_modifiers_;
// wakeup event
int wakeup_fd_;
diff --git a/src/starboard/shared/wayland/dev_input.h b/src/starboard/shared/wayland/dev_input.h
index 7bc5450..fbcbf11 100644
--- a/src/starboard/shared/wayland/dev_input.h
+++ b/src/starboard/shared/wayland/dev_input.h
@@ -349,7 +349,7 @@
struct wl_surface* surface) {
SB_DLOG(INFO) << "[Key] Keyboard lost focus";
ApplicationWayland* wayland_window_ =
- reinterpret_cast<ApplicationWayland*> data;
+ reinterpret_cast<ApplicationWayland*>(data);
wayland_window_->DeleteRepeatKey();
}
@@ -361,7 +361,7 @@
uint32_t state) {
SB_DLOG(INFO) << "[Key] Key :" << key << ", state:" << state;
ApplicationWayland* wayland_window_ =
- reinterpret_cast<ApplicationWayland*> data;
+ reinterpret_cast<ApplicationWayland*>(data);
if (key == KEY_LEFT || key == KEY_RIGHT || key == KEY_UP || key == KEY_DOWN) {
wayland_window_->CreateKey(key, state, true);
} else {
@@ -376,9 +376,23 @@
uint32_t mods_latched,
uint32_t mods_locked,
uint32_t group) {
+ ApplicationWayland* wayland_window_ =
+ reinterpret_cast<ApplicationWayland*>(data);
+ // Convert to SbKeyModifiers.
+ unsigned int modifiers = kSbKeyModifiersNone;
+
+ if (mods_depressed & 1)
+ modifiers |= kSbKeyModifiersShift;
+ if (mods_depressed & 4)
+ modifiers |= kSbKeyModifiersCtrl;
+ if (mods_depressed & 8)
+ modifiers |= kSbKeyModifiersAlt;
+
SB_DLOG(INFO) << "[Key] Modifiers depressed " << mods_depressed
<< ", latched " << mods_latched << ", locked " << mods_locked
- << ", group " << group;
+ << ", group " << group << ", key modifiers " << modifiers;
+
+ wayland_window_->UpdateKeyModifiers(modifiers);
}
static const struct wl_keyboard_listener keyboard_listener_ = {
@@ -391,14 +405,14 @@
struct wl_seat* seat,
unsigned int caps) {
ApplicationWayland* wayland_window_ =
- reinterpret_cast<ApplicationWayland*> data;
+ reinterpret_cast<ApplicationWayland*>(data);
if (!wayland_window_->GetKeyboard()) {
SB_DLOG(INFO) << "[Key] seat_handle_capabilities caps: " << caps;
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
SB_DLOG(INFO) << "[Key] wl_seat_get_keyboard";
wayland_window_->SetKeyboard(wl_seat_get_keyboard(seat));
wl_keyboard_add_listener(wayland_window_->GetKeyboard(),
- &keyboard_listener, data);
+ &keyboard_listener_, data);
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
SB_DLOG(INFO) << "[Key] wl_keyboard_destroy";
wl_keyboard_destroy(wayland_window_->GetKeyboard());
@@ -418,7 +432,7 @@
const char* interface,
uint32_t version) {
ApplicationWayland* wayland_window_ =
- reinterpret_cast<ApplicationWayland*> data;
+ reinterpret_cast<ApplicationWayland*>(data);
if (strcmp(interface, "wl_compositor") == 0) {
wayland_window_->SetCompositor(static_cast<wl_compositor*>(
wl_registry_bind(registry, name, &wl_compositor_interface, 1)));
@@ -454,7 +468,7 @@
SB_DLOG(INFO) << "shell_surface_configure width(" << width << "), height("
<< height << ")";
if (width && height) {
- SbWindowPrivate* window = reinterpret_cast<SbWindowPrivate*> data;
+ SbWindowPrivate* window = reinterpret_cast<SbWindowPrivate*>(data);
wl_egl_window_resize(window->egl_window, width, height, 0, 0);
} else {
SB_DLOG(INFO) << "width and height is 0. we don't resize that";
@@ -470,10 +484,17 @@
struct tizen_visibility* tizen_visibility
EINA_UNUSED,
uint32_t visibility) {
+#if SB_HAS(LAZY_SUSPEND)
if (visibility == TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED)
- ApplicationWayland::Get()->Pause();
+ ApplicationWayland::Get()->Pause(NULL, NULL);
else
- ApplicationWayland::Get()->Unpause();
+ ApplicationWayland::Get()->Unpause(NULL, NULL);
+#else
+ if (visibility == TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED)
+ shared::starboard::Application::Get()->Suspend(NULL, NULL);
+ else
+ shared::starboard::Application::Get()->Unpause(NULL, NULL);
+#endif
}
static const struct tizen_visibility_listener tizen_visibility_listener = {
diff --git a/src/starboard/shared/wayland/window_get_size.cc b/src/starboard/shared/wayland/window_get_size.cc
index 436eec1..72624c5 100644
--- a/src/starboard/shared/wayland/window_get_size.cc
+++ b/src/starboard/shared/wayland/window_get_size.cc
@@ -25,10 +25,6 @@
size->width = window->width;
size->height = window->height;
-#if SB_HAS_MEDIA_4K_SUPPORT
- size->video_pixel_ratio = 2.0f;
-#else
- size->video_pixel_ratio = 1.0f;
-#endif
+ size->video_pixel_ratio = window->video_pixel_ratio;
return true;
}
diff --git a/src/starboard/shared/wayland/window_internal.cc b/src/starboard/shared/wayland/window_internal.cc
index 3b60686..1eb325a 100644
--- a/src/starboard/shared/wayland/window_internal.cc
+++ b/src/starboard/shared/wayland/window_internal.cc
@@ -19,9 +19,11 @@
const int kWindowHeight = 1080;
}
-SbWindowPrivate::SbWindowPrivate(const SbWindowOptions* options) {
+SbWindowPrivate::SbWindowPrivate(const SbWindowOptions* options,
+ float pixel_ratio) {
width = kWindowWidth;
height = kWindowHeight;
+ video_pixel_ratio = pixel_ratio;
if (options && options->size.width > 0 && options->size.height > 0) {
width = options->size.width;
height = options->size.height;
diff --git a/src/starboard/shared/wayland/window_internal.h b/src/starboard/shared/wayland/window_internal.h
index 574e2dd..7d2c616 100644
--- a/src/starboard/shared/wayland/window_internal.h
+++ b/src/starboard/shared/wayland/window_internal.h
@@ -24,7 +24,8 @@
#include "starboard/window.h"
struct SbWindowPrivate {
- explicit SbWindowPrivate(const SbWindowOptions* options);
+ explicit SbWindowPrivate(const SbWindowOptions* options,
+ float pixel_ratio = 1.0);
~SbWindowPrivate() {}
struct wl_surface* surface;
@@ -38,9 +39,10 @@
Evas_Object* video_window;
#endif
- // The width, height of this window.
+ // The width, height, pixel ratio of this window.
int width;
int height;
+ float video_pixel_ratio;
};
#endif // STARBOARD_SHARED_WAYLAND_WINDOW_INTERNAL_H_
diff --git a/src/starboard/shared/win32/adapter_utils.cc b/src/starboard/shared/win32/adapter_utils.cc
new file mode 100644
index 0000000..d102314
--- /dev/null
+++ b/src/starboard/shared/win32/adapter_utils.cc
@@ -0,0 +1,87 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/adapter_utils.h"
+
+#include <winsock2.h>
+
+#include <iphlpapi.h>
+
+#include <memory>
+
+#include "starboard/log.h"
+#include "starboard/shared/win32/socket_internal.h"
+#include "starboard/socket.h"
+
+namespace {
+const ULONG kDefaultAdapterInfoBufferSizeInBytes = 16 * 1024;
+} // namespace
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+bool GetAdapters(const SbSocketAddressType address_type,
+ std::unique_ptr<char[]>* adapter_info) {
+ SB_DCHECK(adapter_info);
+
+ ULONG family = 0;
+ int address_length_bytes = 0;
+
+ switch (address_type) {
+ case kSbSocketAddressTypeIpv4:
+ family = AF_INET;
+ address_length_bytes = kAddressLengthIpv4;
+ break;
+ case kSbSocketAddressTypeIpv6:
+ family = AF_INET6;
+ address_length_bytes = kAddressLengthIpv6;
+ break;
+ default:
+ SB_NOTREACHED() << "Invalid address type: " << address_type;
+ return false;
+ }
+
+ ULONG adapter_addresses_number_bytes = kDefaultAdapterInfoBufferSizeInBytes;
+
+ for (int try_count = 0; try_count != 2; ++try_count) {
+ // Using auto for return value here, since different versions of windows use
+ // slightly different datatypes. These differences do not matter to us, but
+ // the compiler might warn on them.
+ adapter_info->reset(new char[adapter_addresses_number_bytes]);
+ PIP_ADAPTER_ADDRESSES adapter_addresses =
+ reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_info->get());
+
+ // Note: If |GetAdapterAddresses| deems that buffer supplied is not enough,
+ // it will return the recommended number of bytes in
+ // |adapter_addresses_number_bytes|.
+ auto retval = GetAdaptersAddresses(
+ family, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER, nullptr,
+ adapter_addresses, &adapter_addresses_number_bytes);
+
+ if (retval == ERROR_SUCCESS) {
+ return true;
+ }
+ if (retval != ERROR_BUFFER_OVERFLOW) {
+ // Only retry with more memory if the error says so.
+ break;
+ }
+ SB_LOG(ERROR) << "GetAdapterAddresses() failed with error code " << retval;
+ }
+ return false;
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/adapter_utils.h b/src/starboard/shared/win32/adapter_utils.h
new file mode 100644
index 0000000..bbe11fa
--- /dev/null
+++ b/src/starboard/shared/win32/adapter_utils.h
@@ -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.
+
+#ifndef STARBOARD_SHARED_WIN32_ADAPTER_UTILS_H_
+#define STARBOARD_SHARED_WIN32_ADAPTER_UTILS_H_
+
+#include <winsock2.h>
+
+#include <ifdef.h>
+#include <iphlpapi.h>
+
+#include <memory>
+
+#include "starboard/socket.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+// Returns all of the results for wi32's
+bool GetAdapters(const SbSocketAddressType address_type,
+ std::unique_ptr<char[]>* adapter_info);
+
+// Returns true if a IP_ADAPTER_ADDRESSES IfType is a
+// non-loopback Ethernet interface.
+inline bool IsIfTypeEthernet(DWORD iftype) {
+ switch (iftype) {
+ case IF_TYPE_ETHERNET_CSMACD:
+ case IF_TYPE_IEEE80211:
+ return true;
+ case IF_TYPE_SOFTWARE_LOOPBACK:
+ default:
+ return false;
+ }
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_ADAPTER_UTILS_H_
diff --git a/src/starboard/shared/win32/audio_sink.cc b/src/starboard/shared/win32/audio_sink.cc
new file mode 100644
index 0000000..66597c9
--- /dev/null
+++ b/src/starboard/shared/win32/audio_sink.cc
@@ -0,0 +1,304 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/win32/audio_sink.h"
+
+#include <basetyps.h>
+#include <wrl.h>
+#include <xaudio2.h>
+
+#include <limits>
+
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+#include "starboard/mutex.h"
+#include "starboard/thread.h"
+#include "starboard/time.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace {
+// Fails an SB_DCHECK if an HRESULT is not S_OK
+void CHECK_HRESULT_OK(HRESULT hr) {
+ SB_DCHECK(SUCCEEDED(hr)) << std::hex << hr;
+}
+}
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+class XAudioAudioSink : public SbAudioSinkPrivate {
+ public:
+ XAudioAudioSink(Type* type,
+ const WAVEFORMATEX& wfx,
+ SbAudioSinkFrameBuffers frame_buffers,
+ int frame_buffers_size_in_frames,
+ SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
+ SbAudioSinkConsumeFramesFunc consume_frame_func,
+ void* context);
+ ~XAudioAudioSink() SB_OVERRIDE;
+
+ bool IsType(Type* type) SB_OVERRIDE { return type_ == type; }
+ void SetPlaybackRate(double playback_rate) SB_OVERRIDE {
+ SB_DCHECK(playback_rate >= 0.0);
+ if (playback_rate != 0.0 && playback_rate != 1.0) {
+ SB_NOTIMPLEMENTED() << "TODO: Only playback rates of 0.0 and 1.0 are "
+ "currently supported.";
+ playback_rate = (playback_rate > 0.0) ? 1.0 : 0.0;
+ }
+ ScopedLock lock(mutex_);
+ playback_rate_ = playback_rate;
+ }
+
+ private:
+ static void* ThreadEntryPoint(void* context);
+ void AudioThreadFunc();
+ void SubmitSourceBuffer(int offset_in_frames, int count_frames);
+
+ XAudioAudioSinkType* type_;
+ SbAudioSinkUpdateSourceStatusFunc update_source_status_func_;
+ SbAudioSinkConsumeFramesFunc consume_frame_func_;
+ void* context_;
+
+ SbThread audio_out_thread_;
+
+ SbAudioSinkFrameBuffers frame_buffers_;
+ int frame_buffers_size_in_frames_;
+ WAVEFORMATEX wfx_;
+
+ // Note: despite some documentation to the contrary, it appears
+ // that IXAudio2SourceVoice cannot be a ComPtr.
+ IXAudio2SourceVoice* source_voice_;
+
+ // mutex_ protects only destroying_ and playback_rate_.
+ // Everything else is immutable
+ // after the constructor.
+ ::starboard::Mutex mutex_;
+ bool destroying_;
+ double playback_rate_;
+};
+
+XAudioAudioSink::XAudioAudioSink(
+ Type* type,
+ const WAVEFORMATEX& wfx,
+ SbAudioSinkFrameBuffers frame_buffers,
+ int frame_buffers_size_in_frames,
+ SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
+ SbAudioSinkConsumeFramesFunc consume_frame_func,
+ void* context)
+ : type_(static_cast<XAudioAudioSinkType*>(type)),
+ update_source_status_func_(update_source_status_func),
+ consume_frame_func_(consume_frame_func),
+ context_(context),
+ audio_out_thread_(kSbThreadInvalid),
+ frame_buffers_(frame_buffers),
+ frame_buffers_size_in_frames_(frame_buffers_size_in_frames),
+ wfx_(wfx),
+ destroying_(false),
+ playback_rate_(1.0) {
+ // TODO: Check MaxFrequencyRadio
+ CHECK_HRESULT_OK(
+ type_->x_audio2_->CreateSourceVoice(&source_voice_, &wfx, 0,
+ /*MaxFrequencyRadio = */ 1.0));
+
+ CHECK_HRESULT_OK(source_voice_->Start(0));
+
+ audio_out_thread_ =
+ SbThreadCreate(0, kSbThreadPriorityRealTime, kSbThreadNoAffinity, true,
+ "audio_out", &XAudioAudioSink::ThreadEntryPoint, this);
+ SB_DCHECK(SbThreadIsValid(audio_out_thread_));
+}
+
+XAudioAudioSink::~XAudioAudioSink() {
+ {
+ ScopedLock lock(mutex_);
+ destroying_ = true;
+ }
+ SbThreadJoin(audio_out_thread_, nullptr);
+ source_voice_->DestroyVoice();
+}
+
+// static
+void* XAudioAudioSink::ThreadEntryPoint(void* context) {
+ SB_DCHECK(context);
+ XAudioAudioSink* sink = static_cast<XAudioAudioSink*>(context);
+ sink->AudioThreadFunc();
+
+ return nullptr;
+}
+
+void XAudioAudioSink::SubmitSourceBuffer(int offset_in_frames,
+ int count_frames) {
+ XAUDIO2_BUFFER audio_buffer_info;
+
+ audio_buffer_info.Flags = 0;
+ audio_buffer_info.AudioBytes = wfx_.nChannels *
+ frame_buffers_size_in_frames_ *
+ (wfx_.wBitsPerSample / 8);
+ audio_buffer_info.pAudioData = static_cast<const BYTE*>(frame_buffers_[0]);
+ audio_buffer_info.PlayBegin = offset_in_frames;
+ audio_buffer_info.PlayLength = count_frames;
+ audio_buffer_info.LoopBegin = 0;
+ audio_buffer_info.LoopLength = 0;
+ audio_buffer_info.LoopCount = 0;
+ audio_buffer_info.pContext = nullptr;
+ CHECK_HRESULT_OK(source_voice_->SubmitSourceBuffer(&audio_buffer_info));
+}
+
+void XAudioAudioSink::AudioThreadFunc() {
+ const int kMaxFramesToConsumePerRequest = 1024;
+
+ int submitted_frames = 0;
+ uint64_t samples_played = 0;
+ for (;;) {
+ {
+ ScopedLock lock(mutex_);
+ if (destroying_) {
+ break;
+ }
+ }
+ int frames_in_buffer, offset_in_frames;
+ bool is_playing, is_eos_reached;
+ bool is_playback_rate_zero;
+ {
+ ScopedLock lock(mutex_);
+ is_playback_rate_zero = playback_rate_ == 0.0;
+ }
+ update_source_status_func_(&frames_in_buffer, &offset_in_frames,
+ &is_playing, &is_eos_reached, context_);
+
+ // TODO: make sure that frames_in_buffer is large enough
+ // that it exceeds the voice state pool interval
+ if (!is_playing || frames_in_buffer == 0 || is_playback_rate_zero) {
+ SbThreadSleep(kSbTimeMillisecond * 5);
+ continue;
+ }
+ int unsubmitted_frames = frames_in_buffer - submitted_frames;
+ int unsubmitted_start =
+ (offset_in_frames + submitted_frames) % frame_buffers_size_in_frames_;
+ if (unsubmitted_frames == 0) {
+ // submit nothing
+ } else if (unsubmitted_start + unsubmitted_frames <=
+ frame_buffers_size_in_frames_) {
+ SubmitSourceBuffer(unsubmitted_start, unsubmitted_frames);
+ } else {
+ int count_tail_frames = frame_buffers_size_in_frames_ - unsubmitted_start;
+ SubmitSourceBuffer(unsubmitted_start, count_tail_frames);
+ SubmitSourceBuffer(0, unsubmitted_frames - count_tail_frames);
+ }
+ submitted_frames = frames_in_buffer;
+
+ SbThreadSleep(kSbTimeMillisecond);
+
+ XAUDIO2_VOICE_STATE voice_state;
+ source_voice_->GetState(&voice_state);
+
+ int64_t consumed_frames = voice_state.SamplesPlayed - samples_played;
+ SB_DCHECK(consumed_frames >= 0);
+ SB_DCHECK(consumed_frames < std::numeric_limits<int>::max());
+ int consumed_frames_int = static_cast<int>(consumed_frames);
+
+ consume_frame_func_(consumed_frames_int, context_);
+ submitted_frames -= consumed_frames_int;
+ samples_played = voice_state.SamplesPlayed;
+ }
+}
+
+namespace {
+
+WORD SampleTypeToFormatTag(SbMediaAudioSampleType type) {
+ switch (type) {
+ case kSbMediaAudioSampleTypeInt16:
+ return WAVE_FORMAT_PCM;
+ case kSbMediaAudioSampleTypeFloat32:
+ return WAVE_FORMAT_IEEE_FLOAT;
+ default:
+ SB_NOTREACHED();
+ return 0;
+ }
+}
+
+WORD SampleTypeToBitsPerSample(SbMediaAudioSampleType type) {
+ switch (type) {
+ case kSbMediaAudioSampleTypeInt16:
+ return 16;
+ case kSbMediaAudioSampleTypeFloat32:
+ return 32;
+ default:
+ SB_NOTREACHED();
+ return 0;
+ }
+}
+
+} // namespace
+
+XAudioAudioSinkType::XAudioAudioSinkType() {
+ CHECK_HRESULT_OK(XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR));
+ CHECK_HRESULT_OK(x_audio2_->CreateMasteringVoice(&mastering_voice_));
+}
+
+SbAudioSink XAudioAudioSinkType::Create(
+ int channels,
+ int sampling_frequency_hz,
+ SbMediaAudioSampleType audio_sample_type,
+ SbMediaAudioFrameStorageType audio_frame_storage_type,
+ SbAudioSinkFrameBuffers frame_buffers,
+ int frame_buffers_size_in_frames,
+ SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
+ SbAudioSinkConsumeFramesFunc consume_frames_func,
+ void* context) {
+ SB_DCHECK(audio_frame_storage_type ==
+ kSbMediaAudioFrameStorageTypeInterleaved);
+
+ WAVEFORMATEX wfx;
+
+ wfx.wFormatTag = SampleTypeToFormatTag(audio_sample_type);
+ wfx.nChannels = channels;
+ wfx.nSamplesPerSec = sampling_frequency_hz;
+ wfx.nAvgBytesPerSec = channels *
+ SampleTypeToBitsPerSample(audio_sample_type) *
+ sampling_frequency_hz / 8;
+ wfx.wBitsPerSample = SampleTypeToBitsPerSample(audio_sample_type);
+ wfx.nBlockAlign = (channels * wfx.wBitsPerSample) / 8;
+ wfx.cbSize = 0;
+
+ return new XAudioAudioSink(
+ this, wfx, frame_buffers, frame_buffers_size_in_frames,
+ update_source_status_func, consume_frames_func, context);
+}
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+namespace {
+SbAudioSinkPrivate::Type* audio_sink_;
+} // namespace
+
+// static
+void SbAudioSinkPrivate::PlatformInitialize() {
+ SB_DCHECK(!audio_sink_);
+ audio_sink_ = new starboard::shared::win32::XAudioAudioSinkType();
+ SetPrimaryType(audio_sink_);
+ EnableFallbackToStub();
+}
+
+// static
+void SbAudioSinkPrivate::PlatformTearDown() {
+ SB_DCHECK(audio_sink_ == GetPrimaryType());
+ SetPrimaryType(nullptr);
+ delete audio_sink_;
+ audio_sink_ = nullptr;
+}
diff --git a/src/starboard/shared/win32/audio_sink.h b/src/starboard/shared/win32/audio_sink.h
new file mode 100644
index 0000000..f8820f7
--- /dev/null
+++ b/src/starboard/shared/win32/audio_sink.h
@@ -0,0 +1,67 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_WIN32_AUDIO_SINK_H_
+#define STARBOARD_SHARED_WIN32_AUDIO_SINK_H_
+
+#include <wrl.h>
+#include <xaudio2.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/audio_sink/audio_sink_internal.h"
+
+namespace starboard {
+namespace shared {
+namespace win32 {
+
+class XAudioAudioSinkType : public SbAudioSinkPrivate::Type {
+ friend class XAudioAudioSink;
+
+ public:
+ XAudioAudioSinkType();
+
+ SbAudioSink Create(
+ int channels,
+ int sampling_frequency_hz,
+ SbMediaAudioSampleType audio_sample_type,
+ SbMediaAudioFrameStorageType audio_frame_storage_type,
+ SbAudioSinkFrameBuffers frame_buffers,
+ int frame_buffers_size_in_frames,
+ SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
+ SbAudioSinkConsumeFramesFunc consume_frames_func,
+ void* context);
+
+ bool IsValid(SbAudioSink audio_sink) SB_OVERRIDE {
+ return audio_sink != kSbAudioSinkInvalid && audio_sink->IsType(this);
+ }
+
+ void Destroy(SbAudioSink audio_sink) SB_OVERRIDE {
+ if (audio_sink != kSbAudioSinkInvalid && !IsValid(audio_sink)) {
+ SB_LOG(WARNING) << "audio_sink is invalid.";
+ return;
+ }
+ delete audio_sink;
+ }
+
+ private:
+ Microsoft::WRL::ComPtr<IXAudio2> x_audio2_;
+ IXAudio2MasteringVoice* mastering_voice_;
+};
+
+} // namespace win32
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_WIN32_AUDIO_SINK_H_
diff --git a/src/starboard/shared/win32/audio_sink_get_max_channels.cc b/src/starboard/shared/win32/audio_sink_get_max_channels.cc
new file mode 100644
index 0000000..00d7f93
--- /dev/null
+++ b/src/starboard/shared/win32/audio_sink_get_max_channels.cc
@@ -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.
+
+#include "starboard/audio_sink.h"
+
+int SbAudioSinkGetMaxChannels() {
+ return 6;
+}
diff --git a/src/starboard/shared/win32/audio_sink_get_nearest_supported_sample_frequency.cc b/src/starboard/shared/win32/audio_sink_get_nearest_supported_sample_frequency.cc
new file mode 100644
index 0000000..c9d3740
--- /dev/null
+++ b/src/starboard/shared/win32/audio_sink_get_nearest_supported_sample_frequency.cc
@@ -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.
+
+#include "starboard/audio_sink.h"
+
+#include "starboard/log.h"
+
+int SbAudioSinkGetNearestSupportedSampleFrequency(int sampling_frequency_hz) {
+ if (sampling_frequency_hz <= 0) {
+ SB_LOG(ERROR) << "Invalid audio sampling frequency "
+ << sampling_frequency_hz;
+ return 1;
+ }
+ return sampling_frequency_hz;
+}
diff --git a/src/starboard/shared/win32/audio_sink_is_audio_frame_storage_type_supported.cc b/src/starboard/shared/win32/audio_sink_is_audio_frame_storage_type_supported.cc
new file mode 100644
index 0000000..16a603a
--- /dev/null
+++ b/src/starboard/shared/win32/audio_sink_is_audio_frame_storage_type_supported.cc
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/audio_sink.h"
+
+#include "starboard/log.h"
+
+bool SbAudioSinkIsAudioFrameStorageTypeSupported(
+ SbMediaAudioFrameStorageType audio_frame_storage_type) {
+ switch (audio_frame_storage_type) {
+ case kSbMediaAudioFrameStorageTypeInterleaved:
+ return true;
+ case kSbMediaAudioFrameStorageTypePlanar:
+ return false;
+ default:
+ SB_NOTREACHED();
+ return false;
+ }
+}
diff --git a/src/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc b/src/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc
new file mode 100644
index 0000000..07bfe28
--- /dev/null
+++ b/src/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/audio_sink.h"
+
+#include "starboard/log.h"
+
+bool SbAudioSinkIsAudioSampleTypeSupported(
+ SbMediaAudioSampleType audio_sample_type) {
+ switch (audio_sample_type) {
+ case kSbMediaAudioSampleTypeInt16:
+ return true;
+ case kSbMediaAudioSampleTypeFloat32:
+ return true;
+ default:
+ SB_NOTREACHED();
+ return false;
+ }
+}
diff --git a/src/starboard/shared/win32/file_internal.cc b/src/starboard/shared/win32/file_internal.cc
index a2d7621..e6f7927 100644
--- a/src/starboard/shared/win32/file_internal.cc
+++ b/src/starboard/shared/win32/file_internal.cc
@@ -17,8 +17,11 @@
#include <windows.h>
#include "starboard/log.h"
+#include "starboard/shared/win32/error_utils.h"
#include "starboard/shared/win32/wchar_utils.h"
+namespace sbwin32 = starboard::shared::win32;
+
namespace starboard {
namespace shared {
namespace win32 {
@@ -113,11 +116,16 @@
}
}
+ const DWORD last_error = GetLastError();
+ if (!starboard::shared::win32::IsValidHandle(file_handle)) {
+ SB_DLOG(INFO) << "CreateFile2 failed for " << path << ":"
+ << sbwin32::Win32ErrorCode(last_error);
+ }
+
if (out_error) {
if (starboard::shared::win32::IsValidHandle(file_handle)) {
*out_error = kSbFileOk;
} else {
- const DWORD last_error = GetLastError();
switch (last_error) {
case ERROR_ACCESS_DENIED:
*out_error = kSbFileErrorAccessDenied;
diff --git a/src/starboard/shared/win32/get_home_directory.cc b/src/starboard/shared/win32/get_home_directory.cc
new file mode 100644
index 0000000..aa4272f
--- /dev/null
+++ b/src/starboard/shared/win32/get_home_directory.cc
@@ -0,0 +1,44 @@
+// 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 <string>
+
+#include "starboard/log.h"
+#include "starboard/shared/nouser/user_internal.h"
+#include "starboard/shared/uwp/winrt_workaround.h"
+#include "starboard/shared/win32/wchar_utils.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+
+using Windows::Storage::ApplicationData;
+
+namespace sbwin32 = starboard::shared::win32;
+
+namespace starboard {
+namespace shared {
+namespace nouser {
+
+bool GetHomeDirectory(SbUser user, char* out_path, int path_size) {
+ if (user != SbUserGetCurrent()) {
+ return false;
+ }
+ std::string home_directory =
+ sbwin32::platformStringToString(
+ ApplicationData::Current->LocalFolder->Path);
+ return SbStringCopy(out_path, home_directory.c_str(), path_size);
+}
+
+} // namespace nouser
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/win32/gyp_configuration.py b/src/starboard/shared/win32/gyp_configuration.py
index 46fa1a7..0bf0e26 100644
--- a/src/starboard/shared/win32/gyp_configuration.py
+++ b/src/starboard/shared/win32/gyp_configuration.py
@@ -13,106 +13,48 @@
# limitations under the License.
import logging
import os
+import sys
import config.starboard
-import gyp_utils
-required_sdk_version = '10.0.15063.0'
+from starboard.tools.paths import STARBOARD_ROOT
-# Default Windows SDK bin directory.
-windows_sdk_bin_dir = 'C:\\Program Files (x86)\\Windows Kits\\10\\bin'
-
-# Maybe override Windows SDK bin directory with environment variable.
-windows_sdk_bin_var = 'WindowsSdkBinPath'
-if windows_sdk_bin_var in os.environ:
- windows_sdk_bin_dir = os.environ[windows_sdk_bin_var]
-elif not os.path.exists(windows_sdk_bin_dir):
- # If the above fails, this is our last guess.
- windows_sdk_bin_dir = windows_sdk_bin_dir.replace('Program Files (x86)',
- 'mappedProgramFiles')
-
-# Default Visual Studio Install directory.
-vs_install_dir = ('C:\\Program Files (x86)\\Microsoft Visual Studio'
- + '\\2017\\Professional')
-# Maybe override Visual Studio install directory with environment variable.
-vs_install_dir_var = 'VSINSTALLDIR'
-if vs_install_dir_var in os.environ:
- vs_install_dir = os.environ[vs_install_dir_var]
-elif not os.path.exists(vs_install_dir):
- # If the above fails, this is our last guess.
- vs_install_dir = vs_install_dir.replace('Program Files (x86)',
- 'mappedProgramFiles')
-
-
-vs_install_dir_with_version = (vs_install_dir + '\\VC\\Tools\\MSVC'
- + '\\14.10.25017')
-vs_cl_path = vs_install_dir_with_version + '\\bin\\HostX64\\x64'
-
-
-
-def _CheckVisualStudioVersion():
- if os.path.exists(vs_cl_path):
- return True
- logging.critical('Expected Visual Studio path \"%s\" not found.',
- vs_cl_path)
+import sdk_configuration
def _QuotePath(path):
return '"' + path + '"'
-def _CheckWindowsSdkVersion():
- required_sdk_bin_dir = os.path.join(windows_sdk_bin_dir,
- required_sdk_version)
- if os.path.exists(required_sdk_bin_dir):
- return True
-
- if os.path.exists(windows_sdk_bin_dir):
- contents = os.listdir(windows_sdk_bin_dir)
- contents = [content for content in contents
- if os.path.isdir(os.path.join(windows_sdk_bin_dir, content))]
- non_sdk_dirs = ['arm', 'arm64', 'x64', 'x86']
- installed_sdks = [content for content in contents
- if content not in non_sdk_dirs]
- logging.critical('Windows SDK versions \"%s\" found." \"%s\" required.',
- installed_sdks, required_sdk_version)
- else:
- logging.critical('Windows SDK versions \"%s\" required.',
- required_sdk_version)
- return False
-
-
class PlatformConfig(config.starboard.PlatformConfigStarboard):
"""Starboard Microsoft Windows platform configuration."""
def __init__(self, platform):
super(PlatformConfig, self).__init__(platform)
- _CheckWindowsSdkVersion()
- _CheckVisualStudioVersion()
+ self.sdk = sdk_configuration.SdkConfiguration()
def GetVariables(self, configuration):
+ sdk = self.sdk
variables = super(PlatformConfig, self).GetVariables(configuration)
- windows_sdk_path = os.path.abspath(os.path.join(windows_sdk_bin_dir,
- os.pardir))
variables.update({
- 'visual_studio_install_path': vs_install_dir_with_version,
- 'windows_sdk_path': windows_sdk_path,
- 'windows_sdk_version': required_sdk_version,
+ 'visual_studio_install_path': sdk.vs_install_dir_with_version,
+ 'windows_sdk_path': sdk.windows_sdk_path,
+ 'windows_sdk_version': sdk.required_sdk_version,
})
return variables
def GetEnvironmentVariables(self):
- cl = _QuotePath(os.path.join(vs_cl_path, 'cl.exe'))
- lib = _QuotePath(os.path.join(vs_cl_path, 'lib.exe'))
- link = _QuotePath(os.path.join(vs_cl_path, 'link.exe'))
- rc = _QuotePath(os.path.join(windows_sdk_bin_dir, required_sdk_version,
- 'x64', 'rc.exe'))
+ sdk = self.sdk
+ cl = _QuotePath(os.path.join(sdk.vs_host_tools_path, 'cl.exe'))
+ lib = _QuotePath(os.path.join(sdk.vs_host_tools_path, 'lib.exe'))
+ link = _QuotePath(os.path.join(sdk.vs_host_tools_path, 'link.exe'))
+ rc = _QuotePath(os.path.join(sdk.windows_sdk_host_tools, 'rc.exe'))
env_variables = {
- 'AR' : lib,
- 'AR_HOST' : lib,
+ 'AR': lib,
+ 'AR_HOST': lib,
'CC': cl,
'CXX': cl,
'LD': link,
'RC': rc,
- 'VS_INSTALL_DIR': vs_install_dir,
+ 'VS_INSTALL_DIR': sdk.vs_install_dir,
'CC_HOST': cl,
'CXX_HOST': cl,
'LD_HOST': link,
@@ -139,3 +81,9 @@
'qtcreator_session_name_prefix': 'cobalt',
}
return generator_variables
+
+ def GetToolchain(self):
+ sys.path.append(
+ os.path.join(STARBOARD_ROOT, 'shared', 'msvc', 'uwp'))
+ from toolchain import MSVCUWPToolchain # pylint: disable=g-import-not-at-top,g-bad-import-order
+ return MSVCUWPToolchain()
diff --git a/src/starboard/shared/win32/memory_get_stack_bounds.cc b/src/starboard/shared/win32/memory_get_stack_bounds.cc
index 5a92b61..fec877e 100644
--- a/src/starboard/shared/win32/memory_get_stack_bounds.cc
+++ b/src/starboard/shared/win32/memory_get_stack_bounds.cc
@@ -14,10 +14,10 @@
#include "starboard/memory.h"
-#include "starboard/log.h"
+#include <windows.h>
-void SbMemoryGetStackBounds(void** /*out_high*/, void** /*out_low*/) {
- // TODO the common way to do this is with NtQueryInformationThread
- // for ThreadBasicInformation but that may not be available on UWP.
- SB_NOTIMPLEMENTED();
+void SbMemoryGetStackBounds(void** out_high, void** out_low) {
+ _NT_TIB* tib = reinterpret_cast<_NT_TIB*>(NtCurrentTeb());
+ *out_high = tib->StackBase;
+ *out_low = tib->StackLimit;
}
diff --git a/src/starboard/shared/win32/sdk_configuration.py b/src/starboard/shared/win32/sdk_configuration.py
new file mode 100644
index 0000000..6b21f39
--- /dev/null
+++ b/src/starboard/shared/win32/sdk_configuration.py
@@ -0,0 +1,94 @@
+# 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.
+
+import logging
+import os
+
+class SdkConfiguration:
+ required_sdk_version = '10.0.15063.0'
+
+ # Default Windows SDK bin directory.
+ windows_sdk_bin_dir = 'C:\\Program Files (x86)\\Windows Kits\\10\\bin'
+
+ # windows_sdk_host_tools will be set to, eg,
+ # 'C:\\Program Files (x86)\\Windows Kits\\10\\bin\10.0.15063.0'
+
+ # windows_sdk_path will be set to, eg
+ # 'C:\\Program Files (x86)\\Windows Kits\\10'
+
+ # Default Visual Studio Install directory.
+ vs_install_dir = ('C:\\Program Files (x86)\\Microsoft Visual Studio'
+ + '\\2017\\Professional')
+
+ # vs_install_dir_with_version will be set to, eg
+ # "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools
+ # \MSVC\14.10.25017
+
+ # vs_host_tools_path will be set to, eg
+ # "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools
+ # \MSVC\14.10.25017\bin\HostX64\x64
+
+ def __init__(self):
+ # Maybe override Windows SDK bin directory with environment variable.
+ windows_sdk_bin_var = 'WindowsSdkBinPath'
+ if windows_sdk_bin_var in os.environ:
+ self.windows_sdk_bin_dir = os.environ[windows_sdk_bin_var]
+ elif not os.path.exists(self.windows_sdk_bin_dir):
+ # If the above fails, this is our last guess.
+ self.windows_sdk_bin_dir = self.windows_sdk_bin_dir.replace(
+ 'Program Files (x86)', 'mappedProgramFiles')
+
+ self.windows_sdk_host_tools = os.path.join(
+ self.windows_sdk_bin_dir, self.required_sdk_version, 'x64')
+
+ self.windows_sdk_path = os.path.dirname(self.windows_sdk_bin_dir)
+
+ # Maybe override Visual Studio install directory with environment variable.
+ vs_install_dir_var = 'VSINSTALLDIR'
+ if vs_install_dir_var in os.environ:
+ self.vs_install_dir = os.environ[vs_install_dir_var]
+ elif not os.path.exists(self.vs_install_dir):
+ # If the above fails, this is our last guess.
+ self.vs_install_dir = self.vs_install_dir.replace('Program Files (x86)',
+ 'mappedProgramFiles')
+
+ self.vs_install_dir_with_version = (self.vs_install_dir
+ + '\\VC\\Tools\\MSVC' + '\\14.10.25017')
+ self.vs_host_tools_path = (self.vs_install_dir_with_version
+ + '\\bin\\HostX64\\x64')
+
+ if not os.path.exists(self.vs_host_tools_path):
+ logging.critical('Expected Visual Studio path \"%s\" not found.',
+ self.vs_cl_path)
+ self._CheckWindowsSdkVersion()
+
+ def _CheckWindowsSdkVersion(self):
+ if os.path.exists(self.windows_sdk_host_tools):
+ return True
+
+ if os.path.exists(self.windows_sdk_bin_dir):
+ contents = os.listdir(self.windows_sdk_bin_dir)
+ contents = [content for content in contents
+ if os.path.isdir(
+ os.path.join(self.windows_sdk_bin_dir, content))]
+ non_sdk_dirs = ['arm', 'arm64', 'x64', 'x86']
+ installed_sdks = [content for content in contents
+ if content not in non_sdk_dirs]
+ logging.critical('Windows SDK versions \"%s\" found." \"%s\" required.',
+ installed_sdks, self.required_sdk_version)
+ else:
+ logging.critical('Windows SDK versions \"%s\" required.',
+ self.required_sdk_version)
+ return False
+
diff --git a/src/starboard/shared/win32/socket_get_interface_address.cc b/src/starboard/shared/win32/socket_get_interface_address.cc
index 22e2bb9..fa0ded7 100644
--- a/src/starboard/shared/win32/socket_get_interface_address.cc
+++ b/src/starboard/shared/win32/socket_get_interface_address.cc
@@ -25,6 +25,7 @@
#include "starboard/byte_swap.h"
#include "starboard/log.h"
#include "starboard/memory.h"
+#include "starboard/shared/win32/adapter_utils.h"
#include "starboard/shared/win32/socket_internal.h"
namespace sbwin32 = starboard::shared::win32;
@@ -118,61 +119,11 @@
return true;
}
-bool GetAdapters(const SbSocketAddressType address_type,
- std::unique_ptr<char[]>* adapter_info) {
- SB_DCHECK(adapter_info);
-
- ULONG family = 0;
- int address_length_bytes = 0;
-
- switch (address_type) {
- case kSbSocketAddressTypeIpv4:
- family = AF_INET;
- address_length_bytes = sbwin32::kAddressLengthIpv4;
- break;
- case kSbSocketAddressTypeIpv6:
- family = AF_INET6;
- address_length_bytes = sbwin32::kAddressLengthIpv6;
- break;
- default:
- SB_NOTREACHED() << "Invalid address type: " << address_type;
- return false;
- }
-
- ULONG adapter_addresses_number_bytes = kDefaultAdapterInfoBufferSizeInBytes;
-
- for (int try_count = 0; try_count != 2; ++try_count) {
- // Using auto for return value here, since different versions of windows use
- // slightly different datatypes. These differences do not matter to us, but
- // the compiler might warn on them.
- adapter_info->reset(new char[adapter_addresses_number_bytes]);
- PIP_ADAPTER_ADDRESSES adapter_addresses =
- reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_info->get());
-
- // Note: If |GetAdapterAddresses| deems that buffer supplied is not enough,
- // it will return the recommended number of bytes in
- // |adapter_addresses_number_bytes|.
- auto retval = GetAdaptersAddresses(
- family, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER, nullptr,
- adapter_addresses, &adapter_addresses_number_bytes);
-
- if (retval == ERROR_SUCCESS) {
- return true;
- }
- if (retval != ERROR_BUFFER_OVERFLOW) {
- // Only retry with more memory if the error says so.
- break;
- }
- SB_LOG(ERROR) << "GetAdapterAddresses() failed with error code " << retval;
- }
-
- return false;
-}
-
bool GetNetmaskForInterfaceAddress(const SbSocketAddress& interface_address,
SbSocketAddress* out_netmask) {
std::unique_ptr<char[]> adapter_info_memory_block;
- if (!GetAdapters(interface_address.type, &adapter_info_memory_block)) {
+ if (!sbwin32::GetAdapters(
+ interface_address.type, &adapter_info_memory_block)) {
return false;
}
const void* const interface_address_buffer =
@@ -181,8 +132,7 @@
adapter_info_memory_block.get());
adapter != nullptr; adapter = adapter->Next) {
if ((adapter->OperStatus != IfOperStatusUp) ||
- (adapter->IfType != IF_TYPE_ETHERNET_CSMACD)) {
- // Note: IfType == IF_TYPE_SOFTWARE_LOOPBACK is also skipped.
+ !sbwin32::IsIfTypeEthernet(adapter->IfType)) {
continue;
}
@@ -242,7 +192,7 @@
}
std::unique_ptr<char[]> adapter_info_memory_block;
- if (!GetAdapters(address_type, &adapter_info_memory_block)) {
+ if (!sbwin32::GetAdapters(address_type, &adapter_info_memory_block)) {
return false;
}
@@ -250,8 +200,7 @@
adapter_info_memory_block.get());
adapter != nullptr; adapter = adapter->Next) {
if ((adapter->OperStatus != IfOperStatusUp) ||
- (adapter->IfType != IF_TYPE_ETHERNET_CSMACD)) {
- // Note: IfType == IF_TYPE_SOFTWARE_LOOPBACK is also skipped.
+ !sbwin32::IsIfTypeEthernet(adapter->IfType)) {
continue;
}
diff --git a/src/starboard/shared/win32/socket_waiter_internal.cc b/src/starboard/shared/win32/socket_waiter_internal.cc
index 2d10a70..a5b289c 100644
--- a/src/starboard/shared/win32/socket_waiter_internal.cc
+++ b/src/starboard/shared/win32/socket_waiter_internal.cc
@@ -283,7 +283,6 @@
const SbTimeMonotonic start_time = SbTimeGetMonotonicNow();
int64_t duration_left = duration;
- SbSocketWaiterResult result = kSbSocketWaiterResultInvalid;
while (true) {
// |waitees_| could have been modified in the last loop iteration, so
@@ -293,21 +292,16 @@
const DWORD millis = sbwin32::ConvertSbTimeToMillisRoundUp(duration_left);
- bool woke_up = false;
{
starboard::ScopedLock lock(unhandled_wakeup_count_mutex_);
if (unhandled_wakeup_count_ > 0) {
--unhandled_wakeup_count_;
- woke_up = true;
+ // The signaling thread also set the event, so reset it.
+ ResetWakeupEvent();
+ return kSbSocketWaiterResultWokenUp;
}
}
- if (woke_up) {
- // The signaling thread also set the event, so reset it.
- ResetWakeupEvent();
- return kSbSocketWaiterResultWokenUp;
- }
-
// There should always be a wakeup event.
SB_DCHECK(number_events > 0);
@@ -328,20 +322,14 @@
SB_DCHECK(wakeup_event_token_ >= 0);
if (socket_index == wakeup_event_token_) {
- {
- starboard::ScopedLock lock(unhandled_wakeup_count_mutex_);
- SB_DCHECK(unhandled_wakeup_count_ > 0);
+ starboard::ScopedLock lock(unhandled_wakeup_count_mutex_);
+ SB_DCHECK(unhandled_wakeup_count_ > 0);
- if (unhandled_wakeup_count_ > 0) {
- --unhandled_wakeup_count_;
- // This was a dummy event. We were woken up.
- // Note that we do not need to reset the event here,
- // since it was created using an auto-reset flag.
- return kSbSocketWaiterResultWokenUp;
- }
- }
-
- ResetWakeupEvent();
+ --unhandled_wakeup_count_;
+ // This was a dummy event. We were woken up.
+ // Note that we do not need to reset the event here,
+ // since it was created using an auto-reset flag.
+ return kSbSocketWaiterResultWokenUp;
} else {
Waitee* waitee = waitees_.GetWaiteeByIndex(socket_index);
@@ -387,10 +375,13 @@
}
void SbSocketWaiterPrivate::WakeUp() {
- {
- starboard::ScopedLock lock(unhandled_wakeup_count_mutex_);
- ++unhandled_wakeup_count_;
- }
+ // Increasing unhandled_wakeup_count_mutex_ and calling SignalWakeupEvent
+ // atomically helps add additional guarantees of when the waiter can be
+ // woken up. While we can code around this easily, having a stronger
+ // coupling enables us to add DCHECKs for |unhandled_wakeup_count_| in other
+ // parts of the code.
+ starboard::ScopedLock lock(unhandled_wakeup_count_mutex_);
+ ++unhandled_wakeup_count_;
SignalWakeupEvent();
}
diff --git a/src/starboard/shared/win32/socket_waiter_internal.h b/src/starboard/shared/win32/socket_waiter_internal.h
index 193361b..a2147fa 100644
--- a/src/starboard/shared/win32/socket_waiter_internal.h
+++ b/src/starboard/shared/win32/socket_waiter_internal.h
@@ -132,10 +132,10 @@
WaiteeRegistry waitees_;
WaiteeRegistry::LookupToken wakeup_event_token_;
- // Number of times wake up has been called, and not handled.
+ // This mutex covers the next two variables.
starboard::Mutex unhandled_wakeup_count_mutex_;
+ // Number of times wake up has been called, and not handled.
std::int32_t unhandled_wakeup_count_;
-
// The WSAEvent that is set by Wakeup();
sbwin32::AutoEventHandle wakeup_event_;
};
diff --git a/src/starboard/shared/win32/system_clear_last_error.cc b/src/starboard/shared/win32/system_clear_last_error.cc
new file mode 100644
index 0000000..9b1423d
--- /dev/null
+++ b/src/starboard/shared/win32/system_clear_last_error.cc
@@ -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.
+
+#include "starboard/system.h"
+
+#include <windows.h>
+
+void SbSystemClearLastError() {
+ SetLastError(0);
+}
diff --git a/src/starboard/shared/win32/system_get_connection_type.cc b/src/starboard/shared/win32/system_get_connection_type.cc
new file mode 100644
index 0000000..399e2b2
--- /dev/null
+++ b/src/starboard/shared/win32/system_get_connection_type.cc
@@ -0,0 +1,59 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/system.h"
+
+#include <winsock2.h>
+
+#include <ifdef.h>
+#include <iphlpapi.h>
+
+#include "starboard/shared/win32/adapter_utils.h"
+
+namespace sbwin32 = ::starboard::shared::win32;
+
+namespace {
+// Return the connection type of the first "up" ethernet interface,
+// or unknown if none found.
+SbSystemConnectionType FindConnectionType(PIP_ADAPTER_ADDRESSES adapter) {
+ for (; adapter != nullptr; adapter = adapter->Next) {
+ if ((adapter->OperStatus != IfOperStatusUp) ||
+ !sbwin32::IsIfTypeEthernet(adapter->IfType)) {
+ continue;
+ }
+ if (adapter->IfType == IF_TYPE_IEEE80211) {
+ return kSbSystemConnectionTypeWireless;
+ }
+ return kSbSystemConnectionTypeWired;
+ }
+ return kSbSystemConnectionTypeUnknown;
+}
+} // namespace
+
+SbSystemConnectionType SbSystemGetConnectionType() {
+ std::unique_ptr<char[]> buffer;
+ if (!sbwin32::GetAdapters(kSbSocketAddressTypeIpv4, &buffer)) {
+ return kSbSystemConnectionTypeUnknown;
+ }
+ SbSystemConnectionType result = FindConnectionType(
+ reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.get()));
+ if (result != kSbSystemConnectionTypeUnknown) {
+ return result;
+ }
+ if (!sbwin32::GetAdapters(kSbSocketAddressTypeIpv6, &buffer)) {
+ return kSbSystemConnectionTypeUnknown;
+ }
+ return FindConnectionType(
+ reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.get()));
+}
diff --git a/src/starboard/shared/win32/system_get_device_type.cc b/src/starboard/shared/win32/system_get_device_type.cc
new file mode 100644
index 0000000..0957c7a
--- /dev/null
+++ b/src/starboard/shared/win32/system_get_device_type.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/system.h"
+
+#include <string>
+
+#include "starboard/log.h"
+#include "starboard/shared/uwp/winrt_workaround.h"
+#include "starboard/shared/win32/wchar_utils.h"
+
+using Windows::System::Profile::AnalyticsInfo;
+using Windows::System::Profile::AnalyticsVersionInfo;
+
+SbSystemDeviceType SbSystemGetDeviceType() {
+ AnalyticsVersionInfo^ version_info = AnalyticsInfo::VersionInfo;
+ std::string family = starboard::shared::win32::platformStringToString(
+ version_info->DeviceFamily);
+
+ if (family.compare("Windows.Desktop") == 0) {
+ return kSbSystemDeviceTypeDesktopPC;
+ }
+ if (family.compare("Windows.Xbox") == 0) {
+ return kSbSystemDeviceTypeGameConsole;
+ }
+ SB_NOTREACHED();
+ return kSbSystemDeviceTypeUnknown;
+}
diff --git a/src/starboard/shared/win32/system_get_error_string.cc b/src/starboard/shared/win32/system_get_error_string.cc
new file mode 100644
index 0000000..5aa5074
--- /dev/null
+++ b/src/starboard/shared/win32/system_get_error_string.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/system.h"
+
+#include <sstream>
+
+#include "starboard/shared/win32/error_utils.h"
+#include "starboard/string.h"
+
+int SbSystemGetErrorString(SbSystemError error,
+ char* out_string,
+ int string_length) {
+ std::ostringstream out;
+ out << starboard::shared::win32::Win32ErrorCode(error);
+ if (out_string != nullptr) {
+ SbStringCopy(out_string, out.str().c_str(), string_length);
+ }
+ return static_cast<int>(out.str().size());
+}
diff --git a/src/starboard/shared/win32/system_get_last_error.cc b/src/starboard/shared/win32/system_get_last_error.cc
new file mode 100644
index 0000000..aa76753
--- /dev/null
+++ b/src/starboard/shared/win32/system_get_last_error.cc
@@ -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.
+
+#include "starboard/system.h"
+
+#include <windows.h>
+
+SbSystemError SbSystemGetLastError() {
+ return GetLastError();
+}
diff --git a/src/starboard/shared/win32/system_get_locale_id.cc b/src/starboard/shared/win32/system_get_locale_id.cc
new file mode 100644
index 0000000..99dbf34
--- /dev/null
+++ b/src/starboard/shared/win32/system_get_locale_id.cc
@@ -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.
+
+#include "starboard/system.h"
+
+#include <string>
+
+#include "starboard/log.h"
+#include "starboard/once.h"
+
+#include "starboard/shared/win32/error_utils.h"
+#include "starboard/shared/win32/wchar_utils.h"
+
+using starboard::shared::win32::DebugLogWinError;
+using starboard::shared::win32::wchar_tToUTF8;
+
+namespace {
+class LocaleString {
+ public:
+ static LocaleString* Get();
+ const char* value() const { return value_.c_str(); }
+
+ private:
+ LocaleString() {
+ wchar_t name[LOCALE_NAME_MAX_LENGTH];
+ int result = GetUserDefaultLocaleName(name, LOCALE_NAME_MAX_LENGTH);
+ if (result != 0) {
+ value_ = wchar_tToUTF8(name);
+ } else {
+ SB_LOG(ERROR) << "Error retrieving GetUserDefaultLocaleName";
+ DebugLogWinError();
+ value_ ="en-US";
+ }
+ }
+ std::string value_;
+};
+
+SB_ONCE_INITIALIZE_FUNCTION(LocaleString, LocaleString::Get);
+} // namespace
+
+const char* SbSystemGetLocaleId() {
+ return LocaleString::Get()->value();
+}
diff --git a/src/starboard/shared/win32/system_get_number_of_processors.cc b/src/starboard/shared/win32/system_get_number_of_processors.cc
new file mode 100644
index 0000000..59ec4e0
--- /dev/null
+++ b/src/starboard/shared/win32/system_get_number_of_processors.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/system.h"
+
+#include <windows.h>
+
+int SbSystemGetNumberOfProcessors() {
+ // Note that this returns the number of logical processors.
+ SYSTEM_INFO system_info = {0};
+ GetSystemInfo(&system_info);
+ return system_info.dwNumberOfProcessors;
+}
diff --git a/src/starboard/shared/win32/system_get_total_cpu_memory.cc b/src/starboard/shared/win32/system_get_total_cpu_memory.cc
new file mode 100644
index 0000000..7c91399
--- /dev/null
+++ b/src/starboard/shared/win32/system_get_total_cpu_memory.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/system.h"
+
+#include <windows.h>
+
+int64_t SbSystemGetTotalCPUMemory() {
+ MEMORYSTATUSEX statex = {0};
+ statex.dwLength = sizeof(statex);
+ GlobalMemoryStatusEx(&statex);
+ return static_cast<int64_t>(statex.ullTotalPhys);
+}
diff --git a/src/starboard/shared/win32/system_get_used_cpu_memory.cc b/src/starboard/shared/win32/system_get_used_cpu_memory.cc
new file mode 100644
index 0000000..7cddea1
--- /dev/null
+++ b/src/starboard/shared/win32/system_get_used_cpu_memory.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/system.h"
+
+#include <windows.h>
+
+#include "starboard/log.h"
+
+int64_t SbSystemGetUsedCPUMemory() {
+ MEMORYSTATUSEX statex = {0};
+ statex.dwLength = sizeof(statex);
+ GlobalMemoryStatusEx(&statex);
+ int64_t remaining_bytes = static_cast<int64_t>(statex.ullTotalPhys) -
+ static_cast<int64_t>(statex.ullAvailPhys);
+ SB_DCHECK(remaining_bytes >= 0);
+ return remaining_bytes;
+}
diff --git a/src/starboard/shared/win32/thread_private.cc b/src/starboard/shared/win32/thread_private.cc
index e0ee6b1..e7e17cb 100644
--- a/src/starboard/shared/win32/thread_private.cc
+++ b/src/starboard/shared/win32/thread_private.cc
@@ -52,8 +52,17 @@
}
SbThreadPrivate* GetCurrentSbThreadPrivate() {
- return static_cast<SbThreadPrivate*>(SbThreadGetLocalValue(
- GetThreadSubsystemSingleton()->thread_private_key_));
+ SbThreadPrivate* sb_thread_private =
+ static_cast<SbThreadPrivate*>(SbThreadGetLocalValue(
+ GetThreadSubsystemSingleton()->thread_private_key_));
+ if (sb_thread_private == nullptr) {
+ // We are likely on a thread we did not create, so TLS needs to be setup.
+ RegisterMainThread();
+ sb_thread_private = static_cast<SbThreadPrivate*>(SbThreadGetLocalValue(
+ GetThreadSubsystemSingleton()->thread_private_key_));
+ // TODO: Clean up TLS storage for threads we do not create.
+ }
+ return sb_thread_private;
}
} // namespace win32
diff --git a/src/starboard/shared/win32/wchar_utils.h b/src/starboard/shared/win32/wchar_utils.h
index 948b025..304e7d7 100644
--- a/src/starboard/shared/win32/wchar_utils.h
+++ b/src/starboard/shared/win32/wchar_utils.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_WIN32_WCHAR_UTILS_H_
-#define STARBOARD_WIN32_WCHAR_UTILS_H_
+#ifndef STARBOARD_SHARED_WIN32_WCHAR_UTILS_H_
+#define STARBOARD_SHARED_WIN32_WCHAR_UTILS_H_
#include <codecvt>
#include <cwchar>
@@ -40,8 +40,19 @@
return converter.from_bytes(str);
}
+#if defined(__cplusplus_winrt)
+inline std::string platformStringToString(Platform::String^ to_convert) {
+ std::wstring ws(to_convert->Begin(), to_convert->End());
+ return wchar_tToUTF8(ws.data(), ws.size());
+}
+
+inline Platform::String^ stringToPlatformString(const std::string& to_convert) {
+ return ref new Platform::String(CStringToWString(to_convert.c_str()).c_str());
+}
+#endif
+
} // namespace win32
} // namespace shared
} // namespace starboard
-#endif // STARBOARD_WIN32_WCHAR_UTILS_H_
+#endif // STARBOARD_SHARED_WIN32_WCHAR_UTILS_H_
diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc
index 369c2de..9e11f7c 100644
--- a/src/starboard/shared/x11/application_x11.cc
+++ b/src/starboard/shared/x11/application_x11.cc
@@ -14,6 +14,7 @@
#include "starboard/shared/x11/application_x11.h"
+#include <math.h>
#include <stdlib.h>
#include <unistd.h>
#define XK_3270 // for XK_3270_BackTab
@@ -41,7 +42,12 @@
namespace shared {
namespace x11 {
namespace {
-const int kKeyboardDeviceId = 1;
+
+enum {
+ kNoneDeviceId,
+ kKeyboardDeviceId,
+ kMouseDeviceId,
+};
// Key translation taken from cobalt/system_window/linux/keycode_conversion.cc
// Eventually, that code should be removed in favor of this code.
@@ -522,6 +528,47 @@
return key;
}
+bool XButtonEventIsWheelEvent(XButtonEvent* event) {
+ // Buttons 4, 5, 6, and 7 are wheel events.
+ return event->button >= 4 && event->button <= 7;
+}
+
+enum {
+ kWheelUpButton = 4,
+ kWheelDownButton = 5,
+ kWheelLeftButton = 6,
+ kWheelRightButton = 7,
+ kPointerBackButton = 8,
+ kPointerForwardButton = 9,
+};
+
+SbKey XButtonEventToSbKey(XButtonEvent* event) {
+ SbKey key;
+ switch (event->button) {
+ case Button1:
+ return kSbKeyMouse1;
+ case Button2:
+ return kSbKeyMouse2;
+ case Button3:
+ return kSbKeyMouse3;
+ case kWheelUpButton:
+ return kSbKeyUp;
+ case kWheelDownButton:
+ return kSbKeyDown;
+ case kWheelLeftButton:
+ return kSbKeyLeft;
+ case kWheelRightButton:
+ return kSbKeyRight;
+ case kPointerBackButton:
+ return kSbKeyBrowserBack;
+ case kPointerForwardButton:
+ return kSbKeyBrowserForward;
+ default:
+ return kSbKeyUnknown;
+ }
+ return key;
+}
+
// Get a SbKeyLocation from an XKeyEvent.
SbKeyLocation XKeyEventToSbKeyLocation(XKeyEvent* event) {
KeySym keysym = XK_VoidSymbol;
@@ -543,20 +590,61 @@
}
// Get an SbKeyModifiers from an XKeyEvent.
-unsigned int XKeyEventToSbKeyModifiers(XKeyEvent* event) {
+unsigned int XEventStateToSbKeyModifiers(unsigned int state) {
unsigned int key_modifiers = kSbKeyModifiersNone;
- if (event->state & Mod1Mask) {
+ if (state & Mod1Mask) {
key_modifiers |= kSbKeyModifiersAlt;
}
- if (event->state & ControlMask) {
+ if (state & ControlMask) {
key_modifiers |= kSbKeyModifiersCtrl;
}
- if (event->state & ShiftMask) {
+ if (state & Mod4Mask) {
+ key_modifiers |= kSbKeyModifiersMeta;
+ }
+ if (state & ShiftMask) {
key_modifiers |= kSbKeyModifiersShift;
}
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+ if (state & Button1Mask) {
+ key_modifiers |= kSbKeyModifiersPointerButtonLeft;
+ }
+ if (state & Button2Mask) {
+ key_modifiers |= kSbKeyModifiersPointerButtonMiddle;
+ }
+ if (state & Button3Mask) {
+ key_modifiers |= kSbKeyModifiersPointerButtonRight;
+ }
+#endif
+ // Note: Button 4 and button 5 represent vertical wheel motion. As a result,
+ // Button4Mask and Button5Mask do not represent a useful mouse button state
+ // since the wheel up and wheel down do not have 'buttons' that can be held
+ // down. The state of the Back and Forward mouse buttons is not reported to
+ // X11 clients. This is not a hardware limitation, but a result of historical
+ // Xorg X11 mouse driver button mapping choices.
return key_modifiers;
}
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+SbInputVector XButtonEventToSbInputVectorDelta(XButtonEvent* event) {
+ SbInputVector delta = {0, 0};
+ switch (event->button) {
+ case kWheelUpButton:
+ delta.y = -1;
+ break;
+ case kWheelDownButton:
+ delta.y = 1;
+ break;
+ case kWheelLeftButton:
+ delta.x = -1;
+ break;
+ case kWheelRightButton:
+ delta.x = 1;
+ break;
+ }
+ return delta;
+}
+#endif
+
bool XNextEventTimed(Display* display, XEvent* out_event, SbTime duration) {
if (XPending(display) == 0) {
if (duration <= SbTime()) {
@@ -855,7 +943,63 @@
data->device_id = kKeyboardDeviceId;
data->key = XKeyEventToSbKey(x_key_event);
data->key_location = XKeyEventToSbKeyLocation(x_key_event);
- data->key_modifiers = XKeyEventToSbKeyModifiers(x_key_event);
+ data->key_modifiers = XEventStateToSbKeyModifiers(x_key_event->state);
+ data->position.x = x_key_event->x;
+ data->position.y = x_key_event->y;
+ return new Event(kSbEventTypeInput, data, &DeleteDestructor<SbInputData>);
+ }
+ case ButtonPress:
+ case ButtonRelease: {
+ XButtonEvent* x_button_event = reinterpret_cast<XButtonEvent*>(x_event);
+ SbInputData* data = new SbInputData();
+ SbMemorySet(data, 0, sizeof(*data));
+ data->window = FindWindow(x_button_event->window);
+ SB_DCHECK(SbWindowIsValid(data->window));
+ data->key = XButtonEventToSbKey(x_button_event);
+ bool is_press_event = ButtonPress == x_event->type;
+ data->type =
+ is_press_event ? kSbInputEventTypePress : kSbInputEventTypeUnpress;
+ data->device_type = kSbInputDeviceTypeMouse;
+ if (XButtonEventIsWheelEvent(x_button_event)) {
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+ if (!is_press_event) {
+ // unpress events from the wheel are discarded.
+ return NULL;
+ }
+ data->pressure = NAN;
+ data->size = {NAN, NAN};
+ data->tilt = {NAN, NAN};
+ data->type = kSbInputEventTypeWheel;
+ data->delta = XButtonEventToSbInputVectorDelta(x_button_event);
+#else
+ // This version of Starboard does not support wheel event types, send
+ // keyboard event types instead.
+ data->device_type = kSbInputDeviceTypeKeyboard;
+#endif
+ }
+ data->device_id = kMouseDeviceId;
+ data->key_modifiers = XEventStateToSbKeyModifiers(x_button_event->state);
+ data->position.x = x_button_event->x;
+ data->position.y = x_button_event->y;
+ return new Event(kSbEventTypeInput, data, &DeleteDestructor<SbInputData>);
+ }
+ case MotionNotify: {
+ XMotionEvent* x_motion_event = reinterpret_cast<XMotionEvent*>(x_event);
+ SbInputData* data = new SbInputData();
+ SbMemorySet(data, 0, sizeof(*data));
+ data->window = FindWindow(x_motion_event->window);
+ SB_DCHECK(SbWindowIsValid(data->window));
+#if SB_API_VERSION >= SB_POINTER_INPUT_API_VERSION
+ data->pressure = NAN;
+ data->size = {NAN, NAN};
+ data->tilt = {NAN, NAN};
+#endif
+ data->type = kSbInputEventTypeMove;
+ data->device_type = kSbInputDeviceTypeMouse;
+ data->device_id = kMouseDeviceId;
+ data->key_modifiers = XEventStateToSbKeyModifiers(x_motion_event->state);
+ data->position.x = x_motion_event->x;
+ data->position.y = x_motion_event->y;
return new Event(kSbEventTypeInput, data, &DeleteDestructor<SbInputData>);
}
case FocusIn: {
@@ -866,14 +1010,6 @@
Pause(NULL, NULL);
return NULL;
}
- case MapNotify: {
- Resume(NULL, NULL);
- return NULL;
- }
- case UnmapNotify: {
- Suspend(NULL, NULL);
- return NULL;
- }
case ConfigureNotify: {
// Ignore window size, position, border, and stacking order events.
return NULL;
diff --git a/src/starboard/shared/x11/application_x11.h b/src/starboard/shared/x11/application_x11.h
index 182f58c..f61f8ba 100644
--- a/src/starboard/shared/x11/application_x11.h
+++ b/src/starboard/shared/x11/application_x11.h
@@ -54,6 +54,11 @@
int width,
int height) SB_OVERRIDE;
+#if SB_API_VERSION >= SB_PRELOAD_API_VERSION
+ bool IsStartImmediate() SB_OVERRIDE { return !HasPreloadSwitch(); }
+ bool IsPreloadImmediate() SB_OVERRIDE { return HasPreloadSwitch(); }
+#endif // SB_API_VERSION >= SB_PRELOAD_API_VERSION
+
protected:
// --- Application overrides ---
void Initialize() SB_OVERRIDE;
diff --git a/src/starboard/shared/x11/window_internal.cc b/src/starboard/shared/x11/window_internal.cc
index 8235d61..108da65 100644
--- a/src/starboard/shared/x11/window_internal.cc
+++ b/src/starboard/shared/x11/window_internal.cc
@@ -131,6 +131,10 @@
#endif // SB_API_VERSION >= 4 ||
// SB_IS(PLAYER_PUNCHED_OUT)
+ XSelectInput(display, window,
+ VisibilityChangeMask | ExposureMask | FocusChangeMask |
+ StructureNotifyMask | KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask | PointerMotionMask);
XMapWindow(display, window);
}
diff --git a/src/starboard/speech_recognizer.h b/src/starboard/speech_recognizer.h
index 48af10c..7b1bb59 100644
--- a/src/starboard/speech_recognizer.h
+++ b/src/starboard/speech_recognizer.h
@@ -35,8 +35,7 @@
#include "starboard/export.h"
#include "starboard/types.h"
-#if SB_HAS(SPEECH_RECOGNIZER) && \
- SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#ifdef __cplusplus
extern "C" {
@@ -195,7 +194,6 @@
} // extern "C"
#endif
-#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
- // SB_SPEECH_RECOGNIZER_API_VERSION
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#endif // STARBOARD_SPEECH_RECOGNIZER_H_
diff --git a/src/starboard/speech_synthesis.h b/src/starboard/speech_synthesis.h
index fe0de16..a8a46d6 100644
--- a/src/starboard/speech_synthesis.h
+++ b/src/starboard/speech_synthesis.h
@@ -29,7 +29,7 @@
#include "starboard/export.h"
#include "starboard/types.h"
-#if SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
+#if SB_HAS(SPEECH_SYNTHESIS) && SB_API_VERSION >= 3
#ifdef __cplusplus
extern "C" {
@@ -64,6 +64,6 @@
} // extern "C"
#endif
-#endif // SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
+#endif // SB_HAS(SPEECH_SYNTHESIS) && SB_API_VERSION >= 3
#endif // STARBOARD_SPEECH_SYNTHESIS_H_
diff --git a/src/starboard/stub/gyp_configuration.gypi b/src/starboard/stub/gyp_configuration.gypi
index 10e4c16..b93baf9 100644
--- a/src/starboard/stub/gyp_configuration.gypi
+++ b/src/starboard/stub/gyp_configuration.gypi
@@ -141,7 +141,7 @@
},
}, # end of configurations
'target_conditions': [
- ['cobalt_code==1', {
+ ['sb_pedantic_warnings==1', {
'cflags': [
'-Wall',
'-Wextra',
diff --git a/src/starboard/system.h b/src/starboard/system.h
index a56d034..dd08f44 100644
--- a/src/starboard/system.h
+++ b/src/starboard/system.h
@@ -109,17 +109,17 @@
// A universally-unique ID for the current user.
kSbSystemPropertyPlatformUuid,
-#if SB_VERSION(2)
+#if SB_API_VERSION >= 2
// The Google Speech API key. The platform manufacturer is responsible
// for registering a Google Speech API key for their products. In the API
// Console (http://developers.google.com/console), you can enable the
// Speech APIs and generate a Speech API key.
kSbSystemPropertySpeechApiKey,
#endif // SB_VERSION(2)
-#if SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
+#if SB_API_VERSION >= 5
// A field that, if available, is appended to the user agent
kSbSystemPropertyUserAgentAuxField,
-#endif // SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
+#endif // SB_API_VERSION >= 5
} SbSystemPropertyId;
// Enumeration of device types.
@@ -185,14 +185,18 @@
// |SbSystemRaisePlatformError| function.
typedef enum SbSystemPlatformErrorType {
// Cobalt received a network connection error, or a network disconnection
- // event.
+ // event. If the |response| passed to |SbSystemPlatformErrorCallback| is
+ // |kSbSystemPlatformErrorResponsePositive| then the request should be
+ // retried, otherwise the app should be stopped.
kSbSystemPlatformErrorTypeConnectionError,
- // The current user is not signed in (e.g. to PSN network).
+#if SB_API_VERSION < SB_PLATFORM_ERROR_CLEANUP_API_VERSION
+ // The current user is not signed in.
kSbSystemPlatformErrorTypeUserSignedOut,
// The current user does not meet the age requirements to use the app.
kSbSystemPlatformErrorTypeUserAgeRestricted
+#endif
} SbSystemPlatformErrorType;
// Possible responses for |SbSystemPlatformErrorCallback|.
diff --git a/src/starboard/time.h b/src/starboard/time.h
index bae41d4..c9d2a3f 100644
--- a/src/starboard/time.h
+++ b/src/starboard/time.h
@@ -91,7 +91,7 @@
// Gets a monotonically increasing time representing right now.
SB_EXPORT SbTimeMonotonic SbTimeGetMonotonicNow();
-#if SB_VERSION(3) && SB_HAS(TIME_THREAD_NOW)
+#if SB_API_VERSION >= 3 && SB_HAS(TIME_THREAD_NOW)
// Gets a monotonically increasing time representing how long the current
// thread has been in the executing state (i.e. not pre-empted nor waiting
// on an event). This is not necessarily total time and is intended to allow
diff --git a/src/starboard/tizen/armv7l/configuration_public.h b/src/starboard/tizen/armv7l/configuration_public.h
index f27972c..3c2d612 100644
--- a/src/starboard/tizen/armv7l/configuration_public.h
+++ b/src/starboard/tizen/armv7l/configuration_public.h
@@ -22,6 +22,20 @@
#ifndef STARBOARD_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
#define STARBOARD_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
+// The API version implemented by this platform.
+#define SB_API_VERSION 4
+
+// --- System Header Configuration -------------------------------------------
+
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 0
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
// --- Architecture Configuration --------------------------------------------
// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
@@ -100,64 +114,15 @@
// supported composition methods below.
#define SB_HAS_PLAYER 1
-// Specifies whether this platform's player will produce an OpenGL texture that
-// the client must draw every frame with its graphics rendering. It may be that
-// we get a texture handle, but cannot perform operations like GlReadPixels on
-// it if it is DRM-protected.
-#define SB_IS_PLAYER_PRODUCING_TEXTURE 0
+// The maximum audio bitrate the platform can decode. The following value
+// equals to 2M bytes per seconds which is more than enough for compressed
+// audio.
+#define SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND (16 * 1024 * 1024)
-// Specifies whether this platform's player is composited with a formal
-// compositor, where the client must specify how video is to be composited into
-// the graphicals scene.
-#define SB_IS_PLAYER_COMPOSITED 0
-
-// Specifies whether this platform's player uses a "punch-out" model, where
-// video is rendered to the far background, and the graphics plane is
-// automatically composited on top of the video by the platform. The client must
-// punch an alpha hole out of the graphics plane for video to show through. In
-// this case, changing the video bounds must be tightly synchronized between the
-// player and the graphics plane.
-#define SB_IS_PLAYER_PUNCHED_OUT 1
-
-#if SB_API_VERSION < 4
-
-// Specifies the maximum amount of memory used by audio buffers of media source
-// before triggering a garbage collection. A large value will cause more memory
-// being used by audio buffers but will also make JavaScript app less likely to
-// re-download audio data. Note that the JavaScript app may experience
-// significant difficulty if this value is too low.
-#define SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT (3U * 1024U * 1024U)
-
-// Specifies the maximum amount of memory used by video buffers of media source
-// before triggering a garbage collection. A large value will cause more memory
-// being used by video buffers but will also make JavaScript app less likely to
-// re-download video data. Note that the JavaScript app may experience
-// significant difficulty if this value is too low.
-#define SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT (16U * 1024U * 1024U)
-
-// Specifies how much memory to reserve up-front for the main media buffer
-// (usually resides inside the CPU memory) used by media source and demuxers.
-// The main media buffer can work in one of the following two ways:
-// 1. If GPU buffer is used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is non-zero), the
-// main buffer will be used as a cache so a media buffer will be copied from
-// GPU memory to main memory before sending to the decoder for further
-// processing. In this case this macro should be set to a value that is
-// large enough to hold all media buffers being decoded.
-// 2. If GPU buffer is not used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is zero) all
-// media buffers will reside in the main memory buffer. In this case the
-// macro should be set to a value that is greater than the sum of the above
-// source buffer stream memory limits with extra room to take account of
-// fragmentations and memory used by demuxers.
-#define SB_MEDIA_MAIN_BUFFER_BUDGET (80U * 1024U * 1024U)
-
-// Specifies how much GPU memory to reserve up-front for media source buffers.
-// This should only be set to non-zero on system with limited CPU memory and
-// excess GPU memory so the app can store media buffer in GPU memory.
-// SB_MEDIA_MAIN_BUFFER_BUDGET has to be set to a non-zero value to avoid
-// media buffers being decoded when being stored in GPU.
-#define SB_MEDIA_GPU_BUFFER_BUDGET 0U
-
-#endif // SB_API_VERSION < 4
+// The maximum video bitrate the platform can decode. The following value
+// equals to 25M bytes per seconds which is more than enough for compressed
+// video.
+#define SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND (200 * 1024 * 1024)
// Specifies whether this platform has webm/vp9 support. This should be set to
// non-zero on platforms with webm/vp9 support.
@@ -197,4 +162,15 @@
// Include the Tizen configuration that's common between all Tizen.
#include "starboard/tizen/shared/configuration_public.h"
+// --- User Configuration ----------------------------------------------------
+
+// The maximum number of users that can be signed in at the same time.
+#define SB_USER_MAX_SIGNED_IN 1
+
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
#endif // STARBOARD_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/tizen/armv7l/gyp_configuration.gypi b/src/starboard/tizen/armv7l/gyp_configuration.gypi
index 711f40a..9377135 100644
--- a/src/starboard/tizen/armv7l/gyp_configuration.gypi
+++ b/src/starboard/tizen/armv7l/gyp_configuration.gypi
@@ -15,8 +15,6 @@
{
'variables': {
'target_arch': 'arm',
- 'target_os': 'linux',
- 'tizen_os': 1,
'gl_type': 'system_gles2',
@@ -25,25 +23,25 @@
'armv7': 1,
'arm_neon': 0,
- # Some package use tizen system instead of third_party
- 'use_system_icu': 1,
- 'use_system_libxml': 1,
-
- # scratch surface cache is designed to choose large offscreen surfaces so
- # that they can be maximally reused, it is not a very good fit for a tiled
- # renderer.
- 'scratch_surface_cache_size_in_bytes' : 0,
-
- # This should have a default value in cobalt/base.gypi. See the comment
- # there for acceptable values for this variable.
- 'javascript_engine': 'mozjs',
- 'cobalt_enable_jit': 0,
-
# Reduce garbage collection threshold from the default of 8MB in order to
# save on memory. This will mean that garbage collection occurs more
# frequently.
'mozjs_garbage_collection_threshold_in_bytes%': 4 * 1024 * 1024,
+ # The rasterizer does not benefit much from rendering only the dirty
+ # region. Disable this option since it costs GPU time.
+ 'render_dirty_region_only': 0,
+
+ # Use media source extension implementation that is conformed to the
+ # Candidate Recommandation of July 5th 2016.
+ 'cobalt_media_source_2016': 1,
+ 'cobalt_media_buffer_storage_type': 'memory',
+ 'cobalt_media_buffer_initial_capacity': 26 * 1024 * 1024,
+ 'cobalt_media_buffer_allocation_unit': 0 * 1024 * 1024,
+ 'cobalt_media_buffer_non_video_budget': 5 * 1024 * 1024,
+ 'cobalt_media_buffer_video_budget_1080p': 16 * 1024 * 1024,
+ 'cobalt_media_buffer_video_budget_4k': 60 * 1024 * 1024,
+
'platform_libraries': [
'-lasound',
'-lavcodec',
@@ -51,82 +49,9 @@
'-lavutil',
'-ldlog',
],
- 'linker_flags': [
- ],
- 'linker_flags_gold': [
- '-O3',
- '-flto',
- ],
- 'compiler_flags_debug': [
- '-O0',
- ],
- 'compiler_flags_devel': [
- '-O2',
- ],
- 'compiler_flags_cc_qa': [
- '-fno-rtti',
- ],
- 'compiler_flags_qa': [
- '-O3',
- ],
- 'compiler_flags_cc_gold': [
- '-fno-rtti',
- ],
- 'compiler_flags_gold': [
- '-O3',
- ],
- 'conditions': [
- ['cobalt_fastbuild==0', {
- 'compiler_flags_debug': [
- '-g',
- ],
- 'compiler_flags_devel': [
- '-g',
- ],
- 'compiler_flags_qa': [
- ],
- 'compiler_flags_gold': [
- '-flto',
- ],
- }],
- ],
},
'target_defaults': {
- 'defines': [
- # Cobalt on Tizen flag
- 'COBALT_TIZEN',
- 'PNG_SKIP_SETJMP_CHECK',
- '__STDC_FORMAT_MACROS', # so that we get PRI*
- # Enable GNU extensions to get prototypes like ffsl.
- '_GNU_SOURCE=1',
- ],
- 'cflags': [
- '-pthread',
- # Do not warn about locally defined but not used.
- '-Wno-unused-local-typedefs',
- # Do not warn about XXX is deprecated.
- '-Wno-deprecated-declarations',
- # Do not warn about missing initializer for member XXX.
- '-Wno-missing-field-initializers',
- # Do not warn about unused functions.
- '-Wno-unused-function',
- # Do not warn about type qualifiers ignored on function return type.
- '-Wno-ignored-qualifiers',
- # Do not warn about the use of multi-line comments.
- '-Wno-comment',
- # Do not warn about sign compares.
- '-Wno-sign-compare',
- ],
- 'cflags_c': [
- '-std=c11',
- ],
- 'cflags_cc': [
- '-std=gnu++11',
- ],
- 'ldflags': [
- '-pthread',
- ],
'default_configuration': 'tizen-armv7l_debug',
'configurations': {
'tizen-armv7l_debug': {
@@ -143,4 +68,8 @@
},
}, # end of configurations
}, # end of target_defaults
+
+ 'includes': [
+ '../shared/gyp_configuration.gypi',
+ ],
}
diff --git a/src/starboard/tizen/armv7l/starboard_common.gyp b/src/starboard/tizen/armv7l/starboard_common.gyp
index 6da8396..1837af9 100644
--- a/src/starboard/tizen/armv7l/starboard_common.gyp
+++ b/src/starboard/tizen/armv7l/starboard_common.gyp
@@ -73,6 +73,7 @@
'<(DEPTH)/starboard/shared/linux/byte_swap.cc',
'<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc',
'<(DEPTH)/starboard/shared/linux/page_internal.cc',
+ '<(DEPTH)/starboard/shared/linux/socket_get_interface_address.cc',
'<(DEPTH)/starboard/shared/linux/socket_get_local_interface_address.cc',
'<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
'<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
@@ -104,7 +105,6 @@
'<(DEPTH)/starboard/shared/posix/log_format.cc',
'<(DEPTH)/starboard/shared/posix/log_is_tty.cc',
'<(DEPTH)/starboard/shared/posix/log_raw.cc',
- '<(DEPTH)/starboard/shared/posix/memory_flush.cc',
'<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc',
'<(DEPTH)/starboard/shared/posix/socket_accept.cc',
'<(DEPTH)/starboard/shared/posix/socket_bind.cc',
@@ -160,9 +160,7 @@
'<(DEPTH)/starboard/shared/pthread/mutex_destroy.cc',
'<(DEPTH)/starboard/shared/pthread/mutex_release.cc',
'<(DEPTH)/starboard/shared/pthread/once.cc',
- '<(DEPTH)/starboard/shared/pthread/thread_create.cc',
'<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
- '<(DEPTH)/starboard/shared/pthread/thread_create_priority.h',
'<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
'<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
'<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
@@ -224,18 +222,18 @@
'<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc',
'<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
'<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc',
- '<(DEPTH)/starboard/shared/wayland/application_wayland.cc',
'<(DEPTH)/starboard/shared/wayland/window_create.cc',
'<(DEPTH)/starboard/shared/wayland/window_destroy.cc',
'<(DEPTH)/starboard/shared/wayland/window_get_platform_handle.cc',
'<(DEPTH)/starboard/shared/wayland/window_get_size.cc',
'<(DEPTH)/starboard/shared/wayland/window_internal.cc',
'<(DEPTH)/starboard/tizen/shared/system_get_device_type.cc',
- '<(DEPTH)/starboard/tizen/shared/system_get_path.cc',
+ '<(DEPTH)/starboard/tizen/shared/thread_create.cc',
'<(DEPTH)/starboard/tizen/shared/audio/audio_sink_adaptor.cc',
'<(DEPTH)/starboard/tizen/shared/audio/audio_sink_private.cc',
'<(DEPTH)/starboard/tizen/shared/log/log.cc',
'<(DEPTH)/starboard/tizen/shared/get_home_directory.cc',
+ '<(DEPTH)/starboard/tizen/shared/memory_flush.cc',
],
'defines': [
# This must be defined when building Starboard, and must not when
diff --git a/src/starboard/tizen/armv7l/starboard_platform.gyp b/src/starboard/tizen/armv7l/starboard_platform.gyp
index 8cc53ab..01aa556 100644
--- a/src/starboard/tizen/armv7l/starboard_platform.gyp
+++ b/src/starboard/tizen/armv7l/starboard_platform.gyp
@@ -24,35 +24,55 @@
'<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/codec_util.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/codec_util.h',
'<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_sfr_only.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_util.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_util.h',
'<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
'<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
+ '<(DEPTH)/starboard/shared/starboard/new.cc',
'<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/player_components.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
'<(DEPTH)/starboard/shared/starboard/player/job_queue.h',
'<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/player_get_current_frame.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/player_set_playback_rate.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_worker.h',
@@ -60,21 +80,39 @@
'<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
'<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.h',
+ '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
+ '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_create_egl.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
'<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
'<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
'<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
'<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
+ '<(DEPTH)/starboard/shared/stub/image_decode.cc',
+ '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
'<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
+ '<(DEPTH)/starboard/shared/stub/media_is_transfer_characteristics_supported.cc',
+ '<(DEPTH)/starboard/shared/wayland/application_wayland.cc',
'<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc',
'<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc',
+ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h',
'<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc',
'<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_common.h',
'<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc',
'<(DEPTH)/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h',
'<(DEPTH)/starboard/tizen/shared/main.cc',
'<(DEPTH)/starboard/tizen/shared/player/filter/ffmpeg_player_components_impl.cc',
+ '<(DEPTH)/starboard/tizen/shared/system_get_path.cc',
'atomic_public.h',
'configuration_public.h',
'system_get_property.cc',
diff --git a/src/starboard/tizen/packaging/com.youtube.cobalt.spec b/src/starboard/tizen/packaging/com.youtube.cobalt.spec
index de10239..c42faff 100644
--- a/src/starboard/tizen/packaging/com.youtube.cobalt.spec
+++ b/src/starboard/tizen/packaging/com.youtube.cobalt.spec
@@ -31,8 +31,12 @@
BuildRequires: pkgconfig(wayland-client)
BuildRequires: pkgconfig(wayland-egl)
%if "%{?target}" == "samsungtv"
+BuildRequires: pkgconfig(appcore-common)
+BuildRequires: pkgconfig(app-control-api)
BuildRequires: pkgconfig(capi-media-player)
BuildRequires: pkgconfig(drmdecrypt)
+BuildRequires: pkgconfig(soc-pq-interface)
+BuildRequires: pkgconfig(vconf-internal-keys-tv)
%else
BuildRequires: pkgconfig(libavcodec)
BuildRequires: pkgconfig(libavformat)
@@ -46,6 +50,13 @@
%setup -q
#define specific parameters
+
+%if "%{?bin_name}"
+%define _name %{bin_name}
+%else
+%define _name cobalt
+%endif
+
%if "%{?build_type}"
%define _build_type %{build_type}
%else
@@ -64,7 +75,6 @@
%define _chipset armv7l
%endif
-%define _name cobalt
%define _pkgname com.youtube.cobalt
%define _outdir src/out/%{_target}-%{_chipset}_%{_build_type}
%define _manifestdir /usr/share/packages
@@ -100,16 +110,46 @@
install -d %{buildroot}%{_bindir}
install -d %{buildroot}%{_manifestdir}
install -d %{buildroot}%{_contentdir}/data/fonts/
+%if "%{_name}" == "all"
+install -m 0755 %{_outdir}/accessibility_test %{_outdir}/audio_test %{_outdir}/base_test \
+ %{_outdir}/base_unittests %{_outdir}/bindings_sandbox %{_outdir}/bindings_test \
+ %{_outdir}/browser_test %{_outdir}/cobalt %{_outdir}/crypto_unittests %{_outdir}/csp_test \
+ %{_outdir}/cssom_test %{_outdir}/css_parser_test %{_outdir}/dom_parser_test \
+ %{_outdir}/dom_test %{_outdir}/eztime_test %{_outdir}/image_decoder_sandbox \
+ %{_outdir}/layout_benchmarks %{_outdir}/layout_test %{_outdir}/layout_tests \
+ %{_outdir}/loader_test %{_outdir}/math_test %{_outdir}/media2_sandbox \
+ %{_outdir}/media_source_sandbox %{_outdir}/mozjs %{_outdir}/mozjs_engine_test \
+ %{_outdir}/mozjs_keyword_header_gen %{_outdir}/mozjs_opcode_length_header_gen \
+ %{_outdir}/nb_test %{_outdir}/network_test %{_outdir}/nplb %{_outdir}/nplb_blitter_pixel_tests \
+ %{_outdir}/poem_unittests %{_outdir}/renderer_benchmark %{_outdir}/renderer_sandbox \
+ %{_outdir}/renderer_test %{_outdir}/render_tree_test %{_outdir}/reuse_allocator_benchmark \
+ %{_outdir}/sample_benchmark %{_outdir}/scaling_text_sandbox %{_outdir}/simple_example \
+ %{_outdir}/simple_example_test %{_outdir}/snapshot_app_stats %{_outdir}/speech_sandbox \
+ %{_outdir}/sql_unittests %{_outdir}/starboard_blitter_example %{_outdir}/starboard_glclear_example \
+ %{_outdir}/starboard_window_example %{_outdir}/storage_test %{_outdir}/trace_event_test \
+ %{_outdir}/web_animations_test %{_outdir}/webdriver_test %{_outdir}/web_media_player_sandbox \
+ %{_outdir}/web_platform_tests %{_outdir}/websocket_test %{_outdir}/xhr_test \
+ %{buildroot}%{_bindir}
+%else
install -m 0755 %{_outdir}/%{_name} %{buildroot}%{_bindir}
+%endif
+
%if "%{?target}" == "samsungtv"
-cp -rd %{_outdir}/content/data/fonts/fonts.xml %{buildroot}%{_contentdir}/data/fonts/
+cp -rd src/third_party/starboard/samsungtv/%{_chipset}/fonts/fonts.xml %{buildroot}%{_contentdir}/data/fonts/
%else
cp -rd %{_outdir}/content/data/fonts %{buildroot}%{_contentdir}/data/
%endif
+
cp -rd %{_outdir}/content/data/ssl %{buildroot}%{_contentdir}/data/
+
%if %{_build_type} != "gold"
cp -rd %{_outdir}/content/data/web %{buildroot}%{_contentdir}/data/
%endif
+
+%if %{_name} != "cobalt"
+cp -rd %{_outdir}/content/dir_source_root %{buildroot}%{_contentdir}/
+%endif
+
cp src/starboard/tizen/packaging/%{_pkgname}.xml %{buildroot}%{_manifestdir}
%post
@@ -122,7 +162,11 @@
%files
%manifest src/starboard/tizen/packaging/%{_pkgname}.manifest
%defattr(-,root,root,-)
+%if "%{_name}" == "all"
+%{_bindir}/*
+%else
%{_bindir}/%{_name}
+%endif
%{_contentdir}/*
%{_manifestdir}/*
diff --git a/src/starboard/tizen/packaging/gbs.conf b/src/starboard/tizen/packaging/gbs.conf
index 39eaa67..04754b0 100644
--- a/src/starboard/tizen/packaging/gbs.conf
+++ b/src/starboard/tizen/packaging/gbs.conf
@@ -21,7 +21,7 @@
url=~/GBS-ROOT-COBALT/local/repos/cobalt/
[repo.cobalt_base]
-url = http://download.tizen.org/snapshots/tizen/base/latest/repos/arm/packages/
+url = http://download.tizen.org/snapshots/tizen/3.0-base/latest/repos/arm/packages/
[repo.cobalt_tizen3.0]
url = http://download.tizen.org/snapshots/tizen/3.0-tv/latest/repos/arm-wayland/packages/
diff --git a/src/starboard/tizen/shared/audio/audio_sink_private.cc b/src/starboard/tizen/shared/audio/audio_sink_private.cc
index 4d6440a..52c3b95 100644
--- a/src/starboard/tizen/shared/audio/audio_sink_private.cc
+++ b/src/starboard/tizen/shared/audio/audio_sink_private.cc
@@ -51,9 +51,8 @@
<< "channels " << channels << ", frequency "
<< sampling_frequency_hz << ", sample_type "
<< audio_sample_type << ", storage_type "
- << audio_frame_storage_type << ", frame_buffers "
- << static_cast<int>(frame_buffers) << ", frame_buff_sz "
- << frames_per_channel;
+ << audio_frame_storage_type << ", frame_buffers " << std::hex
+ << frame_buffers << ", frame_buff_sz " << frames_per_channel;
int capi_ret;
capi_ret = audio_out_create_new(sampling_frequency_hz, AUDIO_CHANNEL_STEREO,
@@ -191,11 +190,14 @@
}
consumed_frames = bytes_written / bytes_per_frame;
+ // This is commented : Sleep can cause 'underrun'
+ // update_source_status_func controls data's timing.
// SbThreadSleep(consumed_frames * kSbTimeSecond /
// sampling_frequency_hz_);
consume_frames_func_(consumed_frames, context_);
} else {
if (!is_paused_) {
+ audio_out_drain(capi_audio_out_);
audio_out_unprepare(capi_audio_out_);
is_paused_ = true;
SB_DLOG(INFO) << "[MEDIA] audio_out_pause";
diff --git a/src/starboard/tizen/shared/configuration_public.h b/src/starboard/tizen/shared/configuration_public.h
index ff7d28d..9109655 100644
--- a/src/starboard/tizen/shared/configuration_public.h
+++ b/src/starboard/tizen/shared/configuration_public.h
@@ -17,9 +17,6 @@
#ifndef STARBOARD_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
#define STARBOARD_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
-// The API version implemented by this platform.
-#define SB_API_VERSION 1
-
// --- System Header Configuration -------------------------------------------
// Any system headers listed here that are not provided by the platform will be
@@ -49,9 +46,6 @@
// Whether the current platform provides the standard header float.h.
#define SB_HAS_FLOAT_H 1
-// Whether the current platform provides ssize_t.
-#define SB_HAS_SSIZE_T 1
-
// Type detection for wchar_t.
#if defined(__WCHAR_MAX__) && \
(__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
@@ -251,7 +245,7 @@
// --- Network Configuration -------------------------------------------------
// Specifies whether this platform supports IPV6.
-#define SB_HAS_IPV6 1
+#define SB_HAS_IPV6 0
// Specifies whether this platform supports pipe.
#define SB_HAS_PIPE 1
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc
index dec844c..ebe970b 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -22,14 +22,31 @@
namespace shared {
namespace ffmpeg {
+namespace {
+
+AVCodecID GetFfmpegCodecIdByMediaCodec(SbMediaAudioCodec audio_codec) {
+ switch (audio_codec) {
+ case kSbMediaAudioCodecAac:
+ return AV_CODEC_ID_AAC;
+ case kSbMediaAudioCodecOpus:
+ return AV_CODEC_ID_OPUS;
+ default:
+ return AV_CODEC_ID_NONE;
+ }
+}
+
+} // namespace
+
AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
const SbMediaAudioHeader& audio_header)
- : sample_type_(kSbMediaAudioSampleTypeInt16),
+ : audio_codec_(audio_codec),
+ sample_type_(kSbMediaAudioSampleTypeInt16),
codec_context_(NULL),
av_frame_(NULL),
stream_ended_(false),
audio_header_(audio_header) {
- SB_DCHECK(audio_codec == kSbMediaAudioCodecAac);
+ SB_DCHECK(GetFfmpegCodecIdByMediaCodec(audio_codec) != AV_CODEC_ID_NONE)
+ << "Unsupported audio codec " << audio_codec;
InitializeCodec();
}
@@ -70,10 +87,11 @@
if (decoded_audio_size > 0) {
scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
+ codec_context_->channels, sample_type_, GetStorageType(),
input_buffer.pts(),
codec_context_->channels * av_frame_->nb_samples *
(sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4));
- SbMemoryCopy(decoded_audio->buffer(), av_frame_->extended_data,
+ SbMemoryCopy(decoded_audio->buffer(), *av_frame_->extended_data,
decoded_audio->size());
decoded_audios_.push(decoded_audio);
} else {
@@ -125,7 +143,7 @@
}
codec_context_->codec_type = AVMEDIA_TYPE_AUDIO;
- codec_context_->codec_id = AV_CODEC_ID_AAC;
+ codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(audio_codec_);
// Request_sample_fmt is set by us, but sample_fmt is set by the decoder.
if (sample_type_ == kSbMediaAudioSampleTypeInt16) {
codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
@@ -147,7 +165,7 @@
return;
}
- int rv = avcodec_open2(codec_context_, codec, NULL);
+ int rv = OpenCodec(codec_context_, codec);
if (rv < 0) {
SB_LOG(ERROR) << "Unable to open codec";
TeardownCodec();
@@ -163,7 +181,7 @@
void AudioDecoder::TeardownCodec() {
if (codec_context_) {
- avcodec_close(codec_context_);
+ CloseCodec(codec_context_);
av_free(codec_context_);
codec_context_ = NULL;
}
@@ -174,6 +192,5 @@
}
} // namespace ffmpeg
-
} // namespace shared
} // namespace starboard
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h
index cd88d46..7b74d28 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -42,6 +42,9 @@
void Reset() SB_OVERRIDE;
SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE;
int GetSamplesPerSecond() const SB_OVERRIDE;
+ bool CanAcceptMoreData() const SB_OVERRIDE {
+ return !stream_ended_ && decoded_audios_.size() <= kMaxDecodedAudiosSize;
+ }
bool is_valid() const { return codec_context_ != NULL; }
@@ -49,6 +52,9 @@
void InitializeCodec();
void TeardownCodec();
+ static const int kMaxDecodedAudiosSize = 64;
+
+ SbMediaAudioCodec audio_codec_;
SbMediaAudioSampleType sample_type_;
AVCodecContext* codec_context_;
AVFrame* av_frame_;
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc b/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc
index 3667141..7dc091c 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.cc
@@ -15,6 +15,7 @@
#include "starboard/tizen/shared/ffmpeg/ffmpeg_common.h"
#include "starboard/log.h"
+#include "starboard/mutex.h"
#include "starboard/once.h"
namespace starboard {
@@ -24,6 +25,7 @@
namespace {
SbOnceControl ffmpeg_initialization_once = SB_ONCE_INITIALIZER;
+SbMutex codec_mutex = SB_MUTEX_INITIALIZER;
} // namespace
@@ -32,6 +34,19 @@
SB_DCHECK(initialized);
}
+int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec) {
+ SbMutexAcquire(&codec_mutex);
+ int result = avcodec_open2(codec_context, codec, NULL);
+ SbMutexRelease(&codec_mutex);
+ return result;
+}
+
+void CloseCodec(AVCodecContext* codec_context) {
+ SbMutexAcquire(&codec_mutex);
+ avcodec_close(codec_context);
+ SbMutexRelease(&codec_mutex);
+}
+
} // namespace ffmpeg
} // namespace shared
} // namespace starboard
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.h b/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.h
index 3bd1ea9..3db515e 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.h
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_common.h
@@ -32,6 +32,14 @@
void InitializeFfmpeg();
+// In Ffmpeg, the calls to avcodec_open2() and avcodec_close() are not
+// synchronized internally so it is the responsibility of its user to ensure
+// that these calls don't overlap. The following functions acquires a lock
+// internally before calling avcodec_open2() and avcodec_close() to enforce
+// this.
+int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec);
+void CloseCodec(AVCodecContext* codec_context);
+
} // namespace ffmpeg
} // namespace shared
} // namespace starboard
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
index 02aaa9e..9ccc5ce 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -265,7 +265,7 @@
return;
}
- int rv = avcodec_open2(codec_context_, codec, NULL);
+ int rv = OpenCodec(codec_context_, codec);
if (rv < 0) {
SB_LOG(ERROR) << "Unable to open codec";
TeardownCodec();
@@ -281,7 +281,7 @@
void VideoDecoder::TeardownCodec() {
if (codec_context_) {
- avcodec_close(codec_context_);
+ CloseCodec(codec_context_);
av_free(codec_context_);
codec_context_ = NULL;
}
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h
index 09ad253..9f8f961 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.h
@@ -29,7 +29,7 @@
namespace shared {
namespace ffmpeg {
-class VideoDecoder : public starboard::player::filter::VideoDecoder {
+class VideoDecoder : public starboard::player::filter::HostedVideoDecoder {
public:
typedef starboard::player::InputBuffer InputBuffer;
typedef starboard::player::VideoFrame VideoFrame;
diff --git a/src/starboard/tizen/shared/get_home_directory.cc b/src/starboard/tizen/shared/get_home_directory.cc
index fd5c728..5daf338 100644
--- a/src/starboard/tizen/shared/get_home_directory.cc
+++ b/src/starboard/tizen/shared/get_home_directory.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include <app_common.h>
+#include <unistd.h>
#include "starboard/log.h"
#include "starboard/shared/nouser/user_internal.h"
@@ -24,19 +25,27 @@
bool GetHomeDirectory(SbUser user, char* out_path, int path_size) {
if (user != SbUserGetCurrent()) {
+ SB_DLOG(ERROR) << "Invalid User";
return false;
}
- const char* home_directory = app_get_data_path();
-
- if (!home_directory) {
- SB_DLOG(WARNING) << "No HOME environment variable.";
- return false;
+ const char* data_path = app_get_data_path();
+ if (data_path) {
+ SB_DLOG(INFO) << "App data path [" << data_path << "]";
+ SbStringCopy(out_path, data_path, path_size);
+ return true;
}
- SB_DLOG(INFO) << "Tizen Home Directory[" << home_directory << "]";
- SbStringCopy(out_path, home_directory, path_size);
- return true;
+ // for nplb, unittest, ...
+ const char* curr_dir = getcwd(NULL, 0);
+ if (curr_dir) {
+ SB_DLOG(WARNING) << "No data path. Set CWD " << curr_dir;
+ SbStringCopy(out_path, curr_dir, path_size);
+ return true;
+ }
+
+ SB_DLOG(ERROR) << "No home directory variable";
+ return false;
}
} // namespace nouser
diff --git a/src/starboard/tizen/shared/gyp_configuration.gypi b/src/starboard/tizen/shared/gyp_configuration.gypi
new file mode 100644
index 0000000..385c3d9
--- /dev/null
+++ b/src/starboard/tizen/shared/gyp_configuration.gypi
@@ -0,0 +1,113 @@
+# Copyright 2016 Samsung Electronics. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'variables': {
+ 'target_os': 'linux',
+ 'tizen_os': 1,
+
+ # Some package use tizen system instead of third_party
+ 'use_system_icu': 1,
+ 'use_system_libxml': 1,
+
+ # scratch surface cache is designed to choose large offscreen surfaces so
+ # that they can be maximally reused, it is not a very good fit for a tiled
+ # renderer.
+ 'scratch_surface_cache_size_in_bytes' : 0,
+
+ # This should have a default value in cobalt/base.gypi. See the comment
+ # there for acceptable values for this variable.
+ 'javascript_engine': 'mozjs',
+ 'cobalt_enable_jit': 0,
+
+ 'linker_flags': [
+ ],
+ 'linker_flags_gold': [
+ '-O3',
+ '-flto',
+ ],
+ 'compiler_flags_debug': [
+ '-O0',
+ ],
+ 'compiler_flags_devel': [
+ '-O2',
+ ],
+ 'compiler_flags_cc_qa': [
+ '-fno-rtti',
+ ],
+ 'compiler_flags_qa': [
+ '-O3',
+ ],
+ 'compiler_flags_cc_gold': [
+ '-fno-rtti',
+ ],
+ 'compiler_flags_gold': [
+ '-O3',
+ ],
+ 'conditions': [
+ ['cobalt_fastbuild==0', {
+ 'compiler_flags_debug': [
+ '-g',
+ ],
+ 'compiler_flags_devel': [
+ '-g',
+ ],
+ 'compiler_flags_qa': [
+ ],
+ 'compiler_flags_gold': [
+ '-flto',
+ ],
+ }],
+ ],
+ },
+
+ 'target_defaults': {
+ 'defines': [
+ # Cobalt on Tizen flag
+ 'COBALT_TIZEN',
+ 'PNG_SKIP_SETJMP_CHECK',
+ '__STDC_FORMAT_MACROS', # so that we get PRI*
+ # Enable GNU extensions to get prototypes like ffsl.
+ '_GNU_SOURCE=1',
+ ],
+ 'cflags': [
+ '-pthread',
+ # Force char to be signed.
+ '-fsigned-char',
+ # Do not warn about locally defined but not used.
+ '-Wno-unused-local-typedefs',
+ # Do not warn about XXX is deprecated.
+ '-Wno-deprecated-declarations',
+ # Do not warn about missing initializer for member XXX.
+ '-Wno-missing-field-initializers',
+ # Do not warn about unused functions.
+ '-Wno-unused-function',
+ # Do not warn about type qualifiers ignored on function return type.
+ '-Wno-ignored-qualifiers',
+ # Do not warn about the use of multi-line comments.
+ '-Wno-comment',
+ # Do not warn about sign compares.
+ '-Wno-sign-compare',
+ ],
+ 'cflags_c': [
+ '-std=c11',
+ ],
+ 'cflags_cc': [
+ '-std=gnu++11',
+ ],
+ 'ldflags': [
+ '-pthread',
+ ],
+ }, # end of target_defaults
+}
diff --git a/src/starboard/tizen/shared/memory_flush.cc b/src/starboard/tizen/shared/memory_flush.cc
new file mode 100644
index 0000000..641d74e
--- /dev/null
+++ b/src/starboard/tizen/shared/memory_flush.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/memory.h"
+
+#include <sys/mman.h>
+#include <iomanip>
+
+#if !SB_CAN(MAP_EXECUTABLE_MEMORY)
+#error \
+ "You shouldn't implement SbMemoryFlush unless you can map " \
+ "memory pages as executable"
+#endif
+
+void SbMemoryFlush(void* virtual_address, int64_t size_bytes) {
+ char* memory = reinterpret_cast<char*>(virtual_address);
+#if !SB_IS(ARCH_ARM)
+ int result = msync(memory, size_bytes, MS_SYNC);
+ SB_DCHECK(result == 0) << "msync failed: 0x" << std::hex << result << " ("
+ << std::dec << result << "d)";
+#else
+ __clear_cache(memory, memory + size_bytes);
+#endif
+}
diff --git a/src/starboard/tizen/shared/player/filter/ffmpeg_player_components_impl.cc b/src/starboard/tizen/shared/player/filter/ffmpeg_player_components_impl.cc
index d747e00..1081d37 100644
--- a/src/starboard/tizen/shared/player/filter/ffmpeg_player_components_impl.cc
+++ b/src/starboard/tizen/shared/player/filter/ffmpeg_player_components_impl.cc
@@ -50,8 +50,8 @@
new AudioRendererImpl(audio_parameters.job_queue,
scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
audio_parameters.audio_header);
- VideoRendererImpl* video_renderer =
- new VideoRendererImpl(scoped_ptr<VideoDecoder>(video_decoder).Pass());
+ VideoRendererImpl* video_renderer = new VideoRendererImpl(
+ scoped_ptr<HostedVideoDecoder>(video_decoder).Pass());
return scoped_ptr<PlayerComponents>(
new PlayerComponents(audio_renderer, video_renderer));
diff --git a/src/starboard/tizen/shared/system_get_path.cc b/src/starboard/tizen/shared/system_get_path.cc
index b20b051..f3f7c6d 100644
--- a/src/starboard/tizen/shared/system_get_path.cc
+++ b/src/starboard/tizen/shared/system_get_path.cc
@@ -153,10 +153,6 @@
path_size);
case kSbSystemPathExecutableFile:
return GetExecutablePath(out_path, path_size);
-
- default:
- SB_NOTIMPLEMENTED();
- return false;
}
int length = strlen(path);
diff --git a/src/starboard/tizen/shared/thread_create.cc b/src/starboard/tizen/shared/thread_create.cc
new file mode 100644
index 0000000..30c4c0e
--- /dev/null
+++ b/src/starboard/tizen/shared/thread_create.cc
@@ -0,0 +1,97 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/thread.h"
+
+#include <Eina.h>
+
+#include "starboard/log.h"
+#include "starboard/shared/pthread/is_success.h"
+#include "starboard/string.h"
+
+namespace {
+
+struct ThreadParams {
+ SbThreadEntryPoint entry_point;
+ char name[128];
+ void* context;
+};
+
+void* ThreadFunc(void* context, Eina_Thread thread) {
+ ThreadParams* thread_params = static_cast<ThreadParams*>(context);
+ SbThreadEntryPoint entry_point = thread_params->entry_point;
+ void* real_context = thread_params->context;
+
+ if (thread_params->name[0] != '\0') {
+ SbThreadSetName(thread_params->name);
+ }
+
+ delete thread_params;
+
+ return entry_point(real_context);
+}
+
+Eina_Thread_Priority StarboardThreadPriorityToEina(SbThreadPriority priority) {
+ switch (priority) {
+ case kSbThreadPriorityLowest:
+ case kSbThreadPriorityLow:
+ return EINA_THREAD_IDLE;
+ case kSbThreadNoPriority:
+ case kSbThreadPriorityNormal:
+ return EINA_THREAD_BACKGROUND;
+ case kSbThreadPriorityHigh:
+ case kSbThreadPriorityHighest:
+ case kSbThreadPriorityRealTime:
+ return EINA_THREAD_URGENT;
+ default:
+ SB_NOTREACHED();
+ break;
+ }
+}
+
+} // namespace
+
+SbThread SbThreadCreate(int64_t stack_size,
+ SbThreadPriority priority,
+ SbThreadAffinity /*affinity*/,
+ bool joinable,
+ const char* name,
+ SbThreadEntryPoint entry_point,
+ void* context) {
+ if (stack_size < 0 || !entry_point) {
+ return kSbThreadInvalid;
+ }
+
+ ThreadParams* params = new ThreadParams();
+ params->entry_point = entry_point;
+ params->context = context;
+ if (name) {
+ SbStringCopy(params->name, name, sizeof(params->name));
+ } else {
+ params->name[0] = '\0';
+ }
+
+ Eina_Thread thread = kSbThreadInvalid;
+ if (eina_thread_create(&thread, StarboardThreadPriorityToEina(priority), -1,
+ &ThreadFunc, params)) {
+ // Fix nplb SbThreadCreateTest.SunnyDayDetached
+ // We can not pass |joinable| info when call eina_thread_create
+ // If |joinable| is false, we need to detach thread immediately
+ if (!joinable)
+ SbThreadDetach(thread);
+ return thread;
+ }
+
+ return kSbThreadInvalid;
+}
diff --git a/src/starboard/tools/extract_starboard_versions.py b/src/starboard/tools/extract_starboard_versions.py
new file mode 100644
index 0000000..ed03af5
--- /dev/null
+++ b/src/starboard/tools/extract_starboard_versions.py
@@ -0,0 +1,174 @@
+# 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.
+
+"""Extracts the API versions for all available builds.
+
+ Example usage:
+ cd cobalt/src/
+ extract_starboard_versions.py
+"""
+
+from __future__ import print_function
+
+import fnmatch
+import os
+import re
+import sys
+import platform
+import paths
+
+# Sometimes files have weird encodings. This function will use a variety of
+# hand selected encoders that work on the starboard codebase.
+def AutoDecodeString(file_data):
+ for encoding in {'UTF-8', 'utf_16', 'windows-1253', 'iso-8859-7', 'macgreek'}:
+ try:
+ return file_data.decode(encoding)
+ except:
+ continue
+ raise IOError('Could not read file')
+
+# Given a search_term, this will open the file_path and return the first
+# line that contains the search term. This will ignore C-style comments.
+def SearchInFileReturnFirstMatchingLine(file_path, search_term):
+ try:
+ lines = OpenFileAndDecodeLinesAndRemoveComments(file_path)
+ for line in lines:
+ if search_term in line:
+ return line
+ return None
+ except IOError:
+ print (' error while reading file ', file_path)
+
+# Opens a header or cc file and decodes it to utf8 using a variety
+# of decoders. All lines will have their comments stripped out.
+# This will return the lines of the given file.
+def OpenFileAndDecodeLinesAndRemoveComments(file_path):
+ with open(file_path, 'rb+') as fd:
+ lines = AutoDecodeString(fd.read()).splitlines()
+ # remove c-style comments.
+ lines = [ re.sub('//.*', '', line) for line in lines]
+ return lines
+
+
+# Given a file_path, return all include files that it contains.
+def FindIncludeFiles(file_path):
+ try:
+ output_list = []
+ lines = OpenFileAndDecodeLinesAndRemoveComments(file_path)
+ for line in lines:
+ # Remove c-style comments.
+ if '#include' in line:
+ line = re.sub('#include ', '', line).replace('"', '')
+ output_list.append(line)
+ return output_list
+ except IOError:
+ print (' error while reading file ', file_path)
+
+# Searches from the search_location for a configuration.h file that
+# contains the definition of the SB_EXPERIMENTAL_API_VERSION and then
+# returns that as type int.
+def ExtractExperimentalApiVersion(config_file_path):
+ needle = '#define SB_EXPERIMENTAL_API_VERSION'
+ line = SearchInFileReturnFirstMatchingLine(
+ config_file_path,
+ '#define SB_EXPERIMENTAL_API_VERSION')
+ if not line:
+ raise ValueError('Could not find ' + needle + ' in ' + config_file_path)
+
+ elements = line.split(' ')
+ exp_api_version = int(elements[2])
+ return exp_api_version
+
+# Given platform path, this function will try and find the version. Returns
+# either the version if found, or None.
+# If the version string is returned, note that it could be
+# 'SB_EXPERIMENTAL_VERSION' or a number string.
+def FindVersion(platform_path):
+ api_version_str = '#define SB_API_VERSION'
+ result = SearchInFileReturnFirstMatchingLine(platform_path, api_version_str)
+ if not result:
+ return None
+ version_str = result.replace(api_version_str, '')
+ return version_str.strip()
+
+# Given the path to the platform_include_file, this will find the include
+# files with "configuration_public.h" in the name and return those.
+def FindConfigIncludefile(platform_path_config_file):
+ include_files = FindIncludeFiles(platform_path_config_file)
+ include_files = filter(lambda x: 'configuration_public.h' in x, include_files)
+ return include_files
+
+# Given the input starboard directory, this will return a map of
+# 'platform_name' -> 'full_path_to_platform'
+def GeneratePlatformPathMap(starboard_dir):
+ ports = [p for p in platform.PlatformInfo.EnumeratePorts(starboard_dir)]
+ def GenPath(p):
+ full_path = os.path.abspath(os.path.join(p.path, 'configuration_public.h'))
+ if not os.path.exists(full_path):
+ raise IOError("Could not find path " + full_path)
+ return full_path
+
+ port_dict = {}
+ for p in ports:
+ port_dict[p.port_name] = GenPath(p)
+ return port_dict
+
+# Given the root starboard directory, and the full path to the platform,
+# this function will search for the API_VERSION of the platform. It will
+# first see if the version is defined within the include file, if it is
+# not then the include paths for shared platform configurations are
+# searched in the recursive step.
+def FindVersionRecursive(starboard_dir, platform_path):
+ version_str = FindVersion(platform_path)
+ if version_str:
+ return version_str
+ else:
+ config_include_paths = FindConfigIncludefile(platform_path)
+ if len(config_include_paths) == 0:
+ return "<UNKNOWN>"
+ elif len(config_include_paths) > 1:
+ return "<AMBIGUIOUS>"
+ else:
+ include_path = config_include_paths[0]
+ include_path = re.sub(r'^starboard/', '', include_path)
+ full_include_path = os.path.join(starboard_dir, include_path)
+ return FindVersionRecursive(starboard_dir, full_include_path)
+
+def Main():
+ print('\n***** Listing the api versions of all first party ports. *****\n')
+
+ port_dict = GeneratePlatformPathMap(paths.STARBOARD_ROOT)
+
+ experimental_api_version = ExtractExperimentalApiVersion(
+ os.path.join(paths.STARBOARD_ROOT, 'configuration.h'))
+
+ path_map = {}
+
+ print('Experimental API Version: ' + str(experimental_api_version) + '\n')
+
+ for platform_name, platform_path in port_dict.iteritems():
+ version_str = FindVersionRecursive(paths.STARBOARD_ROOT, platform_path)
+ if 'SB_EXPERIMENTAL_API_VERSION' in version_str:
+ version_str = str(experimental_api_version)
+ path_map[platform_name] = version_str
+
+ for platform_name, api_version in sorted(path_map.iteritems()):
+ print(platform_name + ': ' + api_version)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ # All functionality stored in Main() to avoid py-lint from warning about
+ # about shadowing global variables in local functions.
+ Main()
diff --git a/src/starboard/tools/paths.py b/src/starboard/tools/paths.py
index 78179fd..db8c372 100644
--- a/src/starboard/tools/paths.py
+++ b/src/starboard/tools/paths.py
@@ -15,8 +15,18 @@
#
"""Constants for commonly referenced paths."""
+from __future__ import print_function
+
import os
-import starboard
+STARBOARD_ROOT = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), os.path.pardir))
-STARBOARD_ROOT = os.path.abspath(os.path.dirname(starboard.__file__))
+if __name__ == '__main__':
+ # All functionality stored in TestRunPaths() to avoid py-lint from warning'
+ # about shadowing global variables in local functions.
+ def TestRunPaths():
+ print('STARBOARD_ROOT: ' + STARBOARD_ROOT)
+ assert(os.path.isdir(STARBOARD_ROOT))
+
+ TestRunPaths()
diff --git a/src/starboard/tools/raspi/run_test.py b/src/starboard/tools/raspi/run_test.py
index b5420bc..58c925c 100755
--- a/src/starboard/tools/raspi/run_test.py
+++ b/src/starboard/tools/raspi/run_test.py
@@ -35,10 +35,22 @@
_RASPI_PASSWORD = 'raspberry'
# Timeouts are in seconds
-_PEXPECT_DEFAULT_TIMEOUT = 300
+_PEXPECT_DEFAULT_TIMEOUT = 600
_PEXPECT_EXPECT_TIMEOUT = 60
+def _CleanupProcess(process):
+ """Closes current pexpect process.
+
+ Args:
+ process: Current pexpect process.
+ """
+ if process is not None and process.isalive():
+ # Send ctrl-c to the raspi.
+ process.sendline(chr(3))
+ process.close()
+
+
# pylint: disable=unused-argument
def _SigIntOrSigTermHandler(process, signum, frame):
"""Clean up and exit with status |signum|.
@@ -50,14 +62,11 @@
frame: Current stack frame. Passed in when the signal handler is called by
python runtime.
"""
- if process.isalive():
- # Send ctrl-c to the raspi.
- process.sendline(chr(3))
- process.close()
+ _CleanupProcess(process)
sys.exit(signum)
-def RunTest(test_path, raspi_ip, flags):
+def _RunTest(test_path, raspi_ip, flags):
"""Run a test on a Raspi machine and print relevant stdout.
Args:
@@ -67,76 +76,97 @@
Returns:
Exit status of running the test.
-
- Raises:
- ValueError: Raised if test_path is invalid.
"""
- if not os.path.isfile(test_path):
- raise ValueError('test_path ({}) must be a file.'.format(test_path))
+ return_value = 1
- # This is used to strip ansi color codes from output.
- sanitize_line_re = re.compile(r'\x1b[^m]*m')
+ try:
+ process = None
- sys.stdout.write('Process launched, ID={}\n'.format(os.getpid()))
- sys.stdout.flush()
+ if not os.path.isfile(test_path):
+ raise ValueError('test_path ({}) must be a file.'.format(test_path))
- test_dir_path, test_file = os.path.split(test_path)
- test_base_dir = os.path.basename(os.path.normpath(test_dir_path))
+ # This is used to strip ansi color codes from output.
+ sanitize_line_re = re.compile(r'\x1b[^m]*m')
- raspi_user_hostname = _RASPI_USERNAME + '@' + raspi_ip
- raspi_test_path = os.path.join(test_base_dir, test_file)
+ sys.stdout.write('Process launched, ID={}\n'.format(os.getpid()))
+ sys.stdout.flush()
- # rsync the test files to the raspi
- options = '-avzh --exclude obj*'
- source = test_dir_path
- destination = raspi_user_hostname + ':~/'
- rsync_command = 'rsync ' + options + ' ' + source + ' ' + destination
- rsync_process = pexpect.spawn(rsync_command, timeout=_PEXPECT_DEFAULT_TIMEOUT)
+ test_dir_path, test_file = os.path.split(test_path)
+ test_base_dir = os.path.basename(os.path.normpath(test_dir_path))
- signal.signal(signal.SIGINT,
- functools.partial(_SigIntOrSigTermHandler, rsync_process))
- signal.signal(signal.SIGTERM,
- functools.partial(_SigIntOrSigTermHandler, rsync_process))
+ raspi_user_hostname = _RASPI_USERNAME + '@' + raspi_ip
+ raspi_test_path = os.path.join(test_base_dir, test_file)
- rsync_process.expect(r'\S+ password:', timeout=_PEXPECT_EXPECT_TIMEOUT)
- rsync_process.sendline(_RASPI_PASSWORD)
+ # rsync the test files to the raspi
+ options = '-avzh --exclude obj*'
+ source = test_dir_path
+ destination = raspi_user_hostname + ':~/'
+ rsync_command = 'rsync ' + options + ' ' + source + ' ' + destination
+ process = pexpect.spawn(rsync_command, timeout=_PEXPECT_DEFAULT_TIMEOUT)
- while True:
- line = sanitize_line_re.sub('', rsync_process.readline())
- if line:
+ signal.signal(signal.SIGINT,
+ functools.partial(_SigIntOrSigTermHandler, process))
+ signal.signal(signal.SIGTERM,
+ functools.partial(_SigIntOrSigTermHandler, process))
+
+ process.expect(r'\S+ password:', timeout=_PEXPECT_EXPECT_TIMEOUT)
+ process.sendline(_RASPI_PASSWORD)
+
+ while True:
+ line = sanitize_line_re.sub('', process.readline())
+ if line:
+ sys.stdout.write(line)
+ sys.stdout.flush()
+ else:
+ break
+
+ # ssh into the raspi and run the test
+ ssh_command = 'ssh ' + raspi_user_hostname
+ process = pexpect.spawn(ssh_command, timeout=_PEXPECT_DEFAULT_TIMEOUT)
+
+ signal.signal(signal.SIGINT,
+ functools.partial(_SigIntOrSigTermHandler, process))
+ signal.signal(signal.SIGTERM,
+ functools.partial(_SigIntOrSigTermHandler, process))
+
+ process.expect(r'\S+ password:', timeout=_PEXPECT_EXPECT_TIMEOUT)
+ process.sendline(_RASPI_PASSWORD)
+
+ test_command = raspi_test_path + ' ' + flags
+ test_time_tag = 'TEST-{time}'.format(time=time.time())
+ test_success_tag = 'succeeded'
+ test_failure_tag = 'failed'
+ test_success_output = ' && echo ' + test_time_tag + ' ' + test_success_tag
+ test_failure_output = ' || echo ' + test_time_tag + ' ' + test_failure_tag
+ process.sendline(test_command + test_success_output + test_failure_output)
+
+ while True:
+ line = sanitize_line_re.sub('', process.readline())
+ if not line:
+ break
sys.stdout.write(line)
sys.stdout.flush()
- else:
- break
+ if line.startswith(test_time_tag):
+ if line.find(test_success_tag) != -1:
+ return_value = 0
+ break
- # ssh into the raspi and run the test
- ssh_command = 'ssh ' + raspi_user_hostname
- ssh_process = pexpect.spawn(ssh_command, timeout=_PEXPECT_DEFAULT_TIMEOUT)
+ except ValueError:
+ logging.exception('Test path invalid.')
+ except pexpect.EOF:
+ logging.exception('pexpect encountered EOF while reading line.')
+ except pexpect.TIMEOUT:
+ logging.exception('pexpect timed out while reading line.')
+ # pylint: disable=W0703
+ except Exception:
+ logging.exception('Error occured while running test.')
+ finally:
+ _CleanupProcess(process)
- signal.signal(signal.SIGINT,
- functools.partial(_SigIntOrSigTermHandler, ssh_process))
- signal.signal(signal.SIGTERM,
- functools.partial(_SigIntOrSigTermHandler, ssh_process))
-
- ssh_process.expect(r'\S+ password:', timeout=_PEXPECT_EXPECT_TIMEOUT)
- ssh_process.sendline(_RASPI_PASSWORD)
-
- test_command = raspi_test_path + ' ' + flags
- test_end_tag = 'END_TEST-{time}'.format(time=time.time())
- ssh_process.sendline(test_command + '; echo ' + test_end_tag)
-
- while True:
- line = sanitize_line_re.sub('', ssh_process.readline())
- if line and not line.startswith(test_end_tag):
- sys.stdout.write(line)
- sys.stdout.flush()
- else:
- break
-
- return 0
+ return return_value
-def AddTargetFlag(parser):
+def _AddTargetFlag(parser):
"""Add target to argument parser."""
parser.add_argument(
'-t',
@@ -150,7 +180,7 @@
nargs='?')
-def AddFlagsFlag(parser, default=None):
+def _AddFlagsFlag(parser, default=None):
"""Add flags to argument parser.
Args:
@@ -185,21 +215,13 @@
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description=textwrap.dedent(__doc__))
- AddFlagsFlag(parser)
- AddTargetFlag(parser)
+ _AddFlagsFlag(parser)
+ _AddTargetFlag(parser)
parser.add_argument('test_path', help='Path of test to be run.', type=str)
args = parser.parse_args()
- try:
- return_value = RunTest(
- args.test_path, raspi_ip=args.target, flags=args.flags)
- # pylint: disable=W0703
- except Exception:
- logging.exception('Error occured while running binary.')
- return_value = 1
-
- return return_value
+ return _RunTest(args.test_path, raspi_ip=args.target, flags=args.flags)
if __name__ == '__main__':
diff --git a/src/starboard/tools/toolchain.py b/src/starboard/tools/toolchain.py
new file mode 100644
index 0000000..5400a1d
--- /dev/null
+++ b/src/starboard/tools/toolchain.py
@@ -0,0 +1,166 @@
+# Copyright (c) 2017 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Starboard's abstract Toolchain which is implemented for each platform."""
+
+import abc
+import imp
+import logging
+import os
+
+import starboard
+from starboard.tools import paths
+from starboard.tools import platform
+
+
+class ABCMetaSingleton(abc.ABCMeta):
+ instances = {}
+
+ def __call__(self, *args, **kwargs):
+ if self not in self.instances:
+ self.instances[self] = super(ABCMetaSingleton, self).__call__(
+ *args, **kwargs)
+ return self.instances[self]
+
+
+class Toolchain(object):
+ """This is an abstract interface of the Toolchain."""
+ """ TODO: move variables / commands
+ AR / _HOST
+ ARFLAGS / _HOST
+ ARTHINFLAGS / _HOST
+ CC / CC_HOST
+ CXX / CXX_HOST
+ LD / LD_HOST
+ RC / RC_HOST
+ And / or implement NinjaWriter.WriteSources & GenerateOutputForConfig body
+ here.
+"""
+ __metaclass__ = ABCMetaSingleton
+
+ @abc.abstractmethod
+ def Define(self, d):
+ pass
+
+ @abc.abstractmethod
+ def EncodeRspFileList(self, args):
+ pass
+
+ @abc.abstractmethod
+ def ExpandEnvVars(self, path, env):
+ pass
+
+ @abc.abstractmethod
+ def ExpandRuleVariables(self, path, root, dirname, source, ext, name):
+ pass
+
+ @abc.abstractmethod
+ def GenerateEnvironmentFiles(self, toplevel_build_dir, generator_flags,
+ open_out):
+ pass
+
+ @abc.abstractmethod
+ def GetCompilerSettings(self):
+ pass
+
+ @abc.abstractmethod
+ def GetPrecompiledHeader(self, **kwargs):
+ pass
+
+ @abc.abstractmethod
+ def InitCompilerSettings(self, spec, **kwargs):
+ pass
+
+ @abc.abstractmethod
+ def SetAdditionalGypVariables(self, default_variables, **kwargs):
+ pass
+
+ @abc.abstractmethod
+ def VerifyMissingSources(self, sources, **kwargs):
+ pass
+
+ @abc.abstractmethod
+ def QuoteForRspFile(self, arg):
+ pass
+
+
+class PrecompiledHeader:
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def __init__(self):
+ pass
+
+ @abc.abstractmethod
+ def GetFlagsModifications(self, input_flags, output, implicit, command,
+ cflags_c, cflags_cc, expand_special):
+ pass
+
+ @abc.abstractmethod
+ def GetPchBuildCommands(self):
+ pass
+
+ @abc.abstractmethod
+ def GetInclude(self):
+ pass
+
+ @abc.abstractmethod
+ def GetObjDependencies(self, sources, output):
+ pass
+
+
+class CompilerSettings:
+ """Abstract Compiler Settings class."""
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def __init__(self):
+ pass
+
+ @abc.abstractmethod
+ def GetArch(self, config):
+ pass
+
+ @abc.abstractmethod
+ def GetCflags(self, config):
+ pass
+
+ @abc.abstractmethod
+ def GetCflagsC(self, config):
+ pass
+
+ @abc.abstractmethod
+ def GetCflagsCC(self, config):
+ pass
+
+ @abc.abstractmethod
+ def GetCflagsObjectiveC(self, config):
+ pass
+
+ @abc.abstractmethod
+ def GetCflagsObjectiveCC(self, config):
+ pass
+
+ @abc.abstractmethod
+ def GetDefines(self, config):
+ pass
+
+ @abc.abstractmethod
+ def GetLdFlags(self, config, **kwargs):
+ pass
+
+ @abc.abstractmethod
+ def GetLibFlags(self, config, gyp_path_to_ninja):
+ pass
+
+ @abc.abstractmethod
+ def GetRcFlags(self, config, gyp_path_to_ninja):
+ pass
+
+ @abc.abstractmethod
+ def ProcessIncludeDirs(self, include_dirs, config_name):
+ pass
+
+ @abc.abstractmethod
+ def ProcessLibraries(self, libraries, config_name):
+ pass
\ No newline at end of file
diff --git a/src/starboard/win/console/starboard_platform.gyp b/src/starboard/win/console/starboard_platform.gyp
index 653db66..134c3b2 100644
--- a/src/starboard/win/console/starboard_platform.gyp
+++ b/src/starboard/win/console/starboard_platform.gyp
@@ -14,17 +14,24 @@
{
'includes': [ '../shared/starboard_platform.gypi' ],
'variables': {
- 'starboard_platform_dependent_sources': [
+ 'starboard_platform_dependent_files': [
'atomic_public.h',
'configuration_public.h',
'main.cc',
'thread_types_public.h',
'../shared/system_get_path.cc',
+ '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
+ '<(DEPTH)/starboard/shared/starboard/queue_application.h',
+ '<(DEPTH)/starboard/shared/stub/system_request_pause.cc',
+ '<(DEPTH)/starboard/shared/stub/system_request_stop.cc',
+ '<(DEPTH)/starboard/shared/stub/system_request_suspend.cc',
+ '<(DEPTH)/starboard/shared/stub/system_request_unpause.cc',
'<(DEPTH)/starboard/shared/stub/window_create.cc',
'<(DEPTH)/starboard/shared/stub/window_destroy.cc',
'<(DEPTH)/starboard/shared/stub/window_get_platform_handle.cc',
'<(DEPTH)/starboard/shared/stub/window_get_size.cc',
'<(DEPTH)/starboard/shared/stub/window_set_default_options.cc',
+ '<(DEPTH)/starboard/shared/uwp/system_get_property.cc',
'<(DEPTH)/starboard/stub/application_stub.cc',
'<(DEPTH)/starboard/stub/application_stub.h',
],
diff --git a/src/starboard/win/console/starboard_platform_tests.gyp b/src/starboard/win/console/starboard_platform_tests.gyp
index 93f812b..1eb83ba 100644
--- a/src/starboard/win/console/starboard_platform_tests.gyp
+++ b/src/starboard/win/console/starboard_platform_tests.gyp
@@ -12,6 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
{
+ 'variables': {
+ 'sb_pedantic_warnings': 1,
+ },
'targets': [
{
'target_name': 'starboard_platform_tests',
diff --git a/src/starboard/win/lib/starboard_platform.gyp b/src/starboard/win/lib/starboard_platform.gyp
index f6dfb74..d61cc40 100644
--- a/src/starboard/win/lib/starboard_platform.gyp
+++ b/src/starboard/win/lib/starboard_platform.gyp
@@ -12,21 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
{
- 'includes': [ '../shared/starboard_platform.gypi' ],
- 'sources': [
- 'atomic_public.h',
- 'configuration_public.h',
- 'thread_types_public.h',
+ 'includes': [
+ '../shared/starboard_platform.gypi',
+ '../../shared/uwp/starboard_platform.gypi'
],
'variables': {
- 'starboard_platform_dependent_sources': [
+ 'starboard_platform_dependent_files': [
'atomic_public.h',
'configuration_public.h',
'thread_types_public.h',
- 'main.cc',
'../shared/system_get_path.cc',
- '<(DEPTH)/starboard/shared/uwp/application_uwp.cc',
- '<(DEPTH)/starboard/shared/uwp/application_uwp.h',
],
- },
+ }
}
diff --git a/src/starboard/win/lib/starboard_platform_tests.gyp b/src/starboard/win/lib/starboard_platform_tests.gyp
index 93f812b..1eb83ba 100644
--- a/src/starboard/win/lib/starboard_platform_tests.gyp
+++ b/src/starboard/win/lib/starboard_platform_tests.gyp
@@ -12,6 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
{
+ 'variables': {
+ 'sb_pedantic_warnings': 1,
+ },
'targets': [
{
'target_name': 'starboard_platform_tests',
diff --git a/src/starboard/win/shared/configuration_public.h b/src/starboard/win/shared/configuration_public.h
index 47521b3..1d66e6c 100644
--- a/src/starboard/win/shared/configuration_public.h
+++ b/src/starboard/win/shared/configuration_public.h
@@ -245,7 +245,7 @@
// given reason, this is the character that appears between entries. For
// example, the search path of "/etc/search/first:/etc/search/second" uses ':'
// as a search path component separator character.
-#define SB_PATH_SEP_CHAR ':'
+#define SB_PATH_SEP_CHAR ';'
// The string form of SB_FILE_SEP_CHAR.
#define SB_FILE_SEP_STRING "\\"
@@ -254,7 +254,7 @@
#define SB_FILE_ALT_SEP_STRING "/"
// The string form of SB_PATH_SEP_CHAR.
-#define SB_PATH_SEP_STRING ":"
+#define SB_PATH_SEP_STRING ";"
// On some platforms the file system stores access times at a coarser
// granularity than other times. When this quirk is defined, we assume the
diff --git a/src/starboard/win/shared/gyp_configuration.gypi b/src/starboard/win/shared/gyp_configuration.gypi
index 62a2713..8a2ebfd 100644
--- a/src/starboard/win/shared/gyp_configuration.gypi
+++ b/src/starboard/win/shared/gyp_configuration.gypi
@@ -24,35 +24,6 @@
'cobalt_media_source_2016': 1,
- # Define platform specific compiler and linker flags.
- # Refer to base.gypi for a list of all available variables.
- 'compiler_flags_host': [
- '-O2',
- ],
- 'compiler_flags': [
- # We'll pretend not to be Linux, but Starboard instead.
- '-U__linux__',
- ],
- 'linker_flags': [
- ],
- 'compiler_flags_debug': [
- '-frtti',
- '-O0',
- ],
- 'compiler_flags_devel': [
- '-frtti',
- '-O2',
- ],
- 'compiler_flags_qa': [
- '-fno-rtti',
- '-O2',
- '-gline-tables-only',
- ],
- 'compiler_flags_gold': [
- '-fno-rtti',
- '-O2',
- '-gline-tables-only',
- ],
'conditions': [
['cobalt_fastbuild==0', {
'msvs_settings': {
@@ -115,6 +86,15 @@
'msvs_target_platform': 'x64',
# Add the default import libs.
'conditions': [
+ ['sb_pedantic_warnings==1', {
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ # Enable some warnings, even those that are disabled by default.
+ # See https://msdn.microsoft.com/en-us/library/23k5d385.aspx
+ 'WarningLevel': '4',
+ },
+ },
+ }],
['cobalt_fastbuild==0', {
'msvs_settings': {
'VCCLCompilerTool': {
@@ -198,6 +178,11 @@
},
},
'defines': [
+ # Disable warnings. These options were inherited from Chromium.
+ '_CRT_SECURE_NO_DEPRECATE',
+ '_CRT_NONSTDC_NO_WARNINGS',
+ '_CRT_NONSTDC_NO_DEPRECATE',
+ '_SCL_SECURE_NO_DEPRECATE',
# Disable suggestions to switch to Microsoft-specific secure CRT.
'_CRT_SECURE_NO_WARNINGS',
# Disable support for exceptions in STL in order to detect their use
@@ -217,30 +202,37 @@
'msvs_settings': {
'VCCLCompilerTool': {
'ForcedIncludeFiles': [],
+
# Check for buffer overruns.
'BufferSecurityCheck': 'true',
+
'Conformance': [
# "for" loop's initializer go out of scope after the for loop.
'forScope',
# wchar_t is treated as a built-in type.
'wchar_t',
],
+
# Check for 64-bit portability issues.
'Detect64BitPortabilityProblems': 'true',
+
# Disable Microsoft-specific header dependency tracking.
# Incremental linker does not support the Windows metadata included
# in .obj files compiled with C++/CX support (/ZW).
'MinimalRebuild': 'false',
+
# Treat warnings as errors.
'WarnAsError': 'false',
+
# Enable some warnings, even those that are disabled by default.
# See https://msdn.microsoft.com/en-us/library/23k5d385.aspx
- 'WarningLevel': '3',
+ 'WarningLevel': '2',
'AdditionalOptions': [
- '/errorReport:none', # don't send error reports to MS.
+ '/errorReport:none', # Don't send error reports to MS.
'/permissive-', # Visual C++ conformance mode.
'/FS', # Force sync PDB updates for parallel compile.
+ '/w14389', # Turn on warnings for signed/unsigned mismatch.
],
},
'VCLinkerTool': {
@@ -257,7 +249,7 @@
'TargetMachine': '17', # x86 - 64
'AdditionalOptions': [
'/WINMD:NO', # Do not generate a WinMD file.
- '/errorReport:none', # don't send error reports to MS.
+ '/errorReport:none', # Don't send error reports to MS.
],
},
'VCLibrarianTool': {
@@ -338,15 +330,5 @@
# https://connect.microsoft.com/VisualStudio/feedback/details/783808/static-analyzer-warning-c28285-for-std-min-and-std-max
28285,
],
- 'target_conditions': [
- ['cobalt_code==1', {
- 'cflags': [
- '-Wall',
- '-Wextra',
- '-Wunreachable-code',
- ],
- },
- ],
- ],
}, # end of target_defaults
}
diff --git a/src/starboard/win/shared/starboard_platform.gypi b/src/starboard/win/shared/starboard_platform.gypi
index 6d28bf6..2b520d0 100644
--- a/src/starboard/win/shared/starboard_platform.gypi
+++ b/src/starboard/win/shared/starboard_platform.gypi
@@ -12,12 +12,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
{
+ 'variables': {
+ 'sb_pedantic_warnings': 1,
+ },
'targets': [
{
'target_name': 'starboard_platform',
'type': 'static_library',
'msvs_settings': {
'VCCLCompilerTool': {
+ 'AdditionalIncludeDirectories': [
+ '<(DEPTH)/third_party/angle/include',
+ '<(DEPTH)/third_party/angle/include/EGL',
+ '<(DEPTH)/third_party/angle/include/GLES2',
+ '<(DEPTH)/third_party/angle/include/KHR',
+ ],
'AdditionalOptions': [
'/ZW', # Windows Runtime
'/ZW:nostdlib', # Windows Runtime, no default #using
@@ -58,11 +67,30 @@
'<(DEPTH)/starboard/shared/iso/string_parse_uint64.cc',
'<(DEPTH)/starboard/shared/iso/string_parse_unsigned_integer.cc',
'<(DEPTH)/starboard/shared/iso/string_scan.cc',
+ '<(DEPTH)/starboard/shared/iso/system_binary_search.cc',
+ '<(DEPTH)/starboard/shared/iso/system_sort.cc',
+ '<(DEPTH)/starboard/shared/nouser/user_get_current.cc',
+ '<(DEPTH)/starboard/shared/nouser/user_get_property.cc',
+ '<(DEPTH)/starboard/shared/nouser/user_get_signed_in.cc',
+ '<(DEPTH)/starboard/shared/nouser/user_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
+ '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
+ '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_is_valid.cc',
+ '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc',
+ '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.h',
'<(DEPTH)/starboard/shared/starboard/application.cc',
'<(DEPTH)/starboard/shared/starboard/command_line.cc',
'<(DEPTH)/starboard/shared/starboard/command_line.h',
'<(DEPTH)/starboard/shared/starboard/event_cancel.cc',
'<(DEPTH)/starboard/shared/starboard/event_schedule.cc',
+ '<(DEPTH)/starboard/shared/starboard/file_storage/storage_close_record.cc',
+ '<(DEPTH)/starboard/shared/starboard/file_storage/storage_delete_record.cc',
+ '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc',
+ '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc',
+ '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc',
+ '<(DEPTH)/starboard/shared/starboard/file_storage/storage_write_record.cc',
'<(DEPTH)/starboard/shared/starboard/file_mode_string_to_flags.cc',
'<(DEPTH)/starboard/shared/starboard/log_message.cc',
'<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
@@ -72,16 +100,8 @@
'<(DEPTH)/starboard/shared/starboard/string_copy.cc',
'<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
'<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
- '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
'<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
'<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
- '<(DEPTH)/starboard/shared/stub/audio_sink_create.cc',
- '<(DEPTH)/starboard/shared/stub/audio_sink_destroy.cc',
- '<(DEPTH)/starboard/shared/stub/audio_sink_get_max_channels.cc',
- '<(DEPTH)/starboard/shared/stub/audio_sink_get_nearest_supported_sample_frequency.cc',
- '<(DEPTH)/starboard/shared/stub/audio_sink_is_audio_frame_storage_type_supported.cc',
- '<(DEPTH)/starboard/shared/stub/audio_sink_is_audio_sample_type_supported.cc',
- '<(DEPTH)/starboard/shared/stub/audio_sink_is_valid.cc',
'<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
'<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
'<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
@@ -125,42 +145,25 @@
'<(DEPTH)/starboard/shared/stub/player_set_volume.cc',
'<(DEPTH)/starboard/shared/stub/player_write_end_of_stream.cc',
'<(DEPTH)/starboard/shared/stub/player_write_sample.cc',
- '<(DEPTH)/starboard/shared/stub/storage_close_record.cc',
- '<(DEPTH)/starboard/shared/stub/storage_delete_record.cc',
- '<(DEPTH)/starboard/shared/stub/storage_get_record_size.cc',
- '<(DEPTH)/starboard/shared/stub/storage_open_record.cc',
- '<(DEPTH)/starboard/shared/stub/storage_read_record.cc',
- '<(DEPTH)/starboard/shared/stub/storage_write_record.cc',
- '<(DEPTH)/starboard/shared/stub/system_binary_search.cc',
- '<(DEPTH)/starboard/shared/stub/system_clear_last_error.cc',
'<(DEPTH)/starboard/shared/stub/system_clear_platform_error.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_connection_type.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_device_type.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_error_string.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_last_error.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_locale_id.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_number_of_processors.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_property.cc',
'<(DEPTH)/starboard/shared/stub/system_get_stack.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_total_cpu_memory.cc',
'<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_used_cpu_memory.cc',
'<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc',
'<(DEPTH)/starboard/shared/stub/system_has_capability.cc',
'<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
'<(DEPTH)/starboard/shared/stub/system_is_debugger_attached.cc',
'<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc',
- '<(DEPTH)/starboard/shared/stub/system_request_pause.cc',
- '<(DEPTH)/starboard/shared/stub/system_request_stop.cc',
- '<(DEPTH)/starboard/shared/stub/system_request_suspend.cc',
- '<(DEPTH)/starboard/shared/stub/system_request_unpause.cc',
- '<(DEPTH)/starboard/shared/stub/system_sort.cc',
'<(DEPTH)/starboard/shared/stub/system_symbolize.cc',
'<(DEPTH)/starboard/shared/stub/time_zone_get_dst_name.cc',
- '<(DEPTH)/starboard/shared/stub/user_get_current.cc',
- '<(DEPTH)/starboard/shared/stub/user_get_property.cc',
- '<(DEPTH)/starboard/shared/stub/user_get_signed_in.cc',
+ '<(DEPTH)/starboard/shared/win32/audio_sink.cc',
+ '<(DEPTH)/starboard/shared/win32/audio_sink.h',
+ '<(DEPTH)/starboard/shared/win32/adapter_utils.cc',
+ '<(DEPTH)/starboard/shared/win32/adapter_utils.h',
'<(DEPTH)/starboard/shared/win32/atomic_public.h',
+ '<(DEPTH)/starboard/shared/win32/audio_sink_get_max_channels.cc',
+ '<(DEPTH)/starboard/shared/win32/audio_sink_get_nearest_supported_sample_frequency.cc',
+ '<(DEPTH)/starboard/shared/win32/audio_sink_is_audio_frame_storage_type_supported.cc',
+ '<(DEPTH)/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc',
'<(DEPTH)/starboard/shared/win32/auto_event_handle.h',
'<(DEPTH)/starboard/shared/win32/byte_swap.cc',
'<(DEPTH)/starboard/shared/win32/condition_variable_broadcast.cc',
@@ -191,6 +194,7 @@
'<(DEPTH)/starboard/shared/win32/file_seek.cc',
'<(DEPTH)/starboard/shared/win32/file_truncate.cc',
'<(DEPTH)/starboard/shared/win32/file_write.cc',
+ '<(DEPTH)/starboard/shared/win32/get_home_directory.cc',
'<(DEPTH)/starboard/shared/win32/log.cc',
'<(DEPTH)/starboard/shared/win32/log_flush.cc',
'<(DEPTH)/starboard/shared/win32/log_format.cc',
@@ -233,6 +237,12 @@
'<(DEPTH)/starboard/shared/win32/socket_receive_from.cc',
'<(DEPTH)/starboard/shared/win32/socket_resolve.cc',
'<(DEPTH)/starboard/shared/win32/socket_send_to.cc',
+ '<(DEPTH)/starboard/shared/win32/system_get_connection_type.cc',
+ '<(DEPTH)/starboard/shared/win32/system_get_device_type.cc',
+ '<(DEPTH)/starboard/shared/win32/system_get_error_string.cc',
+ '<(DEPTH)/starboard/shared/win32/system_get_number_of_processors.cc',
+ '<(DEPTH)/starboard/shared/win32/system_get_total_cpu_memory.cc',
+ '<(DEPTH)/starboard/shared/win32/system_get_used_cpu_memory.cc',
'<(DEPTH)/starboard/shared/win32/socket_set_broadcast.cc',
'<(DEPTH)/starboard/shared/win32/socket_set_receive_buffer_size.cc',
'<(DEPTH)/starboard/shared/win32/socket_set_reuse_address.cc',
@@ -254,8 +264,11 @@
'<(DEPTH)/starboard/shared/win32/string_format.cc',
'<(DEPTH)/starboard/shared/win32/string_format_wide.cc',
'<(DEPTH)/starboard/shared/win32/system_break_into_debugger.cc',
+ '<(DEPTH)/starboard/shared/win32/system_clear_last_error.cc',
+ '<(DEPTH)/starboard/shared/win32/system_get_locale_id.cc',
'<(DEPTH)/starboard/shared/win32/system_get_random_data.cc',
'<(DEPTH)/starboard/shared/win32/system_get_random_uint64.cc',
+ '<(DEPTH)/starboard/shared/win32/system_get_last_error.cc',
'<(DEPTH)/starboard/shared/win32/thread_create.cc',
'<(DEPTH)/starboard/shared/win32/thread_create_local_key.cc',
'<(DEPTH)/starboard/shared/win32/thread_detach.cc',
@@ -283,7 +296,7 @@
'configuration_public.h',
# Include private stubs, if present.
'<!@(python "<(DEPTH)/starboard/tools/find_private_files.py" "<(DEPTH)" "shared/stub/*.cc")',
- '<@(starboard_platform_dependent_sources)',
+ '<@(starboard_platform_dependent_files)',
],
'defines': [
# This must be defined when building Starboard, and must not when
diff --git a/src/starboard/win/shared/system_get_path.cc b/src/starboard/win/shared/system_get_path.cc
index 21350df2..9165e0a 100644
--- a/src/starboard/win/shared/system_get_path.cc
+++ b/src/starboard/win/shared/system_get_path.cc
@@ -35,7 +35,7 @@
// status. The result being greater than |path_size| - 1 characters is a
// failure. |out_path| may be written to in unsuccessful cases.
bool GetExecutablePath(char* out_path, int path_size) {
- if (!out_path || !path_size) {
+ if (!out_path || (path_size <= 0)) {
return false;
}
@@ -59,7 +59,7 @@
// |path_size| - 1 characters is a failure. |out_path| may be written to in
// unsuccessful cases.
bool GetExecutableDirectory(char* out_path, int path_size) {
- if (!out_path || !path_size) {
+ if (!out_path || (path_size <= 0)) {
return false;
}
@@ -83,7 +83,7 @@
// status. The result being greater than |path_size| - 1 characters is a
// failure. |out_path| may be written to in unsuccessful cases.
bool GetContentPath(char* out_path, int path_size) {
- if (!out_path || !path_size) {
+ if (!out_path || (path_size <= 0)) {
return false;
}
char file_path[SB_FILE_MAX_PATH];
@@ -99,13 +99,14 @@
}
bool CreateAndGetTempPath(char* out_path, int path_size) {
- if (!out_path || !path_size) {
+ if (!out_path || (path_size <= 0)) {
return false;
}
wchar_t w_file_path[SB_FILE_MAX_PATH];
w_file_path[0] = L'\0';
- DWORD characters_written = GetTempPathW(SB_FILE_MAX_PATH, w_file_path);
+ int64_t characters_written =
+ static_cast<int>(GetTempPathW(SB_FILE_MAX_PATH, w_file_path));
if (characters_written >= (path_size + 1) || characters_written < 1) {
return false;
}
@@ -130,7 +131,7 @@
// Note: This function is only minimally implemented to allow tests to run.
bool SbSystemGetPath(SbSystemPathId path_id, char* out_path, int path_size) {
- if (!out_path || !path_size) {
+ if (!out_path || (path_size <= 0)) {
return false;
}
diff --git a/src/testing/gtest.gyp b/src/testing/gtest.gyp
index 2b125f1..888ce5f 100644
--- a/src/testing/gtest.gyp
+++ b/src/testing/gtest.gyp
@@ -50,6 +50,20 @@
'dependencies': [
'gtest_prod',
],
+ 'cflags_cc': [
+ '-frtti',
+ ],
+ 'cflags_cc!': [
+ '-fno-rtti',
+ ],
+ 'all_dependent_settings': {
+ 'cflags_cc': [
+ '-frtti',
+ ],
+ 'cflags_cc!': [
+ '-fno-rtti',
+ ],
+ },
'conditions': [
['OS == "mac" or OS == "ios"', {
'sources': [
diff --git a/src/third_party/angle/src/libANGLE/Caps.cpp b/src/third_party/angle/src/libANGLE/Caps.cpp
index df7947c..0b97fb5 100644
--- a/src/third_party/angle/src/libANGLE/Caps.cpp
+++ b/src/third_party/angle/src/libANGLE/Caps.cpp
@@ -569,7 +569,11 @@
textureHalfFloatLinear = DetermineHalfFloatTextureFilteringSupport(textureCaps);
textureFloat = DetermineFloatTextureSupport(textureCaps);
textureFloatLinear = DetermineFloatTextureFilteringSupport(textureCaps);
+#if defined(OS_STARBOARD)
+ textureRG = false;
+#else
textureRG = DetermineRGTextureSupport(textureCaps, textureHalfFloat, textureFloat);
+#endif // OS_STARBOARD
textureCompressionDXT1 = DetermineDXT1TextureSupport(textureCaps);
textureCompressionDXT3 = DetermineDXT3TextureSupport(textureCaps);
textureCompressionDXT5 = DetermineDXT5TextureSupport(textureCaps);
diff --git a/src/third_party/blink/Source/bindings/scripts/utilities.py b/src/third_party/blink/Source/bindings/scripts/utilities.py
index 4d43f6f..02a1c6c 100644
--- a/src/third_party/blink/Source/bindings/scripts/utilities.py
+++ b/src/third_party/blink/Source/bindings/scripts/utilities.py
@@ -27,6 +27,7 @@
'fetch',
'h5vcc',
'media_session',
+ 'page_visibility',
'speech',
'testing',
'web_animations',
diff --git a/src/third_party/libxml/src/trionan.c b/src/third_party/libxml/src/trionan.c
index 6fbabb5..d922fd2 100644
--- a/src/third_party/libxml/src/trionan.c
+++ b/src/third_party/libxml/src/trionan.c
@@ -44,7 +44,11 @@
#include "trionan.h"
#include <math.h>
+#if !defined(STARBOARD)
+// Avoid "cannot apply asm label to function after its first use" error on
+// memcpy when building with Android NDK.
#include <string.h>
+#endif
#include <limits.h>
#include <float.h>
#if defined(TRIO_PLATFORM_UNIX)
diff --git a/src/third_party/mozjs-45/cobalt_config/include/jscustomallocator.h b/src/third_party/mozjs-45/cobalt_config/include/jscustomallocator.h
index b1c9919..284fa90 100644
--- a/src/third_party/mozjs-45/cobalt_config/include/jscustomallocator.h
+++ b/src/third_party/mozjs-45/cobalt_config/include/jscustomallocator.h
@@ -17,6 +17,7 @@
#include <algorithm>
+#include "memory_allocator_reporter.h"
#include "starboard/memory.h"
#include "starboard/string.h"
@@ -136,7 +137,11 @@
static inline void* js_malloc(size_t bytes)
{
- return SbMemoryAllocate(bytes);
+ size_t reservation_bytes = AllocationMetadata::GetReservationBytes(bytes);
+ MemoryAllocatorReporter::Get()->UpdateAllocatedBytes(reservation_bytes);
+ void* metadata = SbMemoryAllocate(reservation_bytes);
+ AllocationMetadata::SetSizeToBaseAddress(metadata, reservation_bytes);
+ return AllocationMetadata::GetUserAddressFromBaseAddress(metadata);
}
static inline void* js_calloc(size_t nmemb, size_t size)
@@ -156,7 +161,17 @@
static inline void* js_realloc(void* p, size_t bytes)
{
- return SbMemoryReallocate(p, bytes);
+ AllocationMetadata* metadata =
+ AllocationMetadata::GetMetadataFromUserAddress(p);
+ size_t current_size =
+ AllocationMetadata::GetSizeOfAllocationFromMetadata(metadata);
+ size_t adjusted_size = AllocationMetadata::GetReservationBytes(bytes);
+
+ MemoryAllocatorReporter::Get()->UpdateAllocatedBytes(
+ static_cast<ssize_t>(adjusted_size - current_size));
+ void* new_ptr = SbMemoryReallocate(metadata, adjusted_size);
+ AllocationMetadata::SetSizeToBaseAddress(new_ptr, adjusted_size);
+ return AllocationMetadata::GetUserAddressFromBaseAddress(new_ptr);
}
static inline void js_free(void* p)
@@ -164,7 +179,11 @@
if (p == NULL) {
return;
}
- SbMemoryDeallocate(p);
+ AllocationMetadata* metadata =
+ AllocationMetadata::GetMetadataFromUserAddress(p);
+ MemoryAllocatorReporter::Get()->UpdateAllocatedBytes(-static_cast<ssize_t>(
+ AllocationMetadata::GetSizeOfAllocationFromMetadata(metadata)));
+ SbMemoryDeallocate(metadata);
}
static inline char* js_strdup(const char* s)
diff --git a/src/third_party/mozjs-45/js/public/Conversions.h b/src/third_party/mozjs-45/js/public/Conversions.h
index c08da01..3ed04ed 100644
--- a/src/third_party/mozjs-45/js/public/Conversions.h
+++ b/src/third_party/mozjs-45/js/public/Conversions.h
@@ -385,7 +385,8 @@
{
// clang crashes compiling this when targeting arm-darwin:
// https://llvm.org/bugs/show_bug.cgi?id=22974
-#if defined (__arm__) && defined (__GNUC__) && !defined(__APPLE__)
+ // Cobalt update: More than just arm-darwin, just don't bother with this on clang.
+#if defined (__arm__) && defined (__GNUC__) && !defined(__APPLE__) && !defined(__clang__)
int32_t i;
uint32_t tmp0;
uint32_t tmp1;
diff --git a/src/third_party/mozjs-45/js/public/TraceKind.h b/src/third_party/mozjs-45/js/public/TraceKind.h
index c7e1a40..d527646 100644
--- a/src/third_party/mozjs-45/js/public/TraceKind.h
+++ b/src/third_party/mozjs-45/js/public/TraceKind.h
@@ -100,7 +100,9 @@
// the other hand, gets very confused if we have a |template| token there.
// The clang-cl front end defines _MSC_VER, but still requires the explicit
// template declaration, so we must test for __clang__ here as well.
-#if defined(_MSC_VER) && !defined(__clang__)
+#if defined(STARBOARD)
+# define JS_DEPENDENT_TEMPLATE_HINT template
+#elif defined(_MSC_VER) && !defined(__clang__)
# define JS_DEPENDENT_TEMPLATE_HINT
#else
# define JS_DEPENDENT_TEMPLATE_HINT template
diff --git a/src/third_party/mozjs-45/js/public/Utility.h b/src/third_party/mozjs-45/js/public/Utility.h
index be4c600..5aaf48e 100644
--- a/src/third_party/mozjs-45/js/public/Utility.h
+++ b/src/third_party/mozjs-45/js/public/Utility.h
@@ -24,6 +24,7 @@
#endif
#include "jstypes.h"
+#include "js-confdefs.h"
/* The public JS engine namespace. */
namespace JS {}
diff --git a/src/third_party/mozjs-45/js/public/Value.h b/src/third_party/mozjs-45/js/public/Value.h
index 20a441a..e208483 100644
--- a/src/third_party/mozjs-45/js/public/Value.h
+++ b/src/third_party/mozjs-45/js/public/Value.h
@@ -968,6 +968,7 @@
return JSVAL_IS_GCTHING_IMPL(l) && !JSVAL_IS_NULL_IMPL(l);
}
+// TODO : Fix Windows compilation problem with JSVAL_TO_IMPL.
static inline jsval_layout JSVAL_TO_IMPL(JS::Value v);
static inline JS_VALUE_CONSTEXPR JS::Value IMPL_TO_JSVAL(jsval_layout l);
diff --git a/src/third_party/mozjs-45/js/src/gc/GCRuntime.h b/src/third_party/mozjs-45/js/src/gc/GCRuntime.h
index d7cf559..36d2d9d 100644
--- a/src/third_party/mozjs-45/js/src/gc/GCRuntime.h
+++ b/src/third_party/mozjs-45/js/src/gc/GCRuntime.h
@@ -8,6 +8,7 @@
#define gc_GCRuntime_h
#include "mozilla/Atomics.h"
+#include "mozilla/DebugOnly.h"
#include "jsfriendapi.h"
#include "jsgc.h"
diff --git a/src/third_party/mozjs-45/js/src/gc/Memory.cpp b/src/third_party/mozjs-45/js/src/gc/Memory.cpp
index 72bad54..988445a2 100644
--- a/src/third_party/mozjs-45/js/src/gc/Memory.cpp
+++ b/src/third_party/mozjs-45/js/src/gc/Memory.cpp
@@ -12,7 +12,9 @@
#include "js/HeapAPI.h"
#include "vm/Runtime.h"
-#if defined(XP_WIN)
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#elif defined(XP_WIN)
#include "jswin.h"
#include <psapi.h>
@@ -815,7 +817,9 @@
ProtectPages(void* p, size_t size)
{
MOZ_ASSERT(size % pageSize == 0);
-#if defined(XP_WIN)
+#if defined(STARBOARD)
+ SB_NOTIMPLEMENTED();
+#elif defined(XP_WIN)
DWORD oldProtect;
if (!VirtualProtect(p, size, PAGE_NOACCESS, &oldProtect))
MOZ_CRASH("VirtualProtect(PAGE_NOACCESS) failed");
@@ -830,7 +834,9 @@
UnprotectPages(void* p, size_t size)
{
MOZ_ASSERT(size % pageSize == 0);
-#if defined(XP_WIN)
+#if defined(STARBOARD)
+ SB_NOTIMPLEMENTED();
+#elif defined(XP_WIN)
DWORD oldProtect;
if (!VirtualProtect(p, size, PAGE_READWRITE, &oldProtect))
MOZ_CRASH("VirtualProtect(PAGE_READWRITE) failed");
diff --git a/src/third_party/mozjs-45/js/src/irregexp/RegExpEngine.cpp b/src/third_party/mozjs-45/js/src/irregexp/RegExpEngine.cpp
index ba30fc7..b84accb 100644
--- a/src/third_party/mozjs-45/js/src/irregexp/RegExpEngine.cpp
+++ b/src/third_party/mozjs-45/js/src/irregexp/RegExpEngine.cpp
@@ -1643,7 +1643,7 @@
static bool
IsNativeRegExpEnabled(JSContext* cx)
{
-#ifdef JS_CODEGEN_NONE
+#if defined(JS_CODEGEN_NONE) || defined(COBALT_DISABLE_JIT)
return false;
#else
return cx->runtime()->options().nativeRegExp();
diff --git a/src/third_party/mozjs-45/js/src/jit/BaselineJIT.h b/src/third_party/mozjs-45/js/src/jit/BaselineJIT.h
index 1b35e6e..ff177c8 100644
--- a/src/third_party/mozjs-45/js/src/jit/BaselineJIT.h
+++ b/src/third_party/mozjs-45/js/src/jit/BaselineJIT.h
@@ -497,7 +497,7 @@
inline bool
IsBaselineEnabled(JSContext* cx)
{
-#ifdef JS_CODEGEN_NONE
+#if defined(JS_CODEGEN_NONE) || defined(COBALT_DISABLE_JIT)
return false;
#else
return cx->runtime()->options().baseline();
diff --git a/src/third_party/mozjs-45/js/src/jit/ExecutableAllocatorPosix.cpp b/src/third_party/mozjs-45/js/src/jit/ExecutableAllocatorPosix.cpp
index d6d7911..0e99f11 100644
--- a/src/third_party/mozjs-45/js/src/jit/ExecutableAllocatorPosix.cpp
+++ b/src/third_party/mozjs-45/js/src/jit/ExecutableAllocatorPosix.cpp
@@ -37,12 +37,14 @@
using namespace js::jit;
+// TODO: Starboardize.
size_t
ExecutableAllocator::determinePageSize()
{
return getpagesize();
}
+// TODO: Starboardize. PROT_READ, etc. flags are not defined in Starboard.
void*
js::jit::AllocateExecutableMemory(void* addr, size_t bytes, unsigned permissions, const char* tag,
size_t pageSize)
@@ -52,6 +54,7 @@
return p == MAP_FAILED ? nullptr : p;
}
+// TODO: Starboardize.
void
js::jit::DeallocateExecutableMemory(void* addr, size_t bytes, size_t pageSize)
{
@@ -75,9 +78,11 @@
DeallocateExecutableMemory(alloc.pages, alloc.size, pageSize);
}
+// TODO: Starboardize. PROT_READ, etc. flags are not defined in Starboard.
static const unsigned FLAGS_RW = PROT_READ | PROT_WRITE;
static const unsigned FLAGS_RX = PROT_READ | PROT_EXEC;
+// TODO: Starboardize.
void
ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting)
{
@@ -98,6 +103,7 @@
mprotect(pageStart, size, (setting == Writable) ? FLAGS_RW : FLAGS_RX);
}
+// TODO: Starboardize.
/* static */ unsigned
ExecutableAllocator::initialProtectionFlags(ProtectionSetting protection)
{
diff --git a/src/third_party/mozjs-45/js/src/jit/Ion.h b/src/third_party/mozjs-45/js/src/jit/Ion.h
index 8a0b65e..a7f21db 100644
--- a/src/third_party/mozjs-45/js/src/jit/Ion.h
+++ b/src/third_party/mozjs-45/js/src/jit/Ion.h
@@ -155,7 +155,7 @@
IsIonEnabled(JSContext* cx)
{
// The ARM64 Ion engine is not yet implemented.
-#if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
+#if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64) || defined(COBALT_DISABLE_JIT)
return false;
#else
return cx->runtime()->options().ion() &&
diff --git a/src/third_party/mozjs-45/js/src/jit/arm64/vixl/Debugger-vixl.h b/src/third_party/mozjs-45/js/src/jit/arm64/vixl/Debugger-vixl.h
index 86c5a91..cf5e116 100644
--- a/src/third_party/mozjs-45/js/src/jit/arm64/vixl/Debugger-vixl.h
+++ b/src/third_party/mozjs-45/js/src/jit/arm64/vixl/Debugger-vixl.h
@@ -32,8 +32,11 @@
#include <limits.h>
#include "jit/arm64/vixl/Constants-vixl.h"
+#include "jit/arm64/vixl/Decoder-vixl.h"
+#include "jit/arm64/vixl/Disasm-vixl.h"
#include "jit/arm64/vixl/Globals-vixl.h"
#include "jit/arm64/vixl/Simulator-vixl.h"
+#include "jit/arm64/vixl/Simulator-vixl.h"
#include "jit/arm64/vixl/Utils-vixl.h"
namespace vixl {
diff --git a/src/third_party/mozjs-45/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp b/src/third_party/mozjs-45/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp
index f2b7156..952b7c2 100644
--- a/src/third_party/mozjs-45/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp
+++ b/src/third_party/mozjs-45/js/src/jit/arm64/vixl/MozAssembler-vixl.cpp
@@ -425,6 +425,8 @@
Instr dp_op = static_cast<Instr>(op | LogicalShiftedFixed);
return DataProcShiftedRegister(rd, rn, operand, LeaveFlags, dp_op);
}
+
+ return BufferOffset();
}
diff --git a/src/third_party/mozjs-45/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp b/src/third_party/mozjs-45/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp
index fcae1ba..a4fc9dc 100644
--- a/src/third_party/mozjs-45/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp
+++ b/src/third_party/mozjs-45/js/src/jit/arm64/vixl/MozInstructions-vixl.cpp
@@ -115,6 +115,8 @@
default:
VIXL_UNREACHABLE();
}
+
+ return false;
}
diff --git a/src/third_party/mozjs-45/js/src/jsapi.cpp b/src/third_party/mozjs-45/js/src/jsapi.cpp
index adbee56..b9f2ad1 100644
--- a/src/third_party/mozjs-45/js/src/jsapi.cpp
+++ b/src/third_party/mozjs-45/js/src/jsapi.cpp
@@ -3778,6 +3778,7 @@
typedef Vector<char, 8, TempAllocPolicy> FileContents;
+// TODO: Starboardize this similar to mozjs 24.
static bool
ReadCompleteFile(JSContext* cx, FILE* fp, FileContents& buffer)
{
diff --git a/src/third_party/mozjs-45/js/src/jsapi.h b/src/third_party/mozjs-45/js/src/jsapi.h
index 2940624..f506eed 100644
--- a/src/third_party/mozjs-45/js/src/jsapi.h
+++ b/src/third_party/mozjs-45/js/src/jsapi.h
@@ -1085,11 +1085,22 @@
class JS_PUBLIC_API(RuntimeOptions) {
public:
RuntimeOptions()
- : baseline_(true),
+ :
+#if defined(COBALT_DISABLE_JIT)
+ baseline_(false),
+ ion_(false),
+ asmJS_(false),
+#else
+ baseline_(true),
ion_(true),
asmJS_(true),
+#endif
throwOnAsmJSValidationFailure_(false),
+#if defined(COBALT_DISABLE_JIT)
+ nativeRegExp_(false),
+#else
nativeRegExp_(true),
+#endif
unboxedArrays_(false),
asyncStack_(true),
werror_(false),
diff --git a/src/third_party/mozjs-45/js/src/jsgc.cpp b/src/third_party/mozjs-45/js/src/jsgc.cpp
index 23bd1a9..acc2db5 100644
--- a/src/third_party/mozjs-45/js/src/jsgc.cpp
+++ b/src/third_party/mozjs-45/js/src/jsgc.cpp
@@ -188,7 +188,8 @@
#include <ctype.h>
#include <string.h>
-#ifndef XP_WIN
+#if defined(STARBOARD)
+#elif !defined(XP_WIN)
# include <sys/mman.h>
# include <unistd.h>
#endif
diff --git a/src/third_party/mozjs-45/js/src/jsutil.h b/src/third_party/mozjs-45/js/src/jsutil.h
index 9a9dea4..401ca64 100644
--- a/src/third_party/mozjs-45/js/src/jsutil.h
+++ b/src/third_party/mozjs-45/js/src/jsutil.h
@@ -21,6 +21,8 @@
#include "js/Utility.h"
#include "js/Value.h"
+#include "js-confdefs.h"
+
#define JS_ALWAYS_TRUE(expr) MOZ_ALWAYS_TRUE(expr)
#define JS_ALWAYS_FALSE(expr) MOZ_ALWAYS_FALSE(expr)
diff --git a/src/third_party/mozjs-45/js/src/memory_allocator_reporter.cpp b/src/third_party/mozjs-45/js/src/memory_allocator_reporter.cpp
new file mode 100644
index 0000000..a7d8efa
--- /dev/null
+++ b/src/third_party/mozjs-45/js/src/memory_allocator_reporter.cpp
@@ -0,0 +1,87 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "memory_allocator_reporter.h"
+
+#include "starboard/once.h"
+
+namespace {
+// Control to initialize s_instance.
+SbOnceControl s_instance_control = SB_ONCE_INITIALIZER;
+MemoryAllocatorReporter* s_instance = NULL;
+
+void Initialize() {
+ s_instance = new MemoryAllocatorReporter();
+}
+
+void* OffsetPointer(void* base, int64_t offset) {
+ uintptr_t base_as_int = reinterpret_cast<uintptr_t>(base);
+#if defined(STARBOARD)
+ return reinterpret_cast<void*>(base_as_int + static_cast<uintptr_t>(offset));
+#else
+ return reinterpret_cast<void*>(base_as_int + offset);
+#endif
+}
+} // namespace
+
+AllocationMetadata* AllocationMetadata::GetMetadataFromUserAddress(void* ptr) {
+ if (ptr == NULL) {
+ return NULL;
+ }
+
+ // The metadata lives just in front of the data.
+ void* meta_addr =
+ OffsetPointer(ptr, -static_cast<int64_t>(sizeof(AllocationMetadata)));
+ return reinterpret_cast<AllocationMetadata*>(meta_addr);
+}
+
+void* AllocationMetadata::GetUserAddressFromBaseAddress(void* base_ptr) {
+ void* adjusted_base =
+ OffsetPointer(base_ptr, static_cast<int64_t>(sizeof(AllocationMetadata)));
+ return adjusted_base;
+}
+
+void AllocationMetadata::SetSizeToBaseAddress(void* base_ptr, int64_t size) {
+ if (base_ptr) {
+ AllocationMetadata* metadata =
+ reinterpret_cast<AllocationMetadata*>(base_ptr);
+ metadata->set_size_requested(size);
+ }
+}
+
+void MemoryAllocatorReporter::UpdateAllocatedBytes(int64_t bytes) {
+ starboard::ScopedLock lock(mutex_);
+ current_bytes_allocated_ += bytes;
+}
+
+int64_t MemoryAllocatorReporter::GetCurrentBytesAllocated() {
+ starboard::ScopedLock lock(mutex_);
+ return current_bytes_allocated_;
+}
+
+void MemoryAllocatorReporter::UpdateMappedBytes(int64_t bytes) {
+ starboard::ScopedLock lock(mutex_);
+ current_bytes_mapped_ += bytes;
+}
+
+int64_t MemoryAllocatorReporter::GetCurrentBytesMapped() {
+ starboard::ScopedLock lock(mutex_);
+ return current_bytes_mapped_;
+}
+
+// static
+MemoryAllocatorReporter* MemoryAllocatorReporter::Get() {
+ SbOnce(&s_instance_control, &Initialize);
+ return s_instance;
+}
diff --git a/src/third_party/mozjs-45/js/src/memory_allocator_reporter.h b/src/third_party/mozjs-45/js/src/memory_allocator_reporter.h
new file mode 100644
index 0000000..82227e3
--- /dev/null
+++ b/src/third_party/mozjs-45/js/src/memory_allocator_reporter.h
@@ -0,0 +1,61 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef MemoryAllocatorReporter_h
+#define MemoryAllocatorReporter_h
+
+#include "starboard/mutex.h"
+#include "starboard/types.h"
+
+class AllocationMetadata {
+ public:
+ static AllocationMetadata* GetMetadataFromUserAddress(void* ptr);
+ static void* GetUserAddressFromBaseAddress(void* base_ptr);
+ static int64_t GetReservationBytes(int64_t bytes_requested) {
+ return sizeof(AllocationMetadata) + bytes_requested;
+ }
+ static int64_t GetSizeOfAllocationFromMetadata(AllocationMetadata* metadata) {
+ return metadata ? metadata->size_requested() : 0;
+ }
+ static void SetSizeToBaseAddress(void* base_ptr, int64_t size);
+
+ int64_t size_requested() { return size_requested_; }
+ void set_size_requested(int64_t size) { size_requested_ = size; }
+
+ private:
+ // Bytes requested by the underlying allocator.
+ int64_t size_requested_;
+};
+
+// Reporter that is used to report memory allocation.
+class MemoryAllocatorReporter {
+ public:
+ static MemoryAllocatorReporter* Get();
+
+ MemoryAllocatorReporter()
+ : current_bytes_allocated_(0), current_bytes_mapped_(0) {}
+
+ void UpdateAllocatedBytes(int64_t bytes);
+ int64_t GetCurrentBytesAllocated();
+
+ void UpdateMappedBytes(int64_t bytes);
+ int64_t GetCurrentBytesMapped();
+
+ private:
+ starboard::Mutex mutex_;
+ int64_t current_bytes_allocated_;
+ int64_t current_bytes_mapped_;
+};
+
+#endif /* MemoryAllocatorReporter_h */
diff --git a/src/third_party/mozjs-45/js/src/shell/OSObject.cpp b/src/third_party/mozjs-45/js/src/shell/OSObject.cpp
index 6b04f3d..eba8a16 100644
--- a/src/third_party/mozjs-45/js/src/shell/OSObject.cpp
+++ b/src/third_party/mozjs-45/js/src/shell/OSObject.cpp
@@ -37,7 +37,8 @@
#include "jsobjinlines.h"
-#ifdef XP_WIN
+#if defined(STARBOARD)
+#elif defined(XP_WIN)
# define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR)
# define getcwd _getcwd
#else
@@ -521,7 +522,10 @@
{
char buffer[200];
-#if defined(XP_WIN)
+#if defined(STARBOARD)
+ SbSystemGetErrorString(errno, buffer, sizeof(buffer));
+ const char* errstr = buffer;
+#elif defined(XP_WIN)
strerror_s(buffer, sizeof(buffer), errno);
const char* errstr = buffer;
#else
diff --git a/src/third_party/mozjs-45/js/src/vm/ArrayBufferObject.cpp b/src/third_party/mozjs-45/js/src/vm/ArrayBufferObject.cpp
index 5cb626c..dbb67a2 100644
--- a/src/third_party/mozjs-45/js/src/vm/ArrayBufferObject.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/ArrayBufferObject.cpp
@@ -12,7 +12,8 @@
#include "mozilla/TaggedAnonymousMemory.h"
#include <string.h>
-#ifndef XP_WIN
+#if defined(STARBOARD)
+#elif !defined(XP_WIN)
# include <sys/mman.h>
#endif
diff --git a/src/third_party/mozjs-45/js/src/vm/CodeCoverage.cpp b/src/third_party/mozjs-45/js/src/vm/CodeCoverage.cpp
index 4b39d67..e63bb44 100644
--- a/src/third_party/mozjs-45/js/src/vm/CodeCoverage.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/CodeCoverage.cpp
@@ -10,7 +10,9 @@
#include "mozilla/IntegerPrintfMacros.h"
#include <stdio.h>
-#if defined(XP_WIN)
+#if defined(STARBOARD)
+#include "starboard/thread.h"
+#elif defined(XP_WIN)
# include <windows.h>
#else
# include <unistd.h>
@@ -509,7 +511,9 @@
LCovRuntime::LCovRuntime()
: out_(),
-#if defined(XP_WIN)
+#if defined(STARBOARD)
+ pid_(SbThreadGetId()),
+#elif defined(XP_WIN)
pid_(GetCurrentProcessId()),
#else
pid_(getpid()),
@@ -578,7 +582,9 @@
if (!out_.isInitialized())
return;
-#if defined(XP_WIN)
+#if defined(STARBOARD)
+ size_t p = SbThreadGetId();
+#elif defined(XP_WIN)
size_t p = GetCurrentProcessId();
#else
size_t p = getpid();
diff --git a/src/third_party/mozjs-45/js/src/vm/Compression.h b/src/third_party/mozjs-45/js/src/vm/Compression.h
index c2db628..2ebbff9 100644
--- a/src/third_party/mozjs-45/js/src/vm/Compression.h
+++ b/src/third_party/mozjs-45/js/src/vm/Compression.h
@@ -7,7 +7,11 @@
#ifndef vm_Compression_h
#define vm_Compression_h
+#if defined(STARBOARD)
+#include "third_party/zlib/zlib.h"
+#else
#include <zlib.h>
+#endif
#include "jstypes.h"
diff --git a/src/third_party/mozjs-45/js/src/vm/Printer.cpp b/src/third_party/mozjs-45/js/src/vm/Printer.cpp
index 8f7fc86..a4b126c 100644
--- a/src/third_party/mozjs-45/js/src/vm/Printer.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/Printer.cpp
@@ -13,6 +13,7 @@
#include "jscntxt.h"
#include "jsprf.h"
#include "jsutil.h"
+#include "js-confdefs.h"
#include "ds/LifoAlloc.h"
diff --git a/src/third_party/mozjs-45/js/src/vm/Printer.h b/src/third_party/mozjs-45/js/src/vm/Printer.h
index 089f079..b01e342 100644
--- a/src/third_party/mozjs-45/js/src/vm/Printer.h
+++ b/src/third_party/mozjs-45/js/src/vm/Printer.h
@@ -11,6 +11,8 @@
#include <stddef.h>
#include <stdio.h>
+#include "js-confdefs.h"
+
class JSString;
namespace js {
diff --git a/src/third_party/mozjs-45/js/src/vm/Runtime.cpp b/src/third_party/mozjs-45/js/src/vm/Runtime.cpp
index 7df7dee..890da16 100644
--- a/src/third_party/mozjs-45/js/src/vm/Runtime.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/Runtime.cpp
@@ -24,6 +24,7 @@
#include <locale.h>
#include <string.h>
+// TODO: Starboardize.
#ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES
# include <sys/mman.h>
#endif
@@ -259,6 +260,7 @@
return !!js_sb_getenv("JS_DISABLE_SLOW_SCRIPT_SIGNALS") || !!js_sb_getenv("JS_NO_SIGNALS");
}
+// TODO: Starboardize.
bool
JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
{
diff --git a/src/third_party/mozjs-45/js/src/vm/SharedArrayObject.cpp b/src/third_party/mozjs-45/js/src/vm/SharedArrayObject.cpp
index 7f8c797..fe82fb8 100644
--- a/src/third_party/mozjs-45/js/src/vm/SharedArrayObject.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/SharedArrayObject.cpp
@@ -15,7 +15,10 @@
# include "jswin.h"
#endif
#include "jswrapper.h"
-#ifndef XP_WIN
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#elif !defined(XP_WIN)
# include <sys/mman.h>
#endif
#ifdef MOZ_VALGRIND
@@ -33,7 +36,12 @@
static inline void*
MapMemory(size_t length, bool commit)
{
-#ifdef XP_WIN
+#if defined(STARBOARD)
+ if (!commit) {
+ SB_NOTREACHED();
+ }
+ return SbMemoryMap(length, kSbMemoryMapProtectReadWrite, NULL);
+#elif defined(XP_WIN)
int prot = (commit ? MEM_COMMIT : MEM_RESERVE);
int flags = (commit ? PAGE_READWRITE : PAGE_NOACCESS);
return VirtualAlloc(nullptr, length, prot, flags);
@@ -49,7 +57,9 @@
static inline void
UnmapMemory(void* addr, size_t len)
{
-#ifdef XP_WIN
+#if defined(STARBOARD)
+ SbMemoryUnmap(addr, len);
+#elif defined(XP_WIN)
VirtualFree(addr, 0, MEM_RELEASE);
#else
munmap(addr, len);
@@ -59,7 +69,10 @@
static inline bool
MarkValidRegion(void* addr, size_t len)
{
-#ifdef XP_WIN
+#if defined(STARBOARD)
+ SB_NOTIMPLEMENTED();
+ return false;
+#elif defined(XP_WIN)
if (!VirtualAlloc(addr, len, MEM_COMMIT, PAGE_READWRITE))
return false;
return true;
diff --git a/src/third_party/mozjs-45/js/src/vm/String.cpp b/src/third_party/mozjs-45/js/src/vm/String.cpp
index 3771837..c2adbf6 100644
--- a/src/third_party/mozjs-45/js/src/vm/String.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/String.cpp
@@ -18,6 +18,7 @@
#include "jscntxtinlines.h"
#include "jscompartmentinlines.h"
+#include "js-confdefs.h"
// Unified leak fix:
#include "builtin/ModuleObject.h"
diff --git a/src/third_party/mozjs-45/js/src/vm/TypedArrayObject.cpp b/src/third_party/mozjs-45/js/src/vm/TypedArrayObject.cpp
index 16081fe..13f2ac9 100644
--- a/src/third_party/mozjs-45/js/src/vm/TypedArrayObject.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/TypedArrayObject.cpp
@@ -11,7 +11,8 @@
#include "mozilla/PodOperations.h"
#include <string.h>
-#ifndef XP_WIN
+#if defined(STARBOARD)
+#elif !defined(XP_WIN)
# include <sys/mman.h>
#endif
diff --git a/src/third_party/mozjs-45/js/src/vm/UnboxedObject.cpp b/src/third_party/mozjs-45/js/src/vm/UnboxedObject.cpp
index f661dde..14e0413 100644
--- a/src/third_party/mozjs-45/js/src/vm/UnboxedObject.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/UnboxedObject.cpp
@@ -695,7 +695,7 @@
return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind);
}
-#ifndef JS_CODEGEN_NONE
+#if !defined(JS_CODEGEN_NONE) && !defined(COBALT_DISABLE_JIT)
if (cx->isJSContext() &&
!layout.constructorCode() &&
cx->asJSContext()->runtime()->jitSupportsFloatingPoint)
diff --git a/src/third_party/mozjs-45/mfbt/DebugOnly.h b/src/third_party/mozjs-45/mfbt/DebugOnly.h
index 451d9bc..179b773 100644
--- a/src/third_party/mozjs-45/mfbt/DebugOnly.h
+++ b/src/third_party/mozjs-45/mfbt/DebugOnly.h
@@ -14,6 +14,8 @@
#include "mozilla/Attributes.h"
+#include "js-confdefs.h"
+
namespace mozilla {
/**
diff --git a/src/third_party/mozjs-45/mfbt/TaggedAnonymousMemory.cpp b/src/third_party/mozjs-45/mfbt/TaggedAnonymousMemory.cpp
index a2ba9ee..f696028 100644
--- a/src/third_party/mozjs-45/mfbt/TaggedAnonymousMemory.cpp
+++ b/src/third_party/mozjs-45/mfbt/TaggedAnonymousMemory.cpp
@@ -28,6 +28,7 @@
namespace mozilla {
+// TODO: Starboardize.
// Returns 0 for success and -1 (with errno) for error.
static int
TagAnonymousMemoryAligned(const void* aPtr, size_t aLength, const char* aTag)
@@ -39,6 +40,7 @@
reinterpret_cast<unsigned long>(aTag));
}
+// TODO: Starboardize.
// On some architectures, it's possible for the page size to be larger
// than the PAGE_SIZE we were compiled with. This computes the
// equivalent of PAGE_MASK.
@@ -89,6 +91,7 @@
}
}
+// TODO: Starboardize.
void*
MozTaggedAnonymousMmap(void* aAddr, size_t aLength, int aProt, int aFlags,
int aFd, off_t aOffset, const char* aTag)
diff --git a/src/third_party/mozjs-45/mfbt/TaggedAnonymousMemory.h b/src/third_party/mozjs-45/mfbt/TaggedAnonymousMemory.h
index d26b06d..e62335e 100644
--- a/src/third_party/mozjs-45/mfbt/TaggedAnonymousMemory.h
+++ b/src/third_party/mozjs-45/mfbt/TaggedAnonymousMemory.h
@@ -34,6 +34,7 @@
#ifndef XP_WIN
+// TODO: Starboardize
#include <sys/types.h>
#include <sys/mman.h>
@@ -66,6 +67,7 @@
{
}
+// TODO: Starboardize.
static inline void*
MozTaggedAnonymousMmap(void* aAddr, size_t aLength, int aProt, int aFlags,
int aFd, off_t aOffset, const char* aTag)
diff --git a/src/third_party/mozjs-45/mfbt/Types.h b/src/third_party/mozjs-45/mfbt/Types.h
index a5d9363..c5bfdc6 100644
--- a/src/third_party/mozjs-45/mfbt/Types.h
+++ b/src/third_party/mozjs-45/mfbt/Types.h
@@ -37,7 +37,9 @@
* These macros are designed for use by library interfaces -- not for normal
* methods or data used cross-file.
*/
-#if defined(WIN32)
+#if defined(STARBOARD)
+#define MOZ_EXPORT /* nothing */
+#elif defined(WIN32)
# define MOZ_EXPORT __declspec(dllexport)
#else /* Unix */
# ifdef HAVE_VISIBILITY_ATTRIBUTE
@@ -57,7 +59,9 @@
* the export or import version of the macro, depending upon the compilation
* mode.
*/
-#ifdef _WIN32
+#if defined(STARBOARD)
+#define MOZ_IMPORT_API /* nothing */
+#elif defined(_WIN32)
# if defined(__MWERKS__)
# define MOZ_IMPORT_API /* nothing */
# else
diff --git a/src/third_party/mozjs-45/mozglue/misc/TimeStamp.cpp b/src/third_party/mozjs-45/mozglue/misc/TimeStamp.cpp
index 3ff4af4..b640936 100644
--- a/src/third_party/mozjs-45/mozglue/misc/TimeStamp.cpp
+++ b/src/third_party/mozjs-45/mozglue/misc/TimeStamp.cpp
@@ -12,6 +12,8 @@
#include <stdio.h>
#include <string.h>
+#include "js-confdefs.h"
+
namespace mozilla {
/**
@@ -46,7 +48,7 @@
static TimeStampInitialization sInitOnce;
-MFBT_API TimeStamp
+TimeStamp
TimeStamp::ProcessCreation(bool& aIsInconsistent)
{
aIsInconsistent = false;
diff --git a/src/third_party/mozjs-45/mozjs-45.gyp b/src/third_party/mozjs-45/mozjs-45.gyp
index 3b09dfc..3cd5233 100644
--- a/src/third_party/mozjs-45/mozjs-45.gyp
+++ b/src/third_party/mozjs-45/mozjs-45.gyp
@@ -40,85 +40,75 @@
'cobalt_config/include',
'<(DEPTH)/third_party/icu/source/common',
],
+ 'common_msvs_disabled_warnings': [
+ # Level 2, Possible loss of data due to type conversion.
+ 4244,
+ # Level 3, Possible loss of data due to type conversion from size_t.
+ 4267,
+ ],
'conditions': [
['target_arch == "x86"', {
'common_defines': [
'JS_CPU_X86=1',
+ 'JS_CODEGEN_X86=1',
'JS_NUNBOX32=1',
],
}],
['target_arch == "x64"', {
'common_defines': [
'JS_CPU_X64=1',
+ 'JS_CODEGEN_X64=1',
'JS_PUNBOX64=1',
],
}],
['target_arch == "arm"', {
'common_defines': [
'JS_CPU_ARM=1',
+ 'JS_CODEGEN_ARM=1',
'JS_NUNBOX32=1',
],
}],
['target_arch == "arm64"', {
'common_defines': [
'JS_CPU_ARM64=1',
+ 'JS_CODEGEN_ARM64=1',
'JS_PUNBOX64=1',
+ # arm64 jit appears to not be ready, won't even compile without
+ # compiling in the simulator. It is highly recommended that
+ # |cobalt_enable_jit| be set to |0| when building for architecture
+ # |arm64|.
+ 'JS_SIMULATOR=1',
+ 'JS_SIMULATOR_ARM64=1',
],
}],
['target_arch == "mips"', {
'common_defines': [
'JS_CPU_MIPS=1',
+ 'JS_CODEGEN_MIPS32=1',
'JS_NUNBOX32=1',
],
}],
['target_arch == "mips64"', {
'common_defines': [
'JS_CPU_MIPS=1',
- 'JS_PUNBOX64=1',
- ],
- }],
- # TODO: Remove once ps4 configuration todos are addressed.
- ['target_arch == "ps4" or actual_target_arch == "ps4" or sb_target_platform == "ps4"', {
- 'common_defines': [
- 'JS_CPU_X64=1',
+ 'JS_CODEGEN_MIPS64=1',
'JS_PUNBOX64=1',
],
}],
- ['cobalt_enable_jit == 0', {
+ ['cobalt_enable_jit != 1', {
'common_defines': [
- 'JS_CODEGEN_NONE=1',
+ 'COBALT_DISABLE_JIT=1',
],
}],
- ['target_arch == "x86" and cobalt_enable_jit == 1', {
+
+ # TODO: Remove once ps4 configuration todos are addressed.
+ ['target_arch == "ps4" or actual_target_arch == "ps4" or sb_target_platform == "ps4"', {
'common_defines': [
- 'JS_CODEGEN_X86=1',
- ],
- }],
- ['target_arch == "x64" and cobalt_enable_jit == 1', {
- 'common_defines': [
+ 'JS_CPU_X64=1',
'JS_CODEGEN_X64=1',
- ],
- }],
- ['target_arch == "arm" and cobalt_enable_jit == 1', {
- 'common_defines': [
- 'JS_CODEGEN_ARM=1',
- ],
- }],
- ['target_arch == "arm64" and cobalt_enable_jit == 1', {
- 'common_defines': [
- 'JS_CODEGEN_ARM64=1',
- ],
- }],
- ['target_arch == "mips" and cobalt_enable_jit == 1', {
- 'common_defines': [
- 'JS_CODEGEN_MIPS32=1',
- ],
- }],
- ['target_arch == "mips64" and cobalt_enable_jit == 1', {
- 'common_defines': [
- 'JS_CODEGEN_MIPS64=1',
+ 'JS_PUNBOX64=1',
],
}],
@@ -138,6 +128,12 @@
'target_defaults': {
'defines': [ '<@(common_defines)', ],
+ 'msvs_disabled_warnings': [ '<@(common_msvs_disabled_warnings)', ],
+
+ # Unfortunately, there is code that generate warnings in the headers.
+ 'direct_dependent_settings': {
+ 'msvs_disabled_warnings': [ '<@(common_msvs_disabled_warnings)', ],
+ },
},
'targets': [
@@ -165,7 +161,7 @@
'<@(mozjs-45_sources)',
],
'conditions': [
- ['target_arch == "x86" and cobalt_enable_jit == 1', {
+ ['target_arch == "x86"', {
'sources': [
'js/src/jit/x86-shared/Architecture-x86-shared.cpp',
'js/src/jit/x86-shared/Assembler-x86-shared.cpp',
@@ -188,7 +184,8 @@
'js/src/jit/x86/Trampoline-x86.cpp',
],
}],
- ['target_arch == "x64" and cobalt_enable_jit == 1', {
+ # TODO: Remove "* == ps4" once ps4 configuration todos are addressed.
+ ['target_arch == "x64" or target_arch == "ps4" or actual_target_arch == "ps4" or sb_target_platform == "ps4"', {
'sources': [
'js/src/jit/x64/Assembler-x64.cpp',
'js/src/jit/x64/Bailouts-x64.cpp',
@@ -211,7 +208,7 @@
'js/src/jit/x86-shared/MoveEmitter-x86-shared.cpp',
],
}],
- ['target_arch == "arm" and cobalt_enable_jit == 1', {
+ ['target_arch == "arm"', {
'sources': [
'js/src/jit/arm/Architecture-arm.cpp',
'js/src/jit/arm/Architecture-arm.h',
@@ -243,7 +240,7 @@
'js/src/jit/arm/Trampoline-arm.cpp',
],
}],
- ['target_arch == "arm64" and cobalt_enable_jit == 1', {
+ ['target_arch == "arm64"', {
'sources': [
'js/src/jit/arm64/Architecture-arm64.cpp',
'js/src/jit/arm64/Architecture-arm64.h',
@@ -270,9 +267,23 @@
'js/src/jit/arm64/SharedICHelpers-arm64.h',
'js/src/jit/arm64/SharedICRegisters-arm64.h',
'js/src/jit/arm64/Trampoline-arm64.cpp',
+ 'js/src/jit/arm64/vixl/Assembler-vixl.cpp',
+ 'js/src/jit/arm64/vixl/Cpu-vixl.cpp',
+ 'js/src/jit/arm64/vixl/Debugger-vixl.cpp',
+ 'js/src/jit/arm64/vixl/Decoder-vixl.cpp',
+ 'js/src/jit/arm64/vixl/Disasm-vixl.cpp',
+ 'js/src/jit/arm64/vixl/Instructions-vixl.cpp',
+ 'js/src/jit/arm64/vixl/Instrument-vixl.cpp',
+ 'js/src/jit/arm64/vixl/Logic-vixl.cpp',
+ 'js/src/jit/arm64/vixl/MacroAssembler-vixl.cpp',
+ 'js/src/jit/arm64/vixl/MozAssembler-vixl.cpp',
+ 'js/src/jit/arm64/vixl/MozInstructions-vixl.cpp',
+ 'js/src/jit/arm64/vixl/MozSimulator-vixl.cpp',
+ 'js/src/jit/arm64/vixl/Simulator-vixl.cpp',
+ 'js/src/jit/arm64/vixl/Utils-vixl.cpp',
],
}],
- ['target_arch == "mips" and cobalt_enable_jit == 1', {
+ ['target_arch == "mips"', {
'sources': [
'js/src/jit/mips-shared/Architecture-mips-shared.cpp',
'js/src/jit/mips-shared/Assembler-mips-shared.cpp',
@@ -296,7 +307,7 @@
'js/src/jit/mips32/Trampoline-mips32.cpp',
],
}],
- ['target_arch == "mips64" and cobalt_enable_jit == 1', {
+ ['target_arch == "mips64"', {
'sources': [
'js/src/jit/mips-shared/Architecture-mips-shared.cpp',
'js/src/jit/mips-shared/Assembler-mips-shared.cpp',
@@ -320,11 +331,6 @@
'js/src/jit/mips64/Trampoline-mips64.cpp',
],
}],
- ['cobalt_enable_jit == 0', {
- 'sources': [
- 'js/src/jit/none/Trampoline-none.cpp',
- ],
- }],
],
},
diff --git a/src/third_party/mozjs-45/mozjs-45.gypi b/src/third_party/mozjs-45/mozjs-45.gypi
index f3a9b1e..cd06c66 100644
--- a/src/third_party/mozjs-45/mozjs-45.gypi
+++ b/src/third_party/mozjs-45/mozjs-45.gypi
@@ -158,6 +158,7 @@
'js/src/jsutil.cpp',
'js/src/jswatchpoint.cpp',
'js/src/jsweakmap.cpp',
+ 'js/src/memory_allocator_reporter.cpp',
'js/src/perf/jsperf.cpp',
'js/src/perf/pm_stub.cpp',
'js/src/proxy/BaseProxyHandler.cpp',
diff --git a/src/third_party/mozjs/js/src/jsapi.cpp b/src/third_party/mozjs/js/src/jsapi.cpp
index f45fb04..033642a 100644
--- a/src/third_party/mozjs/js/src/jsapi.cpp
+++ b/src/third_party/mozjs/js/src/jsapi.cpp
@@ -692,6 +692,11 @@
suppressGC(0)
{}
+namespace {
+ // Some platforms require atleast a 16-byte alignment for jmp_buf.
+ const std::size_t kDesiredJmpBufAlignment = 16;
+}
+
JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
: mainThread(this),
interrupt(0),
@@ -850,6 +855,12 @@
decimalSeparator(0),
numGrouping(0),
#endif
+ // Note: alignas(16) is too big for some compilers, and thus a more
+ // verbose approach needed to be taken.
+ conservativeGC_memory(reinterpret_cast<js::ConservativeGCData*>(
+ SbMemoryAllocateAligned(kDesiredJmpBufAlignment,
+ sizeof(js::ConservativeGCData)))),
+ conservativeGC(*conservativeGC_memory),
mathCache_(NULL),
dtoaState(NULL),
activeCompilations(0),
@@ -964,6 +975,7 @@
JSRuntime::~JSRuntime()
{
+ SbMemoryDeallocateAligned(conservativeGC_memory);
#ifdef JS_THREADSAFE
clearOwnerThread();
diff --git a/src/third_party/mozjs/js/src/jscntxt.h b/src/third_party/mozjs/js/src/jscntxt.h
index 9339c07..2713d65 100644
--- a/src/third_party/mozjs/js/src/jscntxt.h
+++ b/src/third_party/mozjs/js/src/jscntxt.h
@@ -1331,7 +1331,8 @@
js::DateTimeInfo dateTimeInfo;
- js::ConservativeGCData conservativeGC;
+ js::ConservativeGCData* conservativeGC_memory;
+ js::ConservativeGCData& conservativeGC;
/* Pool of maps used during parse/emit. */
js::frontend::ParseMapPool parseMapPool;
diff --git a/src/third_party/mozjs/js/src/vm/NumericConversions.h b/src/third_party/mozjs/js/src/vm/NumericConversions.h
index 94cddae..a3edb4e 100644
--- a/src/third_party/mozjs/js/src/vm/NumericConversions.h
+++ b/src/third_party/mozjs/js/src/vm/NumericConversions.h
@@ -132,11 +132,10 @@
inline int32_t
ToInt32(double d)
{
-#if defined(__ANDROID__) && defined(__clang__) && \
- __clang_major__ == 3 && __clang_minor__ == 8
-#define ANDROID_CLANG38 1
+#if defined(__ANDROID__) && defined(__clang__)
+#define ANDROID_CLANG 1
#endif
-#if defined (__arm__) && defined (__GNUC__) && !defined(ANDROID_CLANG38)
+#if defined (__arm__) && defined (__GNUC__) && !defined(ANDROID_CLANG)
int32_t i;
uint32_t tmp0;
uint32_t tmp1;
@@ -260,7 +259,7 @@
#else
return detail::ToIntWidth<int32_t>(d);
#endif
-#undef ANDROID_CLANG38
+#undef ANDROID_CLANG
}
/* ES5 9.6 (specialized for doubles). */
diff --git a/src/third_party/mozjs/mozjs.gyp b/src/third_party/mozjs/mozjs.gyp
index 2959aad..0de26f2 100644
--- a/src/third_party/mozjs/mozjs.gyp
+++ b/src/third_party/mozjs/mozjs.gyp
@@ -39,6 +39,25 @@
# them.
'JS_THREADSAFE',
],
+ 'msvs_disabled_warnings': [
+ # Level 2, Typename first seen as 'type1', but then seen as 'type2'.
+ 4099,
+ # Level 2, Possible loss of data due to type conversion.
+ 4244,
+ # Level 3, Possible loss of data due to type conversion from size_t.
+ 4267,
+ ],
+ # Unfortunately, there is code that generate warnings in the headers.
+ 'direct_dependent_settings': {
+ 'msvs_disabled_warnings': [
+ # Level 2, Typename first seen as 'type1', but then seen as 'type2'.
+ 4099,
+ # Level 2, Possible loss of data due to type conversion.
+ 4244,
+ # Level 3, Possible loss of data due to type conversion from size_t.
+ 4267,
+ ],
+ },
'include_dirs': [
'cobalt_config/include',
'js/src',
diff --git a/src/tools/gyp/pylib/gyp/generator/ninja.py b/src/tools/gyp/pylib/gyp/generator/ninja.py
index 6294478..cd5a218 100755
--- a/src/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/src/tools/gyp/pylib/gyp/generator/ninja.py
@@ -12,9 +12,19 @@
import signal
import subprocess
import sys
+
+_COBALT_SRC = os.path.abspath(os.path.join(*([__file__] + 6 * [os.pardir])))
+sys.path.append(os.path.join(_COBALT_SRC, 'cobalt', 'build', 'config'))
+from base import LoadPlatformConfig
+
import gyp
import gyp.common
-import gyp.msvs_emulation
+
+# TODO: These should be replaced with calls to the abstract tool chain, when it
+# is implemented on all supported platforms.
+from gyp.msvs_emulation import EncodeRspFileList
+from gyp.msvs_emulation import GenerateEnvironmentFiles
+from gyp.msvs_emulation import MsvsSettings
import gyp.MSVSUtil as MSVSUtil
import gyp.xcode_emulation
@@ -25,36 +35,36 @@
import cygpath
generator_default_variables = {
- 'EXECUTABLE_PREFIX': '',
- 'EXECUTABLE_SUFFIX': '',
- 'STATIC_LIB_PREFIX': 'lib',
- 'STATIC_LIB_SUFFIX': '.a',
- 'SHARED_LIB_PREFIX': 'lib',
+ 'EXECUTABLE_PREFIX': '',
+ 'EXECUTABLE_SUFFIX': '',
+ 'STATIC_LIB_PREFIX': 'lib',
+ 'STATIC_LIB_SUFFIX': '.a',
+ 'SHARED_LIB_PREFIX': 'lib',
- # Gyp expects the following variables to be expandable by the build
- # system to the appropriate locations. Ninja prefers paths to be
- # known at gyp time. To resolve this, introduce special
- # variables starting with $! and $| (which begin with a $ so gyp knows it
- # should be treated specially, but is otherwise an invalid
- # ninja/shell variable) that are passed to gyp here but expanded
- # before writing out into the target .ninja files; see
- # ExpandSpecial.
- # $! is used for variables that represent a path and that can only appear at
- # the start of a string, while $| is used for variables that can appear
- # anywhere in a string.
- 'INTERMEDIATE_DIR': '$!INTERMEDIATE_DIR',
- 'SHARED_INTERMEDIATE_DIR': '$!PRODUCT_DIR/gen',
- 'PRODUCT_DIR': '$!PRODUCT_DIR',
- 'CONFIGURATION_NAME': '$|CONFIGURATION_NAME',
+ # Gyp expects the following variables to be expandable by the build
+ # system to the appropriate locations. Ninja prefers paths to be
+ # known at gyp time. To resolve this, introduce special
+ # variables starting with $! and $| (which begin with a $ so gyp knows it
+ # should be treated specially, but is otherwise an invalid
+ # ninja/shell variable) that are passed to gyp here but expanded
+ # before writing out into the target .ninja files; see
+ # ExpandSpecial.
+ # $! is used for variables that represent a path and that can only appear at
+ # the start of a string, while $| is used for variables that can appear
+ # anywhere in a string.
+ 'INTERMEDIATE_DIR': '$!INTERMEDIATE_DIR',
+ 'SHARED_INTERMEDIATE_DIR': '$!PRODUCT_DIR/gen',
+ 'PRODUCT_DIR': '$!PRODUCT_DIR',
+ 'CONFIGURATION_NAME': '$|CONFIGURATION_NAME',
- # Special variables that may be used by gyp 'rule' targets.
- # We generate definitions for these variables on the fly when processing a
- # rule.
- 'RULE_INPUT_ROOT': '${root}',
- 'RULE_INPUT_DIRNAME': '${dirname}',
- 'RULE_INPUT_PATH': '${source}',
- 'RULE_INPUT_EXT': '${ext}',
- 'RULE_INPUT_NAME': '${name}',
+ # Special variables that may be used by gyp 'rule' targets.
+ # We generate definitions for these variables on the fly when processing a
+ # rule.
+ 'RULE_INPUT_ROOT': '${root}',
+ 'RULE_INPUT_DIRNAME': '${dirname}',
+ 'RULE_INPUT_PATH': '${source}',
+ 'RULE_INPUT_EXT': '${ext}',
+ 'RULE_INPUT_NAME': '${name}',
}
# Placates pylint.
@@ -64,14 +74,13 @@
# TODO: figure out how to not build extra host objects in the non-cross-compile
# case when this is enabled, and enable unconditionally.
-generator_supports_multiple_toolsets = (
- os.environ.get('GYP_CROSSCOMPILE') or
- os.environ.get('AR_host') or
- os.environ.get('CC_host') or
- os.environ.get('CXX_host') or
- os.environ.get('AR_target') or
- os.environ.get('CC_target') or
- os.environ.get('CXX_target'))
+generator_supports_multiple_toolsets = (os.environ.get('GYP_CROSSCOMPILE') or
+ os.environ.get('AR_host') or
+ os.environ.get('CC_host') or
+ os.environ.get('CXX_host') or
+ os.environ.get('AR_target') or
+ os.environ.get('CC_target') or
+ os.environ.get('CXX_target'))
is_linux = platform.system() == 'Linux'
is_windows = platform.system() == 'Windows'
@@ -80,6 +89,18 @@
sony_flavors = ['ps3', 'ps4']
windows_host_flavors = microsoft_flavors + sony_flavors
+_platform_configs = {}
+
+
+def GetToolchainOrNone(flavor):
+ if not flavor in _platform_configs.keys():
+ _platform_configs[flavor] = LoadPlatformConfig(flavor)
+ toolchain = _platform_configs[flavor].GetToolchain()
+ if toolchain:
+ return toolchain
+ return None
+
+
def StripPrefix(arg, prefix):
if arg.startswith(prefix):
return arg[len(prefix):]
@@ -93,22 +114,17 @@
# whitelist common OK ones and quote anything else.
if re.match(r'^[a-zA-Z0-9_=.\\/-]+$', arg):
return arg # No quoting necessary.
- if flavor in microsoft_flavors:
- return gyp.msvs_emulation.QuoteForRspFile(arg)
- elif flavor in sony_flavors :
+ if GetToolchainOrNone(flavor):
+ return GetToolchainOrNone(flavor).QuoteForRspFile(arg)
+ elif flavor in sony_flavors:
# Escape double quotes.
return '"' + arg.replace('\"', '\\\"') + '"'
- return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'"
+ return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'"
def Define(d, flavor):
"""Takes a preprocessor define and returns a -D parameter that's ninja- and
shell-escaped."""
- if flavor in microsoft_flavors:
- # cl.exe replaces literal # characters with = in preprocesor definitions for
- # some reason. Octal-encode to work around that.
- d = d.replace('#', '\\%03o' % ord('#'))
- return '/D' + gyp.msvs_emulation.QuoteForRspFile(ninja_syntax.escape(d))
return QuoteShellArgument(ninja_syntax.escape('-D' + d), flavor)
@@ -151,6 +167,7 @@
variables only store concrete paths to single files, while methods
compute derived values like "the last output of the target".
"""
+
def __init__(self, type):
# Gyp type ("static_library", etc.) of this target.
self.type = type
@@ -233,12 +250,20 @@
# an output file; the result can be namespaced such that it is unique
# to the input file name as well as the output target name.
+
class NinjaWriter:
- def __init__(self, qualified_target, target_outputs, base_dir, build_dir,
- output_file, flavor, case_sensitive_filesystem,
+
+ def __init__(self,
+ qualified_target,
+ target_outputs,
+ base_dir,
+ build_dir,
+ output_file,
+ flavor,
+ case_sensitive_filesystem,
abs_build_dir=None):
- """
- base_dir: path from source root to directory containing this gyp file,
+ """base_dir: path from source root to directory containing this gyp file,
+
by gyp semantics, all input paths are relative to this
build_dir: path from source root to build output
abs_build_dir: absolute path to the build directory
@@ -298,8 +323,9 @@
def ExpandRuleVariables(self, path, root, dirname, source, ext, name):
if self.flavor == 'win':
- path = self.msvs_settings.ConvertVSMacros(
- path, config=self.config_name)
+ path = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().ConvertVSMacros(
+ path, config=self.config_name)
path = path.replace(generator_default_variables['RULE_INPUT_ROOT'], root)
path = path.replace(generator_default_variables['RULE_INPUT_DIRNAME'],
dirname)
@@ -326,8 +352,8 @@
if env:
if self.flavor == 'mac':
path = gyp.xcode_emulation.ExpandEnvVars(path, env)
- elif self.flavor in microsoft_flavors:
- path = gyp.msvs_emulation.ExpandMacros(path, env)
+ elif GetToolchainOrNone(self.flavor):
+ path = GetToolchainOrNone(self.flavor).ExpandEnvVars(path, env)
if path.startswith('$!'):
expanded = self.ExpandSpecial(path)
if self.flavor == 'win':
@@ -336,11 +362,12 @@
expanded = self.path_module.normpath(expanded)
return self.GypPathCaseCorrection(expanded)
if '$|' in path:
- path = self.ExpandSpecial(path)
+ path = self.ExpandSpecial(path)
assert '$' not in path, path
# TODO: this needs a proper fix.
- is_absolute = path.startswith('C:') or path.startswith('c:') or path.startswith('/')
+ is_absolute = path.startswith('C:') or path.startswith(
+ 'c:') or path.startswith('/')
if not is_absolute:
path = self.path_module.normpath(os.path.join(self.build_to_base, path))
@@ -376,8 +403,8 @@
path_dir, path_basename = os.path.split(path)
if qualified:
path_basename = self.name + '.' + path_basename
- path = self.path_module.normpath(os.path.join(obj, self.base_dir, path_dir,
- path_basename))
+ path = self.path_module.normpath(
+ os.path.join(obj, self.base_dir, path_dir, path_basename))
return self.GypPathCaseCorrection(path)
@@ -412,15 +439,21 @@
spec.get('standalone_static_library', 0))
self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
- self.xcode_settings = self.msvs_settings = None
+ self.xcode_settings = None
if self.flavor == 'mac':
self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
- if (self.flavor in windows_host_flavors
- and is_windows):
- self.msvs_settings = gyp.msvs_emulation.MsvsSettings(spec,
- generator_flags)
- arch = self.msvs_settings.GetArch(config_name)
+ if (self.flavor in windows_host_flavors and is_windows):
+ if self.flavor in sony_flavors:
+ self.msvs_settings = gyp.msvs_emulation.MsvsSettings(
+ spec, generator_flags)
+ arch = self.msvs_settings.GetArch(config_name)
+ else:
+ GetToolchainOrNone(self.flavor).InitCompilerSettings(
+ spec, **{'generator_flags': generator_flags})
+ arch = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetArch(config_name)
self.ninja.variable('arch', self.win_env[arch])
+ None
# Compute predepends for all rules.
# actions_depends is the dependencies this target depends on before running
@@ -465,21 +498,36 @@
sources = spec.get('sources', []) + extra_sources
if sources:
pch = None
- if self.flavor in microsoft_flavors:
- gyp.msvs_emulation.VerifyMissingSources(
- sources, self.abs_build_dir, generator_flags, self.GypPathToNinja)
- pch = gyp.msvs_emulation.PrecompiledHeader(
- self.msvs_settings, config_name, self.GypPathToNinja,
- self.GypPathToUniqueOutput, self.obj_ext)
+ if GetToolchainOrNone(self.flavor):
+ GetToolchainOrNone(self.flavor).VerifyMissingSources(
+ sources, **{
+ 'build_dir': self.abs_build_dir,
+ 'generator_flags': generator_flags,
+ 'gyp_path_to_ninja': self.GypPathToNinja
+ })
+ pch = GetToolchainOrNone(self.flavor).GetPrecompiledHeader(
+ **{
+ 'settings':
+ GetToolchainOrNone(self.flavor).GetCompilerSettings(),
+ 'config':
+ config_name,
+ 'gyp_path_to_ninja':
+ self.GypPathToNinja,
+ 'gyp_path_to_unique_output':
+ self.GypPathToUniqueOutput,
+ 'obj_ext':
+ self.obj_ext
+ })
else:
pch = gyp.xcode_emulation.MacPrefixHeader(
self.xcode_settings, self.GypPathToNinja,
lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang))
- link_deps = self.WriteSources(
- config_name, config, sources, compile_depends_stamp, pch, spec)
+ link_deps = self.WriteSources(config_name, config, sources,
+ compile_depends_stamp, pch, spec)
# Some actions/rules output 'sources' that are already object files.
- link_deps += [self.GypPathToNinja(f)
- for f in sources if f.endswith(self.obj_ext)]
+ link_deps += [
+ self.GypPathToNinja(f) for f in sources if f.endswith(self.obj_ext)
+ ]
if self.flavor in microsoft_flavors and self.target.type == 'static_library':
self.target.component_objs = link_deps
@@ -505,31 +553,34 @@
def _WinIdlRule(self, source, prebuild, outputs):
"""Handle the implicit VS .idl rule for one source file. Fills |outputs|
with files that are generated."""
- outdir, output, vars, flags = self.msvs_settings.GetIdlBuildData(
- source, self.config_name)
+ outdir, output, vars, flags = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetIdlBuildData(
+ source, self.config_name)
outdir = self.GypPathToNinja(outdir)
+
def fix_path(path, rel=None):
path = os.path.join(outdir, path)
dirname, basename = os.path.split(source)
root, ext = os.path.splitext(basename)
- path = self.ExpandRuleVariables(
- path, root, dirname, source, ext, basename)
+ path = self.ExpandRuleVariables(path, root, dirname, source, ext,
+ basename)
if rel:
path = os.path.relpath(path, rel)
return path
+
vars = [(name, fix_path(value, outdir)) for name, value in vars]
output = [fix_path(p) for p in output]
vars.append(('outdir', outdir))
vars.append(('idlflags', flags))
input = self.GypPathToNinja(source)
- self.ninja.build(output, 'idl', input,
- variables=vars, order_only=prebuild)
+ self.ninja.build(output, 'idl', input, variables=vars, order_only=prebuild)
outputs.extend(output)
def WriteWinIdlFiles(self, spec, prebuild):
"""Writes rules to match MSVS's implicit idl handling."""
- assert self.flavor in microsoft_flavors
- if self.msvs_settings.HasExplicitIdlRules(spec):
+ assert GetToolchainOrNone(self.flavor)
+ if GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().HasExplicitIdlRules(spec):
return []
outputs = []
for source in filter(lambda x: x.endswith('.idl'), spec['sources']):
@@ -586,7 +637,7 @@
# Actions cd into the base directory.
env = self.GetSortedXcodeEnv()
if self.flavor == 'win':
- env = self.msvs_settings.GetVSMacroEnv(
+ env = GetToolchainOrNone(self.flavor).GetCompilerSettings().GetVSMacroEnv(
'$!PRODUCT_DIR', config=self.config_name)
all_outputs = []
for action in actions:
@@ -594,12 +645,11 @@
name = '%s_%s' % (action['action_name'],
hashlib.md5(self.qualified_target).hexdigest())
description = self.GenerateDescription('ACTION',
- action.get('message', None),
- name)
+ action.get('message', None), name)
is_cygwin = self.IsCygwinRule(action)
args = action['action']
- rule_name, _ = self.WriteNewNinjaRule(name, args, description,
- is_cygwin, env=env)
+ rule_name, _ = self.WriteNewNinjaRule(
+ name, args, description, is_cygwin, env=env)
inputs = [self.GypPathToNinja(i, env) for i in action['inputs']]
if int(action.get('process_outputs_as_sources', False)):
@@ -609,8 +659,7 @@
outputs = [self.GypPathToNinja(o, env) for o in action['outputs']]
# Then write out an edge using the rule.
- self.ninja.build(outputs, rule_name, inputs,
- order_only=prebuild)
+ self.ninja.build(outputs, rule_name, inputs, order_only=prebuild)
all_outputs += outputs
self.ninja.newline()
@@ -661,12 +710,14 @@
root, ext = os.path.splitext(basename)
# Gather the list of inputs and outputs, expanding $vars if possible.
- outputs = [self.ExpandRuleVariables(o, root, dirname,
- source, ext, basename)
- for o in rule['outputs']]
- inputs = [self.ExpandRuleVariables(i, root, dirname,
- source, ext, basename)
- for i in rule.get('inputs', [])]
+ outputs = [
+ self.ExpandRuleVariables(o, root, dirname, source, ext, basename)
+ for o in rule['outputs']
+ ]
+ inputs = [
+ self.ExpandRuleVariables(i, root, dirname, source, ext, basename)
+ for i in rule.get('inputs', [])
+ ]
if int(rule.get('process_outputs_as_sources', False)):
extra_sources += outputs
@@ -695,11 +746,14 @@
inputs = [self.GypPathToNinja(i, env) for i in inputs]
outputs = [self.GypPathToNinja(o, env) for o in outputs]
extra_bindings.append(('unique_name',
- hashlib.md5(outputs[0]).hexdigest()))
- self.ninja.build(outputs, rule_name, self.GypPathToNinja(source),
- implicit=inputs,
- order_only=prebuild,
- variables=extra_bindings)
+ hashlib.md5(outputs[0]).hexdigest()))
+ self.ninja.build(
+ outputs,
+ rule_name,
+ self.GypPathToNinja(source),
+ implicit=inputs,
+ order_only=prebuild,
+ variables=extra_bindings)
all_outputs.extend(outputs)
@@ -747,16 +801,15 @@
for f in files:
src = self.GypPathToNinja(os.path.join(rel_root, f), env)
common_prefix = os.path.commonprefix([joined_path, root])
- subdir = root[len(common_prefix)+1:]
+ subdir = root[len(common_prefix) + 1:]
dst = os.path.join(destination, basename, subdir, f)
- outputs += self.WriteCopy(src, dst,
- prebuild, env, mac_bundle_depends)
+ outputs += self.WriteCopy(src, dst, prebuild, env,
+ mac_bundle_depends)
else:
src = self.GypPathToNinja(path, env)
dst = os.path.join(destination, basename)
- outputs += self.WriteCopy(src, dst,
- prebuild, env, mac_bundle_depends)
+ outputs += self.WriteCopy(src, dst, prebuild, env, mac_bundle_depends)
return outputs
@@ -765,8 +818,11 @@
for output, res in gyp.xcode_emulation.GetMacBundleResources(
self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
self.xcode_settings, map(self.GypPathToNinja, resources)):
- self.ninja.build(output, 'mac_tool', res,
- variables=[('mactool_cmd', 'copy-bundle-resource')])
+ self.ninja.build(
+ output,
+ 'mac_tool',
+ res,
+ variables=[('mactool_cmd', 'copy-bundle-resource')])
bundle_depends.append(output)
def WriteMacInfoPlist(self, bundle_depends):
@@ -781,15 +837,20 @@
intermediate_plist = self.GypPathToUniqueOutput(
os.path.basename(info_plist))
defines = ' '.join([Define(d, self.flavor) for d in defines])
- info_plist = self.ninja.build(intermediate_plist, 'infoplist', info_plist,
- variables=[('defines',defines)])
+ info_plist = self.ninja.build(
+ intermediate_plist,
+ 'infoplist',
+ info_plist,
+ variables=[('defines', defines)])
env = self.GetSortedXcodeEnv(additional_settings=extra_env)
env = self.ComputeExportEnvString(env)
- self.ninja.build(out, 'mac_tool', info_plist,
- variables=[('mactool_cmd', 'copy-info-plist'),
- ('env', env)])
+ self.ninja.build(
+ out,
+ 'mac_tool',
+ info_plist,
+ variables=[('mactool_cmd', 'copy-info-plist'), ('env', env)])
bundle_depends.append(out)
def WriteSources(self, config_name, config, sources, predepends,
@@ -810,16 +871,20 @@
self.xcode_settings.GetCflagsObjC(config_name)
cflags_objcc = ['$cflags_cc'] + \
self.xcode_settings.GetCflagsObjCC(config_name)
- elif self.flavor in microsoft_flavors:
- cflags = self.msvs_settings.GetCflags(config_name)
- cflags_c = self.msvs_settings.GetCflagsC(config_name)
- cflags_cc = self.msvs_settings.GetCflagsCC(config_name)
- extra_defines = self.msvs_settings.GetComputedDefines(config_name)
+ elif GetToolchainOrNone(self.flavor):
+ cflags = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetCflags(config_name)
+ cflags_c = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetCflagsC(config_name)
+ cflags_cc = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetCflagsCC(config_name)
+ extra_defines = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetDefines(config_name)
obj = 'obj'
if self.toolset != 'target':
obj += '.' + self.toolset
- pdbpath = os.path.normpath(os.path.join(obj, self.base_dir,
- self.name + '.pdb'))
+ pdbpath = os.path.normpath(
+ os.path.join(obj, self.base_dir, self.name + '.pdb'))
self.WriteVariableList('pdbname', [pdbpath])
self.WriteVariableList('pchprefix', [self.name])
else:
@@ -832,26 +897,36 @@
cflags_cc_host = config.get('cflags_cc_host', cflags_cc)
defines = config.get('defines', []) + extra_defines
- self.WriteVariableList('defines', [Define(d, self.flavor) for d in defines])
- if self.flavor in microsoft_flavors:
- self.WriteVariableList('rcflags',
- [QuoteShellArgument(self.ExpandSpecial(f), self.flavor)
- for f in self.msvs_settings.GetRcflags(config_name,
- self.GypPathToNinja)])
+ if GetToolchainOrNone(self.flavor):
+ self.WriteVariableList('defines', [
+ GetToolchainOrNone(self.flavor).Define(d) for d in defines
+ ])
+ else:
+ self.WriteVariableList('defines',
+ [Define(d, self.flavor) for d in defines])
+ if GetToolchainOrNone(self.flavor):
+ self.WriteVariableList('rcflags', [
+ QuoteShellArgument(self.ExpandSpecial(f), self.flavor)
+ for f in GetToolchainOrNone(self.flavor).GetCompilerSettings()
+ .GetRcFlags(config_name, self.GypPathToNinja)
+ ])
include_dirs = config.get('include_dirs', [])
include_dirs += config.get('include_dirs_' + self.toolset, [])
- if self.flavor in microsoft_flavors:
- include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs,
- config_name)
- self.WriteVariableList('includes',
- ['/I' + gyp.msvs_emulation.QuoteForRspFile(self.GypPathToNinja(i))
- for i in include_dirs])
+ if GetToolchainOrNone(self.flavor):
+ include_dirs = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().ProcessIncludeDirs(
+ include_dirs, config_name)
+ self.WriteVariableList('includes', [
+ '/I' + GetToolchainOrNone(self.flavor).QuoteForRspFile(
+ self.GypPathToNinja(i)) for i in include_dirs
+ ])
else:
- self.WriteVariableList('includes',
- [QuoteShellArgument('-I' + self.GypPathToNinja(i), self.flavor)
- for i in include_dirs])
+ self.WriteVariableList('includes', [
+ QuoteShellArgument('-I' + self.GypPathToNinja(i), self.flavor)
+ for i in include_dirs
+ ])
pch_commands = precompiled_header.GetPchBuildCommands()
if self.flavor == 'mac':
@@ -869,14 +944,16 @@
self.WriteVariableList('cflags_cc', map(self.ExpandSpecial, cflags_cc))
self.WriteVariableList('cflags_host', map(self.ExpandSpecial, cflags_host))
- self.WriteVariableList('cflags_c_host', map(self.ExpandSpecial, cflags_c_host))
- self.WriteVariableList('cflags_cc_host', map(self.ExpandSpecial, cflags_cc_host))
+ self.WriteVariableList('cflags_c_host',
+ map(self.ExpandSpecial, cflags_c_host))
+ self.WriteVariableList('cflags_cc_host',
+ map(self.ExpandSpecial, cflags_cc_host))
if self.flavor == 'mac':
self.WriteVariableList('cflags_objc', map(self.ExpandSpecial,
cflags_objc))
- self.WriteVariableList('cflags_objcc', map(self.ExpandSpecial,
- cflags_objcc))
+ self.WriteVariableList('cflags_objcc',
+ map(self.ExpandSpecial, cflags_objcc))
self.ninja.newline()
outputs = []
for source in sources:
@@ -889,9 +966,10 @@
command = 'cc'
elif ext == 's' and self.flavor != 'win': # Doesn't generate .o.d files.
command = 'cc_s'
- elif (self.flavor == 'win' and ext == 'asm' and
- self.msvs_settings.GetArch(config_name) == 'x86' and
- not self.msvs_settings.HasExplicitAsmRules(spec)):
+ elif (self.flavor == 'win' and ext == 'asm' and GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetArch(config_name) == 'x86' and
+ not GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().HasExplicitAsmRules(spec)):
# Asm files only get auto assembled for x86 (not x64).
command = 'asm'
# Add the _asm suffix as msvs is capable of handling .cc and
@@ -902,6 +980,7 @@
elif self.flavor == 'mac' and ext == 'mm':
command = 'objcxx'
elif self.flavor in microsoft_flavors and ext == 'rc':
+ # TODO: Starboardize this.
command = 'rc'
obj_ext = '.res'
else:
@@ -914,13 +993,17 @@
output = self.GypPathToUniqueOutput(filename + obj_ext)
implicit = precompiled_header.GetObjDependencies([input], [output])
variables = []
- if self.flavor in microsoft_flavors:
+ if GetToolchainOrNone(self.flavor):
variables, output, implicit = precompiled_header.GetFlagsModifications(
input, output, implicit, command, cflags_c, cflags_cc,
self.ExpandSpecial)
- self.ninja.build(output, command, input,
- implicit=[gch for _, _, gch in implicit],
- order_only=predepends, variables=variables)
+ self.ninja.build(
+ output,
+ command,
+ input,
+ implicit=[gch for _, _, gch in implicit],
+ order_only=predepends,
+ variables=variables)
outputs.append(output)
self.WritePchTargets(pch_commands)
@@ -935,13 +1018,18 @@
for gch, lang_flag, lang, input in pch_commands:
var_name = {
- 'c': 'cflags_pch_c',
- 'cc': 'cflags_pch_cc',
- 'm': 'cflags_pch_objc',
- 'mm': 'cflags_pch_objcc',
+ 'c': 'cflags_pch_c',
+ 'cc': 'cflags_pch_cc',
+ 'm': 'cflags_pch_objc',
+ 'mm': 'cflags_pch_objcc',
}[lang]
- map = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', }
+ map = {
+ 'c': 'cc',
+ 'cc': 'cxx',
+ 'm': 'objc',
+ 'mm': 'objcxx',
+ }
cmd = map.get(lang)
self.ninja.build(gch, cmd, input, variables=[(var_name, lang_flag)])
@@ -949,9 +1037,9 @@
"""Write out a link step. Fills out target.binary. """
command = {
- 'executable': 'link',
- 'loadable_module': 'solink_module',
- 'shared_library': 'solink',
+ 'executable': 'link',
+ 'loadable_module': 'solink_module',
+ 'shared_library': 'solink',
}[spec['type']]
implicit_deps = set()
@@ -969,9 +1057,11 @@
if not target:
continue
linkable = target.Linkable()
+ # TODO: Starboardize.
if linkable:
if (self.flavor in microsoft_flavors and target.component_objs and
- self.msvs_settings.IsUseLibraryDependencyInputs(config_name)):
+ GetToolchainOrNone(self.flavor).GetCompilerSettings()
+ .IsUseLibraryDependencyInputs(config_name)):
extra_link_deps.extend(target.component_objs)
elif (self.flavor in (microsoft_flavors + ['ps3']) and
target.import_lib):
@@ -988,7 +1078,9 @@
# dedup the extra link deps while preserving order
seen = set()
- extra_link_deps = [ x for x in extra_link_deps if x not in seen and not seen.add(x) ]
+ extra_link_deps = [
+ x for x in extra_link_deps if x not in seen and not seen.add(x)
+ ]
link_deps.extend(extra_link_deps)
@@ -997,35 +1089,41 @@
output = self.ComputeMacBundleBinaryOutput()
else:
output = self.ComputeOutput(spec)
- extra_bindings.append(('postbuilds',
- self.GetPostbuildCommand(spec, output, output)))
+ extra_bindings.append(('postbuilds', self.GetPostbuildCommand(
+ spec, output, output)))
if self.flavor == 'mac':
- ldflags = self.xcode_settings.GetLdflags(config_name,
+ ldflags = self.xcode_settings.GetLdflags(
+ config_name,
self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
self.GypPathToNinja)
- elif self.flavor in microsoft_flavors:
- libflags = self.msvs_settings.GetLibFlags(config_name,
- self.GypPathToNinja)
+ elif GetToolchainOrNone(self.flavor):
+ libflags = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetLibFlags(
+ config_name, self.GypPathToNinja)
self.WriteVariableList(
'libflags', gyp.common.uniquer(map(self.ExpandSpecial, libflags)))
is_executable = spec['type'] == 'executable'
manifest_name = self.GypPathToUniqueOutput(
self.ComputeOutputFileName(spec))
- ldflags, manifest_files = self.msvs_settings.GetLdflags(config_name,
- self.GypPathToNinja, self.ExpandSpecial, manifest_name, is_executable)
+ ldflags, manifest_files = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetLdFlags(config_name, **{
+ 'gyp_path_to_ninja': self.GypPathToNinja,
+ 'expand_special': self.ExpandSpecial,
+ 'manifest_name': manifest_name,
+ 'is_executable': is_executable
+ })
self.WriteVariableList('manifests', manifest_files)
else:
ldflags = config.get('ldflags', [])
ldflags_host = config.get('ldflags_host', ldflags)
self.WriteVariableList('ldflags',
- gyp.common.uniquer(map(self.ExpandSpecial,
- ldflags)))
+ gyp.common.uniquer(map(self.ExpandSpecial, ldflags)))
if ('ldflags_host' in locals()):
- self.WriteVariableList('ldflags_host',
- gyp.common.uniquer(map(self.ExpandSpecial,
- ldflags_host)))
+ self.WriteVariableList(
+ 'ldflags_host',
+ gyp.common.uniquer(map(self.ExpandSpecial, ldflags_host)))
if self.toolset == 'host':
libs = spec.get('libraries_host', [])
@@ -1038,8 +1136,9 @@
if self.flavor == 'mac':
libraries = self.xcode_settings.AdjustLibraries(libraries)
- elif self.flavor in microsoft_flavors:
- libraries = self.msvs_settings.AdjustLibraries(libraries)
+ elif GetToolchainOrNone(self.flavor):
+ libraries = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().ProcessLibraries(libraries)
self.WriteVariableList('libs', libraries)
self.target.binary = output
@@ -1047,7 +1146,8 @@
if command in ('solink', 'solink_module'):
extra_bindings.append(('soname', os.path.split(output)[1]))
extra_bindings.append(('lib',
- gyp.common.EncodePOSIXShellArgument(output)))
+ gyp.common.EncodePOSIXShellArgument(output)))
+ # TODO: Starboardize.
if self.flavor in microsoft_flavors:
extra_bindings.append(('dll', output))
if '/NOENTRY' not in ldflags:
@@ -1077,17 +1177,19 @@
# with an "export pickup" step that runs over the object files
# and produces a new .c file. That .c file should be compiled and linked
# into the PRX.
- gen_files_dir = os.path.join(self.ExpandSpecial(
- generator_default_variables['SHARED_INTERMEDIATE_DIR']), 'prx')
+ gen_files_dir = os.path.join(
+ self.ExpandSpecial(
+ generator_default_variables['SHARED_INTERMEDIATE_DIR']), 'prx')
export_pickup_output = os.path.join(
gen_files_dir, os.path.basename(prx_output_base) + '.prx_export.c')
prx_export_obj_file = export_pickup_output[:-2] + '.o'
- self.ninja.build(export_pickup_output,
- 'prx_export_pickup',
- link_deps,
- implicit=list(implicit_deps),
- order_only=list(order_only_deps))
+ self.ninja.build(
+ export_pickup_output,
+ 'prx_export_pickup',
+ link_deps,
+ implicit=list(implicit_deps),
+ order_only=list(order_only_deps))
self.ninja.build(prx_export_obj_file, 'cc', export_pickup_output)
link_deps.append(prx_export_obj_file)
@@ -1101,10 +1203,13 @@
if self.toolset != 'target':
command += '_' + self.toolset
- self.ninja.build(output, command, link_deps,
- implicit=list(implicit_deps),
- order_only=list(order_only_deps),
- variables=extra_bindings)
+ self.ninja.build(
+ output,
+ command,
+ link_deps,
+ implicit=list(implicit_deps),
+ order_only=list(order_only_deps),
+ variables=extra_bindings)
def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
if spec['type'] == 'none':
@@ -1114,26 +1219,33 @@
elif spec['type'] == 'static_library':
self.target.binary = self.ComputeOutput(spec)
variables = []
- if self.flavor in microsoft_flavors:
- libflags = self.msvs_settings.GetLibFlags(config_name,
- self.GypPathToNinja)
+ if GetToolchainOrNone(self.flavor):
+ libflags = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetLibFlags(
+ config_name, self.GypPathToNinja)
+ # TODO: Starboardize libflags vs libtool_flags.
variables.append(('libflags', ' '.join(libflags)))
- postbuild = self.GetPostbuildCommand(
- spec, self.target.binary, self.target.binary)
+ postbuild = self.GetPostbuildCommand(spec, self.target.binary,
+ self.target.binary)
if postbuild:
variables.append(('postbuilds', postbuild))
if self.xcode_settings:
variables.append(('libtool_flags',
self.xcode_settings.GetLibtoolflags(config_name)))
- if (self.flavor not in (['mac'] + microsoft_flavors) and not
- self.is_standalone_static_library):
+ # TODO: Starboardize.
+ if (self.flavor not in (['mac'] + microsoft_flavors) and
+ not self.is_standalone_static_library):
command = 'alink_thin'
else:
command = 'alink'
if self.toolset != 'target':
command += '_' + self.toolset
- self.ninja.build(self.target.binary, command, link_deps,
- order_only=compile_deps, variables=variables)
+ self.ninja.build(
+ self.target.binary,
+ command,
+ link_deps,
+ order_only=compile_deps,
+ variables=variables)
else:
self.WriteLink(spec, config_name, config, link_deps)
return self.target.binary
@@ -1142,18 +1254,20 @@
assert self.is_mac_bundle
package_framework = spec['type'] in ('shared_library', 'loadable_module')
output = self.ComputeMacBundleOutput()
- postbuild = self.GetPostbuildCommand(spec, output, self.target.binary,
- is_command_start=not package_framework)
+ postbuild = self.GetPostbuildCommand(
+ spec,
+ output,
+ self.target.binary,
+ is_command_start=not package_framework)
variables = []
if postbuild:
variables.append(('postbuilds', postbuild))
if package_framework:
variables.append(('version', self.xcode_settings.GetFrameworkVersion()))
- self.ninja.build(output, 'package_framework', mac_bundle_depends,
- variables=variables)
+ self.ninja.build(
+ output, 'package_framework', mac_bundle_depends, variables=variables)
else:
- self.ninja.build(output, 'stamp', mac_bundle_depends,
- variables=variables)
+ self.ninja.build(output, 'stamp', mac_bundle_depends, variables=variables)
self.target.bundle = output
return output
@@ -1163,8 +1277,8 @@
abs_build_dir = self.abs_build_dir
return gyp.xcode_emulation.GetSortedXcodeEnv(
self.xcode_settings, abs_build_dir,
- os.path.join(abs_build_dir, self.build_to_base), self.config_name,
- additional_settings)
+ os.path.join(abs_build_dir,
+ self.build_to_base), self.config_name, additional_settings)
def GetSortedXcodePostbuildEnv(self):
"""Returns the variables Xcode would set for postbuild steps."""
@@ -1177,7 +1291,10 @@
postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = strip_save_file
return self.GetSortedXcodeEnv(additional_settings=postbuild_settings)
- def GetPostbuildCommand(self, spec, output, output_binary,
+ def GetPostbuildCommand(self,
+ spec,
+ output,
+ output_binary,
is_command_start=False):
"""Returns a shell command that runs all the postbuilds, and removes
|output| if any of them fails. If |is_command_start| is False, then the
@@ -1198,17 +1315,19 @@
return ''
# Postbuilds expect to be run in the gyp file's directory, so insert an
# implicit postbuild to cd to there.
- postbuilds.insert(0, gyp.common.EncodePOSIXShellList(
- ['cd', self.build_to_base]))
+ postbuilds.insert(0,
+ gyp.common.EncodePOSIXShellList(
+ ['cd', self.build_to_base]))
env = self.ComputeExportEnvString(self.GetSortedXcodePostbuildEnv())
# G will be non-null if any postbuild fails. Run all postbuilds in a
# subshell.
commands = env + ' (F=0; ' + \
' '.join([ninja_syntax.escape(command) + ' || F=$$?;'
for command in postbuilds])
- command_string = (commands + ' exit $$F); G=$$?; '
- # Remove the final output if any postbuild failed.
- '((exit $$G) || rm -rf %s) ' % output + '&& exit $$G)')
+ command_string = (
+ commands + ' exit $$F); G=$$?; '
+ # Remove the final output if any postbuild failed.
+ '((exit $$G) || rm -rf %s) ' % output + '&& exit $$G)')
if is_command_start:
return '(' + command_string + ' && '
else:
@@ -1220,7 +1339,8 @@
that exports |env| to the shell."""
export_str = []
for k, v in env:
- export_str.append('export %s=%s;' %
+ export_str.append(
+ 'export %s=%s;' %
(k, ninja_syntax.escape(gyp.common.EncodePOSIXShellArgument(v))))
return ' '.join(export_str)
@@ -1242,16 +1362,20 @@
type = spec['type']
default_variables = copy.copy(generator_default_variables)
- CalculateVariables(default_variables, {'flavor': self.flavor})
+ if GetToolchainOrNone(self.flavor):
+ GetToolchainOrNone(
+ self.flavor).SetAdditionalGypVariables(default_variables)
+ else:
+ CalculateVariables(default_variables, {'flavor': self.flavor})
# Compute filename prefix: the product prefix, or a default for
# the product type.
DEFAULT_PREFIX = {
- 'loadable_module': default_variables['SHARED_LIB_PREFIX'],
- 'shared_library': default_variables['SHARED_LIB_PREFIX'],
- 'static_library': default_variables['STATIC_LIB_PREFIX'],
- 'executable': default_variables['EXECUTABLE_PREFIX'],
- }
+ 'loadable_module': default_variables['SHARED_LIB_PREFIX'],
+ 'shared_library': default_variables['SHARED_LIB_PREFIX'],
+ 'static_library': default_variables['STATIC_LIB_PREFIX'],
+ 'executable': default_variables['EXECUTABLE_PREFIX'],
+ }
prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, ''))
# Compute filename extension: the product extension, or a default
@@ -1261,7 +1385,7 @@
'shared_library': default_variables['SHARED_LIB_SUFFIX'],
'static_library': default_variables['STATIC_LIB_SUFFIX'],
'executable': default_variables['EXECUTABLE_SUFFIX'],
- }
+ }
extension = spec.get('product_extension')
if extension:
extension = '.' + extension
@@ -1284,7 +1408,7 @@
target = StripPrefix(target, 'lib')
if type in ('static_library', 'loadable_module', 'shared_library',
- 'executable'):
+ 'executable'):
return '%s%s%s' % (prefix, target, extension)
elif type == 'none':
return '%s.stamp' % target
@@ -1299,13 +1423,14 @@
type = spec['type']
if self.flavor == 'win':
- override = self.msvs_settings.GetOutputName(self.config_name,
- self.ExpandSpecial)
+ override = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().GetOutputName(
+ self.config_name, self.ExpandSpecial)
if override:
return override
- if self.flavor == 'mac' and type in (
- 'static_library', 'executable', 'shared_library', 'loadable_module'):
+ if self.flavor == 'mac' and type in ('static_library', 'executable',
+ 'shared_library', 'loadable_module'):
filename = self.xcode_settings.GetExecutablePath()
else:
filename = self.ComputeOutputFileName(spec, type)
@@ -1345,11 +1470,13 @@
expanded."""
if self.flavor == 'win':
- args = [self.msvs_settings.ConvertVSMacros(
- arg, self.base_to_build, config=self.config_name)
- for arg in args]
- description = self.msvs_settings.ConvertVSMacros(
- description, config=self.config_name)
+ args = [
+ GetToolchainOrNone(self.flavor).GetCompilerSettings().ConvertVSMacros(
+ arg, self.base_to_build, config=self.config_name) for arg in args
+ ]
+ description = GetToolchainOrNone(
+ self.flavor).GetCompilerSettings().ConvertVSMacros(
+ description, config=self.config_name)
elif self.flavor == 'mac':
# |env| is an empty list on non-mac.
args = [gyp.xcode_emulation.ExpandEnvVars(arg, env) for arg in args]
@@ -1367,7 +1494,7 @@
# Remove variable references, but not if they refer to the magic rule
# variables. This is not quite right, as it also protects these for
# actions, not just for rules where they are valid. Good enough.
- protect = [ '${root}', '${dirname}', '${source}', '${ext}', '${name}' ]
+ protect = ['${root}', '${dirname}', '${source}', '${ext}', '${name}']
protect = '(?!' + '|'.join(map(re.escape, protect)) + ')'
description = re.sub(protect + r'\$', '_', description)
@@ -1377,16 +1504,18 @@
rspfile = None
rspfile_content = None
args = [self.ExpandSpecial(arg, self.base_to_build) for arg in args]
- if (self.flavor in windows_host_flavors
- and is_windows):
+ if (self.flavor in windows_host_flavors and is_windows):
rspfile = rule_name + '.$unique_name.rsp'
# The cygwin case handles this inside the bash sub-shell.
run_in = '' if is_cygwin else ' ' + self.build_to_base
if is_cygwin:
rspfile_content = self.msvs_settings.BuildCygwinBashCommandLine(
args, self.build_to_base)
- else:
+ elif self.flavor in sony_flavors:
rspfile_content = gyp.msvs_emulation.EncodeRspFileList(args)
+ else:
+ rspfile_content = GetToolchainOrNone(
+ self.flavor).EncodeRspFileList(args)
command = ('%s gyp-win-tool action-wrapper $arch ' % sys.executable +
rspfile + run_in)
@@ -1398,8 +1527,13 @@
# GYP rules/actions express being no-ops by not touching their outputs.
# Avoid executing downstream dependencies in this case by specifying
# restat=1 to ninja.
- self.ninja.rule(rule_name, command, description, restat=True,
- rspfile=rspfile, rspfile_content=rspfile_content)
+ self.ninja.rule(
+ rule_name,
+ command,
+ description,
+ restat=True,
+ rspfile=rspfile,
+ rspfile_content=rspfile_content)
self.ninja.newline()
return rule_name, args
@@ -1421,51 +1555,21 @@
# Copy additional generator configuration data from Xcode, which is shared
# by the Mac Ninja generator.
import gyp.generator.xcode as xcode_generator
- generator_additional_non_configuration_keys = getattr(xcode_generator,
- 'generator_additional_non_configuration_keys', [])
- generator_additional_path_sections = getattr(xcode_generator,
- 'generator_additional_path_sections', [])
+ generator_additional_non_configuration_keys = getattr(
+ xcode_generator, 'generator_additional_non_configuration_keys', [])
+ generator_additional_path_sections = getattr(
+ xcode_generator, 'generator_additional_path_sections', [])
global generator_extra_sources_for_rules
- generator_extra_sources_for_rules = getattr(xcode_generator,
- 'generator_extra_sources_for_rules', [])
- elif flavor in microsoft_flavors:
- default_variables.setdefault('OS', 'win')
- default_variables['EXECUTABLE_SUFFIX'] = '.exe'
- default_variables['STATIC_LIB_PREFIX'] = ''
- default_variables['STATIC_LIB_SUFFIX'] = '.lib'
- default_variables['SHARED_LIB_PREFIX'] = ''
- default_variables['SHARED_LIB_SUFFIX'] = '.dll'
- generator_flags = params.get('generator_flags', {})
-
- # Copy additional generator configuration data from VS, which is shared
- # by the Windows Ninja generator.
- import gyp.generator.msvs as msvs_generator
- generator_additional_non_configuration_keys = getattr(msvs_generator,
- 'generator_additional_non_configuration_keys', [])
- generator_additional_path_sections = getattr(msvs_generator,
- 'generator_additional_path_sections', [])
-
- # Set a variable so conditions can be based on msvs_version.
- msvs_version = gyp.msvs_emulation.GetVSVersion(generator_flags)
- default_variables['MSVS_VERSION'] = msvs_version.ShortName()
-
- # To determine processor word size on Windows, in addition to checking
- # PROCESSOR_ARCHITECTURE (which reflects the word size of the current
- # process), it is also necessary to check PROCESSOR_ARCHITEW6432 (which
- # contains the actual word size of the system when running thru WOW64).
- if ('64' in os.environ.get('PROCESSOR_ARCHITECTURE', '') or
- '64' in os.environ.get('PROCESSOR_ARCHITEW6432', '')):
- default_variables['MSVS_OS_BITS'] = 64
- else:
- default_variables['MSVS_OS_BITS'] = 32
+ generator_extra_sources_for_rules = getattr(
+ xcode_generator, 'generator_extra_sources_for_rules', [])
elif flavor in ['ps3']:
if is_windows:
# This is required for BuildCygwinBashCommandLine() to work.
import gyp.generator.msvs as msvs_generator
- generator_additional_non_configuration_keys = getattr(msvs_generator,
- 'generator_additional_non_configuration_keys', [])
- generator_additional_path_sections = getattr(msvs_generator,
- 'generator_additional_path_sections', [])
+ generator_additional_non_configuration_keys = getattr(
+ msvs_generator, 'generator_additional_non_configuration_keys', [])
+ generator_additional_path_sections = getattr(
+ msvs_generator, 'generator_additional_path_sections', [])
default_variables['SHARED_LIB_PREFIX'] = ''
default_variables['SHARED_LIB_SUFFIX'] = '.sprx'
@@ -1475,10 +1579,10 @@
if is_windows:
# This is required for BuildCygwinBashCommandLine() to work.
import gyp.generator.msvs as msvs_generator
- generator_additional_non_configuration_keys = getattr(msvs_generator,
- 'generator_additional_non_configuration_keys', [])
- generator_additional_path_sections = getattr(msvs_generator,
- 'generator_additional_path_sections', [])
+ generator_additional_non_configuration_keys = getattr(
+ msvs_generator, 'generator_additional_non_configuration_keys', [])
+ generator_additional_path_sections = getattr(
+ msvs_generator, 'generator_additional_path_sections', [])
default_variables['EXECUTABLE_SUFFIX'] = '.elf'
default_variables['SHARED_LIB_PREFIX'] = 'lib'
@@ -1487,10 +1591,10 @@
# Copy additional generator configuration data from VS, which is shared
# by the Windows Ninja generator.
import gyp.generator.msvs as msvs_generator
- generator_additional_non_configuration_keys = getattr(msvs_generator,
- 'generator_additional_non_configuration_keys', [])
- generator_additional_path_sections = getattr(msvs_generator,
- 'generator_additional_path_sections', [])
+ generator_additional_non_configuration_keys = getattr(
+ msvs_generator, 'generator_additional_non_configuration_keys', [])
+ generator_additional_path_sections = getattr(
+ msvs_generator, 'generator_additional_path_sections', [])
else:
operating_system = flavor
if flavor == 'android':
@@ -1499,8 +1603,8 @@
default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
default_variables.setdefault('SHARED_LIB_DIR',
os.path.join('$!PRODUCT_DIR', 'lib'))
- default_variables.setdefault('LIB_DIR',
- os.path.join('$!PRODUCT_DIR', 'obj'))
+ default_variables.setdefault('LIB_DIR', os.path.join(
+ '$!PRODUCT_DIR', 'obj'))
def OpenOutput(path, mode='w'):
@@ -1523,15 +1627,15 @@
class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [
- ("dwLength", ctypes.c_ulong),
- ("dwMemoryLoad", ctypes.c_ulong),
- ("ullTotalPhys", ctypes.c_ulonglong),
- ("ullAvailPhys", ctypes.c_ulonglong),
- ("ullTotalPageFile", ctypes.c_ulonglong),
- ("ullAvailPageFile", ctypes.c_ulonglong),
- ("ullTotalVirtual", ctypes.c_ulonglong),
- ("ullAvailVirtual", ctypes.c_ulonglong),
- ("sullAvailExtendedVirtual", ctypes.c_ulonglong),
+ ('dwLength', ctypes.c_ulong),
+ ('dwMemoryLoad', ctypes.c_ulong),
+ ('ullTotalPhys', ctypes.c_ulonglong),
+ ('ullAvailPhys', ctypes.c_ulonglong),
+ ('ullTotalPageFile', ctypes.c_ulonglong),
+ ('ullAvailPageFile', ctypes.c_ulonglong),
+ ('ullTotalVirtual', ctypes.c_ulonglong),
+ ('ullAvailVirtual', ctypes.c_ulonglong),
+ ('sullAvailExtendedVirtual', ctypes.c_ulonglong),
]
stat = MEMORYSTATUSEX()
@@ -1540,26 +1644,26 @@
# VS 2015 uses 20% more working set than VS 2013 and can consume all RAM
# on a 64 GB machine.
- mem_limit = max(1, stat.ullTotalPhys / (5 * (2 ** 30))) # total / 5GB
+ mem_limit = max(1, stat.ullTotalPhys / (5 * (2**30))) # total / 5GB
hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
return min(mem_limit, hard_cap)
elif sys.platform.startswith('linux'):
- if os.path.exists("/proc/meminfo"):
- with open("/proc/meminfo") as meminfo:
+ if os.path.exists('/proc/meminfo'):
+ with open('/proc/meminfo') as meminfo:
memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
for line in meminfo:
match = memtotal_re.match(line)
if not match:
continue
# Allow 6Gb per link on Linux because Gold is quite memory hungry
- return max(1, int(match.group(1)) / (6 * (2 ** 20)))
+ return max(1, int(match.group(1)) / (6 * (2**20)))
return 1
elif sys.platform == 'darwin':
try:
avail_bytes = int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
# A static library debug build of Chromium's unit_tests takes ~2.7GB, so
# 4GB per ld process allows for some more bloat.
- return max(1, avail_bytes / (4 * (2 ** 30))) # total / 4GB
+ return max(1, avail_bytes / (4 * (2**30))) # total / 4GB
except:
return 1
else:
@@ -1583,15 +1687,13 @@
# build_dir: relative path from source root to our output files.
# e.g. "out/Debug"
- build_dir = os.path.normpath(os.path.join(generator_dir,
- output_dir,
- config_name))
+ build_dir = os.path.normpath(
+ os.path.join(generator_dir, output_dir, config_name))
toplevel_build = os.path.join(options.toplevel_dir, build_dir)
master_ninja = ninja_syntax.Writer(
- OpenOutput(os.path.join(toplevel_build, 'build.ninja')),
- width=120)
+ OpenOutput(os.path.join(toplevel_build, 'build.ninja')), width=120)
case_sensitive_filesystem = True
# Put build-time support tools in out/{config_name}.
@@ -1608,14 +1710,15 @@
cc = 'cl.exe'
cxx = 'cl.exe'
ld = 'link.exe'
- gyp.msvs_emulation.GenerateEnvironmentFiles(
- toplevel_build, generator_flags, OpenOutput)
+ gyp.msvs_emulation.GenerateEnvironmentFiles(toplevel_build, generator_flags,
+ OpenOutput)
ld_host = '$ld'
- elif flavor in microsoft_flavors:
+ elif GetToolchainOrNone(flavor):
+ # TODO: starboardize.
cc = 'cl.exe'
cxx = 'cl.exe'
ld = 'link.exe'
- gyp.msvs_emulation.GenerateXB1EnvironmentFiles(
+ GetToolchainOrNone(flavor).GenerateEnvironmentFiles(
toplevel_build, generator_flags, OpenOutput)
ld_host = '$ld'
else:
@@ -1682,7 +1785,8 @@
master_ninja.variable('ld', os.environ.get('LD'))
master_ninja.variable('ar', os.environ.get('AR', 'ar'))
if flavor in ['ps3']:
- master_ninja.variable('prx_export_pickup', os.environ['PRX_EXPORT_PICKUP'])
+ master_ninja.variable('prx_export_pickup',
+ os.environ['PRX_EXPORT_PICKUP'])
ar_flags = os.environ.get('ARFLAGS', 'rcs')
master_ninja.variable('arFlags', ar_flags)
# On the PS3, when we use ps3snarl.exe with a response file, we cannot
@@ -1734,51 +1838,51 @@
master_ninja.newline()
if flavor not in microsoft_flavors:
- if flavor in sony_flavors :
+ if flavor in sony_flavors:
# uca := Unnamed Console A
dep_format = 'snc' if (flavor in ['ps3']) else 'uca'
master_ninja.rule(
- 'cc',
- description='CC $out',
- command=('$cc @$out.rsp'),
- rspfile='$out.rsp',
- rspfile_content=('-c $in -o $out '
- '-MMD $defines $includes $cflags $cflags_c '
- '$cflags_pch_c'),
- depfile='$out_no_ext.d',
- deps='gcc',
- depformat=dep_format)
+ 'cc',
+ description='CC $out',
+ command=('$cc @$out.rsp'),
+ rspfile='$out.rsp',
+ rspfile_content=('-c $in -o $out '
+ '-MMD $defines $includes $cflags $cflags_c '
+ '$cflags_pch_c'),
+ depfile='$out_no_ext.d',
+ deps='gcc',
+ depformat=dep_format)
master_ninja.rule(
- 'cxx',
- description='CXX $out',
- command=('$cxx @$out.rsp'),
- rspfile='$out.rsp',
- rspfile_content=('-c $in -o $out '
- '-MMD $defines $includes $cflags $cflags_cc '
- '$cflags_pch_cc'),
- depfile='$out_no_ext.d',
- deps='gcc',
- depformat=dep_format)
+ 'cxx',
+ description='CXX $out',
+ command=('$cxx @$out.rsp'),
+ rspfile='$out.rsp',
+ rspfile_content=('-c $in -o $out '
+ '-MMD $defines $includes $cflags $cflags_cc '
+ '$cflags_pch_cc'),
+ depfile='$out_no_ext.d',
+ deps='gcc',
+ depformat=dep_format)
else:
master_ninja.rule(
- 'cc',
- description='CC $out',
- command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
- '$cflags_pch_c -c $in -o $out'),
- deps='gcc',
- depfile='$out.d')
+ 'cc',
+ description='CC $out',
+ command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
+ '$cflags_pch_c -c $in -o $out'),
+ deps='gcc',
+ depfile='$out.d')
master_ninja.rule(
- 'cc_s',
- description='CC $out',
- command=('$cc $defines $includes $cflags $cflags_c '
- '$cflags_pch_c -c $in -o $out'))
+ 'cc_s',
+ description='CC $out',
+ command=('$cc $defines $includes $cflags $cflags_c '
+ '$cflags_pch_c -c $in -o $out'))
master_ninja.rule(
- 'cxx',
- description='CXX $out',
- command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
- '$cflags_pch_cc -c $in -o $out'),
- deps='gcc',
- depfile='$out.d')
+ 'cxx',
+ description='CXX $out',
+ command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
+ '$cflags_pch_cc -c $in -o $out'),
+ deps='gcc',
+ depfile='$out.d')
else:
cc_command = ('$cc /nologo /showIncludes /FC '
@@ -1786,33 +1890,32 @@
cxx_command = ('$cxx /nologo /showIncludes /FC '
'@$out.rsp /c $in /Fo$out /Fd$pdbname ')
master_ninja.rule(
- 'cc',
- description='CC $out',
- command=cc_command,
- deps='msvc',
- rspfile='$out.rsp',
- rspfile_content='$defines $includes $cflags $cflags_c')
+ 'cc',
+ description='CC $out',
+ command=cc_command,
+ deps='msvc',
+ rspfile='$out.rsp',
+ rspfile_content='$defines $includes $cflags $cflags_c')
master_ninja.rule(
- 'cxx',
- description='CXX $out',
- command=cxx_command,
- deps='msvc',
- rspfile='$out.rsp',
- rspfile_content='$defines $includes $cflags $cflags_cc')
+ 'cxx',
+ description='CXX $out',
+ command=cxx_command,
+ deps='msvc',
+ rspfile='$out.rsp',
+ rspfile_content='$defines $includes $cflags $cflags_cc')
master_ninja.rule(
- 'rc',
- description='RC $in',
- # Note: $in must be last otherwise rc.exe complains.
- command=('%s gyp-win-tool rc-wrapper '
- '$arch $rc $defines $includes $rcflags /fo$out $in' %
- python_exec))
+ 'rc',
+ description='RC $in',
+ # Note: $in must be last otherwise rc.exe complains.
+ command=(
+ '%s gyp-win-tool rc-wrapper '
+ '$arch $rc $defines $includes $rcflags /fo$out $in' % python_exec))
master_ninja.rule(
- 'asm',
- description='ASM $in',
- command=('%s gyp-win-tool asm-wrapper '
- '$arch $asm $defines $includes /c /Fo $out $in' %
- python_exec))
+ 'asm',
+ description='ASM $in',
+ command=('%s gyp-win-tool asm-wrapper '
+ '$arch $asm $defines $includes /c /Fo $out $in' % python_exec))
if flavor not in (['mac'] + microsoft_flavors):
alink_command = 'rm -f $out && $ar $arFlags $out @$out.rsp'
@@ -1827,17 +1930,17 @@
ld_cmd = '%s gyp-win-tool link-wrapper $arch $ld' % python_exec
master_ninja.rule(
- 'alink',
- description='AR $out',
- command=alink_command,
- rspfile='$out.rsp',
- rspfile_content='$in_newline')
+ 'alink',
+ description='AR $out',
+ command=alink_command,
+ rspfile='$out.rsp',
+ rspfile_content='$in_newline')
master_ninja.rule(
- 'alink_thin',
- description='AR $out',
- command=alink_thin_command,
- rspfile='$out.rsp',
- rspfile_content='$in_newline')
+ 'alink_thin',
+ description='AR $out',
+ command=alink_thin_command,
+ rspfile='$out.rsp',
+ rspfile_content='$in_newline')
if flavor in ['ps3']:
# TODO: Can we suppress the warnings from verlog.txt rather than
@@ -1848,18 +1951,17 @@
prx_flags = '--oformat=fsprx --prx-with-runtime --zgenprx -zgenstub'
master_ninja.rule(
- 'solink',
- description='LINK(PRX) $lib',
- restat=True,
- command=ld_cmd + ' @$prx.rsp',
- rspfile='$prx.rsp',
- rspfile_content='$ldflags %s -o $prx $in $libs' % prx_flags,
- pool='link_pool'
- )
+ 'solink',
+ description='LINK(PRX) $lib',
+ restat=True,
+ command=ld_cmd + ' @$prx.rsp',
+ rspfile='$prx.rsp',
+ rspfile_content='$ldflags %s -o $prx $in $libs' % prx_flags,
+ pool='link_pool')
master_ninja.rule(
- 'prx_export_pickup',
- description='PRX-EXPORT-PICKUP $out',
- command='$prx_export_pickup --output-src=$out $in')
+ 'prx_export_pickup',
+ description='PRX-EXPORT-PICKUP $out',
+ command='$prx_export_pickup --output-src=$out $in')
else: # Assume it is a Linux platform
# This allows targets that only need to depend on $lib's API to declare an
@@ -1872,27 +1974,30 @@
'%(solink)s && %(extract_toc)s > ${lib}.TOC; else '
'%(solink)s && %(extract_toc)s > ${lib}.tmp && '
'if ! cmp -s ${lib}.tmp ${lib}.TOC; then mv ${lib}.tmp ${lib}.TOC ; '
- 'fi; fi'
- % { 'solink':
- (ld_cmd +
- ' -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s'),
- 'extract_toc':
- ('{ readelf -d ${lib} | grep SONAME ; '
- 'nm -gD -f p ${lib} | cut -f1-2 -d\' \'; }')})
+ 'fi; fi' % {
+ 'solink':
+ (ld_cmd +
+ ' -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s'),
+ 'extract_toc': ('{ readelf -d ${lib} | grep SONAME ; '
+ 'nm -gD -f p ${lib} | cut -f1-2 -d\' \'; }')
+ })
master_ninja.rule(
- 'solink',
- description='SOLINK $lib',
- restat=True,
- command=(mtime_preserving_solink_base % {
- 'suffix': '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive '
- '$libs'}))
+ 'solink',
+ description='SOLINK $lib',
+ restat=True,
+ command=(mtime_preserving_solink_base % {
+ 'suffix':
+ '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive '
+ '$libs'
+ }))
master_ninja.rule(
- 'solink_module',
- description='SOLINK(module) $lib',
- restat=True,
- command=(mtime_preserving_solink_base % {
- 'suffix': '-Wl,--start-group $in $solibs -Wl,--end-group $libs'}))
+ 'solink_module',
+ description='SOLINK(module) $lib',
+ restat=True,
+ command=(mtime_preserving_solink_base % {
+ 'suffix': '-Wl,--start-group $in $solibs -Wl,--end-group $libs'
+ }))
if flavor in sony_flavors:
# PS3 and PS4 linkers don't know about rpath.
@@ -1901,20 +2006,19 @@
rpath = r'-Wl,-rpath=\$$ORIGIN/lib'
master_ninja.rule(
- 'link',
- description='LINK $out',
- command=(ld_cmd +' @$out.rsp'),
- rspfile='$out.rsp',
- rspfile_content=('$ldflags -o $out %s -Wl,--start-group $in $solibs '
- '-Wl,--end-group $libs' % rpath),
- pool='link_pool')
+ 'link',
+ description='LINK $out',
+ command=(ld_cmd + ' @$out.rsp'),
+ rspfile='$out.rsp',
+ rspfile_content=('$ldflags -o $out %s -Wl,--start-group $in $solibs '
+ '-Wl,--end-group $libs' % rpath),
+ pool='link_pool')
elif flavor in microsoft_flavors:
master_ninja.rule(
'alink',
description='LIB $out',
command=('%s gyp-win-tool link-wrapper $arch '
- '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' %
- python_exec),
+ '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' % python_exec),
rspfile='$out.rsp',
rspfile_content='$in_newline $libflags')
dlldesc = 'LINK(DLL) $dll'
@@ -1923,31 +2027,37 @@
'/PDB:$dll.pdb @$dll.rsp' % python_exec)
if not flavor in microsoft_flavors:
# XB1 doesn't need a manifest.
- dllcmd += (' && %s gyp-win-tool manifest-wrapper $arch '
- '$mt -nologo -manifest $manifests -out:$dll.manifest' %
- python_exec)
- master_ninja.rule('solink', description=dlldesc, command=dllcmd,
- rspfile='$dll.rsp',
- rspfile_content='$libs $in_newline $ldflags',
- restat=True)
- master_ninja.rule('solink_module', description=dlldesc, command=dllcmd,
- rspfile='$dll.rsp',
- rspfile_content='$libs $in_newline $ldflags',
- restat=True)
+ dllcmd += (
+ ' && %s gyp-win-tool manifest-wrapper $arch '
+ '$mt -nologo -manifest $manifests -out:$dll.manifest' % python_exec)
+ master_ninja.rule(
+ 'solink',
+ description=dlldesc,
+ command=dllcmd,
+ rspfile='$dll.rsp',
+ rspfile_content='$libs $in_newline $ldflags',
+ restat=True)
+ master_ninja.rule(
+ 'solink_module',
+ description=dlldesc,
+ command=dllcmd,
+ rspfile='$dll.rsp',
+ rspfile_content='$libs $in_newline $ldflags',
+ restat=True)
# Note that ldflags goes at the end so that it has the option of
# overriding default settings earlier in the command line.
if flavor == 'win':
- link_command=('%s gyp-win-tool link-wrapper $arch '
- '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp && '
- '%s gyp-win-tool manifest-wrapper $arch '
- '$mt -nologo -manifest $manifests -out:$out.manifest' %
- (python_exec, python_exec))
+ link_command = ('%s gyp-win-tool link-wrapper $arch '
+ '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp && '
+ '%s gyp-win-tool manifest-wrapper $arch '
+ '$mt -nologo -manifest $manifests -out:$out.manifest' %
+ (python_exec, python_exec))
else:
assert flavor in microsoft_flavors
# XB1 doesn't need a manifest.
- link_command=('%s gyp-win-tool link-wrapper $arch '
- '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' %
- (python_exec))
+ link_command = ('%s gyp-win-tool link-wrapper $arch '
+ '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' %
+ (python_exec))
master_ninja.rule(
'link',
@@ -1958,107 +2068,109 @@
pool='link_pool')
else:
master_ninja.rule(
- 'objc',
- description='OBJC $out',
- command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc '
- '$cflags_pch_objc -c $in -o $out'),
- depfile='$out.d')
+ 'objc',
+ description='OBJC $out',
+ command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc '
+ '$cflags_pch_objc -c $in -o $out'),
+ depfile='$out.d')
master_ninja.rule(
- 'objcxx',
- description='OBJCXX $out',
- command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc '
- '$cflags_pch_objcc -c $in -o $out'),
- depfile='$out.d')
+ 'objcxx',
+ description='OBJCXX $out',
+ command=(
+ '$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc '
+ '$cflags_pch_objcc -c $in -o $out'),
+ depfile='$out.d')
master_ninja.rule(
- 'alink',
- description='LIBTOOL-STATIC $out, POSTBUILDS',
- command='rm -f $out && '
- './gyp-mac-tool filter-libtool libtool $libtool_flags '
- '-static -o $out $in'
- '$postbuilds')
+ 'alink',
+ description='LIBTOOL-STATIC $out, POSTBUILDS',
+ command='rm -f $out && '
+ './gyp-mac-tool filter-libtool libtool $libtool_flags '
+ '-static -o $out $in'
+ '$postbuilds')
# Record the public interface of $lib in $lib.TOC. See the corresponding
# comment in the posix section above for details.
mtime_preserving_solink_base = (
'if [ ! -e $lib -o ! -e ${lib}.TOC ] || '
- # Always force dependent targets to relink if this library
- # reexports something. Handling this correctly would require
- # recursive TOC dumping but this is rare in practice, so punt.
- 'otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then '
- '%(solink)s && %(extract_toc)s > ${lib}.TOC; '
+ # Always force dependent targets to relink if this library
+ # reexports something. Handling this correctly would require
+ # recursive TOC dumping but this is rare in practice, so punt.
+ 'otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then '
+ '%(solink)s && %(extract_toc)s > ${lib}.TOC; '
'else '
- '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
- 'if ! cmp -s ${lib}.tmp ${lib}.TOC; then '
- 'mv ${lib}.tmp ${lib}.TOC ; '
- 'fi; '
- 'fi'
- % { 'solink': '$ld -shared $ldflags -o $lib %(suffix)s',
+ '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
+ 'if ! cmp -s ${lib}.tmp ${lib}.TOC; then '
+ 'mv ${lib}.tmp ${lib}.TOC ; '
+ 'fi; '
+ 'fi' % {
+ 'solink': '$ld -shared $ldflags -o $lib %(suffix)s',
'extract_toc':
- '{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
- 'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'})
+ '{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
+ 'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'
+ })
# TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass
# -bundle -single_module here (for osmesa.so).
master_ninja.rule(
- 'solink',
- description='SOLINK $lib, POSTBUILDS',
- restat=True,
- command=(mtime_preserving_solink_base % {
- 'suffix': '$in $solibs $libs$postbuilds'}))
+ 'solink',
+ description='SOLINK $lib, POSTBUILDS',
+ restat=True,
+ command=(mtime_preserving_solink_base % {
+ 'suffix': '$in $solibs $libs$postbuilds'
+ }))
master_ninja.rule(
- 'solink_module',
- description='SOLINK(module) $lib, POSTBUILDS',
- restat=True,
- command=(mtime_preserving_solink_base % {
- 'suffix': '$in $solibs $libs$postbuilds'}))
+ 'solink_module',
+ description='SOLINK(module) $lib, POSTBUILDS',
+ restat=True,
+ command=(mtime_preserving_solink_base % {
+ 'suffix': '$in $solibs $libs$postbuilds'
+ }))
master_ninja.rule(
- 'link',
- description='LINK $out, POSTBUILDS',
- command=('$ld $ldflags -o $out '
- '$in $solibs $libs$postbuilds'),
- pool='link_pool')
+ 'link',
+ description='LINK $out, POSTBUILDS',
+ command=('$ld $ldflags -o $out '
+ '$in $solibs $libs$postbuilds'),
+ pool='link_pool')
master_ninja.rule(
- 'infoplist',
- description='INFOPLIST $out',
- command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && '
- 'plutil -convert xml1 $out $out'))
+ 'infoplist',
+ description='INFOPLIST $out',
+ command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && '
+ 'plutil -convert xml1 $out $out'))
master_ninja.rule(
- 'mac_tool',
- description='MACTOOL $mactool_cmd $in',
- command='$env ./gyp-mac-tool $mactool_cmd $in $out')
+ 'mac_tool',
+ description='MACTOOL $mactool_cmd $in',
+ command='$env ./gyp-mac-tool $mactool_cmd $in $out')
master_ninja.rule(
- 'package_framework',
- description='PACKAGE FRAMEWORK $out, POSTBUILDS',
- command='./gyp-mac-tool package-framework $out $version$postbuilds '
- '&& touch $out')
+ 'package_framework',
+ description='PACKAGE FRAMEWORK $out, POSTBUILDS',
+ command='./gyp-mac-tool package-framework $out $version$postbuilds '
+ '&& touch $out')
if flavor in microsoft_flavors:
master_ninja.rule(
- 'stamp',
- description='STAMP $out',
- command='%s gyp-win-tool stamp $out' % python_exec)
+ 'stamp',
+ description='STAMP $out',
+ command='%s gyp-win-tool stamp $out' % python_exec)
master_ninja.rule(
- 'copy',
- description='COPY $in $out',
- command='%s gyp-win-tool recursive-mirror $in $out' % python_exec)
+ 'copy',
+ description='COPY $in $out',
+ command='%s gyp-win-tool recursive-mirror $in $out' % python_exec)
elif sys.platform in ['cygwin', 'win32']:
master_ninja.rule(
- 'stamp',
- description='STAMP $out',
- command='$python gyp-win-tool stamp $out')
+ 'stamp',
+ description='STAMP $out',
+ command='$python gyp-win-tool stamp $out')
master_ninja.rule(
- 'copy',
- description='COPY $in $out',
- command='$python gyp-win-tool recursive-mirror $in $out')
+ 'copy',
+ description='COPY $in $out',
+ command='$python gyp-win-tool recursive-mirror $in $out')
else:
master_ninja.rule(
- 'stamp',
- description='STAMP $out',
- command='${postbuilds}touch $out')
+ 'stamp', description='STAMP $out', command='${postbuilds}touch $out')
master_ninja.rule(
- 'copy',
- description='COPY $in $out',
- command='rm -rf $out && cp -af $in $out')
+ 'copy',
+ description='COPY $in $out',
+ command='rm -rf $out && cp -af $in $out')
master_ninja.newline()
# Output host building rules
@@ -2068,26 +2180,25 @@
cxx_command = ('$cxx /nologo /showIncludes /FC '
'@$out.rsp /c $in /Fo$out /Fd$pdbname ')
master_ninja.rule(
- 'cc_host',
- description='CC_HOST $out',
- command=cc_command,
- deps='msvc',
- rspfile='$out.rsp',
- rspfile_content='$defines $includes $cflags_host $cflags_c_host')
+ 'cc_host',
+ description='CC_HOST $out',
+ command=cc_command,
+ deps='msvc',
+ rspfile='$out.rsp',
+ rspfile_content='$defines $includes $cflags_host $cflags_c_host')
master_ninja.rule(
- 'cxx_host',
- description='CXX_HOST $out',
- command=cxx_command,
- deps='msvc',
- rspfile='$out.rsp',
- rspfile_content='$defines $includes $cflags_host $cflags_cc_host')
+ 'cxx_host',
+ description='CXX_HOST $out',
+ command=cxx_command,
+ deps='msvc',
+ rspfile='$out.rsp',
+ rspfile_content='$defines $includes $cflags_host $cflags_cc_host')
master_ninja.rule(
'alink_host',
description='LIB_HOST $out',
command=('%s gyp-win-tool link-wrapper $arch '
- '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' %
- python_exec),
+ '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' % python_exec),
rspfile='$out.rsp',
rspfile_content='$in_newline $libflags_host')
@@ -2095,14 +2206,13 @@
'alink_thin_host',
description='LIB_HOST $out',
command=('%s gyp-win-tool link-wrapper $arch '
- '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' %
- python_exec),
+ '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' % python_exec),
rspfile='$out.rsp',
rspfile_content='$in_newline $libflags_host')
- link_command=('%s gyp-win-tool link-wrapper $arch '
- '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' %
- (python_exec))
+ link_command = ('%s gyp-win-tool link-wrapper $arch '
+ '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' %
+ (python_exec))
master_ninja.rule(
'link_host',
@@ -2115,38 +2225,38 @@
cc_command = 'bash -c "$cc_host @$out.rsp"'
cxx_command = 'bash -c "$cxx_host @$out.rsp"'
master_ninja.rule(
- 'cc_host',
- description='CC_HOST $out',
- command=cc_command,
- rspfile='$out.rsp',
- rspfile_content=('-MMD -MF $out.d $defines $includes $cflags_host '
- '$cflags_c_host $cflags_pch_c -c $in -o $out'),
- depfile='$out.d')
+ 'cc_host',
+ description='CC_HOST $out',
+ command=cc_command,
+ rspfile='$out.rsp',
+ rspfile_content=('-MMD -MF $out.d $defines $includes $cflags_host '
+ '$cflags_c_host $cflags_pch_c -c $in -o $out'),
+ depfile='$out.d')
master_ninja.rule(
- 'cxx_host',
- description='CXX_HOST $out',
- command=cxx_command,
- rspfile='$out.rsp',
- rspfile_content=('-MMD -MF $out.d $defines $includes $cflags_host '
- '$cflags_cc_host $cflags_pch_cc -c $in -o $out'),
- depfile='$out.d')
+ 'cxx_host',
+ description='CXX_HOST $out',
+ command=cxx_command,
+ rspfile='$out.rsp',
+ rspfile_content=('-MMD -MF $out.d $defines $includes $cflags_host '
+ '$cflags_cc_host $cflags_pch_cc -c $in -o $out'),
+ depfile='$out.d')
alink_command = 'rm -f $out && $ar_host $arFlags_host $out @$out.rsp'
alink_thin_command = ('rm -f $out && $ar_host $arThinFlags_host $out '
'@$out.rsp')
master_ninja.rule(
- 'alink_host',
- description='AR_HOST $out',
- command='bash -c "' + alink_command + '"',
- rspfile='$out.rsp',
- rspfile_content='$in_newline')
+ 'alink_host',
+ description='AR_HOST $out',
+ command='bash -c "' + alink_command + '"',
+ rspfile='$out.rsp',
+ rspfile_content='$in_newline')
master_ninja.rule(
- 'alink_thin_host',
- description='AR_HOST $out',
- command='bash -c "' + alink_thin_command + '"',
- rspfile='$out.rsp',
- rspfile_content='$in_newline')
+ 'alink_thin_host',
+ description='AR_HOST $out',
+ command='bash -c "' + alink_thin_command + '"',
+ rspfile='$out.rsp',
+ rspfile_content='$in_newline')
beginlinkinlibs = ''
endlinkinlibs = ''
if is_linux:
@@ -2154,16 +2264,15 @@
endlinkinlibs = '-Wl,--end-group'
rpath = '-Wl,-rpath=\$$ORIGIN/lib'
master_ninja.rule(
- 'link_host',
- description='LINK_HOST $out',
- command=('bash -c "$ld_host $ldflags_host -o $out %s '
- '%s $in $solibs %s $libs"' % (rpath,
- beginlinkinlibs, endlinkinlibs)))
+ 'link_host',
+ description='LINK_HOST $out',
+ command=('bash -c "$ld_host $ldflags_host -o $out %s '
+ '%s $in $solibs %s $libs"' % (rpath, beginlinkinlibs,
+ endlinkinlibs)))
all_targets = set()
for build_file in params['build_files']:
- for target in gyp.common.AllTargets(target_list,
- target_dicts,
+ for target in gyp.common.AllTargets(target_list, target_dicts,
os.path.normpath(build_file)):
all_targets.add(target)
all_outputs = set()
@@ -2181,7 +2290,7 @@
this_make_global_settings = data[build_file].get('make_global_settings', [])
assert make_global_settings == this_make_global_settings, (
- "make_global_settings needs to be the same for all targets.")
+ 'make_global_settings needs to be the same for all targets.')
spec = target_dicts[qualified_target]
@@ -2190,7 +2299,8 @@
default_project = name
else:
raise Exception('More than one default_project specified.'
- 'First in {0} and now in {1}'.format(default_project, name))
+ 'First in {0} and now in {1}'.format(
+ default_project, name))
if flavor == 'mac':
gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
@@ -2204,14 +2314,18 @@
output_file = os.path.join(obj, base_path, name + '.ninja')
abs_build_dir = os.path.abspath(toplevel_build)
- writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir,
- OpenOutput(os.path.join(toplevel_build, output_file)),
- flavor, case_sensitive_filesystem,
- abs_build_dir=abs_build_dir)
+ writer = NinjaWriter(
+ qualified_target,
+ target_outputs,
+ base_path,
+ build_dir,
+ OpenOutput(os.path.join(toplevel_build, output_file)),
+ flavor,
+ case_sensitive_filesystem,
+ abs_build_dir=abs_build_dir)
master_ninja.subninja(output_file)
- target = writer.WriteSpec(
- spec, config_name, generator_flags)
+ target = writer.WriteSpec(spec, config_name, generator_flags)
if target:
if name != target.FinalOutput():
out_name = name
@@ -2229,8 +2343,9 @@
master_ninja.newline()
master_ninja.comment('Short names for targets.')
for short_name in target_short_names:
- master_ninja.build(short_name, 'phony', [x.FinalOutput() for x in
- target_short_names[short_name]])
+ master_ninja.build(short_name, 'phony', [
+ x.FinalOutput() for x in target_short_names[short_name]
+ ])
if all_outputs:
master_ninja.newline()
@@ -2261,6 +2376,7 @@
def GenerateOutput(target_list, target_dicts, data, params):
user_config = params.get('generator_flags', {}).get('config', None)
+ # TODO: Replace MSVSUtil with calls to abstract toolchain.
if gyp.common.GetFlavor(params) in microsoft_flavors:
target_list, target_dicts = MSVSUtil.ShardTargets(target_list, target_dicts)
if user_config:
@@ -2273,8 +2389,8 @@
pool = multiprocessing.Pool(len(config_names))
arglists = []
for config_name in config_names:
- arglists.append(
- (target_list, target_dicts, data, params, config_name))
+ arglists.append((target_list, target_dicts, data, params,
+ config_name))
pool.map(CallGenerateOutputForConfig, arglists)
except KeyboardInterrupt, e:
pool.terminate()