Import Cobalt 6.14197
diff --git a/src/base/atomicops.h b/src/base/atomicops.h
index 230a601..6d04858 100644
--- a/src/base/atomicops.h
+++ b/src/base/atomicops.h
@@ -31,6 +31,10 @@
#include "base/basictypes.h"
#include "build/build_config.h"
+#if defined(OS_STARBOARD)
+#include "starboard/atomic.h"
+#endif // defined(OS_STARBOARD)
+
#if (defined(OS_WIN) && defined(ARCH_CPU_64_BITS)) || defined(__LB_XB360__) || defined(__LB_XB1__)
// windows.h #defines this (only on x64). This causes problems because the
// public API also uses MemoryBarrier at the public name for this fence. So, on
@@ -43,6 +47,12 @@
namespace base {
namespace subtle {
+#if defined(OS_STARBOARD)
+typedef SbAtomic32 Atomic32;
+#if defined(ARCH_CPU_64_BITS)
+typedef SbAtomic64 Atomic64;
+#endif // defined(ARCH_CPU_64_BITS)
+#else
typedef int32 Atomic32;
#ifdef ARCH_CPU_64_BITS
// We need to be able to go between Atomic64 and AtomicWord implicitly. This
@@ -55,6 +65,7 @@
typedef intptr_t Atomic64;
#endif
#endif
+#endif // defined(OS_STARBOARD)
// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or
// Atomic64 routines below, depending on your architecture.
diff --git a/src/base/callback_helpers.h b/src/base/callback_helpers.h
index 52cb71b..ada0090 100644
--- a/src/base/callback_helpers.h
+++ b/src/base/callback_helpers.h
@@ -25,6 +25,27 @@
return ret;
}
+inline bool ResetAndRunIfNotNull(base::Closure* cb) {
+ if (cb->is_null()) {
+ return false;
+ }
+ base::Closure ret(*cb);
+ cb->Reset();
+ ret.Run();
+ return true;
+}
+
+template <typename Sig, typename ParamType>
+bool ResetAndRunIfNotNull(base::Callback<Sig>* cb, const ParamType& param) {
+ if (cb->is_null()) {
+ return false;
+ }
+ base::Callback<Sig> ret(*cb);
+ cb->Reset();
+ ret.Run(param);
+ return true;
+}
+
} // namespace base
#endif // BASE_CALLBACK_HELPERS_H_
diff --git a/src/base/file_util_proxy_unittest.cc b/src/base/file_util_proxy_unittest.cc
index e238b32..cfa5023 100644
--- a/src/base/file_util_proxy_unittest.cc
+++ b/src/base/file_util_proxy_unittest.cc
@@ -376,11 +376,17 @@
ASSERT_EQ(10, info.size);
// Run.
+ PlatformFile file = GetTestPlatformFile(PLATFORM_FILE_OPEN |
+ PLATFORM_FILE_WRITE);
FileUtilProxy::Truncate(
file_task_runner(),
- GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE),
+ file,
7,
Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ FileUtilProxy::Flush(
+ file_task_runner(),
+ file,
+ Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
MessageLoop::current()->Run();
// Verify.
@@ -409,11 +415,17 @@
ASSERT_EQ(10, info.size);
// Run.
+ PlatformFile file = GetTestPlatformFile(PLATFORM_FILE_OPEN |
+ PLATFORM_FILE_WRITE);
FileUtilProxy::Truncate(
file_task_runner(),
- GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE),
+ file,
53,
Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ FileUtilProxy::Flush(
+ file_task_runner(),
+ file,
+ Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
MessageLoop::current()->Run();
// Verify.
diff --git a/src/base/file_util_unittest.cc b/src/base/file_util_unittest.cc
index 6f4e111..81fdcc7 100644
--- a/src/base/file_util_unittest.cc
+++ b/src/base/file_util_unittest.cc
@@ -211,7 +211,16 @@
// Simple function to dump some text into a new file.
void CreateTextFile(const FilePath& filename,
const std::wstring& contents) {
-#if defined(__LB_PS3__)
+#if defined(OS_STARBOARD)
+ SbFile file =
+ SbFileOpen(filename.value().c_str(), kSbFileCreateAlways | kSbFileWrite,
+ NULL /*out_created*/, NULL /*out_error*/);
+ EXPECT_TRUE(file != kSbFileInvalid);
+ std::string utf8 = WideToUTF8(contents);
+ int bytes_written = SbFileWrite(file, utf8.c_str(), utf8.length());
+ EXPECT_TRUE(bytes_written == utf8.length());
+ SbFileClose(file);
+#elif defined(__LB_PS3__)
FILE *file = fopen(filename.value().c_str(), "w");
ASSERT_TRUE(file != NULL);
fputws(contents.c_str(), file);
@@ -232,18 +241,35 @@
ASSERT_TRUE(file.is_open());
file << contents;
file.close();
-#endif
+#endif // defined(OS_STARBOARD)
}
// Simple function to take out some text from a file.
std::wstring ReadTextFile(const FilePath& filename) {
+#if defined(OS_STARBOARD)
+ SbFile file =
+ SbFileOpen(filename.value().c_str(), kSbFileOpenOnly | kSbFileRead,
+ NULL /*out_created*/, NULL /*out_error*/);
+ EXPECT_TRUE(file != kSbFileInvalid);
+ char utf8_buffer[64] = {0};
+ int bytes_read =
+ SbFileRead(file, utf8_buffer, SB_ARRAY_SIZE_INT(utf8_buffer));
+ EXPECT_TRUE(bytes_read >= 0);
+ SbFileClose(file);
+ std::wstring result;
+ bool did_convert =
+ UTF8ToWide(utf8_buffer, SbStringGetLength(utf8_buffer), &result);
+ EXPECT_TRUE(did_convert);
+ return result;
+#elif defined(__LB_PS3__)
wchar_t contents[64];
-#if defined(__LB_PS3__)
FILE *file = fopen(filename.value().c_str(), "r");
EXPECT_TRUE(file != NULL);
fgetws(contents, arraysize(contents), file);
fclose(file);
+ return std::wstring(contents);
#elif defined(__LB_WIIU__)
+ wchar_t contents[64];
char mb_sequence_buffer[64];
int fd = open(filename.value().c_str(), O_RDONLY);
EXPECT_TRUE(fd >= 0);
@@ -253,14 +279,16 @@
const char * mb_sequence = mb_sequence_buffer;
size_t nbytes = mbsrtowcs(contents, &mb_sequence, sizeof(contents), NULL);
EXPECT_TRUE(mb_sequence == NULL);
+ return std::wstring(contents);
#else
+ wchar_t contents[64];
std::wifstream file;
file.open(filename.value().c_str());
EXPECT_TRUE(file.is_open());
file.getline(contents, arraysize(contents));
file.close();
-#endif
return std::wstring(contents);
+#endif
}
#if defined(OS_WIN)
diff --git a/src/base/i18n/file_util_icu.cc b/src/base/i18n/file_util_icu.cc
index fc07d13..0dd1523 100644
--- a/src/base/i18n/file_util_icu.cc
+++ b/src/base/i18n/file_util_icu.cc
@@ -159,7 +159,7 @@
// Windows uses UTF-16 encoding for filenames.
U16_NEXT(file_name->data(), cursor, static_cast<int>(file_name->length()),
code_point);
-#elif defined(OS_POSIX) || defined(OS_STARBAORD)
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
// Linux doesn't actually define an encoding. It basically allows anything
// except for a few special ASCII characters.
unsigned char cur_char = static_cast<unsigned char>((*file_name)[cursor++]);
diff --git a/src/base/location.cc b/src/base/location.cc
index b6ded2c..fe8581a 100644
--- a/src/base/location.cc
+++ b/src/base/location.cc
@@ -92,9 +92,11 @@
BASE_EXPORT const void* GetProgramCounter() {
#if defined(COMPILER_MSVC)
return _ReturnAddress();
+#elif defined(COMPILER_SNC)
+ return __builtin_return_address(0);
#elif defined(COMPILER_GCC) && !defined(__LB_PS3__) && !defined(__LB_WIIU__)
return __builtin_extract_return_addr(__builtin_return_address(0));
-#endif // COMPILER_GCC
+#endif // defined(COMPILER_MSVC)
return NULL;
}
diff --git a/src/base/platform_file_unittest.cc b/src/base/platform_file_unittest.cc
index e73d930..607dc66 100644
--- a/src/base/platform_file_unittest.cc
+++ b/src/base/platform_file_unittest.cc
@@ -197,16 +197,8 @@
// Make sure the file was extended.
int64 file_size = 0;
-#if defined(__LB_SHELL__)
- // At this point, the file buffer may not have been flushed to disk,
- // so reading the new file size from disk is flaky. Instead, read the file
- // size from the file descriptor using GetPlatformFileInfo.
- base::PlatformFileInfo file_info;
- EXPECT_TRUE(base::GetPlatformFileInfo(file, &file_info));
- file_size = file_info.size;
-#else
+ base::FlushPlatformFile(file);
EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
-#endif
EXPECT_EQ(kOffsetBeyondEndOfFile + kPartialWriteLength, file_size);
// Make sure the file was zero-padded.
@@ -246,6 +238,7 @@
const int kExtendedFileLength = 10;
int64 file_size = 0;
EXPECT_TRUE(base::TruncatePlatformFile(file, kExtendedFileLength));
+ base::FlushPlatformFile(file);
EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
EXPECT_EQ(kExtendedFileLength, file_size);
@@ -261,6 +254,7 @@
// Truncate the file.
const int kTruncatedFileLength = 2;
EXPECT_TRUE(base::TruncatePlatformFile(file, kTruncatedFileLength));
+ base::FlushPlatformFile(file);
EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
EXPECT_EQ(kTruncatedFileLength, file_size);
diff --git a/src/base/stl_util.h b/src/base/stl_util.h
index c612769..483fd53 100644
--- a/src/base/stl_util.h
+++ b/src/base/stl_util.h
@@ -7,7 +7,7 @@
#include <algorithm>
#include <functional>
-#if defined(__LB_SHELL__) // TODO: Starboard?
+#if defined(__LB_SHELL__) || defined(STARBOARD)
#include <iterator>
#endif
#include <string>
diff --git a/src/base/stringprintf_unittest.cc b/src/base/stringprintf_unittest.cc
index d8fdfee..143fc42 100644
--- a/src/base/stringprintf_unittest.cc
+++ b/src/base/stringprintf_unittest.cc
@@ -180,7 +180,7 @@
// lbshell platforms do not support positional parameters,
// and lbshell does not use the few parts of chromium that
// leverage positional parameter support in the OS.
-#if !defined(__LB_SHELL__)
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
// Test that the positional parameters work.
TEST(StringPrintfTest, PositionalParameters) {
std::string out;
diff --git a/src/base/test/test_file_util_starboard.cc b/src/base/test/test_file_util_starboard.cc
index ef4cdad..f8127c6 100644
--- a/src/base/test/test_file_util_starboard.cc
+++ b/src/base/test/test_file_util_starboard.cc
@@ -36,7 +36,7 @@
// Mostly a verbatim copy of CopyDirectory
bool CopyRecursiveDirNoCache(const FilePath& source_dir,
const FilePath& dest_dir) {
- char top_dir[PATH_MAX];
+ char top_dir[SB_FILE_MAX_PATH];
if (base::strlcpy(top_dir, source_dir.value().c_str(), arraysize(top_dir)) >=
arraysize(top_dir)) {
return false;
diff --git a/src/base/third_party/nspr/prcpucfg_starboard.h b/src/base/third_party/nspr/prcpucfg_starboard.h
index 5bdf40d..3aaa298 100644
--- a/src/base/third_party/nspr/prcpucfg_starboard.h
+++ b/src/base/third_party/nspr/prcpucfg_starboard.h
@@ -55,7 +55,7 @@
# define IS_64
#endif
-#if SB_IS(ARCH_PPC) && SB_IS(32_BIT)
+#if SB_IS(ARCH_PPC)
#define PR_BYTES_PER_BYTE 1
#define PR_BYTES_PER_SHORT 2
#define PR_BYTES_PER_INT 4
diff --git a/src/cobalt/audio/async_audio_decoder.cc b/src/cobalt/audio/async_audio_decoder.cc
index cb23a87..ae86fb4 100644
--- a/src/cobalt/audio/async_audio_decoder.cc
+++ b/src/cobalt/audio/async_audio_decoder.cc
@@ -19,6 +19,7 @@
#include "base/bind.h"
#include "base/logging.h"
#include "cobalt/audio/audio_file_reader.h"
+#include "cobalt/audio/audio_helpers.h"
namespace cobalt {
namespace audio {
@@ -34,11 +35,13 @@
AudioFileReader::TryCreate(audio_data, size));
if (reader) {
- decode_finish_callback.Run(
- reader->sample_rate(), reader->number_of_frames(),
- reader->number_of_channels(), reader->sample_data());
+ decode_finish_callback.Run(reader->sample_rate(),
+ reader->number_of_frames(),
+ reader->number_of_channels(),
+ reader->sample_data(), reader->sample_type());
} else {
- decode_finish_callback.Run(0.f, 0, 0, scoped_array<uint8>());
+ decode_finish_callback.Run(0.f, 0, 0, scoped_array<uint8>(),
+ kSampleTypeFloat32);
}
}
diff --git a/src/cobalt/audio/async_audio_decoder.h b/src/cobalt/audio/async_audio_decoder.h
index b78d2a7..3827260 100644
--- a/src/cobalt/audio/async_audio_decoder.h
+++ b/src/cobalt/audio/async_audio_decoder.h
@@ -20,6 +20,7 @@
#include "base/callback.h"
#include "base/threading/thread.h"
#include "cobalt/audio/audio_buffer.h"
+#include "cobalt/audio/audio_helpers.h"
#include "cobalt/dom/array_buffer.h"
namespace cobalt {
@@ -27,9 +28,10 @@
class AsyncAudioDecoder {
public:
- typedef base::Callback<void(
- float sample_rate, int32 number_of_frames, int32 number_of_channels,
- scoped_array<uint8> channels_data)> DecodeFinishCallback;
+ typedef base::Callback<void(float sample_rate, int32 number_of_frames,
+ int32 number_of_channels,
+ scoped_array<uint8> channels_data,
+ SampleType sample_type)> DecodeFinishCallback;
AsyncAudioDecoder();
diff --git a/src/cobalt/audio/audio.gyp b/src/cobalt/audio/audio.gyp
index f13d2c3..350a599 100644
--- a/src/cobalt/audio/audio.gyp
+++ b/src/cobalt/audio/audio.gyp
@@ -37,6 +37,7 @@
'audio_file_reader.h',
'audio_file_reader_wav.cc',
'audio_file_reader_wav.h',
+ 'audio_helpers.h',
'audio_node.cc',
'audio_node.h',
'audio_node_input.cc',
diff --git a/src/cobalt/audio/audio_buffer.cc b/src/cobalt/audio/audio_buffer.cc
index f6b8d6b..a93eba2 100644
--- a/src/cobalt/audio/audio_buffer.cc
+++ b/src/cobalt/audio/audio_buffer.cc
@@ -16,6 +16,7 @@
#include "cobalt/audio/audio_buffer.h"
+#include "cobalt/audio/audio_helpers.h"
#include "cobalt/dom/dom_exception.h"
namespace cobalt {
@@ -24,35 +25,54 @@
AudioBuffer::AudioBuffer(script::EnvironmentSettings* settings,
float sample_rate, int32 number_of_frames,
int32 number_of_channels,
- scoped_array<uint8> channels_data)
- : sample_rate_(sample_rate), length_(number_of_frames) {
+ scoped_array<uint8> channels_data,
+ SampleType sample_type)
+ : sample_rate_(sample_rate),
+ length_(number_of_frames),
+ sample_type_(sample_type) {
DCHECK_GT(sample_rate_, 0);
DCHECK_GT(length_, 0);
DCHECK_GT(number_of_channels, 0);
// Create an ArrayBuffer stores sample data from all channels.
+ const uint32 length =
+ number_of_frames * number_of_channels *
+ (sample_type == kSampleTypeFloat32 ? sizeof(float) : sizeof(int16));
scoped_refptr<dom::ArrayBuffer> array_buffer(new dom::ArrayBuffer(
- settings, dom::ArrayBuffer::kFromHeap, channels_data.Pass(),
- number_of_frames * number_of_channels * sizeof(float)));
+ settings, dom::ArrayBuffer::kFromHeap, channels_data.Pass(), length));
- channels_data_.resize(static_cast<size_t>(number_of_channels));
- // Each channel should have |number_of_frames * sizeof(float)| bytes. We
- // create |number_of_channels| of Float32Array as views into the above
- // ArrayBuffer, this doesn't need any extra allocation.
- uint32 start_offset_in_bytes = 0;
- for (int32 i = 0; i < number_of_channels; ++i) {
- channels_data_[static_cast<size_t>(i)] =
- new dom::Float32Array(settings, array_buffer, start_offset_in_bytes,
+ // Each channel should have |number_of_frames * size_of_sample_type| bytes.
+ // We create |number_of_channels| of {Float32,Int16}Array as views into the
+ // above ArrayBuffer. This does not need any extra allocation.
+ if (sample_type == kSampleTypeFloat32) {
+ channels_data_.resize(static_cast<size_t>(number_of_channels));
+ uint32 start_offset_in_bytes = 0;
+ for (int32 i = 0; i < number_of_channels; ++i) {
+ channels_data_[static_cast<size_t>(i)] =
+ new dom::Float32Array(settings, array_buffer, start_offset_in_bytes,
+ static_cast<uint32>(number_of_frames), NULL);
+ start_offset_in_bytes += number_of_frames * sizeof(float);
+ }
+ } else if (sample_type == kSampleTypeInt16) {
+ channels_int16_data_.resize(static_cast<size_t>(number_of_channels));
+ uint32 start_offset_in_bytes = 0;
+ for (int32 i = 0; i < number_of_channels; ++i) {
+ channels_int16_data_[static_cast<size_t>(i)] =
+ new dom::Int16Array(settings, array_buffer, start_offset_in_bytes,
static_cast<uint32>(number_of_frames), NULL);
- start_offset_in_bytes += number_of_frames * sizeof(float);
+ start_offset_in_bytes += number_of_frames * sizeof(int16);
+ }
+ } else {
+ NOTREACHED();
}
}
scoped_refptr<dom::Float32Array> AudioBuffer::GetChannelData(
uint32 channel_index, script::ExceptionState* exception_state) const {
+ DCHECK_EQ(sample_type_, kSampleTypeFloat32);
// The index value MUST be less than number_of_channels() or an INDEX_SIZE_ERR
// exception MUST be thrown.
- if (channel_index >= static_cast<uint32>(number_of_channels())) {
+ if (channel_index >= channels_data_.size()) {
dom::DOMException::Raise(dom::DOMException::kIndexSizeErr, exception_state);
return NULL;
}
@@ -60,5 +80,18 @@
return channels_data_[channel_index];
}
+scoped_refptr<dom::Int16Array> AudioBuffer::GetChannelDataInt16(
+ uint32 channel_index, script::ExceptionState* exception_state) const {
+ DCHECK_EQ(sample_type_, kSampleTypeInt16);
+ // The index value MUST be less than number_of_channels() or an INDEX_SIZE_ERR
+ // exception MUST be thrown.
+ if (channel_index >= channels_int16_data_.size()) {
+ dom::DOMException::Raise(dom::DOMException::kIndexSizeErr, exception_state);
+ return NULL;
+ }
+
+ return channels_int16_data_[channel_index];
+}
+
} // namespace audio
} // namespace cobalt
diff --git a/src/cobalt/audio/audio_buffer.h b/src/cobalt/audio/audio_buffer.h
index a1fab1e..c58fef5 100644
--- a/src/cobalt/audio/audio_buffer.h
+++ b/src/cobalt/audio/audio_buffer.h
@@ -21,7 +21,9 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" // For scoped_array
+#include "cobalt/audio/audio_helpers.h"
#include "cobalt/dom/float32_array.h"
+#include "cobalt/dom/int16_array.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/wrappable.h"
@@ -45,7 +47,7 @@
// half.
AudioBuffer(script::EnvironmentSettings* settings, float sample_rate,
int32 number_of_frames, int32 number_of_channels,
- scoped_array<uint8> channels_data);
+ scoped_array<uint8> channels_data, SampleType sample_type);
// Web API: AudioBuffer
//
@@ -60,22 +62,35 @@
// The number of discrete audio channels.
int32 number_of_channels() const {
- return static_cast<int32>(channels_data_.size());
+ if (sample_type_ == kSampleTypeFloat32) {
+ return static_cast<int32>(channels_data_.size());
+ } else if (sample_type_ == kSampleTypeInt16) {
+ return static_cast<int32>(channels_int16_data_.size());
+ } else {
+ NOTREACHED();
+ return 0;
+ }
}
// Represents the PCM audio data for the specific channel.
scoped_refptr<dom::Float32Array> GetChannelData(
uint32 channel_index, script::ExceptionState* exception_state) const;
+ scoped_refptr<dom::Int16Array> GetChannelDataInt16(
+ uint32 channel_index, script::ExceptionState* exception_state) const;
+
DEFINE_WRAPPABLE_TYPE(AudioBuffer);
private:
typedef std::vector<scoped_refptr<dom::Float32Array> > Float32ArrayVector;
+ typedef std::vector<scoped_refptr<dom::Int16Array> > Int16ArrayVector;
float sample_rate_;
int32 length_;
+ SampleType sample_type_;
Float32ArrayVector channels_data_;
+ Int16ArrayVector channels_int16_data_;
DISALLOW_COPY_AND_ASSIGN(AudioBuffer);
};
diff --git a/src/cobalt/audio/audio_buffer_source_node.cc b/src/cobalt/audio/audio_buffer_source_node.cc
index 6f3538f..222ab7a 100644
--- a/src/cobalt/audio/audio_buffer_source_node.cc
+++ b/src/cobalt/audio/audio_buffer_source_node.cc
@@ -94,7 +94,7 @@
}
scoped_ptr<ShellAudioBus> AudioBufferSourceNode::PassAudioBusFromSource(
- int32 number_of_frames) {
+ int32 number_of_frames, SampleType sample_type) {
// This is called by Audio thread.
audio_lock()->AssertLocked();
@@ -108,21 +108,42 @@
size_t channels = static_cast<size_t>(buffer_->number_of_channels());
- std::vector<float*> audio_buffer;
- for (size_t i = 0; i < channels; ++i) {
- scoped_refptr<dom::Float32Array> buffer_data =
- buffer_->GetChannelData(static_cast<uint32>(i), NULL);
- scoped_refptr<dom::Float32Array> sub_array = buffer_data->Subarray(
- NULL, read_index_, read_index_ + number_of_frames);
- audio_buffer.push_back(sub_array->data());
+ if (sample_type == kSampleTypeFloat32) {
+ std::vector<float*> audio_buffer(channels, NULL);
+ for (size_t i = 0; i < channels; ++i) {
+ scoped_refptr<dom::Float32Array> buffer_data =
+ buffer_->GetChannelData(static_cast<uint32>(i), NULL);
+ scoped_refptr<dom::Float32Array> sub_array = buffer_data->Subarray(
+ NULL, read_index_, read_index_ + number_of_frames);
+ audio_buffer[i] = sub_array->data();
+ }
+
+ read_index_ += number_of_frames;
+
+ scoped_ptr<ShellAudioBus> audio_bus(
+ new ShellAudioBus(static_cast<size_t>(number_of_frames), audio_buffer));
+
+ return audio_bus.Pass();
+ } else if (sample_type == kSampleTypeInt16) {
+ std::vector<int16*> audio_buffer(channels, NULL);
+ for (size_t i = 0; i < channels; ++i) {
+ scoped_refptr<dom::Int16Array> buffer_data =
+ buffer_->GetChannelDataInt16(static_cast<uint32>(i), NULL);
+ scoped_refptr<dom::Int16Array> sub_array = buffer_data->Subarray(
+ NULL, read_index_, read_index_ + number_of_frames);
+ audio_buffer[i] = sub_array->data();
+ }
+
+ read_index_ += number_of_frames;
+
+ scoped_ptr<ShellAudioBus> audio_bus(
+ new ShellAudioBus(static_cast<size_t>(number_of_frames), audio_buffer));
+
+ return audio_bus.Pass();
}
- read_index_ += number_of_frames;
-
- scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(static_cast<size_t>(number_of_frames), audio_buffer));
-
- return audio_bus.Pass();
+ NOTREACHED();
+ return scoped_ptr<ShellAudioBus>();
}
} // namespace audio
diff --git a/src/cobalt/audio/audio_buffer_source_node.h b/src/cobalt/audio/audio_buffer_source_node.h
index ee808d3..ce4f42f 100644
--- a/src/cobalt/audio/audio_buffer_source_node.h
+++ b/src/cobalt/audio/audio_buffer_source_node.h
@@ -72,7 +72,7 @@
}
scoped_ptr<ShellAudioBus> PassAudioBusFromSource(
- int32 number_of_frames) OVERRIDE;
+ int32 number_of_frames, SampleType sample_type) OVERRIDE;
DEFINE_WRAPPABLE_TYPE(AudioBufferSourceNode);
diff --git a/src/cobalt/audio/audio_context.cc b/src/cobalt/audio/audio_context.cc
index 178d7b6..0962977 100644
--- a/src/cobalt/audio/audio_context.cc
+++ b/src/cobalt/audio/audio_context.cc
@@ -85,13 +85,14 @@
void AudioContext::DecodeFinish(int callback_id, float sample_rate,
int32 number_of_frames,
int32 number_of_channels,
- scoped_array<uint8> channels_data) {
+ scoped_array<uint8> channels_data,
+ SampleType sample_type) {
if (!main_message_loop_->BelongsToCurrentThread()) {
main_message_loop_->PostTask(
FROM_HERE,
base::Bind(&AudioContext::DecodeFinish, weak_this_, callback_id,
sample_rate, number_of_frames, number_of_channels,
- base::Passed(&channels_data)));
+ base::Passed(&channels_data), sample_type));
return;
}
@@ -105,7 +106,7 @@
if (channels_data) {
const scoped_refptr<AudioBuffer>& audio_buffer =
new AudioBuffer(info->env_settings, sample_rate, number_of_frames,
- number_of_channels, channels_data.Pass());
+ number_of_channels, channels_data.Pass(), sample_type);
info->success_callback.value().Run(audio_buffer);
} else if (info->error_callback) {
info->error_callback.value().value().Run();
diff --git a/src/cobalt/audio/audio_context.h b/src/cobalt/audio/audio_context.h
index 5cd34a8..9b3ac6f 100644
--- a/src/cobalt/audio/audio_context.h
+++ b/src/cobalt/audio/audio_context.h
@@ -165,8 +165,8 @@
void DecodeAudioDataInternal(scoped_ptr<DecodeCallbackInfo> info);
void DecodeFinish(int callback_id, float sample_rate, int32 number_of_frames,
- int32 number_of_channels,
- scoped_array<uint8> channels_data);
+ int32 number_of_channels, scoped_array<uint8> channels_data,
+ SampleType sample_type);
base::WeakPtrFactory<AudioContext> weak_ptr_factory_;
// We construct a WeakPtr upon AudioContext's construction in order to
diff --git a/src/cobalt/audio/audio_destination_node.h b/src/cobalt/audio/audio_destination_node.h
index 7a760a0..14fab99 100644
--- a/src/cobalt/audio/audio_destination_node.h
+++ b/src/cobalt/audio/audio_destination_node.h
@@ -20,6 +20,7 @@
#include <vector>
#include "cobalt/audio/audio_device.h"
+#include "cobalt/audio/audio_helpers.h"
#include "cobalt/audio/audio_node.h"
#include "media/base/shell_audio_bus.h"
@@ -47,8 +48,8 @@
uint32 max_channel_count() const { return max_channel_count_; }
// From AudioNode.
- scoped_ptr<ShellAudioBus> PassAudioBusFromSource(
- int32 /*number_of_frames*/) OVERRIDE {
+ scoped_ptr<ShellAudioBus> PassAudioBusFromSource(int32, /*number_of_frames*/
+ SampleType) OVERRIDE {
NOTREACHED();
return scoped_ptr<ShellAudioBus>();
}
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc
index 8ab8b57..e666970 100644
--- a/src/cobalt/audio/audio_device.cc
+++ b/src/cobalt/audio/audio_device.cc
@@ -18,6 +18,7 @@
#include "base/debug/trace_event.h"
#include "base/memory/scoped_ptr.h"
+#include "cobalt/audio/audio_helpers.h"
#if defined(OS_STARBOARD)
#include "starboard/audio_sink.h"
#include "starboard/configuration.h"
@@ -46,41 +47,6 @@
#if defined(SB_USE_SB_AUDIO_SINK)
-namespace {
-// Helper function to compute the size of the two valid starboard audio sample
-// types.
-size_t GetSampleSize(SbMediaAudioSampleType sample_type) {
- switch (sample_type) {
- case kSbMediaAudioSampleTypeFloat32:
- return sizeof(float);
- case kSbMediaAudioSampleTypeInt16:
- return sizeof(int16);
- }
- NOTREACHED();
- return 0u;
-}
-
-const float kMaxInt16AsFloat32 = 32767.0f;
-
-template <typename SourceType, typename DestType>
-DestType ConvertSample(SourceType sample);
-
-template <>
-int16 ConvertSample<float, int16>(float sample) {
- if (!(-1.0 <= sample && sample <= 1.0)) {
- DLOG(WARNING) <<
- "Sample of type float32 must lie on interval [-1.0, 1.0], got: " <<
- sample << ".";
- }
- return static_cast<int16>(sample * kMaxInt16AsFloat32);
-}
-
-template <>
-float ConvertSample<float, float>(float sample) {
- return sample;
-}
-} // namespace
-
class AudioDevice::Impl {
public:
Impl(int number_of_channels, RenderCallback* callback);
@@ -98,7 +64,7 @@
void FillOutputAudioBus();
- template <typename OutputType>
+ template <typename InputType, typename OutputType>
inline void FillOutputAudioBusForType();
int number_of_channels_;
@@ -126,16 +92,14 @@
// AudioDevice::Impl.
AudioDevice::Impl::Impl(int number_of_channels, RenderCallback* callback)
: number_of_channels_(number_of_channels),
- output_sample_type_(
- SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)
- ? kSbMediaAudioSampleTypeFloat32
- : kSbMediaAudioSampleTypeInt16),
+ output_sample_type_(GetPreferredOutputStarboardSampleType()),
render_callback_(callback),
input_audio_bus_(static_cast<size_t>(number_of_channels),
static_cast<size_t>(kRenderBufferSizeFrames),
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar),
- output_frame_buffer_(new uint8[kFramesPerChannel * number_of_channels_ *
- GetSampleSize(output_sample_type_)]),
+ GetPreferredOutputSampleType(), ShellAudioBus::kPlanar),
+ output_frame_buffer_(
+ new uint8[kFramesPerChannel * number_of_channels_ *
+ GetStarboardSampleTypeSize(output_sample_type_)]),
frames_rendered_(0),
frames_consumed_(0),
was_silence_last_update_(false),
@@ -147,7 +111,7 @@
kSbMediaAudioFrameStorageTypeInterleaved))
<< "Only interleaved frame storage is supported.";
DCHECK(SbAudioSinkIsAudioSampleTypeSupported(output_sample_type_))
- << "Output sample type " << output_sample_type_ << " is not supported";
+ << "Output sample type " << output_sample_type_ << " is not supported.";
frame_buffers_[0] = output_frame_buffer_.get();
audio_sink_ = SbAudioSinkCreate(
@@ -231,7 +195,7 @@
frames_consumed_ += frames_consumed;
}
-template <typename OutputType>
+template <typename InputType, typename OutputType>
inline void AudioDevice::Impl::FillOutputAudioBusForType() {
// Determine the offset into the audio bus that represents the tail of
// buffered data.
@@ -242,8 +206,10 @@
output_buffer += channel_offset * number_of_channels_;
for (size_t frame = 0; frame < kRenderBufferSizeFrames; ++frame) {
for (size_t channel = 0; channel < input_audio_bus_.channels(); ++channel) {
- *output_buffer = ConvertSample<float, OutputType>(
- input_audio_bus_.GetFloat32Sample(channel, frame));
+ *output_buffer = ConvertSample<InputType, OutputType>(
+ input_audio_bus_
+ .GetSampleForType<InputType, media::ShellAudioBus::kPlanar>(
+ channel, frame));
++output_buffer;
}
}
@@ -251,10 +217,20 @@
void AudioDevice::Impl::FillOutputAudioBus() {
TRACE_EVENT0("cobalt::audio", "AudioDevice::Impl::FillOutputAudioBus()");
- if (output_sample_type_ == kSbMediaAudioSampleTypeFloat32) {
- FillOutputAudioBusForType<float>();
- } else if (output_sample_type_ == kSbMediaAudioSampleTypeInt16) {
- FillOutputAudioBusForType<int16>();
+
+ const bool is_input_int16 =
+ input_audio_bus_.sample_type() == media::ShellAudioBus::kInt16;
+ const bool is_output_int16 =
+ output_sample_type_ == kSbMediaAudioSampleTypeInt16;
+
+ if (is_input_int16 && is_output_int16) {
+ FillOutputAudioBusForType<int16, int16>();
+ } else if (!is_input_int16 && is_output_int16) {
+ FillOutputAudioBusForType<float, int16>();
+ } else if (is_input_int16 && !is_output_int16) {
+ FillOutputAudioBusForType<int16, float>();
+ } else if (!is_input_int16 && !is_output_int16) {
+ FillOutputAudioBusForType<float, float>();
} else {
NOTREACHED();
}
@@ -329,7 +305,7 @@
channel_layout, GetAudioHardwareSampleRate(),
bytes_per_sample * 8, kRenderBufferSizeFrames);
- // Create 1 channel audio bus due to we only support interleaved.
+ // Create 1 channel audio bus since we only support interleaved.
output_audio_bus_ =
AudioBus::Create(1, kFramesPerChannel * number_of_channels);
diff --git a/src/cobalt/audio/audio_file_reader.h b/src/cobalt/audio/audio_file_reader.h
index 4897878..e1eacca 100644
--- a/src/cobalt/audio/audio_file_reader.h
+++ b/src/cobalt/audio/audio_file_reader.h
@@ -19,6 +19,8 @@
#include "base/memory/scoped_ptr.h" // For scoped_array
+#include "cobalt/audio/audio_helpers.h"
+
namespace cobalt {
namespace audio {
@@ -35,6 +37,7 @@
virtual float sample_rate() const = 0;
virtual int32 number_of_frames() const = 0;
virtual int32 number_of_channels() const = 0;
+ virtual SampleType sample_type() const = 0;
};
} // namespace audio
diff --git a/src/cobalt/audio/audio_file_reader_wav.cc b/src/cobalt/audio/audio_file_reader_wav.cc
index 322fa0d..fad6ef0 100644
--- a/src/cobalt/audio/audio_file_reader_wav.cc
+++ b/src/cobalt/audio/audio_file_reader_wav.cc
@@ -175,32 +175,52 @@
const uint8* data_samples = data + offset;
// Set number of frames based on size of data chunk.
- int32 bytes_per_src_sample =
+ const int32 bytes_per_src_sample =
static_cast<int32>(is_sample_in_float ? sizeof(float) : sizeof(int16));
number_of_frames_ =
static_cast<int32>(size / (bytes_per_src_sample * number_of_channels_));
+ sample_type_ = GetPreferredOutputSampleType();
+ const int32 bytes_per_dest_sample =
+ static_cast<int32>(GetSampleTypeSize(sample_type_));
+ const bool is_dest_float = sample_type_ == kSampleTypeFloat32;
- // We always store audio samples in float.
- sample_data_.reset(
- new uint8[number_of_frames_ * number_of_channels_ * sizeof(float)]);
+ // We store audio samples in the current platform's preferred format.
+ sample_data_.reset(new uint8[static_cast<size_t>(
+ number_of_frames_ * number_of_channels_ * bytes_per_dest_sample)]);
- // The source data is stored interleaved. We need to convert it into planar.
- float* dest_sample = reinterpret_cast<float*>(sample_data_.get());
+ // Here we handle all 4 possible conversion cases. Also note that the
+ // source data is stored interleaved, and that need to convert it to planar.
+ uint8* dest_sample = sample_data_.get();
for (int32 i = 0; i < number_of_channels_; ++i) {
const uint8* src_samples = data_samples + i * bytes_per_src_sample;
for (int32 j = 0; j < number_of_frames_; ++j) {
- float sample;
- if (is_sample_in_float) {
- uint32 sample_as_uint32 = load_uint32_little_endian(src_samples);
- sample = bit_cast<float>(sample_as_uint32);
+ if (is_dest_float) {
+ float sample;
+ if (is_sample_in_float) {
+ uint32 sample_as_uint32 = load_uint32_little_endian(src_samples);
+ sample = bit_cast<float>(sample_as_uint32);
+ } else {
+ uint16 sample_pcm_unsigned = load_uint16_little_endian(src_samples);
+ int16 sample_pcm = bit_cast<int16>(sample_pcm_unsigned);
+ sample = ConvertSample<int16, float>(sample_pcm);
+ }
+ reinterpret_cast<float*>(dest_sample)[i * number_of_frames_ + j] =
+ sample;
+ src_samples += bytes_per_src_sample * number_of_channels_;
} else {
- uint16 sample_pcm_unsigned = load_uint16_little_endian(src_samples);
- int16 sample_pcm = bit_cast<int16>(sample_pcm_unsigned);
- sample = static_cast<float>(sample_pcm) / 32768.0f;
+ int16 sample;
+ if (is_sample_in_float) {
+ uint32 sample_as_uint32 = load_uint32_little_endian(src_samples);
+ float value = bit_cast<float>(sample_as_uint32);
+ sample = ConvertSample<float, int16>(value);
+ } else {
+ sample = bit_cast<int16>(load_uint16_little_endian(src_samples));
+ }
+ reinterpret_cast<int16*>(dest_sample)[i * number_of_frames_ + j] =
+ sample;
+ src_samples += bytes_per_src_sample * number_of_channels_;
}
- dest_sample[i * number_of_frames_ + j] = sample;
- src_samples += bytes_per_src_sample * number_of_channels_;
}
}
diff --git a/src/cobalt/audio/audio_file_reader_wav.h b/src/cobalt/audio/audio_file_reader_wav.h
index cab1a1d..06e4883 100644
--- a/src/cobalt/audio/audio_file_reader_wav.h
+++ b/src/cobalt/audio/audio_file_reader_wav.h
@@ -18,6 +18,7 @@
#define COBALT_AUDIO_AUDIO_FILE_READER_WAV_H_
#include "cobalt/audio/audio_file_reader.h"
+#include "cobalt/audio/audio_helpers.h"
namespace cobalt {
namespace audio {
@@ -32,6 +33,7 @@
float sample_rate() const OVERRIDE { return sample_rate_; }
int32 number_of_frames() const OVERRIDE { return number_of_frames_; }
int32 number_of_channels() const OVERRIDE { return number_of_channels_; }
+ SampleType sample_type() const OVERRIDE { return sample_type_; }
private:
AudioFileReaderWAV(const uint8* data, size_t size);
@@ -49,6 +51,7 @@
float sample_rate_;
int32 number_of_frames_;
int32 number_of_channels_;
+ SampleType sample_type_;
};
} // namespace audio
diff --git a/src/cobalt/audio/audio_helpers.h b/src/cobalt/audio/audio_helpers.h
new file mode 100644
index 0000000..2f0c06a
--- /dev/null
+++ b/src/cobalt/audio/audio_helpers.h
@@ -0,0 +1,128 @@
+/*
+ * 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_AUDIO_AUDIO_HELPERS_H_
+#define COBALT_AUDIO_AUDIO_HELPERS_H_
+
+#include "media/base/shell_audio_bus.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/audio_sink.h"
+#include "starboard/media.h"
+#endif
+
+namespace cobalt {
+namespace audio {
+
+typedef ::media::ShellAudioBus::SampleType SampleType;
+const SampleType kSampleTypeInt16 = ::media::ShellAudioBus::kInt16;
+const SampleType kSampleTypeFloat32 = ::media::ShellAudioBus::kFloat32;
+
+const float kMaxInt16AsFloat32 = 32767.0f;
+
+#if defined(OS_STARBOARD)
+// Get the size in bytes of an SbMediaAudioSampleType.
+inline size_t GetStarboardSampleTypeSize(SbMediaAudioSampleType sample_type) {
+ switch (sample_type) {
+ case kSbMediaAudioSampleTypeFloat32:
+ return sizeof(float);
+ case kSbMediaAudioSampleTypeInt16:
+ return sizeof(int16);
+ }
+ NOTREACHED();
+ return 0u;
+}
+#endif
+
+// Get the size in bytes of an internal sample type, which is an alias for
+// media::ShellAudioBus::SampleType.
+inline size_t GetSampleTypeSize(SampleType sample_type) {
+ switch (sample_type) {
+ case kSampleTypeInt16:
+ return sizeof(int16);
+ case kSampleTypeFloat32:
+ return sizeof(float);
+ }
+ NOTREACHED();
+ return 0u;
+}
+
+// Get the sample type that we would prefer to output in using starboard, as
+// an internal SampleType. If we are not running on starboard or using the
+// starboard media pipeline, then the preferred sample type is always float32.
+inline SampleType GetPreferredOutputSampleType() {
+#if defined(OS_STARBOARD)
+#if SB_CAN(MEDIA_USE_STARBOARD_PIPELINE)
+ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
+ return kSampleTypeFloat32;
+ }
+ DCHECK(SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16))
+ << "At least one starboard audio sample type must be supported if using "
+ "starboard media pipeline.";
+ return kSampleTypeInt16;
+#else // SB_CAN(MEDIA_USE_STARBOARD_PIPELINE)
+ return kSampleTypeFloat32;
+#endif // SB_CAN(MEDIA_USE_STARBOARD_PIPELINE)
+#else // defined(OS_STARBOARD)
+ return kSampleTypeFloat32;
+#endif // defined(OS_STARBOARD)
+}
+
+#if defined(OS_STARBOARD)
+// The same as GetPreferredOutputSampleType, only as an SbMediaAudioSample
+// rather than an internal SampleType.
+inline SbMediaAudioSampleType GetPreferredOutputStarboardSampleType() {
+ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
+ return kSbMediaAudioSampleTypeFloat32;
+ }
+ return kSbMediaAudioSampleTypeInt16;
+}
+#endif
+
+// Convert a sample value from {int16,float} to {int16,float}.
+template <typename SourceType, typename DestType>
+inline DestType ConvertSample(SourceType sample);
+
+template <>
+inline int16 ConvertSample<float, int16>(float sample) {
+ if (!(-1.0 <= sample && sample <= 1.0)) {
+ DLOG(WARNING)
+ << "Sample of type float32 must lie on interval [-1.0, 1.0], got: "
+ << sample << ".";
+ }
+ return static_cast<int16>(sample * kMaxInt16AsFloat32);
+}
+
+template <>
+inline float ConvertSample<int16, float>(int16 sample) {
+ float int16_sample_as_float = static_cast<float>(sample);
+ return int16_sample_as_float / kMaxInt16AsFloat32;
+}
+
+template <>
+inline float ConvertSample<float, float>(float sample) {
+ return sample;
+}
+
+template <>
+inline int16 ConvertSample<int16, int16>(int16 sample) {
+ return sample;
+}
+
+} // namespace audio
+} // namespace cobalt
+
+#endif // COBALT_AUDIO_AUDIO_HELPERS_H_
diff --git a/src/cobalt/audio/audio_node.h b/src/cobalt/audio/audio_node.h
index 11f4ae4..9564898 100644
--- a/src/cobalt/audio/audio_node.h
+++ b/src/cobalt/audio/audio_node.h
@@ -20,6 +20,7 @@
#include <string>
#include <vector>
+#include "cobalt/audio/audio_helpers.h"
#include "cobalt/audio/audio_node_input.h"
#include "cobalt/audio/audio_node_output.h"
#include "cobalt/dom/dom_exception.h"
@@ -112,7 +113,7 @@
// TODO: Support wrapping ShellAudioBus into another ShellAudioBus.
virtual scoped_ptr<ShellAudioBus> PassAudioBusFromSource(
- int32 number_of_frames) = 0;
+ int32 number_of_frames, SampleType sample_type) = 0;
AudioLock* audio_lock() const { return audio_lock_.get(); }
diff --git a/src/cobalt/audio/audio_node_input.cc b/src/cobalt/audio/audio_node_input.cc
index 9be0c32..b0c8597 100644
--- a/src/cobalt/audio/audio_node_input.cc
+++ b/src/cobalt/audio/audio_node_input.cc
@@ -243,7 +243,8 @@
for (std::set<AudioNodeOutput*>::iterator iter = outputs_.begin();
iter != outputs_.end(); ++iter) {
scoped_ptr<ShellAudioBus> audio_bus = (*iter)->PassAudioBusFromSource(
- static_cast<int32>(output_audio_bus->frames()));
+ static_cast<int32>(output_audio_bus->frames()),
+ output_audio_bus->sample_type());
if (audio_bus) {
MixAudioBuffer(owner_node_->channel_interpretation(), audio_bus.get(),
diff --git a/src/cobalt/audio/audio_node_input_output_test.cc b/src/cobalt/audio/audio_node_input_output_test.cc
index b72579e..095217f 100644
--- a/src/cobalt/audio/audio_node_input_output_test.cc
+++ b/src/cobalt/audio/audio_node_input_output_test.cc
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include "cobalt/audio/audio_context.h"
-
#include "cobalt/audio/audio_buffer_source_node.h"
+#include "cobalt/audio/audio_context.h"
+#include "cobalt/audio/audio_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
@@ -37,8 +37,8 @@
}
// From AudioNode.
- scoped_ptr<ShellAudioBus> PassAudioBusFromSource(
- int32 /*number_of_frames*/) OVERRIDE {
+ scoped_ptr<ShellAudioBus> PassAudioBusFromSource(int32, /*number_of_frames*/
+ SampleType) OVERRIDE {
NOTREACHED();
return scoped_ptr<ShellAudioBus>();
}
@@ -58,7 +58,8 @@
scoped_refptr<AudioBufferSourceNode> source(new AudioBufferSourceNode(NULL));
scoped_refptr<AudioBuffer> buffer(
new AudioBuffer(NULL, 44100, static_cast<int32>(num_of_frames),
- static_cast<int32>(num_of_src_channel), src_data.Pass()));
+ static_cast<int32>(num_of_src_channel), src_data.Pass(),
+ GetPreferredOutputSampleType()));
source->set_buffer(buffer);
scoped_refptr<AudioDestinationNodeMock> destination(
@@ -625,9 +626,10 @@
memcpy(src_buffer_1, src_data_in_float_1, 200 * sizeof(uint8));
scoped_refptr<AudioBufferSourceNode> source_1(
new AudioBufferSourceNode(NULL));
- scoped_refptr<AudioBuffer> buffer_1(new AudioBuffer(
- NULL, 44100, static_cast<int32>(num_of_frames_1),
- static_cast<int32>(num_of_src_channel), src_data_1.Pass()));
+ scoped_refptr<AudioBuffer> buffer_1(
+ new AudioBuffer(NULL, 44100, static_cast<int32>(num_of_frames_1),
+ static_cast<int32>(num_of_src_channel), src_data_1.Pass(),
+ GetPreferredOutputSampleType()));
source_1->set_buffer(buffer_1);
size_t num_of_frames_2 = 50;
@@ -642,9 +644,10 @@
memcpy(src_buffer_2, src_data_in_float_2, 400 * sizeof(uint8));
scoped_refptr<AudioBufferSourceNode> source_2(
new AudioBufferSourceNode(NULL));
- scoped_refptr<AudioBuffer> buffer_2(new AudioBuffer(
- NULL, 44100, static_cast<int32>(num_of_frames_2),
- static_cast<int32>(num_of_src_channel), src_data_2.Pass()));
+ scoped_refptr<AudioBuffer> buffer_2(
+ new AudioBuffer(NULL, 44100, static_cast<int32>(num_of_frames_2),
+ static_cast<int32>(num_of_src_channel), src_data_2.Pass(),
+ GetPreferredOutputSampleType()));
source_2->set_buffer(buffer_2);
scoped_refptr<AudioDestinationNodeMock> destination(
diff --git a/src/cobalt/audio/audio_node_output.cc b/src/cobalt/audio/audio_node_output.cc
index d2ce36c..747bc01 100644
--- a/src/cobalt/audio/audio_node_output.cc
+++ b/src/cobalt/audio/audio_node_output.cc
@@ -58,12 +58,13 @@
}
scoped_ptr<ShellAudioBus> AudioNodeOutput::PassAudioBusFromSource(
- int32 number_of_frames) {
+ int32 number_of_frames, SampleType sample_type) {
// This is called by Audio thread.
owner_node_->audio_lock()->AssertLocked();
// Pull audio buffer from its owner node.
- return owner_node_->PassAudioBusFromSource(number_of_frames).Pass();
+ return owner_node_->PassAudioBusFromSource(number_of_frames, sample_type)
+ .Pass();
}
} // namespace audio
diff --git a/src/cobalt/audio/audio_node_output.h b/src/cobalt/audio/audio_node_output.h
index f01b274..decd54c 100644
--- a/src/cobalt/audio/audio_node_output.h
+++ b/src/cobalt/audio/audio_node_output.h
@@ -22,6 +22,7 @@
#include "base/memory/ref_counted.h"
#include "cobalt/audio/audio_buffer.h"
+#include "cobalt/audio/audio_helpers.h"
#include "media/base/shell_audio_bus.h"
namespace cobalt {
@@ -44,7 +45,8 @@
void DisconnectAll();
- scoped_ptr<ShellAudioBus> PassAudioBusFromSource(int32 number_of_frames);
+ scoped_ptr<ShellAudioBus> PassAudioBusFromSource(int32 number_of_frames,
+ SampleType sample_type);
private:
AudioNode* const owner_node_;
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsArbitraryInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsArbitraryInterface.cc
index 20bae1a..38699b2 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsArbitraryInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsArbitraryInterface.cc
@@ -222,6 +222,15 @@
JSBool set_arbitraryProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ArbitraryInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsBooleanTypeTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsBooleanTypeTestInterface.cc
index 67e7e5f..2599232 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsBooleanTypeTestInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsBooleanTypeTestInterface.cc
@@ -220,6 +220,15 @@
JSBool set_booleanProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<BooleanTypeTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackFunctionInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackFunctionInterface.cc
index 8ebf68c..fce394f 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackFunctionInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackFunctionInterface.cc
@@ -224,6 +224,15 @@
JSBool set_callbackAttribute(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<CallbackFunctionInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -277,6 +286,15 @@
JSBool set_nullableCallbackAttribute(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<CallbackFunctionInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackInterfaceInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackInterfaceInterface.cc
index e510b12..468faf3 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackInterfaceInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsCallbackInterfaceInterface.cc
@@ -224,6 +224,15 @@
JSBool set_callbackAttribute(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<CallbackInterfaceInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsConditionalInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsConditionalInterface.cc
index 12fda2f..2610e00 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsConditionalInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsConditionalInterface.cc
@@ -223,6 +223,15 @@
JSBool set_enabledAttribute(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ConditionalInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -278,6 +287,15 @@
JSBool set_disabledAttribute(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ConditionalInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsDOMStringTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsDOMStringTestInterface.cc
index 4524287..7092da5 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsDOMStringTestInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsDOMStringTestInterface.cc
@@ -220,6 +220,15 @@
JSBool set_property(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<DOMStringTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -335,6 +344,15 @@
JSBool set_nullIsEmptyProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<DOMStringTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -388,6 +406,15 @@
JSBool set_undefinedIsEmptyProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<DOMStringTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -441,6 +468,15 @@
JSBool set_nullableUndefinedIsEmptyProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<DOMStringTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc
index 647ef55..1c88411 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsDerivedGetterSetterInterface.cc
@@ -436,6 +436,15 @@
JSBool set_propertyOnDerivedClass(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<DerivedGetterSetterInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsDisabledInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsDisabledInterface.cc
index 5fb88e5..5e3f995 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsDisabledInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsDisabledInterface.cc
@@ -222,6 +222,15 @@
JSBool set_disabledProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<DisabledInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsEnumerationInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsEnumerationInterface.cc
index ea07296..38c99f1 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsEnumerationInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsEnumerationInterface.cc
@@ -231,6 +231,15 @@
JSBool set_enumProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<EnumerationInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsExceptionsInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsExceptionsInterface.cc
index b23ae9f..6ab9aab 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsExceptionsInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsExceptionsInterface.cc
@@ -222,6 +222,15 @@
JSBool set_attributeThrowsException(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ExceptionsInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc
index df1b224..9ba778e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsGarbageCollectionTestInterface.cc
@@ -233,6 +233,15 @@
JSBool set_previous(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<GarbageCollectionTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -286,6 +295,15 @@
JSBool set_next(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<GarbageCollectionTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc
index 7f0e6b7..e027bc0 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsNamedIndexedGetterInterface.cc
@@ -436,6 +436,15 @@
JSBool set_propertyOnBaseClass(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NamedIndexedGetterInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsNestedPutForwardsInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsNestedPutForwardsInterface.cc
index c7945ee..9b732fe 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsNestedPutForwardsInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsNestedPutForwardsInterface.cc
@@ -224,6 +224,15 @@
JSBool set_nestedForwardingAttribute(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NestedPutForwardsInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsNullableTypesTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsNullableTypesTestInterface.cc
index a2d5622..e7533d8 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsNullableTypesTestInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsNullableTypesTestInterface.cc
@@ -224,6 +224,15 @@
JSBool set_nullableBooleanProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NullableTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -277,6 +286,15 @@
JSBool set_nullableNumericProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NullableTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -330,6 +348,15 @@
JSBool set_nullableStringProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NullableTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -383,6 +410,15 @@
JSBool set_nullableObjectProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NullableTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsNumericTypesTestInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsNumericTypesTestInterface.cc
index c64b5d9..eb604d8 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsNumericTypesTestInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsNumericTypesTestInterface.cc
@@ -220,6 +220,15 @@
JSBool set_byteProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NumericTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -273,6 +282,15 @@
JSBool set_octetProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NumericTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -326,6 +344,15 @@
JSBool set_shortProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NumericTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -379,6 +406,15 @@
JSBool set_unsignedShortProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NumericTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -432,6 +468,15 @@
JSBool set_longProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NumericTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -485,6 +530,15 @@
JSBool set_unsignedLongProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NumericTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -538,6 +592,15 @@
JSBool set_longLongProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NumericTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -591,6 +654,15 @@
JSBool set_unsignedLongLongProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NumericTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -644,6 +716,15 @@
JSBool set_doubleProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NumericTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -697,6 +778,15 @@
JSBool set_unrestrictedDoubleProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<NumericTypesTestInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsObjectTypeBindingsInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsObjectTypeBindingsInterface.cc
index d25a5b1..77631f6 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsObjectTypeBindingsInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsObjectTypeBindingsInterface.cc
@@ -232,6 +232,15 @@
JSBool set_arbitraryObject(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ObjectTypeBindingsInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -316,6 +325,15 @@
JSBool set_derivedInterface(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ObjectTypeBindingsInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -369,6 +387,15 @@
JSBool set_objectProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ObjectTypeBindingsInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsPutForwardsInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsPutForwardsInterface.cc
index 325c6fd..dfc7ffa 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsPutForwardsInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsPutForwardsInterface.cc
@@ -224,6 +224,15 @@
JSBool set_forwardingAttribute(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<PutForwardsInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAttributeInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAttributeInterface.cc
index 30cfd0a..428506c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAttributeInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsStringifierAttributeInterface.cc
@@ -220,6 +220,15 @@
JSBool set_theStringifierAttribute(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<StringifierAttributeInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsUnionTypesInterface.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsUnionTypesInterface.cc
index a81efa7..e6cf73b 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsUnionTypesInterface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsUnionTypesInterface.cc
@@ -228,6 +228,15 @@
JSBool set_unionProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<UnionTypesInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -281,6 +290,15 @@
JSBool set_unionWithNullableMemberProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<UnionTypesInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -334,6 +352,15 @@
JSBool set_nullableUnionProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<UnionTypesInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
@@ -387,6 +414,15 @@
JSBool set_unionBaseProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<UnionTypesInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/generated/mozjs/testing/MozjsWindow.cc b/src/cobalt/bindings/generated/mozjs/testing/MozjsWindow.cc
index 673aed8..212ecca 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/MozjsWindow.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/MozjsWindow.cc
@@ -413,6 +413,15 @@
JSBool set_windowProperty(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<Window>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
diff --git a/src/cobalt/bindings/mozjs/templates/interface.cc.template b/src/cobalt/bindings/mozjs/templates/interface.cc.template
index 2dfc9eb..3307b67 100644
--- a/src/cobalt/bindings/mozjs/templates/interface.cc.template
+++ b/src/cobalt/bindings/mozjs/templates/interface.cc.template
@@ -485,6 +485,7 @@
JSBool set_{{attribute.idl_name}}(
JSContext* context, JS::HandleObject object, JS::HandleId id,
JSBool strict, JS::MutableHandleValue vp) {
+{{ check_if_object_implements_interface() }}
{{ nonstatic_function_prologue(impl_class)}}
{% endif %} {#- attribute.is_static #}
{{ set_attribute_implementation(attribute, impl_class) -}}
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 9385cc0..170907c 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -222,6 +222,8 @@
&options->scratch_surface_cache_size_in_bytes);
SetIntegerIfSwitchIsSet(browser::switches::kSkiaCacheSizeInBytes,
&options->skia_cache_size_in_bytes);
+ SetIntegerIfSwitchIsSet(browser::switches::kSoftwareSurfaceCacheSizeInBytes,
+ &options->software_surface_cache_size_in_bytes);
}
void ApplyCommandLineSettingsToWebModuleOptions(WebModule::Options* options) {
diff --git a/src/cobalt/browser/browser_bindings.gyp b/src/cobalt/browser/browser_bindings.gyp
index 329d8e2..e73cd8b 100644
--- a/src/cobalt/browser/browser_bindings.gyp
+++ b/src/cobalt/browser/browser_bindings.gyp
@@ -179,6 +179,8 @@
'../web_animations/KeyframeEffectReadOnly.idl',
'../webdriver/ScriptExecutor.idl',
+ '../webdriver/ScriptExecutorParams.idl',
+ '../webdriver/ScriptExecutorResult.idl',
'../xhr/XMLHttpRequest.idl',
'../xhr/XMLHttpRequestEventTarget.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 72f1dd8..2f8beaf 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -273,6 +273,13 @@
array_buffer_allocator_.get();
options.dom_settings_options.array_buffer_cache = array_buffer_cache_.get();
#endif // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
+#if defined(ENABLE_FAKE_MICROPHONE)
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kFakeMicrophone) ||
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kInputFuzzer)) {
+ options.dom_settings_options.enable_fake_microphone = true;
+ }
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+
options.image_cache_capacity_multiplier_when_playing_video =
COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO;
web_module_.reset(new WebModule(
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 73b734a..6332a9d 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -45,6 +45,10 @@
// Additional base directory for accessing web files via file://.
const char kExtraWebFileDir[] = "web_file_path";
+// If this flag is set, fake microphone will be used to mock the user voice
+// input.
+const char kFakeMicrophone[] = "fake_microphone";
+
// Setting this switch causes all certificate errors to be ignored.
const char kIgnoreCertificateErrors[] = "ignore_certificate_errors";
@@ -129,6 +133,13 @@
// setting may affect GPU memory usage.
const char kSkiaCacheSizeInBytes[] = "skia_cache_size_in_bytes";
+// Only relevant if you are using the Blitter API.
+// Determines the capacity of the software surface cache, which is used to
+// cache all surfaces that are rendered via a software rasterizer to avoid
+// re-rendering them.
+const char kSoftwareSurfaceCacheSizeInBytes[] =
+ "software_surface_cache_size_in_bytes";
+
// Determines the capacity of the surface cache. The surface cache tracks which
// render tree nodes are being re-used across frames and stores the nodes that
// are most CPU-expensive to render into surfaces. While it depends on the
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index fe02027..a9b3d5f 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -29,6 +29,7 @@
extern const char kDisableWebmVp9[];
extern const char kEnableWebDriver[];
extern const char kExtraWebFileDir[];
+extern const char kFakeMicrophone[];
extern const char kIgnoreCertificateErrors[];
extern const char kInputFuzzer[];
extern const char kMinLogLevel[];
@@ -50,6 +51,7 @@
extern const char kRemoteTypefaceCacheSizeInBytes[];
extern const char kScratchSurfaceCacheSizeInBytes[];
extern const char kSkiaCacheSizeInBytes[];
+extern const char kSoftwareSurfaceCacheSizeInBytes[];
extern const char kSurfaceCacheSizeInBytes[];
extern const char kViewport[];
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index a23cdb6..c21d48b 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -29,8 +29,10 @@
#include "cobalt/browser/switches.h"
#include "cobalt/browser/web_module_stat_tracker.h"
#include "cobalt/debug/debug_server_module.h"
+#include "cobalt/dom/blob.h"
#include "cobalt/dom/csp_delegate_factory.h"
#include "cobalt/dom/storage.h"
+#include "cobalt/dom/url.h"
#include "cobalt/h5vcc/h5vcc.h"
#include "cobalt/storage/storage_manager.h"
@@ -111,9 +113,8 @@
#endif // ENABLE_DEBUG_CONSOLE
// Called to inject a keyboard event into the web module.
- void InjectKeyboardEvent(
- scoped_refptr<dom::Element> element,
- const dom::KeyboardEvent::Data& event);
+ void InjectKeyboardEvent(scoped_refptr<dom::Element> element,
+ const dom::KeyboardEvent::Data& event);
// Called to execute JavaScript in this WebModule. Sets the |result|
// output parameter and signals |got_result|.
@@ -239,6 +240,9 @@
// Object to register and retrieve MediaSource object with a string key.
scoped_ptr<dom::MediaSource::Registry> media_source_registry_;
+ // Object to register and retrieve Blob objects with a string key.
+ scoped_ptr<dom::Blob::Registry> blob_registry_;
+
// The Window object wraps all DOM-related components.
scoped_refptr<dom::Window> window_;
@@ -311,8 +315,11 @@
base::Bind(&WebModule::Impl::OnError, base::Unretained(this))));
DCHECK(dom_parser_);
+ blob_registry_.reset(new dom::Blob::Registry);
+
fetcher_factory_.reset(new loader::FetcherFactory(
- data.network_module, data.options.extra_web_file_dir));
+ data.network_module, data.options.extra_web_file_dir,
+ dom::URL::MakeBlobResolverCallback(blob_registry_.get())));
DCHECK(fetcher_factory_);
loader_factory_.reset(
@@ -390,8 +397,9 @@
environment_settings_.reset(new dom::DOMSettings(
kDOMMaxElementDepth, fetcher_factory_.get(), data.network_module, window_,
- media_source_registry_.get(), data.media_module, javascript_engine_.get(),
- global_environment_.get(), data.options.dom_settings_options));
+ media_source_registry_.get(), blob_registry_.get(), data.media_module,
+ javascript_engine_.get(), global_environment_.get(),
+ data.options.dom_settings_options));
DCHECK(environment_settings_);
global_environment_->CreateGlobalObject(window_, environment_settings_.get());
@@ -458,6 +466,7 @@
window_weak_.reset();
window_ = NULL;
media_source_registry_.reset();
+ blob_registry_.reset();
script_runner_.reset();
execution_state_.reset();
global_environment_ = NULL;
@@ -757,20 +766,17 @@
impl_.reset(new Impl(data));
}
-void WebModule::InjectKeyboardEvent(
- const dom::KeyboardEvent::Data& event) {
+void WebModule::InjectKeyboardEvent(const dom::KeyboardEvent::Data& event) {
DCHECK(message_loop());
DCHECK(impl_);
message_loop()->PostTask(FROM_HERE,
base::Bind(&WebModule::Impl::InjectKeyboardEvent,
base::Unretained(impl_.get()),
- scoped_refptr<dom::Element>(),
- event));
+ scoped_refptr<dom::Element>(), event));
}
-void WebModule::InjectKeyboardEvent(
- scoped_refptr<dom::Element> element,
- const dom::KeyboardEvent::Data& event) {
+void WebModule::InjectKeyboardEvent(scoped_refptr<dom::Element> element,
+ const dom::KeyboardEvent::Data& event) {
TRACE_EVENT1("cobalt::browser", "WebModule::InjectKeyboardEvent()", "type",
event.type);
DCHECK(message_loop());
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index b82f6f7..ba8bf30 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -35,6 +35,7 @@
#include "cobalt/debug/debug_server.h"
#include "cobalt/debug/render_overlay.h"
#endif // ENABLE_DEBUG_CONSOLE
+#include "cobalt/dom/blob.h"
#include "cobalt/dom/csp_delegate.h"
#include "cobalt/dom/dom_settings.h"
#include "cobalt/dom/keyboard_event.h"
@@ -163,13 +164,11 @@
// 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 InjectKeyboardEvent(
- scoped_refptr<dom::Element> element,
- const dom::KeyboardEvent::Data& event);
+ void InjectKeyboardEvent(scoped_refptr<dom::Element> element,
+ const dom::KeyboardEvent::Data& event);
// Call this to inject a keyboard event into the web module.
- void InjectKeyboardEvent(
- const dom::KeyboardEvent::Data& event);
+ void InjectKeyboardEvent(const dom::KeyboardEvent::Data& 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
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 8352629..a9747b9 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -64,6 +64,7 @@
'<(DEPTH)/cobalt/trace_event/trace_event.gyp:*',
'<(DEPTH)/cobalt/web_animations/web_animations.gyp:*',
'<(DEPTH)/cobalt/webdriver/webdriver.gyp:*',
+ '<(DEPTH)/cobalt/webdriver/webdriver_test.gyp:*',
'<(DEPTH)/cobalt/xhr/xhr.gyp:*',
'<(DEPTH)/crypto/crypto.gyp:crypto_unittests',
'<(DEPTH)/sql/sql.gyp:sql_unittests',
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index d690671..4868c74 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-13403
\ No newline at end of file
+14197
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index 8ba37eb..314650b 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -95,9 +95,11 @@
'lbshell_root%': '<(DEPTH)/lbshell',
# The relative path from src/ to the directory containing the
- # starboard_platform.gyp file, or the empty string if not an autodiscovered
- # platform.
- 'starboard_path%': '',
+ # starboard_platform.gyp file. It is currently set to
+ # 'starboard/<(target_arch)' to make semi-starboard platforms work.
+ # TODO: Set the default value to '' once all semi-starboard platforms are
+ # moved to starboard.
+ 'starboard_path%': 'starboard/<(target_arch)',
# The source of EGL and GLES headers and libraries.
# Valid values (case and everything sensitive!):
@@ -160,6 +162,12 @@
# typefaces downloaded from a web page.
'remote_typeface_cache_size_in_bytes%': 5 * 1024 * 1024,
+ # Only relevant if you are using the Blitter API.
+ # Determines the capacity of the software surface cache, which is used to
+ # cache all surfaces that are rendered via a software rasterizer to avoid
+ # re-rendering them.
+ 'software_surface_cache_size_in_bytes%': 10 * 1024 * 1024,
+
# Modifying this value to be non-1.0f will result in the image cache
# capacity being cleared and then temporarily reduced for the duration that
# a video is playing. This can be useful for some platforms if they are
@@ -449,11 +457,13 @@
'cobalt_copy_debug_console': 1,
'cobalt_copy_test_data': 1,
'enable_about_scheme': 1,
+ 'enable_fake_microphone': 1,
'enable_file_scheme': 1,
'enable_network_logging': 1,
'enable_remote_debugging%': 1,
'enable_screenshot': 1,
'enable_webdriver%': 1,
+ 'sb_allows_memory_tracking': 1,
},
},
{
@@ -461,11 +471,13 @@
'cobalt_copy_debug_console': 0,
'cobalt_copy_test_data': 0,
'enable_about_scheme': 0,
+ 'enable_fake_microphone': 0,
'enable_file_scheme': 0,
'enable_network_logging': 0,
'enable_remote_debugging%': 0,
'enable_screenshot': 0,
'enable_webdriver': 0,
+ 'sb_allows_memory_tracking': 0,
},
}],
],
diff --git a/src/cobalt/build/gyp_cobalt b/src/cobalt/build/gyp_cobalt
index 0e60bb9..030de0f 100755
--- a/src/cobalt/build/gyp_cobalt
+++ b/src/cobalt/build/gyp_cobalt
@@ -218,6 +218,7 @@
full_starboard_path = platforms[platform]
assert full_starboard_path[:len(source_tree_dir)] == source_tree_dir
starboard_path = full_starboard_path[len(source_tree_dir) + 1:]
+ starboard_path.replace(os.sep, '/')
assert starboard_path[0] not in [ os.sep, os.altsep ]
variables['starboard_path'] = starboard_path
_AppendVariables(variables, self.common_args)
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y
index 2b32f70..e4bb9a1 100644
--- a/src/cobalt/css_parser/grammar.y
+++ b/src/cobalt/css_parser/grammar.y
@@ -221,6 +221,7 @@
// %token kLeftToken // left - also property name token
%token kMaroonToken // maroon
%token kMiddleToken // middle
+%token kMonoscopicToken // monoscopic
%token kMonospaceToken // monospace
%token kNavyToken // navy
%token kNoneToken // none
@@ -248,6 +249,8 @@
%token kStaticToken // static
%token kStepEndToken // step-end
%token kStepStartToken // step-start
+%token kStereoscopicLeftRightToken // stereoscopic-left-right
+%token kStereoscopicTopBottomToken // stereoscopic-top-bottom
%token kTealToken // teal
%token kToToken // to
// %token kTopToken // top - also property name token
@@ -793,6 +796,11 @@
%type <cobalt_mtm_resolution_matched_mesh> cobalt_mtm_resolution_matched_mesh
%destructor { delete $$; } <cobalt_mtm_resolution_matched_mesh>
+%union { cssom::KeywordValue* stereo_mode; }
+%type <stereo_mode> maybe_cobalt_mtm_stereo_mode;
+%type <stereo_mode> cobalt_mtm_stereo_mode;
+%destructor { SafeRelease($$); } <stereo_mode>
+
%union { cssom::TimeListValue::Builder* time_list; }
%type <time_list> comma_separated_time_list
%destructor { delete $$; } <time_list>
@@ -1752,6 +1760,9 @@
| kMiddleToken {
$$ = TrivialStringPiece::FromCString(cssom::kMiddleKeywordName);
}
+ | kMonoscopicToken {
+ $$ = TrivialStringPiece::FromCString(cssom::kMonoscopicKeywordName);
+ }
| kMonospaceToken {
$$ = TrivialStringPiece::FromCString(cssom::kMonospaceKeywordName);
}
@@ -1830,6 +1841,14 @@
| kStepStartToken {
$$ = TrivialStringPiece::FromCString(cssom::kStepStartKeywordName);
}
+ | kStereoscopicLeftRightToken {
+ $$ = TrivialStringPiece::FromCString(
+ cssom::kStereoscopicLeftRightKeywordName);
+ }
+ | kStereoscopicTopBottomToken {
+ $$ = TrivialStringPiece::FromCString(
+ cssom::kStereoscopicTopBottomKeywordName);
+ }
| kTealToken {
$$ = TrivialStringPiece::FromCString(cssom::kTealKeywordName);
}
@@ -3418,7 +3437,7 @@
validated_box_shadow_list:
box_shadow_list {
scoped_ptr<ShadowPropertyInfo> shadow_property_info($1);
- if (!shadow_property_info->IsShadowPropertyValid(ShadowType::kBoxShadow)) {
+ if (!shadow_property_info->IsShadowPropertyValid(kBoxShadow)) {
parser_impl->LogWarning(@1, "invalid box shadow property.");
$$ = NULL;
} else {
@@ -4236,7 +4255,7 @@
validated_text_shadow_list:
text_shadow_list {
scoped_ptr<ShadowPropertyInfo> shadow_property_info($1);
- if (!shadow_property_info->IsShadowPropertyValid(ShadowType::kTextShadow)) {
+ if (!shadow_property_info->IsShadowPropertyValid(kTextShadow)) {
parser_impl->LogWarning(@1, "invalid text shadow property.");
$$ = NULL;
} else {
@@ -6482,7 +6501,8 @@
// Encodes an mtm filter. Currently the only type of filter function supported.
kCobaltMtmFunctionToken maybe_whitespace url
cobalt_mtm_resolution_matched_mesh_list comma angle angle comma
- cobalt_mtm_transform_function ')' maybe_whitespace {
+ cobalt_mtm_transform_function maybe_cobalt_mtm_stereo_mode
+ ')' maybe_whitespace {
scoped_ptr<cssom::MTMFunction::ResolutionMatchedMeshListBuilder>
resolution_matched_mesh_urls($4);
scoped_ptr<glm::mat4> transform($9);
@@ -6492,7 +6512,8 @@
resolution_matched_mesh_urls->Pass(),
$6,
$7,
- *transform);
+ *transform,
+ MakeScopedRefPtrAndRelease($10));
}
;
@@ -6544,3 +6565,24 @@
$$->push_back($3);
}
;
+
+maybe_cobalt_mtm_stereo_mode:
+ /* empty */ {
+ $$ = AddRef(cssom::KeywordValue::GetMonoscopic().get());
+ }
+ | comma cobalt_mtm_stereo_mode {
+ $$ = $2;
+ }
+ ;
+
+cobalt_mtm_stereo_mode:
+ kMonoscopicToken maybe_whitespace {
+ $$ = AddRef(cssom::KeywordValue::GetMonoscopic().get());
+ }
+ | kStereoscopicLeftRightToken maybe_whitespace {
+ $$ = AddRef(cssom::KeywordValue::GetStereoscopicLeftRight().get());
+ }
+ | kStereoscopicTopBottomToken maybe_whitespace {
+ $$ = AddRef(cssom::KeywordValue::GetStereoscopicTopBottom().get());
+ }
+ ;
diff --git a/src/cobalt/css_parser/parser_test.cc b/src/cobalt/css_parser/parser_test.cc
index fe7b721..2de5da7 100644
--- a/src/cobalt/css_parser/parser_test.cc
+++ b/src/cobalt/css_parser/parser_test.cc
@@ -8311,6 +8311,9 @@
EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
EXPECT_EQ(1.5f, mtm_function->vertical_fov());
+
+ EXPECT_EQ(mtm_function->stereo_mode()->value(),
+ cssom::KeywordValue::Value::kMonoscopic);
}
TEST_F(ParserTest, ParsesMtmResolutionMatchedUrlsFilter) {
@@ -8345,6 +8348,9 @@
EXPECT_EQ(
"yeehaw.msh",
dynamic_cast<cssom::URLValue*>(meshes[1]->mesh_url().get())->value());
+
+ EXPECT_EQ(mtm_function->stereo_mode()->value(),
+ cssom::KeywordValue::Value::kMonoscopic);
}
TEST_F(ParserTest, ParsesMtmTransformMatrixFilter) {
@@ -8375,6 +8381,122 @@
EXPECT_EQ(2.0f, actual[1][1]);
EXPECT_EQ(0.0f, actual[2][3]);
EXPECT_EQ(4.0f, actual[3][3]);
+
+ EXPECT_EQ(mtm_function->stereo_mode()->value(),
+ cssom::KeywordValue::Value::kMonoscopic);
+}
+
+TEST_F(ParserTest, ParsesMtmMonoscopicStereoModeFilter) {
+ scoped_refptr<cssom::CSSDeclaredStyleData> style =
+ parser_.ParseStyleDeclarationList(
+ "filter: -cobalt-mtm(url(p.msh),"
+ " 100deg 60deg,"
+ " matrix3d(1, 0, 0, 5,"
+ " 0, 2, 0, 0,"
+ " 6, 0, 3, 0,"
+ " 0, 7, 0, 4),"
+ " monoscopic);",
+ source_location_);
+ scoped_refptr<cssom::FilterFunctionListValue> filter_list =
+ dynamic_cast<cssom::FilterFunctionListValue*>(
+ style->GetPropertyValue(cssom::kFilterProperty).get());
+
+ ASSERT_TRUE(filter_list);
+ ASSERT_EQ(1, filter_list->value().size());
+
+ const cssom::MTMFunction* mtm_function =
+ dynamic_cast<const cssom::MTMFunction*>(filter_list->value()[0]);
+ ASSERT_TRUE(mtm_function);
+
+ EXPECT_EQ(mtm_function->stereo_mode()->value(),
+ cssom::KeywordValue::Value::kMonoscopic);
+}
+
+TEST_F(ParserTest, ParsesMtmStereoscopicLeftRightStereoModeFilter) {
+ scoped_refptr<cssom::CSSDeclaredStyleData> style =
+ parser_.ParseStyleDeclarationList(
+ "filter: -cobalt-mtm(url(p.msh),"
+ " 100deg 60deg,"
+ " matrix3d(1, 0, 0, 5,"
+ " 0, 2, 0, 0,"
+ " 6, 0, 3, 0,"
+ " 0, 7, 0, 4),"
+ " stereoscopic-left-right);",
+ source_location_);
+ scoped_refptr<cssom::FilterFunctionListValue> filter_list =
+ dynamic_cast<cssom::FilterFunctionListValue*>(
+ style->GetPropertyValue(cssom::kFilterProperty).get());
+
+ ASSERT_TRUE(filter_list);
+ ASSERT_EQ(1, filter_list->value().size());
+
+ const cssom::MTMFunction* mtm_function =
+ dynamic_cast<const cssom::MTMFunction*>(filter_list->value()[0]);
+ ASSERT_TRUE(mtm_function);
+
+ EXPECT_EQ(mtm_function->stereo_mode()->value(),
+ cssom::KeywordValue::Value::kStereoscopicLeftRight);
+}
+
+TEST_F(ParserTest, ParsesMtmStereoscopicTopBottomStereoModeFilter) {
+ scoped_refptr<cssom::CSSDeclaredStyleData> style =
+ parser_.ParseStyleDeclarationList(
+ "filter: -cobalt-mtm(url(p.msh),"
+ " 100deg 60deg,"
+ " matrix3d(1, 0, 0, 5,"
+ " 0, 2, 0, 0,"
+ " 6, 0, 3, 0,"
+ " 0, 7, 0, 4),"
+ " stereoscopic-top-bottom);",
+ source_location_);
+ scoped_refptr<cssom::FilterFunctionListValue> filter_list =
+ dynamic_cast<cssom::FilterFunctionListValue*>(
+ style->GetPropertyValue(cssom::kFilterProperty).get());
+
+ ASSERT_TRUE(filter_list);
+ ASSERT_EQ(1, filter_list->value().size());
+
+ const cssom::MTMFunction* mtm_function =
+ dynamic_cast<const cssom::MTMFunction*>(filter_list->value()[0]);
+ ASSERT_TRUE(mtm_function);
+
+ EXPECT_EQ(mtm_function->stereo_mode()->value(),
+ cssom::KeywordValue::Value::kStereoscopicTopBottom);
+}
+
+TEST_F(ParserTest, HandlesInvalidMtmStereoMode) {
+ EXPECT_CALL(parser_observer_,
+ OnWarning("[object ParserTest]:1:9: warning: unsupported value"));
+
+ scoped_refptr<cssom::CSSDeclaredStyleData> style =
+ parser_.ParseStyleDeclarationList(
+ "filter: -cobalt-mtm(url(p.msh),"
+ " 100deg 60deg,"
+ " matrix3d(1, 0, 0, 5,"
+ " 0, 2, 0, 0,"
+ " 6, 0, 3, 0,"
+ " 0, 7, 0, 4),"
+ " invalid-stereo-mode);",
+ source_location_);
+ scoped_refptr<cssom::FilterFunctionListValue> filter_list =
+ dynamic_cast<cssom::FilterFunctionListValue*>(
+ style->GetPropertyValue(cssom::kFilterProperty).get());
+
+ ASSERT_FALSE(filter_list);
+}
+
+TEST_F(ParserTest, EmptyPropertyValueRemovesProperty) {
+ // Test that parsing an empty property value removes the previously declared
+ // property value.
+ scoped_refptr<cssom::CSSDeclaredStyleData> style_data =
+ parser_.ParseStyleDeclarationList("display: inline;", source_location_);
+
+ scoped_refptr<cssom::CSSDeclaredStyleDeclaration> style =
+ new cssom::CSSDeclaredStyleDeclaration(style_data, &parser_);
+
+ style->SetPropertyValue(std::string("display"), std::string(), NULL);
+ EXPECT_EQ(style->GetPropertyValue("display"), "");
+ EXPECT_FALSE(style_data->GetPropertyValue(cssom::kDisplayProperty));
}
} // namespace css_parser
diff --git a/src/cobalt/css_parser/scanner.cc b/src/cobalt/css_parser/scanner.cc
index b295346..a19db4d 100644
--- a/src/cobalt/css_parser/scanner.cc
+++ b/src/cobalt/css_parser/scanner.cc
@@ -2204,6 +2204,10 @@
*property_value_token = kStepStartToken;
return true;
}
+ if (IsEqualToCssIdentifier(name.begin, cssom::kMonoscopicKeywordName)) {
+ *property_value_token = kMonoscopicToken;
+ return true;
+ }
return false;
case 11:
@@ -2262,6 +2266,19 @@
return true;
}
return false;
+
+ case 23:
+ if (IsEqualToCssIdentifier(name.begin,
+ cssom::kStereoscopicLeftRightKeywordName)) {
+ *property_value_token = kStereoscopicLeftRightToken;
+ return true;
+ }
+ if (IsEqualToCssIdentifier(name.begin,
+ cssom::kStereoscopicTopBottomKeywordName)) {
+ *property_value_token = kStereoscopicTopBottomToken;
+ return true;
+ }
+ return false;
}
return false;
diff --git a/src/cobalt/cssom/CSSStyleDeclaration.idl b/src/cobalt/cssom/CSSStyleDeclaration.idl
index fc13d7a..12793bf 100644
--- a/src/cobalt/cssom/CSSStyleDeclaration.idl
+++ b/src/cobalt/cssom/CSSStyleDeclaration.idl
@@ -111,6 +111,11 @@
readonly attribute unsigned long length;
getter DOMString? item(unsigned long index);
DOMString getPropertyValue(DOMString property);
- [RaisesException] void setPropertyValue(DOMString property, DOMString value);
+ [RaisesException] void setProperty(
+ DOMString property, [TreatNullAs=EmptyString] DOMString value,
+ [TreatNullAs=EmptyString] optional DOMString priority);
+ [RaisesException] void setPropertyValue(
+ DOMString property, [TreatNullAs=EmptyString] DOMString value);
+ [RaisesException] DOMString removeProperty(DOMString property);
readonly attribute CSSRule? parentRule;
};
diff --git a/src/cobalt/cssom/computed_style.cc b/src/cobalt/cssom/computed_style.cc
index 756819f..5fa9201 100644
--- a/src/cobalt/cssom/computed_style.cc
+++ b/src/cobalt/cssom/computed_style.cc
@@ -363,6 +363,7 @@
case KeywordValue::kLeft:
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kMonospace:
case KeywordValue::kNone:
case KeywordValue::kNoRepeat:
@@ -379,6 +380,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -458,6 +461,7 @@
case KeywordValue::kLeft:
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kMonospace:
case KeywordValue::kNoRepeat:
case KeywordValue::kNone:
@@ -475,6 +479,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -561,6 +567,7 @@
case KeywordValue::kLeft:
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kMonospace:
case KeywordValue::kNone:
case KeywordValue::kNoRepeat:
@@ -578,6 +585,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -680,6 +689,7 @@
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
case KeywordValue::kMonospace:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kNone:
case KeywordValue::kNoRepeat:
case KeywordValue::kNormal:
@@ -696,6 +706,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -798,6 +810,7 @@
case KeywordValue::kLeft:
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kMonospace:
case KeywordValue::kNoRepeat:
case KeywordValue::kNormal:
@@ -814,6 +827,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -916,6 +931,7 @@
case KeywordValue::kLeft:
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kMonospace:
case KeywordValue::kNone:
case KeywordValue::kNoRepeat:
@@ -933,6 +949,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -1029,6 +1047,7 @@
case KeywordValue::kLeft:
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kMonospace:
case KeywordValue::kNone:
case KeywordValue::kNoRepeat:
@@ -1046,6 +1065,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -1138,6 +1159,7 @@
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
case KeywordValue::kMonospace:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kNoRepeat:
case KeywordValue::kNormal:
case KeywordValue::kNoWrap:
@@ -1153,6 +1175,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -1551,6 +1575,7 @@
case KeywordValue::kLeft:
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kMonospace:
case KeywordValue::kNoRepeat:
case KeywordValue::kNormal:
@@ -1567,6 +1592,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -1824,6 +1851,7 @@
case KeywordValue::kLeft:
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kMonospace:
case KeywordValue::kNone:
case KeywordValue::kNoRepeat:
@@ -1841,6 +1869,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -2071,6 +2101,7 @@
case KeywordValue::kLeft:
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kMonospace:
case KeywordValue::kNormal:
case KeywordValue::kNoRepeat:
@@ -2087,6 +2118,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
@@ -2455,6 +2488,7 @@
case KeywordValue::kLeft:
case KeywordValue::kLineThrough:
case KeywordValue::kMiddle:
+ case KeywordValue::kMonoscopic:
case KeywordValue::kMonospace:
case KeywordValue::kNoRepeat:
case KeywordValue::kNormal:
@@ -2471,6 +2505,8 @@
case KeywordValue::kSolid:
case KeywordValue::kStart:
case KeywordValue::kStatic:
+ case KeywordValue::kStereoscopicLeftRight:
+ case KeywordValue::kStereoscopicTopBottom:
case KeywordValue::kTop:
case KeywordValue::kUppercase:
case KeywordValue::kVisible:
diff --git a/src/cobalt/cssom/css_computed_style_data.cc b/src/cobalt/cssom/css_computed_style_data.cc
index 3866deb..509a9b3 100644
--- a/src/cobalt/cssom/css_computed_style_data.cc
+++ b/src/cobalt/cssom/css_computed_style_data.cc
@@ -67,16 +67,6 @@
CSSComputedStyleData::~CSSComputedStyleData() {}
-unsigned int CSSComputedStyleData::length() const {
- // Computed style declarations have all known longhand properties.
- return kMaxLonghandPropertyKey + 1;
-}
-
-const char* CSSComputedStyleData::Item(unsigned int index) const {
- if (index >= length()) return NULL;
- return GetPropertyName(GetLexicographicalLonghandPropertyKey(index));
-}
-
const scoped_refptr<PropertyValue>&
CSSComputedStyleData::GetPropertyValueReference(PropertyKey key) const {
DCHECK_GT(key, kNoneProperty);
diff --git a/src/cobalt/cssom/css_computed_style_data.h b/src/cobalt/cssom/css_computed_style_data.h
index c2c82c1..fb12f37 100644
--- a/src/cobalt/cssom/css_computed_style_data.h
+++ b/src/cobalt/cssom/css_computed_style_data.h
@@ -62,16 +62,6 @@
CSSComputedStyleData();
~CSSComputedStyleData();
- // The length attribute must return the number of CSS declarations in the
- // declarations.
- // https://www.w3.org/TR/cssom/#dom-cssstyledeclaration-length
- unsigned int length() const;
-
- // The item(index) method must return the property name of the CSS declaration
- // at position index.
- // https://www.w3.org/TR/cssom/#dom-cssstyledeclaration-item
- const char* Item(unsigned int index) const;
-
void SetPropertyValue(const PropertyKey key,
const scoped_refptr<PropertyValue>& value);
diff --git a/src/cobalt/cssom/css_computed_style_declaration.cc b/src/cobalt/cssom/css_computed_style_declaration.cc
index ed4b4c8..1ca1566 100644
--- a/src/cobalt/cssom/css_computed_style_declaration.cc
+++ b/src/cobalt/cssom/css_computed_style_declaration.cc
@@ -42,7 +42,8 @@
// declarations.
// https://www.w3.org/TR/cssom/#dom-cssstyledeclaration-length
unsigned int CSSComputedStyleDeclaration::length() const {
- return data_ ? data_->length() : 0;
+ // Computed style declarations have all known longhand properties.
+ return kMaxLonghandPropertyKey + 1;
}
// The item(index) method must return the property name of the CSS declaration
@@ -50,8 +51,9 @@
// https://www.w3.org/TR/cssom/#dom-cssstyledeclaration-item
base::optional<std::string> CSSComputedStyleDeclaration::Item(
unsigned int index) const {
- const char* item = data_ ? data_->Item(index) : NULL;
- return item ? base::optional<std::string>(item) : base::nullopt;
+ if (index >= length()) return base::nullopt;
+ return base::optional<std::string>(
+ GetPropertyName(GetLexicographicalLonghandPropertyKey(index)));
}
std::string CSSComputedStyleDeclaration::GetDeclaredPropertyValueStringByKey(
@@ -72,6 +74,13 @@
exception_state);
}
+void CSSComputedStyleDeclaration::SetProperty(
+ const std::string& /*property_name*/, const std::string& /*property_value*/,
+ const std::string& /*priority*/, script::ExceptionState* exception_state) {
+ dom::DOMException::Raise(dom::DOMException::kInvalidAccessErr,
+ exception_state);
+}
+
void CSSComputedStyleDeclaration::SetData(
const scoped_refptr<const CSSComputedStyleData>& data) {
data_ = data;
diff --git a/src/cobalt/cssom/css_computed_style_declaration.h b/src/cobalt/cssom/css_computed_style_declaration.h
index e166979..1735690 100644
--- a/src/cobalt/cssom/css_computed_style_declaration.h
+++ b/src/cobalt/cssom/css_computed_style_declaration.h
@@ -38,6 +38,8 @@
// for computed styles.
class CSSComputedStyleDeclaration : public CSSStyleDeclaration {
public:
+ using CSSStyleDeclaration::SetProperty;
+
CSSComputedStyleDeclaration() {}
// From CSSStyleDeclaration.
@@ -53,6 +55,11 @@
const std::string& property_value,
script::ExceptionState* exception_state) OVERRIDE;
+ void SetProperty(const std::string& property_name,
+ const std::string& property_value,
+ const std::string& priority,
+ script::ExceptionState* exception_state) OVERRIDE;
+
// Custom.
const scoped_refptr<const CSSComputedStyleData>& data() const {
diff --git a/src/cobalt/cssom/css_computed_style_declaration_test.cc b/src/cobalt/cssom/css_computed_style_declaration_test.cc
new file mode 100644
index 0000000..d5abd5a
--- /dev/null
+++ b/src/cobalt/cssom/css_computed_style_declaration_test.cc
@@ -0,0 +1,212 @@
+/*
+ * 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/cssom/css_computed_style_data.h"
+#include "cobalt/cssom/css_computed_style_declaration.h"
+#include "cobalt/cssom/keyword_value.h"
+#include "cobalt/cssom/length_value.h"
+#include "cobalt/cssom/property_definitions.h"
+#include "cobalt/dom/dom_exception.h"
+#include "cobalt/script/exception_state.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace cssom {
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace {
+class FakeExceptionState : public script::ExceptionState {
+ public:
+ void SetException(
+ const scoped_refptr<script::ScriptException>& exception) OVERRIDE {
+ dom_exception_ = make_scoped_refptr(
+ base::polymorphic_downcast<dom::DOMException*>(exception.get()));
+ }
+ void SetSimpleException(script::MessageType /*message_type*/, ...) OVERRIDE {
+ // no-op
+ }
+ dom::DOMException::ExceptionCode GetExceptionCode() {
+ return dom_exception_ ? static_cast<dom::DOMException::ExceptionCode>(
+ dom_exception_->code())
+ : dom::DOMException::kNone;
+ }
+
+ private:
+ scoped_refptr<dom::DOMException> dom_exception_;
+};
+} // namespace
+
+TEST(CSSComputedStyleDeclarationTest, CSSTextSetterRaisesException) {
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+ FakeExceptionState exception_state;
+
+ const std::string css_text = "font-size: 100px; color: #0047ab;";
+ style->set_css_text(css_text, &exception_state);
+ EXPECT_EQ(dom::DOMException::kInvalidAccessErr,
+ exception_state.GetExceptionCode());
+}
+
+TEST(CSSComputedStyleDeclarationTest, DISABLED_CSSTextGetter) {
+ scoped_refptr<LengthValue> background_size =
+ new LengthValue(100, kPixelsUnit);
+ scoped_refptr<LengthValue> bottom = new LengthValue(16, kPixelsUnit);
+
+ scoped_refptr<CSSComputedStyleData> style_data = new CSSComputedStyleData();
+ style_data->SetPropertyValue(kBackgroundSizeProperty, background_size);
+ style_data->SetPropertyValue(kBottomProperty, bottom);
+
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+ style->SetData(style_data);
+ EXPECT_EQ(style->css_text(NULL), "background-size: 100px; bottom: 16px;");
+}
+
+TEST(CSSComputedStyleDeclarationTest, PropertyValueSetterRaisesException) {
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+ const std::string background = "rgba(0, 0, 0, .8)";
+ FakeExceptionState exception_state;
+
+ style->SetPropertyValue(GetPropertyName(kBackgroundProperty), background,
+ &exception_state);
+ EXPECT_EQ(dom::DOMException::kInvalidAccessErr,
+ exception_state.GetExceptionCode());
+}
+
+TEST(CSSComputedStyleDeclarationTest,
+ PropertySetterWithTwoValuesRaisesException) {
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+ const std::string background = "rgba(0, 0, 0, .8)";
+ FakeExceptionState exception_state;
+
+ style->SetProperty(GetPropertyName(kBackgroundProperty), background,
+ &exception_state);
+ EXPECT_EQ(dom::DOMException::kInvalidAccessErr,
+ exception_state.GetExceptionCode());
+}
+
+TEST(CSSComputedStyleDeclarationTest,
+ PropertySetterWithThreeValuesRaisesException) {
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+ const std::string background = "rgba(0, 0, 0, .8)";
+ FakeExceptionState exception_state;
+
+ style->SetProperty(GetPropertyName(kBackgroundProperty), background,
+ std::string(), &exception_state);
+ EXPECT_EQ(dom::DOMException::kInvalidAccessErr,
+ exception_state.GetExceptionCode());
+}
+
+TEST(CSSComputedStyleDeclarationTest, RemovePropertyRaisesException) {
+ scoped_refptr<CSSComputedStyleData> initial_style =
+ new CSSComputedStyleData();
+ initial_style->SetPropertyValue(kDisplayProperty, KeywordValue::GetInline());
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+ style->SetData(initial_style);
+
+ const std::string background = "rgba(0, 0, 0, .8)";
+ FakeExceptionState exception_state;
+
+ style->RemoveProperty(GetPropertyName(kDisplayProperty), &exception_state);
+ EXPECT_EQ(dom::DOMException::kInvalidAccessErr,
+ exception_state.GetExceptionCode());
+}
+
+TEST(CSSComputedStyleDeclarationTest, PropertyValueGetter) {
+ scoped_refptr<CSSComputedStyleData> initial_style =
+ new CSSComputedStyleData();
+ initial_style->SetPropertyValue(kTextAlignProperty,
+ KeywordValue::GetCenter());
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+ style->SetData(initial_style);
+
+ EXPECT_EQ(style->GetPropertyValue(GetPropertyName(kTextAlignProperty)),
+ "center");
+}
+
+TEST(CSSComputedStyleDeclarationTest,
+ UnknownDeclaredStylePropertyValueShouldBeEmpty) {
+ scoped_refptr<CSSComputedStyleData> initial_style =
+ new CSSComputedStyleData();
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+ style->SetData(initial_style);
+
+ EXPECT_EQ(style->GetPropertyValue("cobalt_cobalt_cobalt"), "");
+}
+
+TEST(CSSComputedStyleDeclarationTest, LengthAttributeGetterEmpty) {
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+
+ EXPECT_EQ(style->length(), kMaxLonghandPropertyKey + 1);
+}
+
+TEST(CSSComputedStyleDeclarationTest, LengthAttributeGetterNotEmpty) {
+ scoped_refptr<CSSComputedStyleData> initial_style =
+ new CSSComputedStyleData();
+ initial_style->SetPropertyValue(kDisplayProperty, KeywordValue::GetInline());
+ initial_style->SetPropertyValue(kTextAlignProperty,
+ KeywordValue::GetCenter());
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+ style->SetData(initial_style);
+
+ EXPECT_EQ(style->length(), kMaxLonghandPropertyKey + 1);
+}
+
+TEST(CSSComputedStyleDeclarationTest, ItemGetterEmpty) {
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+
+ // Computed styles are never actually empty.
+ EXPECT_TRUE(style->Item(0));
+}
+
+TEST(CSSComputedStyleDeclarationTest, ItemGetterNotEmpty) {
+ scoped_refptr<CSSComputedStyleData> initial_style =
+ new CSSComputedStyleData();
+ initial_style->SetPropertyValue(kDisplayProperty, KeywordValue::GetInline());
+ initial_style->SetPropertyValue(kTextAlignProperty,
+ KeywordValue::GetCenter());
+ scoped_refptr<CSSComputedStyleDeclaration> style =
+ new CSSComputedStyleDeclaration();
+ style->SetData(initial_style);
+
+ int property_count = 0;
+ for (unsigned int key = 0; key <= kMaxLonghandPropertyKey; ++key) {
+ // The order is not important, as long as all properties are represented.
+ EXPECT_TRUE(style->Item(key));
+ if (style->Item(key).value() == GetPropertyName(kDisplayProperty)) {
+ ++property_count;
+ }
+ if (style->Item(key).value() == GetPropertyName(kTextAlignProperty)) {
+ ++property_count;
+ }
+ }
+ EXPECT_EQ(property_count, 2);
+}
+
+} // namespace cssom
+} // namespace cobalt
diff --git a/src/cobalt/cssom/css_declared_style_declaration.cc b/src/cobalt/cssom/css_declared_style_declaration.cc
index 6c4557e..2e9b046 100644
--- a/src/cobalt/cssom/css_declared_style_declaration.cc
+++ b/src/cobalt/cssom/css_declared_style_declaration.cc
@@ -125,6 +125,22 @@
RecordMutation();
}
+void CSSDeclaredStyleDeclaration::SetProperty(
+ const std::string& property_name, const std::string& property_value,
+ const std::string& priority, script::ExceptionState* /*exception_state*/) {
+ DLOG(INFO) << "CSSDeclaredStyleDeclaration::SetProperty(" << property_name
+ << "," << property_value << "," << priority << ")";
+ DCHECK(css_parser_);
+ if (!data_) {
+ data_ = new CSSDeclaredStyleData();
+ }
+ css_parser_->ParsePropertyIntoDeclarationData(
+ property_name, property_value, non_trivial_static_fields.Get().location,
+ data_.get());
+
+ RecordMutation();
+}
+
void CSSDeclaredStyleDeclaration::RecordMutation() {
if (mutation_observer_) {
// Trigger layout update.
diff --git a/src/cobalt/cssom/css_declared_style_declaration.h b/src/cobalt/cssom/css_declared_style_declaration.h
index 344513d..9466df9 100644
--- a/src/cobalt/cssom/css_declared_style_declaration.h
+++ b/src/cobalt/cssom/css_declared_style_declaration.h
@@ -39,6 +39,8 @@
// for declared styles, such as css style rules and inline styles.
class CSSDeclaredStyleDeclaration : public CSSStyleDeclaration {
public:
+ using CSSStyleDeclaration::SetProperty;
+
explicit CSSDeclaredStyleDeclaration(CSSParser* css_parser);
CSSDeclaredStyleDeclaration(const scoped_refptr<CSSDeclaredStyleData>& data,
@@ -58,6 +60,11 @@
const std::string& property_value,
script::ExceptionState* exception_state) OVERRIDE;
+ void SetProperty(const std::string& property_name,
+ const std::string& property_value,
+ const std::string& priority,
+ script::ExceptionState* exception_state) OVERRIDE;
+
// Custom.
const scoped_refptr<CSSDeclaredStyleData>& data() const { return data_; }
diff --git a/src/cobalt/cssom/css_style_declaration_test.cc b/src/cobalt/cssom/css_declared_style_declaration_test.cc
similarity index 85%
rename from src/cobalt/cssom/css_style_declaration_test.cc
rename to src/cobalt/cssom/css_declared_style_declaration_test.cc
index 0e71d24..fb8d97d 100644
--- a/src/cobalt/cssom/css_style_declaration_test.cc
+++ b/src/cobalt/cssom/css_declared_style_declaration_test.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Google Inc. All Rights Reserved.
+ * 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.
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "cobalt/cssom/css_style_declaration.h"
-
#include "cobalt/cssom/css_declared_style_data.h"
#include "cobalt/cssom/css_declared_style_declaration.h"
#include "cobalt/cssom/css_parser.h"
@@ -37,12 +35,14 @@
using ::testing::_;
using ::testing::Return;
+namespace {
class MockMutationObserver : public MutationObserver {
public:
MOCK_METHOD0(OnCSSMutation, void());
};
+} // namespace
-TEST(CSSStyleDeclarationTest, BackgroundSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BackgroundSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -58,7 +58,7 @@
style->set_background(background, NULL);
}
-TEST(CSSStyleDeclarationTest, BackgroundColorSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BackgroundColorSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -74,7 +74,7 @@
style->set_background_color(background_color, NULL);
}
-TEST(CSSStyleDeclarationTest, BackgroundImageSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BackgroundImageSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -90,7 +90,7 @@
style->set_background_image(background_image, NULL);
}
-TEST(CSSStyleDeclarationTest, BackgroundPositionSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BackgroundPositionSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -106,7 +106,7 @@
style->set_background_position(background_position, NULL);
}
-TEST(CSSStyleDeclarationTest, BackgroundRepeatSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BackgroundRepeatSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -122,7 +122,7 @@
style->set_background_repeat(background_repeat, NULL);
}
-TEST(CSSStyleDeclarationTest, BackgroundSizeSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BackgroundSizeSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -138,7 +138,7 @@
style->set_background_size(background_size, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -153,7 +153,7 @@
style->set_border(border, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderBottomSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderBottomSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -169,7 +169,7 @@
style->set_border_bottom(border_bottom, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderBottomColorSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderBottomColorSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -185,7 +185,7 @@
style->set_border_bottom_color(border_bottom_color, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderBottomStyleSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderBottomStyleSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -201,7 +201,7 @@
style->set_border_bottom_style(border_bottom_style, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderBottomWidthSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderBottomWidthSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> width =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -217,7 +217,7 @@
width->set_border_bottom_width(border_bottom_width, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderColorSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderColorSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -233,7 +233,7 @@
style->set_border_color(border_color, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderLeftSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderLeftSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -249,7 +249,7 @@
style->set_border_left(border_left, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderLeftColorSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderLeftColorSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -265,7 +265,7 @@
style->set_border_left_color(border_left_color, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderLeftStyleSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderLeftStyleSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -281,7 +281,7 @@
style->set_border_left_style(border_left_style, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderLeftWidthSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderLeftWidthSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> width =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -297,7 +297,7 @@
width->set_border_left_width(border_left_width, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderRadiusSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderRadiusSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -313,7 +313,7 @@
style->set_border_radius(border_radius, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderRightSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderRightSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -329,7 +329,7 @@
style->set_border_right(border_right, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderRightColorSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderRightColorSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -345,7 +345,7 @@
style->set_border_right_color(border_right_color, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderRightStyleSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderRightStyleSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -361,7 +361,7 @@
style->set_border_right_style(border_right_style, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderRightWidthSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderRightWidthSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> width =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -377,7 +377,7 @@
width->set_border_right_width(border_right_width, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderStyleSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderStyleSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -393,7 +393,7 @@
style->set_border_style(border_style, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderTopSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderTopSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -409,7 +409,7 @@
style->set_border_top(border_top, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderTopColorSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderTopColorSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -425,7 +425,7 @@
style->set_border_top_color(border_top_color, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderTopStyleSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderTopStyleSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -441,7 +441,7 @@
style->set_border_top_style(border_top_style, NULL);
}
-TEST(CSSStyleDeclarationTest, BorderTopWidthSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BorderTopWidthSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> width =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -457,7 +457,7 @@
width->set_border_top_width(border_top_width, NULL);
}
-TEST(CSSStyleDeclarationTest, BottomSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BottomSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -472,7 +472,7 @@
style->set_bottom(bottom, NULL);
}
-TEST(CSSStyleDeclarationTest, BoxShadowSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, BoxShadowSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -488,7 +488,7 @@
style->set_box_shadow(box_shadow, NULL);
}
-TEST(CSSStyleDeclarationTest, ColorSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, ColorSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -503,7 +503,7 @@
style->set_color(color, NULL);
}
-TEST(CSSStyleDeclarationTest, ContentSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, ContentSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -519,7 +519,7 @@
style->set_content(content, NULL);
}
-TEST(CSSStyleDeclarationTest, DisplaySetter) {
+TEST(CSSDeclaredStyleDeclarationTest, DisplaySetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -535,7 +535,7 @@
style->set_display(display, NULL);
}
-TEST(CSSStyleDeclarationTest, FontSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, FontSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -550,7 +550,7 @@
style->set_font(font, NULL);
}
-TEST(CSSStyleDeclarationTest, FontFamilySetter) {
+TEST(CSSDeclaredStyleDeclarationTest, FontFamilySetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -566,7 +566,7 @@
style->set_font_family(font_family, NULL);
}
-TEST(CSSStyleDeclarationTest, FontSizeSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, FontSizeSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -582,7 +582,7 @@
style->set_font_size(font_size, NULL);
}
-TEST(CSSStyleDeclarationTest, FontStyleSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, FontStyleSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -598,7 +598,7 @@
style->set_font_style(font_style, NULL);
}
-TEST(CSSStyleDeclarationTest, FontWeightSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, FontWeightSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -614,7 +614,7 @@
style->set_font_weight(font_weight, NULL);
}
-TEST(CSSStyleDeclarationTest, HeightSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, HeightSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -629,7 +629,7 @@
style->set_height(height, NULL);
}
-TEST(CSSStyleDeclarationTest, LeftSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, LeftSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -644,7 +644,7 @@
style->set_left(left, NULL);
}
-TEST(CSSStyleDeclarationTest, LineHeightSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, LineHeightSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -660,7 +660,7 @@
style->set_line_height(line_height, NULL);
}
-TEST(CSSStyleDeclarationTest, MarginSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, MarginSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -675,7 +675,7 @@
style->set_margin(margin, NULL);
}
-TEST(CSSStyleDeclarationTest, MarginBottomSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, MarginBottomSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -691,7 +691,7 @@
style->set_margin_bottom(margin_bottom, NULL);
}
-TEST(CSSStyleDeclarationTest, MarginLeftSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, MarginLeftSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -707,7 +707,7 @@
style->set_margin_left(margin_left, NULL);
}
-TEST(CSSStyleDeclarationTest, MarginRightSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, MarginRightSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -723,7 +723,7 @@
style->set_margin_right(margin_right, NULL);
}
-TEST(CSSStyleDeclarationTest, MarginTopSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, MarginTopSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -739,7 +739,7 @@
style->set_margin_top(margin_top, NULL);
}
-TEST(CSSStyleDeclarationTest, MaxHeightSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, MaxHeightSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -755,7 +755,7 @@
style->set_max_height(max_height, NULL);
}
-TEST(CSSStyleDeclarationTest, MaxWidthSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, MaxWidthSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -771,7 +771,7 @@
style->set_max_width(max_width, NULL);
}
-TEST(CSSStyleDeclarationTest, MinHeightSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, MinHeightSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -787,7 +787,7 @@
style->set_min_height(min_height, NULL);
}
-TEST(CSSStyleDeclarationTest, MinWidthSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, MinWidthSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -803,7 +803,7 @@
style->set_min_width(min_width, NULL);
}
-TEST(CSSStyleDeclarationTest, OpacitySetter) {
+TEST(CSSDeclaredStyleDeclarationTest, OpacitySetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -819,7 +819,7 @@
style->set_opacity(opacity, NULL);
}
-TEST(CSSStyleDeclarationTest, OverflowSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, OverflowSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -835,7 +835,7 @@
style->set_overflow(overflow, NULL);
}
-TEST(CSSStyleDeclarationTest, OverflowWrapSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, OverflowWrapSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -852,7 +852,7 @@
style->set_overflow_wrap(overflow_wrap, NULL);
}
-TEST(CSSStyleDeclarationTest, PaddingSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, PaddingSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -868,7 +868,7 @@
style->set_padding(padding, NULL);
}
-TEST(CSSStyleDeclarationTest, PaddingBottomSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, PaddingBottomSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -884,7 +884,7 @@
style->set_padding_bottom(padding_bottom, NULL);
}
-TEST(CSSStyleDeclarationTest, PaddingLeftSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, PaddingLeftSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -900,7 +900,7 @@
style->set_padding_left(padding_left, NULL);
}
-TEST(CSSStyleDeclarationTest, PaddingRightSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, PaddingRightSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -916,7 +916,7 @@
style->set_padding_right(padding_right, NULL);
}
-TEST(CSSStyleDeclarationTest, PaddingTopSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, PaddingTopSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -932,7 +932,7 @@
style->set_padding_top(padding_top, NULL);
}
-TEST(CSSStyleDeclarationTest, PositionSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, PositionSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -948,7 +948,7 @@
style->set_position(position, NULL);
}
-TEST(CSSStyleDeclarationTest, RightSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, RightSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -963,7 +963,7 @@
style->set_right(right, NULL);
}
-TEST(CSSStyleDeclarationTest, TextAlignSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TextAlignSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -979,7 +979,7 @@
style->set_text_align(text_align, NULL);
}
-TEST(CSSStyleDeclarationTest, TextDecorationColorSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TextDecorationColorSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -996,7 +996,7 @@
style->set_text_decoration_color(text_decoration_color, NULL);
}
-TEST(CSSStyleDeclarationTest, TextDecorationLineSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TextDecorationLineSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1013,7 +1013,7 @@
style->set_text_decoration_line(text_decoration_line, NULL);
}
-TEST(CSSStyleDeclarationTest, TextIndentSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TextIndentSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1030,7 +1030,7 @@
style->set_text_indent(text_indent, NULL);
}
-TEST(CSSStyleDeclarationTest, TextOverflowSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TextOverflowSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1047,7 +1047,7 @@
style->set_text_overflow(text_overflow, NULL);
}
-TEST(CSSStyleDeclarationTest, TextShadowSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TextShadowSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1064,7 +1064,7 @@
style->set_text_shadow(text_shadow, NULL);
}
-TEST(CSSStyleDeclarationTest, TextTransformSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TextTransformSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1081,7 +1081,7 @@
style->set_text_transform(text_transform, NULL);
}
-TEST(CSSStyleDeclarationTest, TopSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TopSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1096,7 +1096,7 @@
style->set_top(top, NULL);
}
-TEST(CSSStyleDeclarationTest, TransformSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TransformSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1112,7 +1112,7 @@
style->set_transform(transform, NULL);
}
-TEST(CSSStyleDeclarationTest, TransformOriginSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TransformOriginSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1128,7 +1128,7 @@
style->set_transform_origin(transform_origin, NULL);
}
-TEST(CSSStyleDeclarationTest, TransitionSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TransitionSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1144,7 +1144,7 @@
style->set_transition(transition, NULL);
}
-TEST(CSSStyleDeclarationTest, TransitionDelaySetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TransitionDelaySetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1160,7 +1160,7 @@
style->set_transition_delay(transition_delay, NULL);
}
-TEST(CSSStyleDeclarationTest, TransitionDurationSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TransitionDurationSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1176,7 +1176,7 @@
style->set_transition_duration(transition_duration, NULL);
}
-TEST(CSSStyleDeclarationTest, TransitionPropertySetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TransitionPropertySetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1192,7 +1192,7 @@
style->set_transition_property(transition_property, NULL);
}
-TEST(CSSStyleDeclarationTest, TransitionTimingFunctionSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, TransitionTimingFunctionSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1209,7 +1209,7 @@
style->set_transition_timing_function(transition_timing_function, NULL);
}
-TEST(CSSStyleDeclarationTest, VerticalAlignSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, VerticalAlignSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1225,7 +1225,7 @@
style->set_vertical_align(vertical_align, NULL);
}
-TEST(CSSStyleDeclarationTest, VisibilitySetter) {
+TEST(CSSDeclaredStyleDeclarationTest, VisibilitySetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1241,7 +1241,7 @@
style->set_visibility(visibility, NULL);
}
-TEST(CSSStyleDeclarationTest, WhiteSpaceSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, WhiteSpaceSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1258,7 +1258,7 @@
style->set_white_space(white_space, NULL);
}
-TEST(CSSStyleDeclarationTest, WidthSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, WidthSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1273,7 +1273,7 @@
style->set_width(width, NULL);
}
-TEST(CSSStyleDeclarationTest, WordWrapSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, WordWrapSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1290,7 +1290,7 @@
style->set_word_wrap(word_wrap, NULL);
}
-TEST(CSSStyleDeclarationTest, ZIndexSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, ZIndexSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1306,7 +1306,7 @@
style->set_z_index(z_index, NULL);
}
-TEST(CSSStyleDeclarationTest, CSSTextSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, CSSTextSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1318,7 +1318,7 @@
style->set_css_text(css_text, NULL);
}
-TEST(CSSStyleDeclarationTest, CSSTextSetterEmptyString) {
+TEST(CSSDeclaredStyleDeclarationTest, CSSTextSetterEmptyString) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleData> initial_style =
new CSSDeclaredStyleData();
@@ -1338,7 +1338,7 @@
style->set_css_text(css_text, NULL);
}
-TEST(CSSStyleDeclarationTest, CSSTextGetter) {
+TEST(CSSDeclaredStyleDeclarationTest, CSSTextGetter) {
testing::MockCSSParser css_parser;
scoped_refptr<PercentageValue> background_size = new PercentageValue(0.50f);
scoped_refptr<LengthValue> bottom = new LengthValue(16, kPixelsUnit);
@@ -1357,7 +1357,7 @@
// TODO: Add GetPropertyValue tests, property getter tests and tests
// that checking if the attributes' setter and the getter are consistent when
// fully support converting PropertyValue to std::string.
-TEST(CSSStyleDeclarationTest, PropertyValueSetter) {
+TEST(CSSDeclaredStyleDeclarationTest, PropertyValueSetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1374,7 +1374,57 @@
NULL);
}
-TEST(CSSStyleDeclarationTest, PropertyValueGetter) {
+TEST(CSSDeclaredStyleDeclarationTest, PropertyValueSetterTwice) {
+ testing::MockCSSParser css_parser;
+ scoped_refptr<CSSDeclaredStyleDeclaration> style =
+ new CSSDeclaredStyleDeclaration(&css_parser);
+
+ const std::string background = "rgba(0, 0, 0, .8)";
+ MockMutationObserver observer;
+ style->set_mutation_observer(&observer);
+
+ EXPECT_CALL(css_parser,
+ ParsePropertyIntoDeclarationData(
+ GetPropertyName(kBackgroundProperty), background, _, _));
+ EXPECT_CALL(observer, OnCSSMutation()).Times(1);
+ style->SetPropertyValue(GetPropertyName(kBackgroundProperty), background,
+ NULL);
+}
+
+TEST(CSSDeclaredStyleDeclarationTest, PropertySetterWithTwoValues) {
+ testing::MockCSSParser css_parser;
+ scoped_refptr<CSSDeclaredStyleDeclaration> style =
+ new CSSDeclaredStyleDeclaration(&css_parser);
+
+ const std::string background = "rgba(0, 0, 0, .8)";
+ MockMutationObserver observer;
+ style->set_mutation_observer(&observer);
+
+ EXPECT_CALL(css_parser,
+ ParsePropertyIntoDeclarationData(
+ GetPropertyName(kBackgroundProperty), background, _, _));
+ EXPECT_CALL(observer, OnCSSMutation()).Times(1);
+ style->SetProperty(GetPropertyName(kBackgroundProperty), background, NULL);
+}
+
+TEST(CSSDeclaredStyleDeclarationTest, PropertySetterWithThreeValues) {
+ testing::MockCSSParser css_parser;
+ scoped_refptr<CSSDeclaredStyleDeclaration> style =
+ new CSSDeclaredStyleDeclaration(&css_parser);
+
+ const std::string background = "rgba(0, 0, 0, .8)";
+ MockMutationObserver observer;
+ style->set_mutation_observer(&observer);
+
+ EXPECT_CALL(css_parser,
+ ParsePropertyIntoDeclarationData(
+ GetPropertyName(kBackgroundProperty), background, _, _));
+ EXPECT_CALL(observer, OnCSSMutation()).Times(1);
+ style->SetProperty(GetPropertyName(kBackgroundProperty), background,
+ std::string(), NULL);
+}
+
+TEST(CSSDeclaredStyleDeclarationTest, PropertyValueGetter) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleData> initial_style =
new CSSDeclaredStyleData();
@@ -1387,7 +1437,8 @@
"center");
}
-TEST(CSSStyleDeclarationTest, UnknownDeclaredStylePropertyValueShouldBeEmpty) {
+TEST(CSSDeclaredStyleDeclarationTest,
+ UnknownDeclaredStylePropertyValueShouldBeEmpty) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleData> initial_style =
new CSSDeclaredStyleData();
@@ -1397,7 +1448,26 @@
EXPECT_EQ(style->GetPropertyValue("cobalt_cobalt_cobalt"), "");
}
-TEST(CSSStyleDeclarationTest, LengthAttributeGetterEmpty) {
+TEST(CSSDeclaredStyleDeclarationTest, RemoveProperty) {
+ testing::MockCSSParser css_parser;
+ scoped_refptr<CSSDeclaredStyleData> initial_style =
+ new CSSDeclaredStyleData();
+ initial_style->SetPropertyValueAndImportance(
+ kDisplayProperty, KeywordValue::GetInline(), false);
+ scoped_refptr<CSSDeclaredStyleDeclaration> style =
+ new CSSDeclaredStyleDeclaration(initial_style, &css_parser);
+
+ MockMutationObserver observer;
+ style->set_mutation_observer(&observer);
+
+ EXPECT_CALL(observer, OnCSSMutation()).Times(1);
+ EXPECT_CALL(css_parser,
+ ParsePropertyIntoDeclarationData(
+ GetPropertyName(kDisplayProperty), std::string(""), _, _));
+ style->RemoveProperty(GetPropertyName(kDisplayProperty), NULL);
+}
+
+TEST(CSSDeclaredStyleDeclarationTest, LengthAttributeGetterEmpty) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1405,7 +1475,7 @@
EXPECT_EQ(style->length(), 0);
}
-TEST(CSSStyleDeclarationTest, LengthAttributeGetterNotEmpty) {
+TEST(CSSDeclaredStyleDeclarationTest, LengthAttributeGetterNotEmpty) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleData> initial_style =
new CSSDeclaredStyleData();
@@ -1419,7 +1489,7 @@
EXPECT_EQ(style->length(), 2);
}
-TEST(CSSStyleDeclarationTest, ItemGetterEmpty) {
+TEST(CSSDeclaredStyleDeclarationTest, ItemGetterEmpty) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleDeclaration> style =
new CSSDeclaredStyleDeclaration(&css_parser);
@@ -1427,7 +1497,7 @@
EXPECT_FALSE(style->Item(0));
}
-TEST(CSSStyleDeclarationTest, ItemGetterNotEmpty) {
+TEST(CSSDeclaredStyleDeclarationTest, ItemGetterNotEmpty) {
testing::MockCSSParser css_parser;
scoped_refptr<CSSDeclaredStyleData> initial_style =
new CSSDeclaredStyleData();
@@ -1442,7 +1512,7 @@
EXPECT_TRUE(style->Item(1));
EXPECT_FALSE(style->Item(2));
- // The order is not important, as long as with properties are represented.
+ // The order is not important, as long as declared properties are represented.
if (style->Item(0).value() == GetPropertyName(kDisplayProperty)) {
EXPECT_EQ(style->Item(1).value(), GetPropertyName(kTextAlignProperty));
} else {
diff --git a/src/cobalt/cssom/css_style_declaration.h b/src/cobalt/cssom/css_style_declaration.h
index 6bfcd8b..dc542d2 100644
--- a/src/cobalt/cssom/css_style_declaration.h
+++ b/src/cobalt/cssom/css_style_declaration.h
@@ -427,6 +427,26 @@
const std::string& property_value,
script::ExceptionState* exception_state) = 0;
+ virtual void SetProperty(const std::string& property_name,
+ const std::string& property_value,
+ const std::string& priority,
+ script::ExceptionState* exception_state) = 0;
+
+ void SetProperty(const std::string& property_name,
+ const std::string& property_value,
+ script::ExceptionState* exception_state) {
+ SetPropertyValue(property_name, property_value, exception_state);
+ }
+
+ std::string RemoveProperty(const std::string& property_name,
+ script::ExceptionState* exception_state) {
+ std::string retval = GetPropertyValue(property_name);
+ if (!retval.empty()) {
+ SetPropertyValue(property_name, "", exception_state);
+ }
+ return retval;
+ }
+
// The parent rule is the CSS rule that the CSS declaration block is
// associated with, if any, or null otherwise.
// https://www.w3.org/TR/2013/WD-cssom-20131205/#css-declaration-block
diff --git a/src/cobalt/cssom/cssom_test.gyp b/src/cobalt/cssom/cssom_test.gyp
index a776fa4..3c57b62 100644
--- a/src/cobalt/cssom/cssom_test.gyp
+++ b/src/cobalt/cssom/cssom_test.gyp
@@ -26,14 +26,15 @@
'computed_style_test.cc',
'css_computed_style_data_property_set_matcher_test.cc',
'css_computed_style_data_test.cc',
+ 'css_computed_style_declaration_test.cc',
'css_declared_style_data_test.cc',
+ 'css_declared_style_declaration_test.cc',
'css_font_face_declaration_data_test.cc',
'css_font_face_rule_test.cc',
'css_grouping_rule_test.cc',
'css_property_definitions_test.cc',
'css_rule_list_test.cc',
'css_rule_visitor_test.cc',
- 'css_style_declaration_test.cc',
'css_style_sheet_test.cc',
'css_transition_set_test.cc',
'interpolate_property_value_test.cc',
diff --git a/src/cobalt/cssom/keyword_names.cc b/src/cobalt/cssom/keyword_names.cc
index 28cbfbf..34fde90 100644
--- a/src/cobalt/cssom/keyword_names.cc
+++ b/src/cobalt/cssom/keyword_names.cc
@@ -73,6 +73,7 @@
const char kLineThroughKeywordName[] = "line-through";
const char kMaroonKeywordName[] = "maroon";
const char kMiddleKeywordName[] = "middle";
+const char kMonoscopicKeywordName[] = "monoscopic";
const char kMonospaceKeywordName[] = "monospace";
const char kNavyKeywordName[] = "navy";
const char kNoneKeywordName[] = "none";
@@ -100,6 +101,8 @@
const char kStaticKeywordName[] = "static";
const char kStepEndKeywordName[] = "step-end";
const char kStepStartKeywordName[] = "step-start";
+const char kStereoscopicLeftRightKeywordName[] = "stereoscopic-left-right";
+const char kStereoscopicTopBottomKeywordName[] = "stereoscopic-top-bottom";
const char kTealKeywordName[] = "teal";
const char kToKeywordName[] = "to";
const char kTopKeywordName[] = "top";
diff --git a/src/cobalt/cssom/keyword_names.h b/src/cobalt/cssom/keyword_names.h
index ccac4d5..86f0eda 100644
--- a/src/cobalt/cssom/keyword_names.h
+++ b/src/cobalt/cssom/keyword_names.h
@@ -75,6 +75,7 @@
extern const char kLineThroughKeywordName[];
extern const char kMaroonKeywordName[];
extern const char kMiddleKeywordName[];
+extern const char kMonoscopicKeywordName[];
extern const char kMonospaceKeywordName[];
extern const char kNavyKeywordName[];
extern const char kNoneKeywordName[];
@@ -102,6 +103,8 @@
extern const char kStaticKeywordName[];
extern const char kStepEndKeywordName[];
extern const char kStepStartKeywordName[];
+extern const char kStereoscopicLeftRightKeywordName[];
+extern const char kStereoscopicTopBottomKeywordName[];
extern const char kTealKeywordName[];
extern const char kToKeywordName[];
extern const char kTopKeywordName[];
diff --git a/src/cobalt/cssom/keyword_value.cc b/src/cobalt/cssom/keyword_value.cc
index 73aba27..0d90535 100644
--- a/src/cobalt/cssom/keyword_value.cc
+++ b/src/cobalt/cssom/keyword_value.cc
@@ -56,6 +56,7 @@
left_value(new KeywordValue(KeywordValue::kLeft)),
line_through_value(new KeywordValue(KeywordValue::kLineThrough)),
middle_value(new KeywordValue(KeywordValue::kMiddle)),
+ monoscopic_value(new KeywordValue(KeywordValue::kMonoscopic)),
monospace_value(new KeywordValue(KeywordValue::kMonospace)),
none_value(new KeywordValue(KeywordValue::kNone)),
no_repeat_value(new KeywordValue(KeywordValue::kNoRepeat)),
@@ -73,6 +74,10 @@
solid_value(new KeywordValue(KeywordValue::kSolid)),
start_value(new KeywordValue(KeywordValue::kStart)),
static_value(new KeywordValue(KeywordValue::kStatic)),
+ stereoscopic_left_right_value(
+ new KeywordValue(KeywordValue::kStereoscopicLeftRight)),
+ stereoscopic_top_bottom_value(
+ new KeywordValue(KeywordValue::kStereoscopicTopBottom)),
top_value(new KeywordValue(KeywordValue::kTop)),
uppercase_value(new KeywordValue(KeywordValue::kUppercase)),
visible_value(new KeywordValue(KeywordValue::kVisible)) {}
@@ -107,6 +112,7 @@
const scoped_refptr<KeywordValue> left_value;
const scoped_refptr<KeywordValue> line_through_value;
const scoped_refptr<KeywordValue> middle_value;
+ const scoped_refptr<KeywordValue> monoscopic_value;
const scoped_refptr<KeywordValue> monospace_value;
const scoped_refptr<KeywordValue> none_value;
const scoped_refptr<KeywordValue> no_repeat_value;
@@ -124,6 +130,8 @@
const scoped_refptr<KeywordValue> solid_value;
const scoped_refptr<KeywordValue> start_value;
const scoped_refptr<KeywordValue> static_value;
+ const scoped_refptr<KeywordValue> stereoscopic_left_right_value;
+ const scoped_refptr<KeywordValue> stereoscopic_top_bottom_value;
const scoped_refptr<KeywordValue> top_value;
const scoped_refptr<KeywordValue> uppercase_value;
const scoped_refptr<KeywordValue> visible_value;
@@ -259,6 +267,10 @@
return non_trivial_static_fields.Get().middle_value;
}
+const scoped_refptr<KeywordValue>& KeywordValue::GetMonoscopic() {
+ return non_trivial_static_fields.Get().monoscopic_value;
+}
+
const scoped_refptr<KeywordValue>& KeywordValue::GetMonospace() {
return non_trivial_static_fields.Get().monospace_value;
}
@@ -327,6 +339,14 @@
return non_trivial_static_fields.Get().static_value;
}
+const scoped_refptr<KeywordValue>& KeywordValue::GetStereoscopicLeftRight() {
+ return non_trivial_static_fields.Get().stereoscopic_left_right_value;
+}
+
+const scoped_refptr<KeywordValue>& KeywordValue::GetStereoscopicTopBottom() {
+ return non_trivial_static_fields.Get().stereoscopic_top_bottom_value;
+}
+
const scoped_refptr<KeywordValue>& KeywordValue::GetTop() {
return non_trivial_static_fields.Get().top_value;
}
@@ -407,6 +427,8 @@
return kMiddleKeywordName;
case kMonospace:
return kMonospaceKeywordName;
+ case kMonoscopic:
+ return kMonoscopicKeywordName;
case kNone:
return kNoneKeywordName;
case kNoRepeat:
@@ -439,6 +461,10 @@
return kStartKeywordName;
case kStatic:
return kStaticKeywordName;
+ case kStereoscopicLeftRight:
+ return kStereoscopicLeftRightKeywordName;
+ case kStereoscopicTopBottom:
+ return kStereoscopicTopBottomKeywordName;
case kTop:
return kTopKeywordName;
case kUppercase:
diff --git a/src/cobalt/cssom/keyword_value.h b/src/cobalt/cssom/keyword_value.h
index 2af8b00..bed4ba0 100644
--- a/src/cobalt/cssom/keyword_value.h
+++ b/src/cobalt/cssom/keyword_value.h
@@ -202,6 +202,10 @@
// https://www.w3.org/TR/css3-fonts/#generic-font-families
kMonospace,
+ // "monoscopic" is a value of the "cobalt-mtm" property which indicates
+ // that the mesh should only be rendered through one eye.
+ kMonoscopic,
+
// "none" is a value of "transform" property which means that HTML element
// is rendered as is.
// https://www.w3.org/TR/css3-transforms/#transform-property
@@ -291,6 +295,15 @@
// https://www.w3.org/TR/CSS21/visuren.html#choose-position
kStatic,
+ // "stereoscopic-left-right" is a value of the "cobalt-mtm" property which
+ // indicates that the mesh should be rendered in two views
+ // side-by-side.
+ kStereoscopicLeftRight,
+
+ // "stereoscopic-top-bottom" is a value of the "cobalt-mtm" property which
+ // indicates that the mesh should be rendered in two views above and below.
+ kStereoscopicTopBottom,
+
// "top" is a value of "vertical-align" property that indicates that the
// content should be aligned vertically at the top.
// https://www.w3.org/TR/CSS21/visudet.html#propdef-vertical-align
@@ -345,6 +358,7 @@
static const scoped_refptr<KeywordValue>& GetLeft();
static const scoped_refptr<KeywordValue>& GetLineThrough();
static const scoped_refptr<KeywordValue>& GetMiddle();
+ static const scoped_refptr<KeywordValue>& GetMonoscopic();
static const scoped_refptr<KeywordValue>& GetMonospace();
static const scoped_refptr<KeywordValue>& GetNone();
static const scoped_refptr<KeywordValue>& GetNoRepeat();
@@ -362,6 +376,8 @@
static const scoped_refptr<KeywordValue>& GetSolid();
static const scoped_refptr<KeywordValue>& GetStart();
static const scoped_refptr<KeywordValue>& GetStatic();
+ static const scoped_refptr<KeywordValue>& GetStereoscopicLeftRight();
+ static const scoped_refptr<KeywordValue>& GetStereoscopicTopBottom();
static const scoped_refptr<KeywordValue>& GetTop();
static const scoped_refptr<KeywordValue>& GetUppercase();
static const scoped_refptr<KeywordValue>& GetVisible();
diff --git a/src/cobalt/cssom/mtm_function.cc b/src/cobalt/cssom/mtm_function.cc
index ee51938..661060b 100644
--- a/src/cobalt/cssom/mtm_function.cc
+++ b/src/cobalt/cssom/mtm_function.cc
@@ -29,13 +29,16 @@
MTMFunction::MTMFunction(
const scoped_refptr<PropertyValue>& mesh_url,
ResolutionMatchedMeshListBuilder resolution_matched_meshes,
- float horizontal_fov, float vertical_fov, const glm::mat4& transform)
+ float horizontal_fov, float vertical_fov, const glm::mat4& transform,
+ const scoped_refptr<KeywordValue>& stereo_mode)
: mesh_url_(mesh_url),
resolution_matched_meshes_(resolution_matched_meshes.Pass()),
horizontal_fov_(horizontal_fov),
vertical_fov_(vertical_fov),
- transform_(transform) {
+ transform_(transform),
+ stereo_mode_(stereo_mode) {
DCHECK(mesh_url_);
+ DCHECK(stereo_mode_);
}
std::string MTMFunction::ToString() const {
@@ -65,7 +68,10 @@
result.append(base::StringPrintf("%.7g", transform()[col][row]));
}
}
- result.append("))");
+
+ result.append("), ");
+ result.append(stereo_mode()->ToString());
+ result.append(")");
return result;
}
@@ -114,7 +120,8 @@
bool MTMFunction::operator==(const MTMFunction& rhs) const {
if (!mesh_url()->Equals(*rhs.mesh_url()) ||
horizontal_fov() != rhs.horizontal_fov() ||
- horizontal_fov() != rhs.horizontal_fov()) {
+ horizontal_fov() != rhs.horizontal_fov() ||
+ !stereo_mode()->Equals(*rhs.stereo_mode())) {
return false;
}
const ResolutionMatchedMeshListBuilder& lhs_meshes =
diff --git a/src/cobalt/cssom/mtm_function.h b/src/cobalt/cssom/mtm_function.h
index 59498b4..764c0cb 100644
--- a/src/cobalt/cssom/mtm_function.h
+++ b/src/cobalt/cssom/mtm_function.h
@@ -25,6 +25,7 @@
#include "base/memory/scoped_vector.h"
#include "cobalt/base/polymorphic_equatable.h"
#include "cobalt/cssom/filter_function.h"
+#include "cobalt/cssom/keyword_value.h"
#include "cobalt/cssom/property_value.h"
#include "cobalt/cssom/url_value.h"
#include "third_party/glm/glm/mat4x4.hpp"
@@ -56,7 +57,8 @@
MTMFunction(const scoped_refptr<PropertyValue>& mesh_url,
ResolutionMatchedMeshListBuilder resolution_matched_meshes,
float horizontal_fov, float vertical_fov,
- const glm::mat4& transform);
+ const glm::mat4& transform,
+ const scoped_refptr<KeywordValue>& stereo_mode);
~MTMFunction() OVERRIDE {}
@@ -68,6 +70,9 @@
float horizontal_fov() const { return horizontal_fov_; }
float vertical_fov() const { return vertical_fov_; }
const glm::mat4& transform() const { return transform_; }
+ const scoped_refptr<KeywordValue>& stereo_mode() const {
+ return stereo_mode_;
+ }
std::string ToString() const OVERRIDE;
@@ -83,6 +88,7 @@
const float horizontal_fov_;
const float vertical_fov_;
const glm::mat4 transform_;
+ const scoped_refptr<KeywordValue> stereo_mode_;
DISALLOW_COPY_AND_ASSIGN(MTMFunction);
};
diff --git a/src/cobalt/cssom/property_value_is_equal_test.cc b/src/cobalt/cssom/property_value_is_equal_test.cc
index 0b647d0..263634b 100644
--- a/src/cobalt/cssom/property_value_is_equal_test.cc
+++ b/src/cobalt/cssom/property_value_is_equal_test.cc
@@ -604,12 +604,14 @@
new URLValue("somemesh.msh"),
MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 0.70707f, 6.28f,
glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 9.0f, 0.0f, 0.0f,
- 1.0f, 0.07878f, 0.0f, 0.0f, 0.0f, 1.0f)));
+ 1.0f, 0.07878f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetMonoscopic()));
filter_list_a.push_back(new MTMFunction(
new URLValue("sphere.msh"),
MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 0.676f, 6.28f,
glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)));
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetStereoscopicLeftRight()));
scoped_refptr<FilterFunctionListValue> value_a(
new FilterFunctionListValue(filter_list_a.Pass()));
@@ -618,12 +620,14 @@
new URLValue("somemesh.msh"),
MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 0.70707f, 6.28f,
glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 9.0f, 0.0f, 0.0f,
- 1.0f, 0.07878f, 0.0f, 0.0f, 0.0f, 1.0f)));
+ 1.0f, 0.07878f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetMonoscopic()));
filter_list_b.push_back(new MTMFunction(
new URLValue("sphere.msh"),
MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 0.676f, 6.28f,
glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)));
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetStereoscopicLeftRight()));
scoped_refptr<FilterFunctionListValue> value_b(
new FilterFunctionListValue(filter_list_b.Pass()));
@@ -636,7 +640,8 @@
new URLValue("format.msh"),
MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 8.5f, 3.14f,
glm::mat4(1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)));
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetMonoscopic()));
scoped_refptr<FilterFunctionListValue> value_a(
new FilterFunctionListValue(filter_list_a.Pass()));
@@ -645,7 +650,8 @@
new URLValue("format.msh"),
MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 8.5f, 3.14f,
glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)));
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetMonoscopic()));
scoped_refptr<FilterFunctionListValue> value_b(
new FilterFunctionListValue(filter_list_b.Pass()));
diff --git a/src/cobalt/cssom/property_value_to_string_test.cc b/src/cobalt/cssom/property_value_to_string_test.cc
index fd51fe9..7d18941 100644
--- a/src/cobalt/cssom/property_value_to_string_test.cc
+++ b/src/cobalt/cssom/property_value_to_string_test.cc
@@ -54,9 +54,9 @@
namespace cssom {
TEST(PropertyValueToStringTest, AbsoluteURLValue) {
- GURL url("https://www.youtube.com");
+ GURL url("https://www.test.com");
scoped_refptr<AbsoluteURLValue> property(new AbsoluteURLValue(url));
- EXPECT_EQ(property->ToString(), "url(https://www.youtube.com/)");
+ EXPECT_EQ(property->ToString(), "url(https://www.test.com/)");
}
TEST(PropertyValueToStringTest, FontStyleValue) {
@@ -353,11 +353,13 @@
new URLValue("-.msh"),
MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 2.5f, 3.14f,
glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)));
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetMonoscopic()));
EXPECT_EQ(
"-cobalt-mtm(url(-.msh), "
"2.5rad 3.14rad, "
- "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))",
+ "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
+ "monoscopic)",
function->ToString());
}
@@ -367,14 +369,16 @@
1920, 2000000, new URLValue("a.msh")));
meshes.push_back(
new MTMFunction::ResolutionMatchedMesh(640, 5, new URLValue("b.msh")));
- scoped_ptr<MTMFunction> function(new MTMFunction(
- new URLValue("-.msh"), meshes.Pass(), 28.5f, 3.14f,
- glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)));
+ scoped_ptr<MTMFunction> function(
+ new MTMFunction(new URLValue("-.msh"), meshes.Pass(), 28.5f, 3.14f,
+ glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetStereoscopicLeftRight()));
EXPECT_EQ(
"-cobalt-mtm(url(-.msh) 1920 2000000 url(a.msh) 640 5 url(b.msh), "
"28.5rad 3.14rad, "
- "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))",
+ "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
+ "stereoscopic-left-right)",
function->ToString());
}
@@ -384,12 +388,26 @@
new URLValue("-.msh"),
MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 8.5f, 3.14f,
glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)));
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetMonoscopic()));
filter_list.push_back(new MTMFunction(
new URLValue("world.msh"),
MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 8.5f, 39.0f,
glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)));
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetMonoscopic()));
+ filter_list.push_back(new MTMFunction(
+ new URLValue("stereoscopic-world.msh"),
+ MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 8.5f, 39.0f,
+ glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetStereoscopicLeftRight()));
+ filter_list.push_back(new MTMFunction(
+ new URLValue("stereoscopic-top-bottom-world.msh"),
+ MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 8.5f, 39.0f,
+ glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetStereoscopicTopBottom()));
scoped_refptr<FilterFunctionListValue> property(
new FilterFunctionListValue(filter_list.Pass()));
@@ -397,10 +415,20 @@
EXPECT_EQ(
"-cobalt-mtm(url(-.msh), "
"8.5rad 3.14rad, "
- "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) "
+ "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
+ "monoscopic) "
"-cobalt-mtm(url(world.msh), "
"8.5rad 39rad, "
- "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))",
+ "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
+ "monoscopic) "
+ "-cobalt-mtm(url(stereoscopic-world.msh), "
+ "8.5rad 39rad, "
+ "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
+ "stereoscopic-left-right) "
+ "-cobalt-mtm(url(stereoscopic-top-bottom-world.msh), "
+ "8.5rad 39rad, "
+ "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
+ "stereoscopic-top-bottom)",
property->ToString());
}
diff --git a/src/cobalt/cssom/property_value_visitor_test.cc b/src/cobalt/cssom/property_value_visitor_test.cc
index 550c8b9..1ff138d 100644
--- a/src/cobalt/cssom/property_value_visitor_test.cc
+++ b/src/cobalt/cssom/property_value_visitor_test.cc
@@ -121,7 +121,8 @@
FilterFunctionListValue::Builder builder;
builder.push_back(new MTMFunction(new URLValue("p.msh"), resMs.Pass(), 120,
- 60, glm::mat4(1.0f)));
+ 60, glm::mat4(1.0f),
+ KeywordValue::GetMonoscopic()));
scoped_refptr<FilterFunctionListValue> filter_list_value =
new FilterFunctionListValue(builder.Pass());
diff --git a/src/cobalt/dom/Blob.idl b/src/cobalt/dom/Blob.idl
index aa0fcb6..edb972d 100644
--- a/src/cobalt/dom/Blob.idl
+++ b/src/cobalt/dom/Blob.idl
@@ -14,7 +14,16 @@
* limitations under the License.
*/
-// https://www.w3.org/TR/FileAPI/#dfn-Blob
+// https://www.w3.org/TR/2015/WD-FileAPI-20150421/#dfn-Blob
-// Stub implementation of Blob so we can call "instanceof Blob".
-interface Blob {};
+[
+ //Constructor,
+ // TODO: When arrays or sequences are supported, support the constructor
+ // defined in the standard with multiple blobparts, and drop the single
+ // ArrayBuffer constructor.
+ Constructor(optional ArrayBuffer buffer),
+ ConstructorCallWith=EnvironmentSettings
+]
+interface Blob {
+ readonly attribute unsigned long long size;
+};
diff --git a/src/cobalt/dom/Int16Array.idl b/src/cobalt/dom/Int16Array.idl
new file mode 100644
index 0000000..3cc4d3c
--- /dev/null
+++ b/src/cobalt/dom/Int16Array.idl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+// https://www.khronos.org/registry/typedarray/specs/latest/#7
+[
+ Constructor(unsigned long length),
+ Constructor(Int16Array array),
+ Constructor(ArrayBuffer buffer, optional unsigned long byteOffset, optional unsigned long length),
+ ConstructorCallWith=EnvironmentSettings,
+ RaisesException=Constructor
+]
+interface Int16Array : ArrayBufferView {
+ const unsigned long BYTES_PER_ELEMENT = 2;
+
+ readonly attribute unsigned long length;
+
+ getter short get(unsigned long index);
+ setter void set(unsigned long index, short value);
+
+ // TODO: When arrays or sequences are supported, support them here.
+ // void set(sequence<long> array, optional unsigned long offset);
+
+ // Copy items into this array from source, starting at this[offset].
+ [RaisesException] void set(Int16Array source, optional unsigned long offset);
+
+ // Return a new Int16Array that is a view on top of this one.
+ // Contains this[start]..this[end]. end defaults to length if unspecified.
+ [CallWith=EnvironmentSettings] Int16Array subarray(long start, optional long end);
+};
diff --git a/src/cobalt/dom/URL.idl b/src/cobalt/dom/URL.idl
index 0273f57..8784d1d 100644
--- a/src/cobalt/dom/URL.idl
+++ b/src/cobalt/dom/URL.idl
@@ -19,5 +19,7 @@
interface URL {
[CallWith=EnvironmentSettings] static DOMString
createObjectURL(MediaSource mediaSource);
+ [CallWith=EnvironmentSettings] static DOMString
+ createObjectURL(Blob blob);
[CallWith=EnvironmentSettings] static void revokeObjectURL(DOMString url);
};
diff --git a/src/cobalt/dom/blob.h b/src/cobalt/dom/blob.h
index 8f11422..d677e04 100644
--- a/src/cobalt/dom/blob.h
+++ b/src/cobalt/dom/blob.h
@@ -17,15 +17,45 @@
#ifndef COBALT_DOM_BLOB_H_
#define COBALT_DOM_BLOB_H_
+#include <vector>
+
+#include "cobalt/dom/array_buffer.h"
+#include "cobalt/dom/url_registry.h"
+#include "cobalt/script/environment_settings.h"
#include "cobalt/script/wrappable.h"
+#include "googleurl/src/gurl.h"
namespace cobalt {
namespace dom {
-// Stub implementation of Blob so we can call "instanceof Blob".
+// A Blob object refers to a byte sequence, and has a size attribute which is
+// the total number of bytes in the byte sequence, and a type attribute, which
+// is an ASCII-encoded string in lower case representing the media type of the
+// byte sequence.
+// https://www.w3.org/TR/2015/WD-FileAPI-20150421/#dfn-Blob
+//
+// Note: Cobalt currently does not implement nor need the type attribute.
class Blob : public script::Wrappable {
public:
+ typedef UrlRegistry<Blob> Registry;
+
+ Blob(script::EnvironmentSettings* settings,
+ const scoped_refptr<ArrayBuffer>& buffer = NULL)
+ : buffer_(
+ buffer ? buffer->Slice(settings, 0)
+ : scoped_refptr<ArrayBuffer>(new ArrayBuffer(settings, 0))) {
+ }
+
+ const uint8* data() { return buffer_->data(); }
+
+ uint64 size() { return static_cast<uint64>(buffer_->byte_length()); }
+
DEFINE_WRAPPABLE_TYPE(Blob);
+
+ private:
+ scoped_refptr<ArrayBuffer> buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(Blob);
};
} // namespace dom
diff --git a/src/cobalt/dom/blob_test.cc b/src/cobalt/dom/blob_test.cc
new file mode 100644
index 0000000..5d252da
--- /dev/null
+++ b/src/cobalt/dom/blob_test.cc
@@ -0,0 +1,80 @@
+/*
+ * 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 <algorithm>
+
+#include "cobalt/dom/blob.h"
+#include "cobalt/dom/data_view.h"
+#include "cobalt/script/testing/mock_exception_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace dom {
+namespace {
+
+using script::testing::MockExceptionState;
+using testing::_;
+using testing::SaveArg;
+using testing::StrictMock;
+
+TEST(BlobTest, Constructors) {
+ scoped_refptr<Blob> blob_default_buffer = new Blob(NULL);
+
+ EXPECT_EQ(0, blob_default_buffer->size());
+
+ StrictMock<MockExceptionState> exception_state;
+ scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 5);
+ scoped_refptr<DataView> data_view =
+ new DataView(array_buffer, &exception_state);
+ data_view->SetInt16(0, static_cast<int16>(0x0607), &exception_state);
+
+ scoped_refptr<Blob> blob_with_buffer = new Blob(NULL, array_buffer);
+
+ ASSERT_EQ(5, blob_with_buffer->size());
+ ASSERT_TRUE(blob_with_buffer->data());
+
+ EXPECT_EQ(0x6, blob_with_buffer->data()[0]);
+ EXPECT_EQ(0x7, blob_with_buffer->data()[1]);
+ EXPECT_EQ(0, blob_with_buffer->data()[2]);
+ EXPECT_EQ(0, blob_with_buffer->data()[3]);
+ EXPECT_EQ(0, blob_with_buffer->data()[4]);
+}
+
+// Tests that further changes to a buffer from which a blob was constructed
+// no longer affect the blob's buffer, since it must be a separate copy of
+// its construction arguments.
+TEST(BlobTest, HasOwnBuffer) {
+ StrictMock<MockExceptionState> exception_state;
+ scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 2);
+ scoped_refptr<DataView> data_view =
+ new DataView(array_buffer, &exception_state);
+ data_view->SetInt16(0, static_cast<int16>(0x0607), &exception_state);
+
+ scoped_refptr<Blob> blob_with_buffer = new Blob(NULL, array_buffer);
+
+ ASSERT_EQ(2, blob_with_buffer->size());
+ ASSERT_TRUE(blob_with_buffer->data());
+ EXPECT_NE(array_buffer->data(), blob_with_buffer->data());
+
+ data_view->SetUint8(1, static_cast<uint8>(0xff), &exception_state);
+
+ EXPECT_EQ(0x6, blob_with_buffer->data()[0]);
+ EXPECT_EQ(0x7, blob_with_buffer->data()[1]);
+}
+
+} // namespace
+} // namespace dom
+} // namespace cobalt
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 6087406..4ffeaa6 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -234,6 +234,7 @@
'uint8_array.h',
'url.cc',
'url.h',
+ 'url_registry.h',
'url_utils.cc',
'url_utils.h',
'window.cc',
diff --git a/src/cobalt/dom/dom_settings.cc b/src/cobalt/dom/dom_settings.cc
index 1eca2a2..0db0ac9 100644
--- a/src/cobalt/dom/dom_settings.cc
+++ b/src/cobalt/dom/dom_settings.cc
@@ -21,22 +21,22 @@
namespace cobalt {
namespace dom {
-DOMSettings::DOMSettings(const int max_dom_element_depth,
- loader::FetcherFactory* fetcher_factory,
- network::NetworkModule* network_module,
- const scoped_refptr<Window>& window,
- MediaSource::Registry* media_source_registry,
- media::CanPlayTypeHandler* can_play_type_handler,
- script::JavaScriptEngine* engine,
- script::GlobalEnvironment* global_environment,
- const Options& options)
+DOMSettings::DOMSettings(
+ const int max_dom_element_depth, loader::FetcherFactory* fetcher_factory,
+ network::NetworkModule* network_module, const scoped_refptr<Window>& window,
+ MediaSource::Registry* media_source_registry, Blob::Registry* blob_registry,
+ media::CanPlayTypeHandler* can_play_type_handler,
+ script::JavaScriptEngine* engine,
+ script::GlobalEnvironment* global_environment, const Options& options)
: max_dom_element_depth_(max_dom_element_depth),
+ enable_fake_microphone_(options.enable_fake_microphone),
fetcher_factory_(fetcher_factory),
network_module_(network_module),
window_(window),
array_buffer_allocator_(options.array_buffer_allocator),
array_buffer_cache_(options.array_buffer_cache),
media_source_registry_(media_source_registry),
+ blob_registry_(blob_registry),
can_play_type_handler_(can_play_type_handler),
javascript_engine_(engine),
global_environment_(global_environment) {
diff --git a/src/cobalt/dom/dom_settings.h b/src/cobalt/dom/dom_settings.h
index 5bd39e1..492d21b 100644
--- a/src/cobalt/dom/dom_settings.h
+++ b/src/cobalt/dom/dom_settings.h
@@ -20,6 +20,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "cobalt/dom/array_buffer.h"
+#include "cobalt/dom/blob.h"
#include "cobalt/dom/media_source.h"
#include "cobalt/dom/window.h"
#include "cobalt/media/can_play_type_handler.h"
@@ -45,7 +46,10 @@
public:
// Hold optional settings for DOMSettings.
struct Options {
- Options() : array_buffer_allocator(NULL), array_buffer_cache(NULL) {}
+ Options()
+ : array_buffer_allocator(NULL),
+ array_buffer_cache(NULL),
+ enable_fake_microphone(false) {}
// ArrayBuffer allocates its memory on the heap by default and ArrayBuffers
// may occupy a lot of memory. It is possible to provide an allocator via
@@ -56,6 +60,8 @@
// amount of ArrayBuffer inside main memory. So we have provide the
// following cache to manage ArrayBuffer in main memory.
ArrayBuffer::Cache* array_buffer_cache;
+ // Use fake microphone if this flag is set to true.
+ bool enable_fake_microphone;
};
DOMSettings(const int max_dom_element_depth,
@@ -63,6 +69,7 @@
network::NetworkModule* network_module,
const scoped_refptr<Window>& window,
MediaSource::Registry* media_source_registry,
+ Blob::Registry* blob_registry,
media::CanPlayTypeHandler* can_play_type_handler,
script::JavaScriptEngine* engine,
script::GlobalEnvironment* global_environment_proxy,
@@ -70,6 +77,7 @@
~DOMSettings() OVERRIDE;
int max_dom_element_depth() { return max_dom_element_depth_; }
+ bool enable_fake_microphone() const { return enable_fake_microphone_; }
void set_window(const scoped_refptr<Window>& window) { window_ = window; }
scoped_refptr<Window> window() const { return window_; }
@@ -100,18 +108,21 @@
media::CanPlayTypeHandler* can_play_type_handler() const {
return can_play_type_handler_;
}
+ Blob::Registry* blob_registry() const { return blob_registry_; }
// An absolute URL used to resolve relative URLs.
virtual GURL base_url() const;
private:
const int max_dom_element_depth_;
+ const bool enable_fake_microphone_;
loader::FetcherFactory* fetcher_factory_;
network::NetworkModule* network_module_;
scoped_refptr<Window> window_;
ArrayBuffer::Allocator* array_buffer_allocator_;
ArrayBuffer::Cache* array_buffer_cache_;
MediaSource::Registry* media_source_registry_;
+ Blob::Registry* blob_registry_;
media::CanPlayTypeHandler* can_play_type_handler_;
script::JavaScriptEngine* javascript_engine_;
script::GlobalEnvironment* global_environment_;
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index 281ac59..b96ce65 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -24,6 +24,7 @@
'target_name': 'dom_test',
'type': '<(gtest_target_type)',
'sources': [
+ 'blob_test.cc',
'comment_test.cc',
'crypto_test.cc',
'csp_delegate_test.cc',
diff --git a/src/cobalt/dom/event_listener.cc b/src/cobalt/dom/event_listener.cc
index 45da194..4d9959e 100644
--- a/src/cobalt/dom/event_listener.cc
+++ b/src/cobalt/dom/event_listener.cc
@@ -59,8 +59,9 @@
snprintf(event_log_stack_[current_stack_depth_], kLogEntryMaxLength,
"%s@%s", event->type().c_str(),
event->current_target()->GetDebugName().c_str());
- } else {
- NOTREACHED();
+ } else if (current_stack_depth_ == base::UserLog::kEventStackMaxDepth) {
+ DLOG(WARNING) << "Reached maximum depth of " << kLogEntryMaxLength
+ << ". Subsequent events will not be logged.";
}
current_stack_depth_++;
}
@@ -68,7 +69,9 @@
void PopEvent() {
DCHECK(current_stack_depth_);
current_stack_depth_--;
- memset(event_log_stack_[current_stack_depth_], 0, kLogEntryMaxLength);
+ if (current_stack_depth_ < base::UserLog::kEventStackMaxDepth) {
+ memset(event_log_stack_[current_stack_depth_], 0, kLogEntryMaxLength);
+ }
}
private:
diff --git a/src/cobalt/dom/font_face_updater.cc b/src/cobalt/dom/font_face_updater.cc
index dbcd212..16de45f 100644
--- a/src/cobalt/dom/font_face_updater.cc
+++ b/src/cobalt/dom/font_face_updater.cc
@@ -158,6 +158,7 @@
case cssom::KeywordValue::kLeft:
case cssom::KeywordValue::kLineThrough:
case cssom::KeywordValue::kMiddle:
+ case cssom::KeywordValue::kMonoscopic:
case cssom::KeywordValue::kNone:
case cssom::KeywordValue::kNoRepeat:
case cssom::KeywordValue::kNormal:
@@ -172,6 +173,8 @@
case cssom::KeywordValue::kSolid:
case cssom::KeywordValue::kStart:
case cssom::KeywordValue::kStatic:
+ case cssom::KeywordValue::kStereoscopicLeftRight:
+ case cssom::KeywordValue::kStereoscopicTopBottom:
case cssom::KeywordValue::kTop:
case cssom::KeywordValue::kUppercase:
case cssom::KeywordValue::kVisible:
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index eda59cb..a2851ee 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -57,26 +57,11 @@
#if LOG_MEDIA_ELEMENT_ACTIVITIES
-template <typename T>
-void LogMediaElementActivity(const char* function_name, const T& t) {
- LOG(INFO) << function_name << " " << t;
-}
-
-template <typename T1, typename T2>
-void LogMediaElementActivity(const char* function_name, const T1& t1,
- const T2& t2) {
- LOG(INFO) << function_name << " " << t1 << " " << t2;
-}
-
-#define MLOG() LOG(INFO) << __FUNCTION__
-#define MLOG_1(x) LogMediaElementActivity(__FUNCTION__, x)
-#define MLOG_2(x, y) LogMediaElementActivity(__FUNCTION__, x, y)
+#define MLOG() LOG(INFO) << __FUNCTION__ << ": "
#else // LOG_MEDIA_ELEMENT_ACTIVITIES
-#define MLOG() do {} while (false)
-#define MLOG_1(x) do {} while (false)
-#define MLOG_2(x, y) do {} while (false)
+#define MLOG() EAT_STREAM_PARAMETERS
#endif // LOG_MEDIA_ELEMENT_ACTIVITIES
@@ -157,24 +142,24 @@
}
scoped_refptr<MediaError> HTMLMediaElement::error() const {
- MLOG_1(error_->code());
+ MLOG() << error_->code();
return error_;
}
std::string HTMLMediaElement::src() const {
- MLOG_1(GetAttribute("src").value_or(""));
+ MLOG() << GetAttribute("src").value_or("");
return GetAttribute("src").value_or("");
}
void HTMLMediaElement::set_src(const std::string& src) {
- MLOG_1(src);
+ MLOG() << src;
SetAttribute("src", src);
ClearMediaPlayer();
ScheduleLoad();
}
uint16_t HTMLMediaElement::network_state() const {
- MLOG_1(network_state_);
+ MLOG() << network_state_;
return static_cast<uint16_t>(network_state_);
}
@@ -182,17 +167,17 @@
scoped_refptr<TimeRanges> buffered = new TimeRanges;
if (!player_) {
- MLOG_1("empty");
+ MLOG() << "(empty)";
return buffered;
}
const ::media::Ranges<base::TimeDelta>& player_buffered =
player_->GetBufferedTimeRanges();
- MLOG_1("================================");
+ MLOG() << "================================";
for (int i = 0; i < static_cast<int>(player_buffered.size()); ++i) {
- MLOG_2(player_buffered.start(i).InSecondsF(),
- player_buffered.end(i).InSecondsF());
+ MLOG() << player_buffered.start(i).InSecondsF() << " - "
+ << player_buffered.end(i).InSecondsF();
buffered->Add(player_buffered.start(i).InSecondsF(),
player_buffered.end(i).InSecondsF());
}
@@ -218,7 +203,7 @@
std::string result =
html_element_context()->can_play_type_handler()->CanPlayType(mime_type,
key_system);
- MLOG_2(mime_type + ',' + key_system, result);
+ MLOG() << "(" << mime_type << ", " << key_system << ") => " << result;
DLOG(INFO) << "HTMLMediaElement::canPlayType(" << mime_type << ", "
<< key_system << ") -> " << result;
return result;
@@ -228,18 +213,18 @@
const std::string& key_system,
const base::optional<scoped_refptr<Uint8Array> >& init_data,
script::ExceptionState* exception_state) {
- MLOG_1(key_system);
+ MLOG() << key_system;
// https://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-generatekeyrequest
// 1. If the first argument is null, throw a SYNTAX_ERR.
if (key_system.empty()) {
- MLOG_1("syntax error");
+ MLOG() << "syntax error";
DOMException::Raise(DOMException::kSyntaxErr, exception_state);
return;
}
// 2. If networkState is NETWORK_EMPTY, throw an INVALID_STATE_ERR.
if (network_state_ == kNetworkEmpty || !player_) {
- MLOG_1("invalid state error");
+ MLOG() << "invalid state error";
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
return;
}
@@ -256,7 +241,7 @@
}
if (exception != WebMediaPlayer::kMediaKeyExceptionNoError) {
- MLOG_2("exception:", exception);
+ MLOG() << "exception: " << exception;
RaiseMediaKeyException(exception, exception_state);
}
}
@@ -266,25 +251,25 @@
const base::optional<scoped_refptr<Uint8Array> >& init_data,
const base::optional<std::string>& session_id,
script::ExceptionState* exception_state) {
- MLOG_1(key_system);
+ MLOG() << key_system;
// https://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-addkey
// 1. If the first or second argument is null, throw a SYNTAX_ERR.
if (key_system.empty() || !key) {
- MLOG_1("syntax error");
+ MLOG() << "syntax error";
DOMException::Raise(DOMException::kSyntaxErr, exception_state);
return;
}
// 2. If the second argument is an empty array, throw a TYPE_MISMATCH_ERR.
if (!key->length()) {
- MLOG_1("type mismatch error");
+ MLOG() << "type mismatch error";
DOMException::Raise(DOMException::kTypeMismatchErr, exception_state);
return;
}
// 3. If networkState is NETWORK_EMPTY, throw an INVALID_STATE_ERR.
if (network_state_ == kNetworkEmpty || !player_) {
- MLOG_1("invalid state error");
+ MLOG() << "invalid state error";
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
return;
}
@@ -303,7 +288,7 @@
}
if (exception != WebMediaPlayer::kMediaKeyExceptionNoError) {
- MLOG_2("exception:", exception);
+ MLOG() << "exception: " << exception;
RaiseMediaKeyException(exception, exception_state);
}
}
@@ -315,13 +300,13 @@
// https://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-addkey
// 1. If the first argument is null, throw a SYNTAX_ERR.
if (key_system.empty()) {
- MLOG_1("syntax error");
+ MLOG() << "syntax error";
DOMException::Raise(DOMException::kSyntaxErr, exception_state);
return;
}
if (!player_) {
- MLOG_1("invalid state error");
+ MLOG() << "invalid state error";
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
return;
}
@@ -330,18 +315,18 @@
WebMediaPlayer::MediaKeyException exception =
player_->CancelKeyRequest(key_system, session_id.value_or(""));
if (exception != WebMediaPlayer::kMediaKeyExceptionNoError) {
- MLOG_2("exception:", exception);
+ MLOG() << "exception: " << exception;
RaiseMediaKeyException(exception, exception_state);
}
}
WebMediaPlayer::ReadyState HTMLMediaElement::ready_state() const {
- MLOG_1(ready_state_);
+ MLOG() << ready_state_;
return ready_state_;
}
bool HTMLMediaElement::seeking() const {
- MLOG_1(seeking_);
+ MLOG() << seeking_;
return seeking_;
}
@@ -350,17 +335,18 @@
UNREFERENCED_PARAMETER(exception_state);
if (!player_) {
- MLOG_2("player is NULL", 0);
+ MLOG() << 0 << " (because player is NULL)";
return 0;
}
if (seeking_) {
- MLOG_2("seeking", last_seek_time_);
+ MLOG() << last_seek_time_ << " (seeking)";
return last_seek_time_;
}
- MLOG_2("player time", player_->GetCurrentTime());
- return player_->GetCurrentTime();
+ float time = player_->GetCurrentTime();
+ MLOG() << time << " (from player)";
+ return time;
}
void HTMLMediaElement::set_current_time(
@@ -371,36 +357,37 @@
// WebMediaPlayer::kReadyStateHaveNothing, then raise an INVALID_STATE_ERR
// exception.
if (ready_state_ == WebMediaPlayer::kReadyStateHaveNothing || !player_) {
- MLOG_1("invalid state error");
+ MLOG() << "invalid state error";
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
return;
}
- MLOG_2("seek to", time);
+ MLOG() << "seek to " << time;
Seek(time);
}
float HTMLMediaElement::duration() const {
if (player_ && ready_state_ >= WebMediaPlayer::kReadyStateHaveMetadata) {
- MLOG_2("player duration", player_->GetDuration());
- return player_->GetDuration();
+ float duration = player_->GetDuration();
+ MLOG() << "player duration: " << duration;
+ return duration;
}
- MLOG_1("NaN");
+ MLOG() << "NaN";
return std::numeric_limits<float>::quiet_NaN();
}
bool HTMLMediaElement::paused() const {
- MLOG_1(paused_);
+ MLOG() << paused_;
return paused_;
}
float HTMLMediaElement::default_playback_rate() const {
- MLOG_1(default_playback_rate_);
+ MLOG() << default_playback_rate_;
return default_playback_rate_;
}
void HTMLMediaElement::set_default_playback_rate(float rate) {
- MLOG_1(rate);
+ MLOG() << rate;
if (default_playback_rate_ != rate) {
default_playback_rate_ = rate;
ScheduleEvent(base::Tokens::ratechange());
@@ -408,12 +395,12 @@
}
float HTMLMediaElement::playback_rate() const {
- MLOG_1(playback_rate_);
+ MLOG() << playback_rate_;
return playback_rate_;
}
void HTMLMediaElement::set_playback_rate(float rate) {
- MLOG_1(rate);
+ MLOG() << rate;
if (playback_rate_ != rate) {
playback_rate_ = rate;
ScheduleEvent(base::Tokens::ratechange());
@@ -442,10 +429,11 @@
scoped_refptr<TimeRanges> HTMLMediaElement::seekable() const {
if (player_ && player_->GetMaxTimeSeekable() != 0) {
- MLOG_2(0, player_->GetMaxTimeSeekable());
- return new TimeRanges(0, player_->GetMaxTimeSeekable());
+ double max_time_seekable = player_->GetMaxTimeSeekable();
+ MLOG() << "(0, " << max_time_seekable << ")";
+ return new TimeRanges(0, max_time_seekable);
}
- MLOG_1("empty");
+ MLOG() << "(empty)";
return new TimeRanges;
}
@@ -453,17 +441,18 @@
// 4.8.10.8 Playing the media resource
// The ended attribute must return true if the media element has ended
// playback and the direction of playback is forwards, and false otherwise.
- MLOG_1(EndedPlayback() && playback_rate_ > 0);
- return EndedPlayback() && playback_rate_ > 0;
+ bool playback_ended = EndedPlayback() && playback_rate_ > 0;
+ MLOG() << playback_ended;
+ return playback_ended;
}
bool HTMLMediaElement::autoplay() const {
- MLOG_1(HasAttribute("autoplay"));
+ MLOG() << HasAttribute("autoplay");
return HasAttribute("autoplay");
}
void HTMLMediaElement::set_autoplay(bool autoplay) {
- MLOG_1(autoplay);
+ MLOG() << autoplay;
// The value of 'autoplay' is true when the 'autoplay' attribute is present.
// The value of the attribute is irrelevant.
if (autoplay) {
@@ -474,12 +463,12 @@
}
bool HTMLMediaElement::loop() const {
- MLOG_1(loop_);
+ MLOG() << loop_;
return loop_;
}
void HTMLMediaElement::set_loop(bool loop) {
- MLOG_1(loop);
+ MLOG() << loop;
loop_ = loop;
}
@@ -528,25 +517,25 @@
}
bool HTMLMediaElement::controls() const {
- MLOG_1(controls_);
+ MLOG() << controls_;
return controls_;
}
void HTMLMediaElement::set_controls(bool controls) {
- MLOG_1(controls);
+ MLOG() << controls_;
controls_ = controls;
ConfigureMediaControls();
}
float HTMLMediaElement::volume(script::ExceptionState* exception_state) const {
UNREFERENCED_PARAMETER(exception_state);
- MLOG_1(volume_);
+ MLOG() << volume_;
return volume_;
}
void HTMLMediaElement::set_volume(float volume,
script::ExceptionState* exception_state) {
- MLOG_1(volume);
+ MLOG() << volume;
if (volume < 0.0f || volume > 1.0f) {
DOMException::Raise(DOMException::kIndexSizeErr, exception_state);
return;
@@ -560,12 +549,12 @@
}
bool HTMLMediaElement::muted() const {
- MLOG_1(muted_);
+ MLOG() << muted_;
return muted_;
}
void HTMLMediaElement::set_muted(bool muted) {
- MLOG_1(muted);
+ MLOG() << muted;
if (muted_ != muted) {
muted_ = muted;
// Avoid recursion when the player reports volume changes.
@@ -976,7 +965,7 @@
}
void HTMLMediaElement::ScheduleEvent(base::Token event_name) {
- MLOG_1(event_name);
+ MLOG() << event_name;
scoped_refptr<Event> event =
new Event(event_name, Event::kNotBubbles, Event::kCancelable);
event->set_target(this);
@@ -1330,7 +1319,7 @@
}
void HTMLMediaElement::MediaEngineError(scoped_refptr<MediaError> error) {
- MLOG_1(error->code());
+ MLOG() << error->code();
DLOG(WARNING) << "HTMLMediaElement::MediaEngineError " << error->code();
// 1 - The user agent should cancel the fetching process.
@@ -1474,7 +1463,7 @@
void HTMLMediaElement::KeyAdded(const std::string& key_system,
const std::string& session_id) {
- MLOG_1(key_system);
+ MLOG() << key_system;
event_queue_.Enqueue(new MediaKeyCompleteEvent(key_system, session_id));
}
@@ -1482,7 +1471,7 @@
const std::string& session_id,
MediaKeyErrorCode error_code,
uint16 system_code) {
- MLOG_1(key_system);
+ MLOG() << key_system;
MediaKeyError::Code code;
switch (error_code) {
case kUnknownError:
@@ -1517,7 +1506,7 @@
const unsigned char* message,
unsigned int message_length,
const std::string& default_url) {
- MLOG_1(key_system);
+ MLOG() << key_system;
event_queue_.Enqueue(new MediaKeyMessageEvent(
key_system, session_id,
new Uint8Array(NULL, message, message_length, NULL), default_url));
@@ -1527,14 +1516,14 @@
const std::string& session_id,
const unsigned char* init_data,
unsigned int init_data_length) {
- MLOG_1(key_system);
+ MLOG() << key_system;
event_queue_.Enqueue(new MediaKeyNeededEvent(
key_system, session_id,
new Uint8Array(NULL, init_data, init_data_length, NULL)));
}
void HTMLMediaElement::SetSourceState(MediaSource::ReadyState ready_state) {
- MLOG_1(ready_state);
+ MLOG() << ready_state;
if (!media_source_) {
return;
}
diff --git a/src/cobalt/dom/int16_array.h b/src/cobalt/dom/int16_array.h
new file mode 100644
index 0000000..a4a2821
--- /dev/null
+++ b/src/cobalt/dom/int16_array.h
@@ -0,0 +1,30 @@
+/*
+ * 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_DOM_INT16_ARRAY_H_
+#define COBALT_DOM_INT16_ARRAY_H_
+
+#include "cobalt/dom/typed_array.h"
+
+namespace cobalt {
+namespace dom {
+
+DEFINE_TYPED_ARRAY(Int16Array, int16);
+
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_INT16_ARRAY_H_
diff --git a/src/cobalt/dom/media_source.cc b/src/cobalt/dom/media_source.cc
index f20ffb0..83bc8c0 100644
--- a/src/cobalt/dom/media_source.cc
+++ b/src/cobalt/dom/media_source.cc
@@ -73,35 +73,6 @@
} // namespace
-void MediaSource::Registry::Register(
- const std::string& blob_url,
- const scoped_refptr<MediaSource>& media_source) {
- DCHECK(media_source);
- DCHECK(media_source_registry_.find(blob_url) == media_source_registry_.end());
- media_source_registry_.insert(std::make_pair(blob_url, media_source));
-}
-
-scoped_refptr<MediaSource> MediaSource::Registry::Retrieve(
- const std::string& blob_url) {
- MediaSourceRegistry::iterator iter = media_source_registry_.find(blob_url);
- if (iter == media_source_registry_.end()) {
- DLOG(WARNING) << "Cannot find MediaSource object for blob url " << blob_url;
- return NULL;
- }
-
- return iter->second;
-}
-
-void MediaSource::Registry::Unregister(const std::string& blob_url) {
- MediaSourceRegistry::iterator iter = media_source_registry_.find(blob_url);
- if (iter == media_source_registry_.end()) {
- DLOG(WARNING) << "Cannot find MediaSource object for blob url " << blob_url;
- return;
- }
-
- media_source_registry_.erase(iter);
-}
-
MediaSource::MediaSource()
: ready_state_(kReadyStateClosed),
player_(NULL),
diff --git a/src/cobalt/dom/media_source.h b/src/cobalt/dom/media_source.h
index a30e5c3..b8aa55a 100644
--- a/src/cobalt/dom/media_source.h
+++ b/src/cobalt/dom/media_source.h
@@ -25,6 +25,7 @@
#include "cobalt/dom/event_target.h"
#include "cobalt/dom/source_buffer.h"
#include "cobalt/dom/source_buffer_list.h"
+#include "cobalt/dom/url_registry.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/exception_state.h"
#include "media/player/web_media_player.h"
@@ -42,27 +43,7 @@
// Note that our implementation is based on the MSE draft on 09 August 2012.
class MediaSource : public EventTarget {
public:
- // This class manages a registry of MediaSource objects. Its user can
- // associate a MediaSource object to a blob url as well as to retrieve a
- // MediaSource object by a blob url. This is because to assign a MediaSource
- // object to the src of an HTMLMediaElement, we have to first convert the
- // MediaSource object into a blob url by calling URL.createObjectURL().
- // And eventually the HTMLMediaElement have to retrieve the MediaSource object
- // from the blob url.
- // Note: It is unsafe to directly encode the pointer to the MediaSource object
- // in the url as the url is assigned from JavaScript.
- class Registry {
- public:
- void Register(const std::string& blob_url,
- const scoped_refptr<MediaSource>& media_source);
- scoped_refptr<MediaSource> Retrieve(const std::string& blob_url);
- void Unregister(const std::string& blob_url);
-
- private:
- typedef base::hash_map<std::string, scoped_refptr<MediaSource> >
- MediaSourceRegistry;
- MediaSourceRegistry media_source_registry_;
- };
+ typedef UrlRegistry<MediaSource> Registry;
// Custom, not in any spec.
//
diff --git a/src/cobalt/dom/time_ranges_test.cc b/src/cobalt/dom/time_ranges_test.cc
index 4404477..cc8f12f 100644
--- a/src/cobalt/dom/time_ranges_test.cc
+++ b/src/cobalt/dom/time_ranges_test.cc
@@ -33,7 +33,14 @@
void SetSimpleException(script::MessageType /*message_type*/, ...) OVERRIDE {
// no-op
}
- scoped_refptr<DOMException> dom_exception_;
+ dom::DOMException::ExceptionCode GetExceptionCode() {
+ return dom_exception_ ? static_cast<dom::DOMException::ExceptionCode>(
+ dom_exception_->code())
+ : dom::DOMException::kNone;
+ }
+
+ private:
+ scoped_refptr<dom::DOMException> dom_exception_;
};
} // namespace
@@ -180,16 +187,12 @@
{
FakeExceptionState exception_state;
time_ranges->Start(2, &exception_state);
- ASSERT_TRUE(exception_state.dom_exception_);
- EXPECT_EQ(DOMException::kIndexSizeErr,
- exception_state.dom_exception_->code());
+ EXPECT_EQ(DOMException::kIndexSizeErr, exception_state.GetExceptionCode());
}
{
FakeExceptionState exception_state;
time_ranges->End(2, &exception_state);
- ASSERT_TRUE(exception_state.dom_exception_);
- EXPECT_EQ(DOMException::kIndexSizeErr,
- exception_state.dom_exception_->code());
+ EXPECT_EQ(DOMException::kIndexSizeErr, exception_state.GetExceptionCode());
}
}
diff --git a/src/cobalt/dom/url.cc b/src/cobalt/dom/url.cc
index 7388773..911c619 100644
--- a/src/cobalt/dom/url.cc
+++ b/src/cobalt/dom/url.cc
@@ -50,6 +50,24 @@
}
// static
+std::string URL::CreateObjectURL(
+ script::EnvironmentSettings* environment_settings,
+ const scoped_refptr<Blob>& blob) {
+ DOMSettings* dom_settings =
+ base::polymorphic_downcast<DOMSettings*>(environment_settings);
+ DCHECK(dom_settings);
+ DCHECK(dom_settings->blob_registry());
+ if (!blob) {
+ return "";
+ }
+
+ std::string blob_url = kBlobUrlProtocol;
+ blob_url += ':' + base::GenerateGUID();
+ dom_settings->blob_registry()->Register(blob_url, blob);
+ return blob_url;
+}
+
+// static
void URL::RevokeObjectURL(script::EnvironmentSettings* environment_settings,
const std::string& url) {
DOMSettings* dom_settings =
@@ -69,7 +87,26 @@
// 2. Otherwise, user agents must remove the entry from the Blob URL Store for
// url.
- dom_settings->media_source_registry()->Unregister(url);
+ if (!dom_settings->media_source_registry()->Unregister(url) &&
+ !dom_settings->blob_registry()->Unregister(url)) {
+ DLOG(WARNING) << "Cannot find object for blob url " << url;
+ }
+}
+
+bool URL::BlobResolver(dom::Blob::Registry* registry, const GURL& url,
+ const char** data, size_t* size) {
+ DCHECK(data);
+ DCHECK(size);
+ dom::Blob* blob = registry->Retrieve(url.spec()).get();
+
+ if (blob) {
+ *size = static_cast<size_t>(blob->size());
+ *data = reinterpret_cast<const char*>(blob->data());
+
+ return true;
+ } else {
+ return false;
+ }
}
} // namespace dom
diff --git a/src/cobalt/dom/url.h b/src/cobalt/dom/url.h
index f2286aa..6899612 100644
--- a/src/cobalt/dom/url.h
+++ b/src/cobalt/dom/url.h
@@ -19,7 +19,9 @@
#include <string>
+#include "cobalt/dom/blob.h"
#include "cobalt/dom/media_source.h"
+#include "cobalt/loader/blob_fetcher.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/wrappable.h"
@@ -34,18 +36,28 @@
// The Media Source Extension extends it to create an url from a MediaSource
// object so we can assign it to HTMLMediaElement.src.
// https://rawgit.com/w3c/media-source/cfb1b3d4309a6e6e2c01bd87e048758172a86e4b/media-source.html#dom-createobjecturl
-//
-// Note: We only implemented the MediaSource related functions as they are all
-// what we need for Cobalt.
class URL : public script::Wrappable {
public:
static std::string CreateObjectURL(
script::EnvironmentSettings* environment_settings,
const scoped_refptr<MediaSource>& media_source);
+ static std::string CreateObjectURL(
+ script::EnvironmentSettings* environment_settings,
+ const scoped_refptr<Blob>& blob);
static void RevokeObjectURL(script::EnvironmentSettings* environment_settings,
const std::string& url);
+ static loader::BlobFetcher::ResolverCallback MakeBlobResolverCallback(
+ dom::Blob::Registry* blob_registry) {
+ DCHECK(blob_registry);
+ return base::Bind(&BlobResolver, blob_registry);
+ }
+
DEFINE_WRAPPABLE_TYPE(URL);
+
+ private:
+ static bool BlobResolver(dom::Blob::Registry* registry, const GURL& url,
+ const char** data, size_t* size);
};
} // namespace dom
diff --git a/src/cobalt/dom/url_registry.h b/src/cobalt/dom/url_registry.h
new file mode 100644
index 0000000..3619762
--- /dev/null
+++ b/src/cobalt/dom/url_registry.h
@@ -0,0 +1,84 @@
+/*
+ * 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_DOM_URL_REGISTRY_H_
+#define COBALT_DOM_URL_REGISTRY_H_
+
+#include <string>
+
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
+
+namespace cobalt {
+namespace dom {
+
+// This class manages a registry of objects. Its user can associate a
+// object to a blob url as well as to retrieve a object by a blob url.
+// This is because to assign a object to the src of an HTMLMediaElement
+// (when the object is MediaSource) or to another kind of element or CSS
+// property (when the object is a Blob), we have to first convert the
+// object into a blob url by calling URL.createObjectURL(). And eventually
+// the element or property has to retrieve the object from the blob url.
+// Note: It is unsafe to directly encode the pointer to the object in the
+// url as the url is assigned from JavaScript.
+template <typename ObjectType>
+class UrlRegistry {
+ public:
+ void Register(const std::string& blob_url,
+ const scoped_refptr<ObjectType>& object);
+ scoped_refptr<ObjectType> Retrieve(const std::string& blob_url);
+ bool Unregister(const std::string& blob_url);
+
+ private:
+ typedef base::hash_map<std::string, scoped_refptr<ObjectType> > UrlMap;
+ UrlMap object_registry_;
+};
+
+template <typename ObjectType>
+void UrlRegistry<ObjectType>::Register(
+ const std::string& blob_url, const scoped_refptr<ObjectType>& object) {
+ DCHECK(object);
+ DCHECK(object_registry_.find(blob_url) == object_registry_.end());
+ object_registry_.insert(std::make_pair(blob_url, object));
+}
+
+template <typename ObjectType>
+scoped_refptr<ObjectType> UrlRegistry<ObjectType>::Retrieve(
+ const std::string& blob_url) {
+ typename UrlMap::iterator iter = object_registry_.find(blob_url);
+ if (iter == object_registry_.end()) {
+ DLOG(WARNING) << "Cannot find object for blob url " << blob_url;
+ return NULL;
+ }
+
+ return iter->second;
+}
+
+template <typename ObjectType>
+bool UrlRegistry<ObjectType>::Unregister(const std::string& blob_url) {
+ typename UrlMap::iterator iter = object_registry_.find(blob_url);
+ if (iter == object_registry_.end()) {
+ return false;
+ }
+
+ object_registry_.erase(iter);
+ return true;
+}
+
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_URL_REGISTRY_H_
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc
index 92736ea..70019a1 100644
--- a/src/cobalt/dom_parser/html_decoder_test.cc
+++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -382,6 +382,34 @@
EXPECT_EQ("💩", text->data());
}
+TEST_F(HTMLDecoderTest, CanParseUTF8SplitInChunks) {
+ const std::string input = "<p>💩</p>";
+
+ for (size_t first_chunk_size = 0; first_chunk_size < input.length();
+ first_chunk_size++) {
+ root_ = new dom::Element(document_, base::Token("element"));
+ html_decoder_.reset(new HTMLDecoder(
+ document_, root_, NULL, kDOMMaxElementDepth, source_location_,
+ base::Closure(), base::Bind(&MockErrorCallback::Run,
+ base::Unretained(&mock_error_callback_)),
+ true));
+
+ // This could cut the input in the middle of a UTF8 character.
+ html_decoder_->DecodeChunk(input.c_str(), first_chunk_size);
+ html_decoder_->DecodeChunk(input.c_str() + first_chunk_size,
+ input.length() - first_chunk_size);
+ html_decoder_->Finish();
+
+ dom::Element* element = root_->first_element_child();
+ ASSERT_TRUE(element);
+ EXPECT_EQ("p", element->tag_name());
+
+ dom::Text* text = element->first_child()->AsText();
+ ASSERT_TRUE(text);
+ EXPECT_EQ("💩", text->data());
+ }
+}
+
// Misnested tags: <b><i></b></i>
// https://www.w3.org/TR/html5/syntax.html#misnested-tags:-b-i-/b-/i
//
diff --git a/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc
index 968dbe5..7a05cb8 100644
--- a/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc
+++ b/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc
@@ -110,7 +110,10 @@
return;
}
- if (CheckInputAndUpdateSeverity(data, size) == kFatal) {
+ std::string current_chunk;
+ PreprocessChunk(data, size, ¤t_chunk);
+
+ if (max_severity() == kFatal) {
return;
}
@@ -120,9 +123,10 @@
// when used for setting an element's innerHTML.
htmlEmitImpliedRootLevelParagraph(0);
- html_parser_context_ = htmlCreatePushParserCtxt(
- &html_sax_handler, this, data, static_cast<int>(size),
- NULL /*filename*/, XML_CHAR_ENCODING_UTF8);
+ html_parser_context_ =
+ htmlCreatePushParserCtxt(&html_sax_handler, this, current_chunk.c_str(),
+ static_cast<int>(current_chunk.size()),
+ NULL /*filename*/, XML_CHAR_ENCODING_UTF8);
if (!html_parser_context_) {
static const char kErrorUnableCreateParser[] =
@@ -135,7 +139,8 @@
}
} else {
DCHECK(html_parser_context_);
- htmlParseChunk(html_parser_context_, data, static_cast<int>(size),
+ htmlParseChunk(html_parser_context_, current_chunk.c_str(),
+ static_cast<int>(current_chunk.size()),
0 /*do not terminate*/);
}
}
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
index ef19c762..a86753e 100644
--- a/src/cobalt/dom_parser/libxml_parser_wrapper.cc
+++ b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
@@ -19,6 +19,8 @@
#include "base/logging.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
+#include "base/third_party/icu/icu_utf.h"
+#include "base/utf_string_conversion_utils.h"
#include "cobalt/base/tokens.h"
#include "cobalt/dom/cdata_section.h"
#include "cobalt/dom/comment.h"
@@ -314,34 +316,32 @@
node_stack_.top()->AppendChild(new dom::CDATASection(document_, value));
}
-LibxmlParserWrapper::IssueSeverity
-LibxmlParserWrapper::CheckInputAndUpdateSeverity(const char* data,
- size_t size) {
- if (max_severity_ == kFatal) {
- return max_severity_;
- }
-
+void LibxmlParserWrapper::PreprocessChunk(const char* data, size_t size,
+ std::string* current_chunk) {
+ DCHECK(current_chunk);
// Check the total input size.
total_input_size_ += size;
if (total_input_size_ > kMaxTotalInputSize) {
static const char kMessageInputTooLong[] = "Parser input is too long.";
OnParsingIssue(kFatal, kMessageInputTooLong);
- return max_severity_;
+ return;
}
// Check the encoding of the input.
- if (!IsStringUTF8(std::string(data, size))) {
+ std::string input = next_chunk_start_ + std::string(data, size);
+ TruncateUTF8ToByteSize(input, input.size(), current_chunk);
+ next_chunk_start_ = input.substr(current_chunk->size());
+ if (!IsStringUTF8(*current_chunk)) {
+ current_chunk->clear();
static const char kMessageInputNotUTF8[] =
"Parser input contains non-UTF8 characters.";
OnParsingIssue(kFatal, kMessageInputNotUTF8);
- return max_severity_;
+ return;
}
#if defined(HANDLE_CORE_DUMP)
libxml_parser_wrapper_log.Get().IncrementParsedBytes(static_cast<int>(size));
#endif
-
- return max_severity_;
}
} // namespace dom_parser
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.h b/src/cobalt/dom_parser/libxml_parser_wrapper.h
index 98d210e..4c5e233 100644
--- a/src/cobalt/dom_parser/libxml_parser_wrapper.h
+++ b/src/cobalt/dom_parser/libxml_parser_wrapper.h
@@ -121,8 +121,10 @@
// Returns true when the input is a full document, false when it's a fragment.
bool IsFullDocument() { return document_ == parent_node_; }
- // Checks the input, updates and returns the maximum issue severity.
- IssueSeverity CheckInputAndUpdateSeverity(const char* data, size_t size);
+ // Preprocesses the input chunk and updates the max error severity level.
+ // Sets current chunk if successful.
+ void PreprocessChunk(const char* data, size_t size,
+ std::string* current_chunk);
const scoped_refptr<dom::Document>& document() { return document_; }
const base::SourceLocation& first_chunk_location() {
@@ -136,6 +138,8 @@
return node_stack_;
}
+ IssueSeverity max_severity() const { return max_severity_; }
+
private:
// Maximum total input size, as specified in Libxml's value
// XML_MAX_TEXT_LENGTH in parserInternals.h.
@@ -154,6 +158,7 @@
IssueSeverity max_severity_;
size_t total_input_size_;
+ std::string next_chunk_start_;
std::stack<scoped_refptr<dom::Node> > node_stack_;
DISALLOW_COPY_AND_ASSIGN(LibxmlParserWrapper);
diff --git a/src/cobalt/dom_parser/libxml_xml_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_xml_parser_wrapper.cc
index 787970d..dfbb290 100644
--- a/src/cobalt/dom_parser/libxml_xml_parser_wrapper.cc
+++ b/src/cobalt/dom_parser/libxml_xml_parser_wrapper.cc
@@ -79,14 +79,17 @@
return;
}
- if (CheckInputAndUpdateSeverity(data, size) == kFatal) {
+ std::string current_chunk;
+ PreprocessChunk(data, size, ¤t_chunk);
+
+ if (max_severity() == kFatal) {
return;
}
if (!xml_parser_context_) {
- xml_parser_context_ =
- xmlCreatePushParserCtxt(&xml_sax_handler, this, data,
- static_cast<int>(size), NULL /*filename*/);
+ xml_parser_context_ = xmlCreatePushParserCtxt(
+ &xml_sax_handler, this, current_chunk.c_str(),
+ static_cast<int>(current_chunk.size()), NULL /*filename*/);
if (!xml_parser_context_) {
static const char kErrorUnableCreateParser[] =
@@ -94,7 +97,8 @@
OnParsingIssue(kFatal, kErrorUnableCreateParser);
}
} else {
- xmlParseChunk(xml_parser_context_, data, static_cast<int>(size),
+ xmlParseChunk(xml_parser_context_, current_chunk.c_str(),
+ static_cast<int>(current_chunk.size()),
0 /*do not terminate*/);
}
}
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index d19d55e..7b646c0 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -247,6 +247,7 @@
case cssom::KeywordValue::kLeft:
case cssom::KeywordValue::kLineThrough:
case cssom::KeywordValue::kMiddle:
+ case cssom::KeywordValue::kMonoscopic:
case cssom::KeywordValue::kMonospace:
case cssom::KeywordValue::kNoRepeat:
case cssom::KeywordValue::kNormal:
@@ -263,6 +264,8 @@
case cssom::KeywordValue::kSolid:
case cssom::KeywordValue::kStart:
case cssom::KeywordValue::kStatic:
+ case cssom::KeywordValue::kStereoscopicLeftRight:
+ case cssom::KeywordValue::kStereoscopicTopBottom:
case cssom::KeywordValue::kTop:
case cssom::KeywordValue::kUppercase:
case cssom::KeywordValue::kVisible:
@@ -541,6 +544,7 @@
case cssom::KeywordValue::kLeft:
case cssom::KeywordValue::kLineThrough:
case cssom::KeywordValue::kMiddle:
+ case cssom::KeywordValue::kMonoscopic:
case cssom::KeywordValue::kMonospace:
case cssom::KeywordValue::kNoRepeat:
case cssom::KeywordValue::kNormal:
@@ -557,6 +561,8 @@
case cssom::KeywordValue::kSolid:
case cssom::KeywordValue::kStart:
case cssom::KeywordValue::kStatic:
+ case cssom::KeywordValue::kStereoscopicLeftRight:
+ case cssom::KeywordValue::kStereoscopicTopBottom:
case cssom::KeywordValue::kTop:
case cssom::KeywordValue::kUppercase:
case cssom::KeywordValue::kVisible:
@@ -688,6 +694,7 @@
case cssom::KeywordValue::kLeft:
case cssom::KeywordValue::kLineThrough:
case cssom::KeywordValue::kMiddle:
+ case cssom::KeywordValue::kMonoscopic:
case cssom::KeywordValue::kMonospace:
case cssom::KeywordValue::kNoRepeat:
case cssom::KeywordValue::kNoWrap:
@@ -703,6 +710,8 @@
case cssom::KeywordValue::kSolid:
case cssom::KeywordValue::kStart:
case cssom::KeywordValue::kStatic:
+ case cssom::KeywordValue::kStereoscopicLeftRight:
+ case cssom::KeywordValue::kStereoscopicTopBottom:
case cssom::KeywordValue::kTop:
case cssom::KeywordValue::kUppercase:
case cssom::KeywordValue::kVisible:
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc
index 1be9e1a..7bcb96a 100644
--- a/src/cobalt/layout/replaced_box.cc
+++ b/src/cobalt/layout/replaced_box.cc
@@ -60,6 +60,25 @@
// means, as per https://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width.
const float kFallbackWidth = 300.0f;
+// Convert the parsed keyword value for a stereo mode into a stereo mode enum
+// value.
+render_tree::StereoMode ReadStereoMode(
+ const scoped_refptr<cssom::KeywordValue>& keyword_value) {
+ cssom::KeywordValue::Value value = keyword_value->value();
+
+ if (value == cssom::KeywordValue::kMonoscopic) {
+ return render_tree::kMono;
+ } else if (value == cssom::KeywordValue::kStereoscopicLeftRight) {
+ return render_tree::kLeftRight;
+ } else if (value == cssom::KeywordValue::kStereoscopicTopBottom) {
+ return render_tree::kTopBottom;
+ } else {
+ LOG(DFATAL) << "Stereo mode has an invalid non-NULL value, defaulting to "
+ << "monoscopic";
+ return render_tree::kMono;
+ }
+}
+
} // namespace
ReplacedBox::ReplacedBox(
@@ -172,10 +191,10 @@
return GetMarginBoxHeight();
}
-namespace {
-
#if !PUNCH_THROUGH_VIDEO_RENDERING
+namespace {
+
void AddLetterboxFillRects(const LetterboxDimensions& dimensions,
CompositionNode::Builder* composition_node_builder) {
const render_tree::ColorRGBA kSolidBlack(0, 0, 0, 1);
@@ -200,10 +219,7 @@
AddLetterboxFillRects(dimensions, composition_node_builder);
}
-#endif // !PUNCH_THROUGH_VIDEO_RENDERING
-
void AnimateCB(const ReplacedBox::ReplaceImageCB& replace_image_cb,
- const ReplacedBox::SetBoundsCB& set_bounds_cb,
math::SizeF destination_size,
CompositionNode::Builder* composition_node_builder,
base::TimeDelta time) {
@@ -212,14 +228,6 @@
DCHECK(!replace_image_cb.is_null());
DCHECK(composition_node_builder);
-#if PUNCH_THROUGH_VIDEO_RENDERING
- // For systems that have their own path to blitting video to the display, we
- // simply punch a hole through our scene so that the video can appear there.
- PunchThroughVideoNode::Builder builder(math::RectF(destination_size),
- set_bounds_cb);
- composition_node_builder->AddChild(new PunchThroughVideoNode(builder));
-#else // PUNCH_THROUGH_VIDEO_RENDERING
- UNREFERENCED_PARAMETER(set_bounds_cb);
scoped_refptr<render_tree::Image> image = replace_image_cb.Run();
// TODO: Detect better when the intrinsic video size is used for the
@@ -234,11 +242,12 @@
GetLetterboxDimensions(image->GetSize(), destination_size), image,
composition_node_builder);
}
-#endif // PUNCH_THROUGH_VIDEO_RENDERING
}
} // namespace
+#endif // !PUNCH_THROUGH_VIDEO_RENDERING
+
void ReplacedBox::RenderAndAnimateContent(
CompositionNode::Builder* border_node_builder) const {
if (computed_style()->visibility() != cssom::KeywordValue::GetVisible()) {
@@ -257,19 +266,35 @@
scoped_refptr<CompositionNode> composition_node =
new CompositionNode(composition_node_builder);
+#if PUNCH_THROUGH_VIDEO_RENDERING
+ // For systems that have their own path to blitting video to the display, we
+ // simply punch a hole through our scene so that the video can appear there.
+ PunchThroughVideoNode::Builder builder(math::RectF(content_box_size()),
+ set_bounds_cb_);
+ Node* frame_node = new PunchThroughVideoNode(builder);
+#else
AnimateNode::Builder animate_node_builder;
animate_node_builder.Add(
- composition_node, base::Bind(AnimateCB, replace_image_cb_, set_bounds_cb_,
+ composition_node, base::Bind(AnimateCB, replace_image_cb_,
content_box_size()));
- Node* animate_node = new AnimateNode(animate_node_builder, composition_node);
+ Node* frame_node = new AnimateNode(animate_node_builder, composition_node);
+#endif // PUNCH_THROUGH_VIDEO_RENDERING
const cssom::MTMFunction* mtm_filter_function =
cssom::MTMFunction::ExtractFromFilterList(computed_style()->filter());
- border_node_builder->AddChild(
- mtm_filter_function ? new FilterNode(MapToMeshFilter(), animate_node)
- : animate_node);
+ Node* content_node;
+ if (mtm_filter_function) {
+ const scoped_refptr<cssom::KeywordValue>& stereo_mode_keyword_value =
+ mtm_filter_function->stereo_mode();
+ render_tree::StereoMode stereo_mode =
+ ReadStereoMode(stereo_mode_keyword_value);
+ content_node = new FilterNode(MapToMeshFilter(stereo_mode), frame_node);
+ } else {
+ content_node = frame_node;
+ }
+ border_node_builder->AddChild(content_node);
}
void ReplacedBox::UpdateContentSizeAndMargins(
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index 02227e0..f252231 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -267,6 +267,7 @@
case cssom::KeywordValue::kLeft:
case cssom::KeywordValue::kLineThrough:
case cssom::KeywordValue::kMiddle:
+ case cssom::KeywordValue::kMonoscopic:
case cssom::KeywordValue::kMonospace:
case cssom::KeywordValue::kNone:
case cssom::KeywordValue::kNoRepeat:
@@ -284,6 +285,8 @@
case cssom::KeywordValue::kSolid:
case cssom::KeywordValue::kStart:
case cssom::KeywordValue::kStatic:
+ case cssom::KeywordValue::kStereoscopicLeftRight:
+ case cssom::KeywordValue::kStereoscopicTopBottom:
case cssom::KeywordValue::kTop:
case cssom::KeywordValue::kUppercase:
case cssom::KeywordValue::kVisible:
@@ -356,6 +359,7 @@
case cssom::KeywordValue::kLeft:
case cssom::KeywordValue::kLineThrough:
case cssom::KeywordValue::kMiddle:
+ case cssom::KeywordValue::kMonoscopic:
case cssom::KeywordValue::kNone:
case cssom::KeywordValue::kNoRepeat:
case cssom::KeywordValue::kNormal:
@@ -370,6 +374,8 @@
case cssom::KeywordValue::kSolid:
case cssom::KeywordValue::kStart:
case cssom::KeywordValue::kStatic:
+ case cssom::KeywordValue::kStereoscopicLeftRight:
+ case cssom::KeywordValue::kStereoscopicTopBottom:
case cssom::KeywordValue::kTop:
case cssom::KeywordValue::kUppercase:
case cssom::KeywordValue::kVisible:
@@ -1163,6 +1169,7 @@
case cssom::KeywordValue::kLeft:
case cssom::KeywordValue::kLineThrough:
case cssom::KeywordValue::kMiddle:
+ case cssom::KeywordValue::kMonoscopic:
case cssom::KeywordValue::kMonospace:
case cssom::KeywordValue::kNone:
case cssom::KeywordValue::kNoRepeat:
@@ -1180,6 +1187,8 @@
case cssom::KeywordValue::kSolid:
case cssom::KeywordValue::kStart:
case cssom::KeywordValue::kStatic:
+ case cssom::KeywordValue::kStereoscopicLeftRight:
+ case cssom::KeywordValue::kStereoscopicTopBottom:
case cssom::KeywordValue::kTop:
case cssom::KeywordValue::kUppercase:
case cssom::KeywordValue::kVisible:
@@ -1341,6 +1350,7 @@
case cssom::KeywordValue::kLeft:
case cssom::KeywordValue::kLineThrough:
case cssom::KeywordValue::kMiddle:
+ case cssom::KeywordValue::kMonoscopic:
case cssom::KeywordValue::kMonospace:
case cssom::KeywordValue::kNone:
case cssom::KeywordValue::kNoRepeat:
@@ -1358,6 +1368,8 @@
case cssom::KeywordValue::kSolid:
case cssom::KeywordValue::kStart:
case cssom::KeywordValue::kStatic:
+ case cssom::KeywordValue::kStereoscopicLeftRight:
+ case cssom::KeywordValue::kStereoscopicTopBottom:
case cssom::KeywordValue::kTop:
case cssom::KeywordValue::kUppercase:
case cssom::KeywordValue::kVisible:
@@ -1411,6 +1423,7 @@
case cssom::KeywordValue::kLeft:
case cssom::KeywordValue::kLineThrough:
case cssom::KeywordValue::kMiddle:
+ case cssom::KeywordValue::kMonoscopic:
case cssom::KeywordValue::kMonospace:
case cssom::KeywordValue::kNoRepeat:
case cssom::KeywordValue::kNormal:
@@ -1427,6 +1440,8 @@
case cssom::KeywordValue::kSolid:
case cssom::KeywordValue::kStart:
case cssom::KeywordValue::kStatic:
+ case cssom::KeywordValue::kStereoscopicLeftRight:
+ case cssom::KeywordValue::kStereoscopicTopBottom:
case cssom::KeywordValue::kTop:
case cssom::KeywordValue::kUppercase:
case cssom::KeywordValue::kVisible:
diff --git a/src/cobalt/layout_tests/testdata/cobalt/image-from-blob-expected.png b/src/cobalt/layout_tests/testdata/cobalt/image-from-blob-expected.png
new file mode 100644
index 0000000..7c0e8e1
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cobalt/image-from-blob-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html b/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html
new file mode 100644
index 0000000..aa33681
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!--
+ | Loading an image from a Blob created from a buffer in memory.
+ -->
+<html>
+<head>
+ <style>
+ div {
+ width: 500px;
+ height: 400px;
+ }
+ </style>
+</head>
+<body>
+ <div id='image'></div>
+ <script>
+ if (window.testRunner) {
+ window.testRunner.waitUntilDone();
+ }
+
+ var image_bytes = [
+ 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0,
+ 6, 0, 0, 0, 6, 8, 2, 0, 0, 0, 111, 174, 120, 31, 0, 0, 0, 3, 115, 66,
+ 73, 84, 8, 8, 8, 219, 225, 79, 224, 0, 0, 0, 25, 116, 69, 88, 116, 83,
+ 111, 102, 116, 119, 97, 114, 101, 0, 103, 110, 111, 109, 101, 45, 115,
+ 99, 114, 101, 101, 110, 115, 104, 111, 116, 239, 3, 191, 62, 0, 0, 0,
+ 36, 73, 68, 65, 84, 8, 153, 99, 252, 80, 238, 198, 128, 10, 88, 24, 24,
+ 24, 154, 119, 190, 132, 243, 107, 221, 197, 153, 24, 48, 0, 113, 66,
+ 140, 152, 198, 3, 0, 225, 21, 6, 103, 179, 203, 171, 64, 0, 0, 0, 0,
+ 73, 69, 78, 68, 174, 66, 96, 130];
+
+ // TODO(jsalvadorp): When TypedArray supports setting from a Javascript
+ // array, replace the following loop.
+ var image_array = new Uint8Array(image_bytes.length);
+ for (var i = 0; i < image_array.length; i++) {
+ image_array[i] = image_bytes[i];
+ }
+
+ // TODO(jsalvadorp): When the proper Blob constructor that takes an array
+ // of arguments is supported, change the following to reflect the new
+ // signature.
+ var image_blob = new Blob(image_array.buffer);
+ var image_url = URL.createObjectURL(image_blob);
+
+ var image = new Image();
+ image.onload = function() {
+ var cobalt = document.getElementById('image');
+ cobalt.style.background = 'url(' + image_url + ')';
+
+ if (window.testRunner) {
+ window.testRunner.notifyDone();
+ }
+ }
+ image.src = image_url;
+ </script>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
index e336781..03292a0 100644
--- a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
@@ -8,6 +8,7 @@
divs-with-background-color-and-text
fixed-width-divs-with-background-color
font-weight
+image-from-blob
inline-box-with-overflow-words
inline-style-allowed-while-cloning-objects
onload_event_fired_even_though_link_file_does_not_exist
diff --git a/src/cobalt/loader/blob_fetcher.cc b/src/cobalt/loader/blob_fetcher.cc
new file mode 100644
index 0000000..d6a48b8
--- /dev/null
+++ b/src/cobalt/loader/blob_fetcher.cc
@@ -0,0 +1,54 @@
+/*
+ * 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/loader/blob_fetcher.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+
+namespace cobalt {
+namespace loader {
+
+BlobFetcher::BlobFetcher(const GURL& url, Handler* handler,
+ const ResolverCallback& resolver_callback)
+ : Fetcher(handler),
+ url_(url),
+ resolver_callback_(resolver_callback),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+ DCHECK(!resolver_callback_.is_null());
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&BlobFetcher::Fetch, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BlobFetcher::Fetch() {
+ size_t buffer_size = 0;
+ const char* buffer_data = NULL;
+
+ if (resolver_callback_.Run(url_, &buffer_data, &buffer_size)) {
+ if (buffer_size > 0) {
+ handler()->OnReceived(this, buffer_data, buffer_size);
+ }
+ handler()->OnDone(this);
+ } else {
+ handler()->OnError(this, "Blob URL not found in object store.");
+ }
+}
+
+BlobFetcher::~BlobFetcher() {}
+
+} // namespace loader
+} // namespace cobalt
diff --git a/src/cobalt/loader/blob_fetcher.h b/src/cobalt/loader/blob_fetcher.h
new file mode 100644
index 0000000..aa8f991
--- /dev/null
+++ b/src/cobalt/loader/blob_fetcher.h
@@ -0,0 +1,54 @@
+/*
+ * 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_LOADER_BLOB_FETCHER_H_
+#define COBALT_LOADER_BLOB_FETCHER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "cobalt/loader/fetcher.h"
+
+namespace cobalt {
+namespace loader {
+
+// For fetching the 'blob:' scheme.
+class BlobFetcher : public Fetcher {
+ public:
+ // Returns true if the blob is succesfully fetched, and only then writes
+ // the address of the buffer and its size to |data| and |size|. |data| can
+ // be NULL when size is 0. This callback avoids a dependency from the
+ // fetcher to the actual blob implementation.
+ typedef base::Callback<bool(const GURL& url, const char** data, size_t* size)>
+ ResolverCallback;
+
+ explicit BlobFetcher(const GURL& url, Handler* handler,
+ const ResolverCallback& resolver_callback);
+
+ void Fetch();
+
+ ~BlobFetcher() OVERRIDE;
+
+ private:
+ void GetData();
+
+ GURL url_;
+ ResolverCallback resolver_callback_;
+ base::WeakPtrFactory<BlobFetcher> weak_ptr_factory_;
+};
+
+} // namespace loader
+} // namespace cobalt
+
+#endif // COBALT_LOADER_BLOB_FETCHER_H_
diff --git a/src/cobalt/loader/blob_fetcher_test.cc b/src/cobalt/loader/blob_fetcher_test.cc
new file mode 100644
index 0000000..a1e1fd6
--- /dev/null
+++ b/src/cobalt/loader/blob_fetcher_test.cc
@@ -0,0 +1,133 @@
+/*
+ * 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/loader/blob_fetcher.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/run_loop.h"
+#include "cobalt/loader/fetcher_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AtLeast;
+using ::testing::InSequence;
+using ::testing::StrictMock;
+using ::testing::_;
+
+namespace cobalt {
+namespace loader {
+namespace {
+
+typedef std::map<std::string, std::vector<char> > TestRegistry;
+
+bool TestResolver(const TestRegistry& registry, const GURL& url,
+ const char** data, size_t* size) {
+ DCHECK(data);
+ DCHECK(size);
+
+ TestRegistry::const_iterator match = registry.find(url.spec());
+ if (match != registry.end()) {
+ const std::vector<char>& buffer = match->second;
+ *size = buffer.size();
+ *data = &buffer[0];
+ return true;
+ } else {
+ return false;
+ }
+}
+
+TEST(BlobFetcherTest, NonExistentBlobURL) {
+ MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ base::RunLoop run_loop;
+
+ StrictMock<MockFetcherHandler> fetcher_handler_mock(&run_loop);
+ EXPECT_CALL(fetcher_handler_mock, OnError(_, _));
+
+ TestRegistry registry;
+
+ scoped_ptr<BlobFetcher> blob_fetcher = make_scoped_ptr(
+ new loader::BlobFetcher(GURL("blob:sd98sdfuh8sdh"), &fetcher_handler_mock,
+ base::Bind(&TestResolver, registry)));
+
+ run_loop.Run();
+
+ EXPECT_EQ(blob_fetcher.get(), fetcher_handler_mock.fetcher());
+}
+
+TEST(BlobFetcherTest, EmptyBlob) {
+ MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ base::RunLoop run_loop;
+ InSequence dummy;
+
+ StrictMock<MockFetcherHandler> fetcher_handler_mock(&run_loop);
+ EXPECT_CALL(fetcher_handler_mock, OnDone(_));
+
+ const char* url = "blob:28y3-fsdaf-dsfa";
+
+ TestRegistry registry;
+ registry[url];
+
+ scoped_ptr<BlobFetcher> blob_fetcher = make_scoped_ptr(
+ new loader::BlobFetcher(GURL(url), &fetcher_handler_mock,
+ base::Bind(&TestResolver, registry)));
+
+ run_loop.Run();
+
+ EXPECT_EQ(0, fetcher_handler_mock.data().size());
+
+ EXPECT_EQ(blob_fetcher.get(), fetcher_handler_mock.fetcher());
+}
+
+TEST(BlobFetcherTest, ValidBlob) {
+ MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ base::RunLoop run_loop;
+ InSequence dummy;
+
+ StrictMock<MockFetcherHandler> fetcher_handler_mock(&run_loop);
+ EXPECT_CALL(fetcher_handler_mock, OnReceived(_, _, _)).Times(AtLeast(1));
+ EXPECT_CALL(fetcher_handler_mock, OnDone(_));
+
+ const char* url = "blob:28y3-fsdaf-dsfa";
+
+ TestRegistry registry;
+ std::vector<char>& buffer = registry[url];
+ buffer.push_back('a');
+ buffer.push_back(0);
+ buffer.push_back(7);
+
+ scoped_ptr<BlobFetcher> blob_fetcher = make_scoped_ptr(
+ new loader::BlobFetcher(GURL(url), &fetcher_handler_mock,
+ base::Bind(&TestResolver, registry)));
+
+ run_loop.Run();
+
+ const std::string& data = fetcher_handler_mock.data();
+ ASSERT_EQ(3, data.size());
+ EXPECT_EQ('a', data[0]);
+ EXPECT_EQ(0, data[1]);
+ EXPECT_EQ(7, data[2]);
+
+ EXPECT_EQ(blob_fetcher.get(), fetcher_handler_mock.fetcher());
+}
+
+} // namespace
+} // namespace loader
+} // namespace cobalt
diff --git a/src/cobalt/loader/decoder.h b/src/cobalt/loader/decoder.h
index bdd4040..2a0adcb 100644
--- a/src/cobalt/loader/decoder.h
+++ b/src/cobalt/loader/decoder.h
@@ -17,6 +17,9 @@
#ifndef COBALT_LOADER_DECODER_H_
#define COBALT_LOADER_DECODER_H_
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
#include "cobalt/loader/loader_types.h"
#include "cobalt/render_tree/resource_provider.h"
#include "net/http/http_response_headers.h"
@@ -43,6 +46,16 @@
// This is the interface that chunks of bytes can be sent in to be decoded.
virtual void DecodeChunk(const char* data, size_t size) = 0;
+ // A decoder can choose to implement |DecodeChunkPassed| in order to take
+ // ownership of the chunk data. Taking ownership over the chunk data can
+ // allow data copies to be avoided, such as when passing the data off to be
+ // decoded asynchronously. Not all fetchers are guaranteed to support this
+ // though, in which case they simply forward the scoped pointer data to
+ // DecodeChunk.
+ virtual void DecodeChunkPassed(scoped_ptr<std::string> data) {
+ DecodeChunk(data->data(), data->size());
+ }
+
// This is called when all data are sent in and decoding should be finalized.
virtual void Finish() = 0;
diff --git a/src/cobalt/loader/fetcher.h b/src/cobalt/loader/fetcher.h
index 4ca6d3f..e998f44 100644
--- a/src/cobalt/loader/fetcher.h
+++ b/src/cobalt/loader/fetcher.h
@@ -46,6 +46,14 @@
virtual void OnDone(Fetcher* fetcher) = 0;
virtual void OnError(Fetcher* fetcher, const std::string& error) = 0;
+ // By default, |OnReceivedPassed| forwards the scoped_ptr<std::string>
+ // data into |OnReceived|. Implementations have the opportunity to hold
+ // onto the scoped_ptr through overriding |OnReceivedPassed|.
+ virtual void OnReceivedPassed(Fetcher* fetcher,
+ scoped_ptr<std::string> data) {
+ OnReceived(fetcher, data->data(), data->length());
+ }
+
protected:
Handler() {}
virtual ~Handler() {}
diff --git a/src/cobalt/loader/fetcher_factory.cc b/src/cobalt/loader/fetcher_factory.cc
index e16c970..6b478e3 100644
--- a/src/cobalt/loader/fetcher_factory.cc
+++ b/src/cobalt/loader/fetcher_factory.cc
@@ -23,6 +23,7 @@
#include "base/logging.h"
#include "base/path_service.h"
#include "cobalt/loader/about_fetcher.h"
+#include "cobalt/loader/blob_fetcher.h"
#include "cobalt/loader/embedded_fetcher.h"
#include "cobalt/loader/file_fetcher.h"
#include "cobalt/loader/net_fetcher.h"
@@ -69,6 +70,16 @@
file_thread_.Start();
}
+FetcherFactory::FetcherFactory(
+ network::NetworkModule* network_module, const FilePath& extra_search_dir,
+ const BlobFetcher::ResolverCallback& blob_resolver)
+ : file_thread_("File"),
+ network_module_(network_module),
+ extra_search_dir_(extra_search_dir),
+ blob_resolver_(blob_resolver) {
+ file_thread_.Start();
+}
+
scoped_ptr<Fetcher> FetcherFactory::CreateFetcher(const GURL& url,
Fetcher::Handler* handler) {
return CreateSecureFetcher(url, csp::SecurityCallback(), handler).Pass();
@@ -104,7 +115,15 @@
fetcher.reset(new AboutFetcher(handler));
}
#endif
- else { // NOLINT(readability/braces)
+ else if (url.SchemeIs("blob")) { // NOLINT(readability/braces)
+ if (!blob_resolver_.is_null()) {
+ fetcher.reset(new BlobFetcher(url, handler, blob_resolver_));
+ } else {
+ LOG(ERROR) << "Fetcher factory not provided the blob registry, "
+ "could not fetch the URL: "
+ << url;
+ }
+ } else { // NOLINT(readability/braces)
DCHECK(network_module_) << "Network module required.";
NetFetcher::Options options;
fetcher.reset(new NetFetcher(url, url_security_callback, handler,
diff --git a/src/cobalt/loader/fetcher_factory.h b/src/cobalt/loader/fetcher_factory.h
index 3dd64a5..e4f5661 100644
--- a/src/cobalt/loader/fetcher_factory.h
+++ b/src/cobalt/loader/fetcher_factory.h
@@ -18,8 +18,10 @@
#define COBALT_LOADER_FETCHER_FACTORY_H_
#include "base/file_path.h"
+#include "base/optional.h"
#include "base/threading/thread.h"
#include "cobalt/csp/content_security_policy.h"
+#include "cobalt/loader/blob_fetcher.h"
#include "cobalt/loader/fetcher.h"
#include "googleurl/src/gurl.h"
@@ -35,6 +37,9 @@
explicit FetcherFactory(network::NetworkModule* network_module);
FetcherFactory(network::NetworkModule* network_module,
const FilePath& extra_search_dir);
+ FetcherFactory(network::NetworkModule* network_module,
+ const FilePath& extra_search_dir,
+ const BlobFetcher::ResolverCallback& blob_resolver);
// Creates a fetcher. Returns NULL if the creation fails.
scoped_ptr<Fetcher> CreateFetcher(const GURL& url, Fetcher::Handler* handler);
@@ -48,6 +53,7 @@
base::Thread file_thread_;
network::NetworkModule* network_module_;
FilePath extra_search_dir_;
+ BlobFetcher::ResolverCallback blob_resolver_;
};
} // namespace loader
diff --git a/src/cobalt/loader/fetcher_test.h b/src/cobalt/loader/fetcher_test.h
new file mode 100644
index 0000000..0b92378
--- /dev/null
+++ b/src/cobalt/loader/fetcher_test.h
@@ -0,0 +1,100 @@
+/*
+ * 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_LOADER_FETCHER_TEST_H_
+#define COBALT_LOADER_FETCHER_TEST_H_
+
+#include "cobalt/loader/fetcher.h"
+
+#include <string>
+
+#include "base/message_loop.h"
+#include "base/run_loop.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::Invoke;
+using ::testing::_;
+
+namespace cobalt {
+namespace loader {
+
+class FetcherHandlerForTest : public Fetcher::Handler {
+ public:
+ explicit FetcherHandlerForTest(base::RunLoop* run_loop)
+ : fetcher_(NULL), run_loop_(run_loop) {}
+
+ // From Fetcher::Handler.
+ void OnReceived(Fetcher* fetcher, const char* data, size_t size) OVERRIDE {
+ CheckFetcher(fetcher);
+ data_.append(data, size);
+ }
+ void OnDone(Fetcher* fetcher) OVERRIDE {
+ CheckFetcher(fetcher);
+ MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
+ }
+ void OnError(Fetcher* fetcher, const std::string& error) OVERRIDE {
+ UNREFERENCED_PARAMETER(error);
+ CheckFetcher(fetcher);
+ MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
+ }
+
+ const std::string& data() const { return data_; }
+ Fetcher* fetcher() const { return fetcher_; }
+
+ private:
+ void CheckFetcher(Fetcher* fetcher) {
+ EXPECT_TRUE(fetcher);
+ if (fetcher_ == NULL) {
+ fetcher_ = fetcher;
+ return;
+ }
+ EXPECT_EQ(fetcher_, fetcher);
+ }
+
+ std::string data_;
+ Fetcher* fetcher_;
+ base::RunLoop* run_loop_;
+};
+
+class MockFetcherHandler : public Fetcher::Handler {
+ public:
+ explicit MockFetcherHandler(base::RunLoop* run_loop) : real_(run_loop) {
+ ON_CALL(*this, OnReceived(_, _, _))
+ .WillByDefault(Invoke(&real_, &FetcherHandlerForTest::OnReceived));
+ ON_CALL(*this, OnDone(_))
+ .WillByDefault(Invoke(&real_, &FetcherHandlerForTest::OnDone));
+ ON_CALL(*this, OnError(_, _))
+ .WillByDefault(Invoke(&real_, &FetcherHandlerForTest::OnError));
+ }
+
+ MOCK_METHOD2(OnResponseStarted,
+ LoadResponseType(
+ Fetcher*, const scoped_refptr<net::HttpResponseHeaders>&));
+ MOCK_METHOD3(OnReceived, void(Fetcher*, const char*, size_t));
+ MOCK_METHOD1(OnDone, void(Fetcher*));
+ MOCK_METHOD2(OnError, void(Fetcher*, const std::string&));
+
+ const std::string& data() const { return real_.data(); }
+ Fetcher* fetcher() const { return real_.fetcher(); }
+
+ private:
+ FetcherHandlerForTest real_;
+};
+
+} // namespace loader
+} // namespace cobalt
+
+#endif // COBALT_LOADER_FETCHER_TEST_H_
diff --git a/src/cobalt/loader/file_fetcher_test.cc b/src/cobalt/loader/file_fetcher_test.cc
index 932584a..0b22f71 100644
--- a/src/cobalt/loader/file_fetcher_test.cc
+++ b/src/cobalt/loader/file_fetcher_test.cc
@@ -22,83 +22,17 @@
#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/run_loop.h"
+#include "cobalt/loader/fetcher_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::AtLeast;
using ::testing::InSequence;
-using ::testing::Invoke;
using ::testing::StrictMock;
using ::testing::_;
namespace cobalt {
namespace loader {
-namespace {
-
-class FetcherHandlerForTest : public Fetcher::Handler {
- public:
- explicit FetcherHandlerForTest(base::RunLoop* run_loop)
- : fetcher_(NULL), run_loop_(run_loop) {}
-
- // From Fetcher::Handler.
- void OnReceived(Fetcher* fetcher, const char* data, size_t size) OVERRIDE {
- CheckFetcher(fetcher);
- data_.append(data, size);
- }
- void OnDone(Fetcher* fetcher) OVERRIDE {
- CheckFetcher(fetcher);
- MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
- }
- void OnError(Fetcher* fetcher, const std::string& error) OVERRIDE {
- UNREFERENCED_PARAMETER(error);
- CheckFetcher(fetcher);
- MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
- }
-
- const std::string& data() const { return data_; }
- Fetcher* fetcher() const { return fetcher_; }
-
- private:
- void CheckFetcher(Fetcher* fetcher) {
- EXPECT_TRUE(fetcher);
- if (fetcher_ == NULL) {
- fetcher_ = fetcher;
- return;
- }
- EXPECT_EQ(fetcher_, fetcher);
- }
-
- std::string data_;
- Fetcher* fetcher_;
- base::RunLoop* run_loop_;
-};
-
-class MockFetcherHandler : public Fetcher::Handler {
- public:
- explicit MockFetcherHandler(base::RunLoop* run_loop) : real_(run_loop) {
- ON_CALL(*this, OnReceived(_, _, _))
- .WillByDefault(Invoke(&real_, &FetcherHandlerForTest::OnReceived));
- ON_CALL(*this, OnDone(_))
- .WillByDefault(Invoke(&real_, &FetcherHandlerForTest::OnDone));
- ON_CALL(*this, OnError(_, _))
- .WillByDefault(Invoke(&real_, &FetcherHandlerForTest::OnError));
- }
-
- MOCK_METHOD2(OnResponseStarted,
- LoadResponseType(
- Fetcher*, const scoped_refptr<net::HttpResponseHeaders>&));
- MOCK_METHOD3(OnReceived, void(Fetcher*, const char*, size_t));
- MOCK_METHOD1(OnDone, void(Fetcher*));
- MOCK_METHOD2(OnError, void(Fetcher*, const std::string&));
-
- const std::string& data() const { return real_.data(); }
- Fetcher* fetcher() const { return real_.fetcher(); }
-
- private:
- FetcherHandlerForTest real_;
-};
-
-} // namespace
class FileFetcherTest : public ::testing::Test {
protected:
diff --git a/src/cobalt/loader/image/image_data_decoder.cc b/src/cobalt/loader/image/image_data_decoder.cc
index 7d91bb8..3698bff 100644
--- a/src/cobalt/loader/image/image_data_decoder.cc
+++ b/src/cobalt/loader/image/image_data_decoder.cc
@@ -102,11 +102,15 @@
return state_ == kDone;
}
-void ImageDataDecoder::AllocateImageData(const math::Size& size) {
+void ImageDataDecoder::AllocateImageData(const math::Size& size,
+ bool has_alpha) {
+ DCHECK(resource_provider_->AlphaFormatSupported(
+ render_tree::kAlphaFormatOpaque));
DCHECK(resource_provider_->AlphaFormatSupported(
render_tree::kAlphaFormatPremultiplied));
image_data_ = resource_provider_->AllocateImageData(
- size, pixel_format(), render_tree::kAlphaFormatPremultiplied);
+ size, pixel_format(), has_alpha ? render_tree::kAlphaFormatPremultiplied
+ : render_tree::kAlphaFormatOpaque);
}
void ImageDataDecoder::CalculatePixelFormat() {
diff --git a/src/cobalt/loader/image/image_data_decoder.h b/src/cobalt/loader/image/image_data_decoder.h
index 62beb19..d97c329 100644
--- a/src/cobalt/loader/image/image_data_decoder.h
+++ b/src/cobalt/loader/image/image_data_decoder.h
@@ -63,7 +63,7 @@
// Subclass can override this function to get a last chance to do some work.
virtual void FinishInternal() {}
- void AllocateImageData(const math::Size& size);
+ void AllocateImageData(const math::Size& size, bool has_alpha);
render_tree::ImageData* image_data() const { return image_data_.get(); }
diff --git a/src/cobalt/loader/image/jpeg_image_decoder.cc b/src/cobalt/loader/image/jpeg_image_decoder.cc
index a249c7e..6fe1d91 100644
--- a/src/cobalt/loader/image/jpeg_image_decoder.cc
+++ b/src/cobalt/loader/image/jpeg_image_decoder.cc
@@ -183,7 +183,8 @@
}
AllocateImageData(math::Size(static_cast<int>(info_.image_width),
- static_cast<int>(info_.image_height)));
+ static_cast<int>(info_.image_height)),
+ false);
return true;
}
diff --git a/src/cobalt/loader/image/png_image_decoder.cc b/src/cobalt/loader/image/png_image_decoder.cc
index e048d41..e87eeb9 100644
--- a/src/cobalt/loader/image/png_image_decoder.cc
+++ b/src/cobalt/loader/image/png_image_decoder.cc
@@ -236,7 +236,8 @@
}
AllocateImageData(
- math::Size(static_cast<int>(width), static_cast<int>(height)));
+ math::Size(static_cast<int>(width), static_cast<int>(height)),
+ has_alpha_);
set_state(kReadLines);
}
diff --git a/src/cobalt/loader/image/stub_image_decoder.h b/src/cobalt/loader/image/stub_image_decoder.h
index 99c3979..1d8de85 100644
--- a/src/cobalt/loader/image/stub_image_decoder.h
+++ b/src/cobalt/loader/image/stub_image_decoder.h
@@ -47,7 +47,7 @@
UNREFERENCED_PARAMETER(data);
UNREFERENCED_PARAMETER(input_byte);
if (!image_data()) {
- AllocateImageData(math::Size(4, 4));
+ AllocateImageData(math::Size(4, 4), true);
}
set_state(kDone);
return input_byte;
diff --git a/src/cobalt/loader/image/threaded_image_decoder_proxy.cc b/src/cobalt/loader/image/threaded_image_decoder_proxy.cc
new file mode 100644
index 0000000..53fa8ce
--- /dev/null
+++ b/src/cobalt/loader/image/threaded_image_decoder_proxy.cc
@@ -0,0 +1,134 @@
+/*
+ * 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/loader/image/threaded_image_decoder_proxy.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/threading/thread.h"
+#include "cobalt/loader/image/image_decoder.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+namespace {
+
+// Helper function that is run on the WebModule thread to run a Callback if
+// and only if |threaded_image_decoder_proxy| is still alive.
+template <typename Callback, typename Arg>
+void MaybeRun(
+ base::WeakPtr<ThreadedImageDecoderProxy> threaded_image_decoder_proxy,
+ const Callback& callback, const Arg& arg) {
+ if (threaded_image_decoder_proxy) {
+ callback.Run(arg);
+ }
+}
+
+// Helper function to post a Callback back to the WebModule thread, that
+// checks whether the ThreadedImageDecoderProxy it came from is alive or not
+// before it runs.
+template <typename Callback, typename Arg>
+void PostToMessageLoopChecked(
+ base::WeakPtr<ThreadedImageDecoderProxy> threaded_image_decoder_proxy,
+ const Callback& callback, MessageLoop* message_loop, const Arg& arg) {
+ message_loop->PostTask(
+ FROM_HERE, base::Bind(&MaybeRun<Callback, Arg>,
+ threaded_image_decoder_proxy, callback, arg));
+}
+
+} // namespace
+
+ThreadedImageDecoderProxy::ThreadedImageDecoderProxy(
+ render_tree::ResourceProvider* resource_provider,
+ const SuccessCallback& success_callback,
+ const FailureCallback& failure_callback,
+ const ErrorCallback& error_callback, MessageLoop* load_message_loop)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ weak_this_(weak_ptr_factory_.GetWeakPtr())),
+ load_message_loop_(load_message_loop),
+ result_message_loop_(MessageLoop::current()),
+ image_decoder_(new ImageDecoder(
+ resource_provider,
+ base::Bind(
+ &PostToMessageLoopChecked<SuccessCallback,
+ scoped_refptr<render_tree::Image> >,
+ weak_this_, success_callback, result_message_loop_),
+ base::Bind(&PostToMessageLoopChecked<FailureCallback, std::string>,
+ weak_this_, failure_callback, result_message_loop_),
+ base::Bind(&PostToMessageLoopChecked<ErrorCallback, std::string>,
+ weak_this_, error_callback, result_message_loop_))) {
+ DCHECK(load_message_loop_);
+ DCHECK(result_message_loop_);
+ DCHECK(image_decoder_);
+}
+
+// We're dying, but |image_decoder_| might still be doing work on the load
+// thread. Because of this, we transfer ownership of it into the load message
+// loop, where it will be deleted after any pending tasks involving it are
+// done.
+ThreadedImageDecoderProxy::~ThreadedImageDecoderProxy() {
+ load_message_loop_->DeleteSoon(FROM_HERE, image_decoder_.release());
+}
+
+LoadResponseType ThreadedImageDecoderProxy::OnResponseStarted(
+ Fetcher* fetcher, const scoped_refptr<net::HttpResponseHeaders>& headers) {
+ UNREFERENCED_PARAMETER(fetcher);
+ return image_decoder_->OnResponseStarted(fetcher, headers);
+}
+
+void ThreadedImageDecoderProxy::DecodeChunk(const char* data, size_t size) {
+ scoped_ptr<std::string> scoped_data(new std::string(data, size));
+ load_message_loop_->PostTask(
+ FROM_HERE, base::Bind(&Decoder::DecodeChunkPassed,
+ base::Unretained(image_decoder_.get()),
+ base::Passed(&scoped_data)));
+}
+
+void ThreadedImageDecoderProxy::DecodeChunkPassed(
+ scoped_ptr<std::string> data) {
+ load_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&Decoder::DecodeChunkPassed,
+ base::Unretained(image_decoder_.get()), base::Passed(&data)));
+}
+
+void ThreadedImageDecoderProxy::Finish() {
+ load_message_loop_->PostTask(
+ FROM_HERE, base::Bind(&ImageDecoder::Finish,
+ base::Unretained(image_decoder_.get())));
+}
+
+bool ThreadedImageDecoderProxy::Suspend() {
+ load_message_loop_->PostTask(
+ FROM_HERE, base::Bind(base::IgnoreResult(&ImageDecoder::Suspend),
+ base::Unretained(image_decoder_.get())));
+ return true;
+}
+
+void ThreadedImageDecoderProxy::Resume(
+ render_tree::ResourceProvider* resource_provider) {
+ load_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&ImageDecoder::Resume, base::Unretained(image_decoder_.get()),
+ resource_provider));
+}
+
+} // namespace image
+} // namespace loader
+} // namespace cobalt
diff --git a/src/cobalt/loader/image/threaded_image_decoder_proxy.h b/src/cobalt/loader/image/threaded_image_decoder_proxy.h
new file mode 100644
index 0000000..c6b559b
--- /dev/null
+++ b/src/cobalt/loader/image/threaded_image_decoder_proxy.h
@@ -0,0 +1,83 @@
+/*
+ * 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_LOADER_IMAGE_THREADED_IMAGE_DECODER_PROXY_H_
+#define COBALT_LOADER_IMAGE_THREADED_IMAGE_DECODER_PROXY_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "cobalt/loader/decoder.h"
+#include "cobalt/loader/image/image_data_decoder.h"
+#include "cobalt/loader/image/image_decoder.h"
+#include "cobalt/render_tree/image.h"
+#include "cobalt/render_tree/resource_provider.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+// This class handles the layer of indirection between images that web module
+// thread wants to decode, and the ResourceLoader thread that does the actual
+// work.
+class ThreadedImageDecoderProxy : public Decoder {
+ public:
+ typedef base::Callback<void(const scoped_refptr<render_tree::Image>&)>
+ SuccessCallback;
+ typedef base::Callback<void(const std::string&)> FailureCallback;
+ typedef base::Callback<void(const std::string&)> ErrorCallback;
+
+ ThreadedImageDecoderProxy(render_tree::ResourceProvider* resource_provider,
+ const SuccessCallback& success_callback,
+ const FailureCallback& failure_callback,
+ const ErrorCallback& error_callback,
+ MessageLoop* load_message_loop_);
+
+ ~ThreadedImageDecoderProxy();
+
+ // From the Decoder interface.
+ LoadResponseType OnResponseStarted(
+ Fetcher* fetcher,
+ const scoped_refptr<net::HttpResponseHeaders>& headers) OVERRIDE;
+ void DecodeChunk(const char* data, size_t size) OVERRIDE;
+ void DecodeChunkPassed(scoped_ptr<std::string> data) OVERRIDE;
+ void Finish() OVERRIDE;
+ bool Suspend() OVERRIDE;
+ void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE;
+
+ private:
+ base::WeakPtrFactory<ThreadedImageDecoderProxy> weak_ptr_factory_;
+ base::WeakPtr<ThreadedImageDecoderProxy> weak_this_;
+
+ // The message loop that |image_decoder_| should run on.
+ MessageLoop* load_message_loop_;
+
+ // The message loop that the original callbacks passed into us should run
+ // on.
+ MessageLoop* result_message_loop_;
+
+ // The actual image decoder.
+ scoped_ptr<ImageDecoder> image_decoder_;
+};
+
+} // namespace image
+} // namespace loader
+} // namespace cobalt
+
+#endif // COBALT_LOADER_IMAGE_THREADED_IMAGE_DECODER_PROXY_H_
diff --git a/src/cobalt/loader/image/webp_image_decoder.cc b/src/cobalt/loader/image/webp_image_decoder.cc
index b735d54..53f24eb 100644
--- a/src/cobalt/loader/image/webp_image_decoder.cc
+++ b/src/cobalt/loader/image/webp_image_decoder.cc
@@ -92,7 +92,7 @@
int height = config_.input.height;
*has_alpha = !!config_.input.has_alpha;
- AllocateImageData(math::Size(width, height));
+ AllocateImageData(math::Size(width, height), *has_alpha);
return true;
} else if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
// Data is not enough for decoding the header.
diff --git a/src/cobalt/loader/loader.cc b/src/cobalt/loader/loader.cc
index a5d86e7..b01a21a 100644
--- a/src/cobalt/loader/loader.cc
+++ b/src/cobalt/loader/loader.cc
@@ -51,6 +51,11 @@
UNREFERENCED_PARAMETER(fetcher);
decoder_->DecodeChunk(data, size);
}
+ void OnReceivedPassed(Fetcher* fetcher,
+ scoped_ptr<std::string> data) OVERRIDE {
+ UNREFERENCED_PARAMETER(fetcher);
+ decoder_->DecodeChunkPassed(data.Pass());
+ }
void OnDone(Fetcher* fetcher) OVERRIDE {
UNREFERENCED_PARAMETER(fetcher);
decoder_->Finish();
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index c0a7f79..b80a2cb 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -21,6 +21,8 @@
'target_name': 'loader',
'type': 'static_library',
'sources': [
+ 'blob_fetcher.cc',
+ 'blob_fetcher.h',
'decoder.h',
'embedded_fetcher.cc',
'embedded_fetcher.h',
@@ -30,9 +32,9 @@
'fetcher_factory.h',
'file_fetcher.cc',
'file_fetcher.h',
+ 'font/remote_typeface_cache.h',
'font/typeface_decoder.cc',
'font/typeface_decoder.h',
- 'font/remote_typeface_cache.h',
'image/dummy_gif_image_decoder.cc',
'image/dummy_gif_image_decoder.h',
'image/image_cache.h',
@@ -45,6 +47,8 @@
'image/png_image_decoder.cc',
'image/png_image_decoder.h',
'image/stub_image_decoder.h',
+ 'image/threaded_image_decoder_proxy.cc',
+ 'image/threaded_image_decoder_proxy.h',
'image/webp_image_decoder.cc',
'image/webp_image_decoder.h',
'loader.cc',
@@ -91,7 +95,9 @@
'target_name': 'loader_test',
'type': '<(gtest_target_type)',
'sources': [
+ 'blob_fetcher_test.cc',
'fetcher_factory_test.cc',
+ 'fetcher_test.h',
'file_fetcher_test.cc',
'font/typeface_decoder_test.cc',
'image/image_decoder_test.cc',
diff --git a/src/cobalt/loader/loader_factory.cc b/src/cobalt/loader/loader_factory.cc
index e39a512..c3d2138 100644
--- a/src/cobalt/loader/loader_factory.cc
+++ b/src/cobalt/loader/loader_factory.cc
@@ -16,13 +16,28 @@
#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"
+
namespace cobalt {
namespace loader {
+namespace {
+// The ResourceLoader thread uses the default stack size, which is requested
+// by passing in 0 for its stack size.
+const size_t kLoadThreadStackSize = 0;
+} // namespace
+
LoaderFactory::LoaderFactory(FetcherFactory* fetcher_factory,
render_tree::ResourceProvider* resource_provider)
: fetcher_factory_(fetcher_factory),
- resource_provider_(resource_provider) {}
+ resource_provider_(resource_provider),
+ load_thread_("ResourceLoader") {
+ base::Thread::Options options(MessageLoop::TYPE_DEFAULT, kLoadThreadStackSize,
+ base::kThreadPriority_Low);
+ load_thread_.StartWithOptions(options);
+}
scoped_ptr<Loader> LoaderFactory::CreateImageLoader(
const GURL& url, const csp::SecurityCallback& url_security_callback,
@@ -33,9 +48,9 @@
scoped_ptr<Loader> loader(new Loader(
MakeFetcherCreator(url, url_security_callback),
- scoped_ptr<Decoder>(
- new image::ImageDecoder(resource_provider_, success_callback,
- failure_callback, error_callback)),
+ scoped_ptr<Decoder>(new image::ThreadedImageDecoderProxy(
+ resource_provider_, success_callback, failure_callback,
+ error_callback, load_thread_.message_loop())),
error_callback,
base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this))));
OnLoaderCreated(loader.get());
@@ -78,6 +93,13 @@
iter != active_loaders_.end(); ++iter) {
(*iter)->Suspend();
}
+
+ // 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();
}
void LoaderFactory::Resume(render_tree::ResourceProvider* resource_provider) {
diff --git a/src/cobalt/loader/loader_factory.h b/src/cobalt/loader/loader_factory.h
index 000e7ad..fe79673 100644
--- a/src/cobalt/loader/loader_factory.h
+++ b/src/cobalt/loader/loader_factory.h
@@ -19,6 +19,7 @@
#include <set>
+#include "base/threading/thread.h"
#include "cobalt/csp/content_security_policy.h"
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/loader/font/typeface_decoder.h"
@@ -81,6 +82,10 @@
// can be aborted.
typedef std::set<Loader*> LoaderSet;
LoaderSet active_loaders_;
+
+ // Thread to run asynchronous fetchers and decoders on. At the moment,
+ // image decoding is the only thing done on this thread.
+ base::Thread load_thread_;
};
} // namespace loader
diff --git a/src/cobalt/loader/loader_test.cc b/src/cobalt/loader/loader_test.cc
index c7e637c..ed43dfa 100644
--- a/src/cobalt/loader/loader_test.cc
+++ b/src/cobalt/loader/loader_test.cc
@@ -100,7 +100,7 @@
struct MockFetcherFactory {
public:
- MockFetcherFactory(): count(0) {}
+ MockFetcherFactory() : count(0) {}
// Way to access the last fetcher created by the fetcher factory.
MockFetcher* fetcher;
int count;
diff --git a/src/cobalt/loader/net_fetcher.cc b/src/cobalt/loader/net_fetcher.cc
index 0f60e72..1ed1376 100644
--- a/src/cobalt/loader/net_fetcher.cc
+++ b/src/cobalt/loader/net_fetcher.cc
@@ -156,7 +156,7 @@
net_fetcher_log.Get().IncrementFetchedBytes(
static_cast<int>(download_data->length()));
#endif
- handler()->OnReceived(this, download_data->data(), download_data->length());
+ handler()->OnReceivedPassed(this, download_data.Pass());
}
}
diff --git a/src/cobalt/loader/text_decoder.h b/src/cobalt/loader/text_decoder.h
index 5de3c4c..dd191d7 100644
--- a/src/cobalt/loader/text_decoder.h
+++ b/src/cobalt/loader/text_decoder.h
@@ -17,11 +17,13 @@
#ifndef COBALT_LOADER_TEXT_DECODER_H_
#define COBALT_LOADER_TEXT_DECODER_H_
+#include <algorithm>
#include <string>
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_checker.h"
#include "cobalt/loader/decoder.h"
@@ -50,6 +52,21 @@
}
text_.append(data, size);
}
+
+ void DecodeChunkPassed(scoped_ptr<std::string> data) OVERRIDE {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(data);
+ if (suspended_) {
+ return;
+ }
+
+ if (text_.empty()) {
+ std::swap(*data, text_);
+ } else {
+ text_.append(*data);
+ }
+ }
+
void Finish() OVERRIDE {
DCHECK(thread_checker_.CalledOnValidThread());
if (suspended_) {
diff --git a/src/cobalt/render_tree/image.h b/src/cobalt/render_tree/image.h
index 8faa040..ee58ada 100644
--- a/src/cobalt/render_tree/image.h
+++ b/src/cobalt/render_tree/image.h
@@ -67,6 +67,12 @@
// This alpha format implies standard alpha, where each component is
// independent of the alpha.
kAlphaFormatUnpremultiplied,
+
+ // Indicates that all alpha values in the image data are opaque. If
+ // non-opaque alpha data is used with this format, visual output will be
+ // undefined. This information may be used to enable optimizations, and can
+ // result in Image::IsOpaque() returning true.
+ kAlphaFormatOpaque,
};
// Describes the format of a contiguous block of memory that represents an
@@ -216,6 +222,12 @@
BytesPerPixel(kPixelFormatRGBA8));
}
+ // If an Image is able to know that it contains no alpha data (e.g. if it
+ // was constructed from ImageData whose alpha format is set to
+ // kAlphaFormatOpaque), then this method can return true, and code can
+ // be written to take advantage of this and perform optimizations.
+ virtual bool IsOpaque() const { return false; }
+
protected:
virtual ~Image() {}
diff --git a/src/cobalt/render_tree/map_to_mesh_filter.h b/src/cobalt/render_tree/map_to_mesh_filter.h
index 5451c11..0f996fa 100644
--- a/src/cobalt/render_tree/map_to_mesh_filter.h
+++ b/src/cobalt/render_tree/map_to_mesh_filter.h
@@ -17,14 +17,25 @@
#ifndef COBALT_RENDER_TREE_MAP_TO_MESH_FILTER_H_
#define COBALT_RENDER_TREE_MAP_TO_MESH_FILTER_H_
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+
namespace cobalt {
namespace render_tree {
+enum StereoMode { kMono, kLeftRight, kTopBottom };
+
// A MapToMeshFilter can be used to map source content onto a 3D mesh, within a
// specified well-defined viewport.
class MapToMeshFilter {
public:
- MapToMeshFilter() {}
+ explicit MapToMeshFilter(const StereoMode stereo_mode)
+ : stereo_mode_(stereo_mode) {}
+
+ StereoMode GetStereoMode() const { return stereo_mode_; }
+
+ private:
+ StereoMode stereo_mode_;
};
} // namespace render_tree
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/vertex_mesh.glsl b/src/cobalt/renderer/glimp_shaders/glsl/vertex_mesh.glsl
index cce9ab8..b41b78f 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/vertex_mesh.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/vertex_mesh.glsl
@@ -1,8 +1,11 @@
attribute vec3 a_position;
attribute vec2 a_tex_coord;
varying vec2 v_tex_coord;
+uniform vec2 u_tex_offset;
+uniform vec2 u_tex_multiplier;
uniform mat4 u_mvp_matrix;
void main() {
gl_Position = u_mvp_matrix * vec4(a_position.xyz, 1.0);
- v_tex_coord = a_tex_coord;
+ v_tex_coord.x = a_tex_coord.x * u_tex_multiplier.x + u_tex_offset.x;
+ v_tex_coord.y = a_tex_coord.y * u_tex_multiplier.y + u_tex_offset.y;
}
diff --git a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
new file mode 100644
index 0000000..7739988
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
@@ -0,0 +1,216 @@
+/*
+ * 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/renderer/rasterizer/blitter/cached_software_rasterizer.h"
+
+#include <utility>
+
+#include "cobalt/math/vector2d_f.h"
+#include "cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+
+#if SB_HAS(BLITTER)
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace blitter {
+
+size_t CachedSoftwareRasterizer::Surface::GetEstimatedMemoryUsage() const {
+ // We assume 4-bytes-per-pixel.
+ return coord_mapping.output_bounds.size().GetArea() * 4;
+}
+
+CachedSoftwareRasterizer::CachedSoftwareRasterizer(SbBlitterDevice device,
+ SbBlitterContext context,
+ int cache_capacity)
+ : cache_capacity_(cache_capacity),
+ device_(device),
+ context_(context),
+ software_rasterizer_(0),
+ cache_memory_usage_(
+ "Memory.CachedSoftwareRasterizer.CacheUsage", 0,
+ "Total memory occupied by cached software-rasterized surfaces.") {}
+
+CachedSoftwareRasterizer::~CachedSoftwareRasterizer() {
+ // Clean up any leftover surfaces.
+ for (CacheMap::iterator iter = surface_map_.begin();
+ iter != surface_map_.end(); ++iter) {
+ DCHECK(iter->second.cached);
+ SbBlitterDestroySurface(iter->second.surface);
+ }
+}
+
+void CachedSoftwareRasterizer::OnStartNewFrame() {
+ for (CacheMap::iterator iter = surface_map_.begin();
+ iter != surface_map_.end();) {
+ CacheMap::iterator current = iter;
+ ++iter;
+
+ // If the surface wasn't referenced, destroy it. If it was, mark it as
+ // being unreferenced.
+ if (!current->second.referenced) {
+ cache_memory_usage_ -= current->second.GetEstimatedMemoryUsage();
+ SbBlitterDestroySurface(current->second.surface);
+ surface_map_.erase(current);
+ } else {
+#if defined(ENABLE_DEBUG_CONSOLE)
+ current->second.created = false;
+#endif // defined(ENABLE_DEBUG_CONSOLE)
+ current->second.referenced = false;
+ }
+ }
+}
+
+CachedSoftwareRasterizer::Surface CachedSoftwareRasterizer::GetSurface(
+ render_tree::Node* node, const Transform& transform) {
+ CacheMap::iterator found = surface_map_.find(node);
+ if (found != surface_map_.end()) {
+#if SB_HAS(BILINEAR_FILTERING_SUPPORT)
+ if (found->second.scale.x() >= transform.scale().x() &&
+ found->second.scale.y() >= transform.scale().y()) {
+#else // SB_HAS(BILINEAR_FILTERING_SUPPORT)
+ if (found->second.scale.x() == transform.scale().x() &&
+ found->second.scale.y() == transform.scale().y()) {
+#endif
+ found->second.referenced = true;
+ return found->second;
+ }
+ }
+
+ Surface software_surface;
+ software_surface.referenced = true;
+#if defined(ENABLE_DEBUG_CONSOLE)
+ software_surface.created = true;
+#endif // defined(ENABLE_DEBUG_CONSOLE)
+ software_surface.node = node;
+ software_surface.surface = kSbBlitterInvalidSurface;
+ software_surface.cached = false;
+
+ // We ensure that scale is at least 1. Since it is common to have animating
+ // scale between 0 and 1 (e.g. an item animating from 0 to 1 to "grow" into
+ // the scene), this ensures that rendered items will not look pixellated as
+ // they grow (e.g. if they were cached at scale 0.2, they would be stretched
+ // x5 if the scale were to animate to 1.0).
+ if (transform.scale().x() == 0.0f || transform.scale().y() == 0.0f) {
+ // There won't be anything to render if the transform squishes one dimension
+ // to 0.
+ return software_surface;
+ }
+ DCHECK_LT(0, transform.scale().x());
+ DCHECK_LT(0, transform.scale().y());
+ Transform scaled_transform(transform);
+// If bilinear filtering is not supported, do not rely on the rasterizer to
+// scale down for us, it results in too many artifacts.
+#if SB_HAS(BILINEAR_FILTERING_SUPPORT)
+ math::Vector2dF apply_scale(1.0f, 1.0f);
+ if (transform.scale().x() < 1.0f) {
+ apply_scale.set_x(1.0f / transform.scale().x());
+ }
+ if (transform.scale().y() < 1.0f) {
+ apply_scale.set_y(1.0f / transform.scale().y());
+ }
+ scaled_transform.ApplyScale(apply_scale);
+#endif // SB_HAS(BILINEAR_FILTERING_SUPPORT)
+
+ software_surface.scale = scaled_transform.scale();
+
+ common::OffscreenRenderCoordinateMapping coord_mapping =
+ common::GetOffscreenRenderCoordinateMapping(node->GetBounds(),
+ scaled_transform.ToMatrix(),
+ base::optional<math::Rect>());
+
+ software_surface.coord_mapping = coord_mapping;
+
+ if (coord_mapping.output_bounds.IsEmpty()) {
+ // There's nothing to render if the bounds are 0.
+ return software_surface;
+ }
+
+ DCHECK_GE(0.001f, std::abs(1.0f -
+ scaled_transform.scale().x() *
+ coord_mapping.output_post_scale.x()));
+ DCHECK_GE(0.001f, std::abs(1.0f -
+ scaled_transform.scale().y() *
+ coord_mapping.output_post_scale.y()));
+
+ SkImageInfo output_image_info = SkImageInfo::MakeN32(
+ coord_mapping.output_bounds.width(), coord_mapping.output_bounds.height(),
+ kPremul_SkAlphaType);
+
+ // Allocate the pixels for the output image.
+ SbBlitterPixelDataFormat blitter_pixel_data_format =
+ SkiaToBlitterPixelFormat(output_image_info.colorType());
+ DCHECK(SbBlitterIsPixelFormatSupportedByPixelData(device_,
+ blitter_pixel_data_format));
+ SbBlitterPixelData pixel_data = SbBlitterCreatePixelData(
+ device_, coord_mapping.output_bounds.width(),
+ coord_mapping.output_bounds.height(), blitter_pixel_data_format);
+ CHECK(SbBlitterIsPixelDataValid(pixel_data));
+
+ SkBitmap bitmap;
+ bitmap.installPixels(output_image_info,
+ SbBlitterGetPixelDataPointer(pixel_data),
+ SbBlitterGetPixelDataPitchInBytes(pixel_data));
+
+ // Setup our Skia canvas that we will be using as the target for all CPU Skia
+ // output.
+ SkCanvas canvas(bitmap);
+ canvas.clear(SkColorSetARGB(0, 0, 0, 0));
+
+ Transform sub_render_transform(coord_mapping.sub_render_transform);
+
+ // Now setup our canvas so that the render tree will be rendered to the top
+ // left corner instead of at node->GetBounds().origin().
+ canvas.translate(sub_render_transform.translate().x(),
+ sub_render_transform.translate().y());
+ // And finally set the scale on our target canvas to match that of the current
+ // |transform|.
+ canvas.scale(sub_render_transform.scale().x(),
+ sub_render_transform.scale().y());
+
+ // Use the Skia software rasterizer to render our subtree.
+ software_rasterizer_.Submit(node, &canvas);
+
+ // Create a surface out of the now populated pixel data.
+ software_surface.surface =
+ SbBlitterCreateSurfaceFromPixelData(device_, pixel_data);
+
+ if (software_surface.GetEstimatedMemoryUsage() +
+ cache_memory_usage_.value() <=
+ cache_capacity_) {
+ software_surface.cached = true;
+ if (found != surface_map_.end()) {
+ cache_memory_usage_ -= found->second.GetEstimatedMemoryUsage();
+ found->second = software_surface;
+ } else {
+ std::pair<CacheMap::iterator, bool> inserted =
+ surface_map_.insert(std::make_pair(node, software_surface));
+ }
+ cache_memory_usage_ += software_surface.GetEstimatedMemoryUsage();
+ }
+
+ return software_surface;
+}
+
+} // namespace blitter
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // #if SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
new file mode 100644
index 0000000..f71d49b
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
@@ -0,0 +1,145 @@
+/*
+ * 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_RENDERER_RASTERIZER_BLITTER_CACHED_SOFTWARE_RASTERIZER_H_
+#define COBALT_RENDERER_RASTERIZER_BLITTER_CACHED_SOFTWARE_RASTERIZER_H_
+
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "cobalt/base/c_val.h"
+#include "cobalt/render_tree/node.h"
+#include "cobalt/renderer/rasterizer/blitter/render_state.h"
+#include "cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h"
+#include "cobalt/renderer/rasterizer/skia/software_rasterizer.h"
+#include "starboard/blitter.h"
+
+#if SB_HAS(BLITTER)
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace blitter {
+
+// This class is responsible for creating SbBlitterSurfaces and software
+// rasterizing render tree nodes to them to be used by clients. The class
+// manages a cache of software surfaces in order to avoid duplicate work. Its
+// main public interface is for clients to construct
+// CachedSoftwareRasterizer::SurfaceReference objects from which they can
+// extract the software-rasterized surface. Internally, software rasterization
+// is handled via a skia::SoftwareRasterizer object.
+class CachedSoftwareRasterizer {
+ public:
+ struct Surface {
+ // The actual surface containing rendered data.
+ SbBlitterSurface surface;
+
+ // True if this surface was referenced at least once since the last time
+ // OnStartNewFrame() was called.
+ bool referenced;
+
+#if defined(ENABLE_DEBUG_CONSOLE)
+ // True if OnStartNewFrame() has not been called since this surface was
+ // created.
+ bool created;
+#endif // defined(ENABLE_DEBUG_CONSOLE)
+
+ // Transform information detailing how the surface should be output to
+ // the display such that sub-pixel alignments are respected.
+ common::OffscreenRenderCoordinateMapping coord_mapping;
+
+ // A duplicate of the key, though as a smart pointer, to keep a reference
+ // to the node alive.
+ scoped_refptr<render_tree::Node> node;
+
+ // Is this surface cached or not.
+ bool cached;
+
+ // The scale used for rasterization when the surface was cached.
+ math::Vector2dF scale;
+
+ size_t GetEstimatedMemoryUsage() const;
+ };
+
+ CachedSoftwareRasterizer(SbBlitterDevice device, SbBlitterContext context,
+ int cache_capacity);
+ ~CachedSoftwareRasterizer();
+
+ // Should be called once per frame. This method will remove from the cache
+ // anything that hasn't been referenced in the last frame.
+ void OnStartNewFrame();
+
+ // A SurfaceReference is the mechanism through which clients can request
+ // renders from the CachedSoftwareRasterizer. The main reason that we don't
+ // make GetSurface() the direct public interface is so that we can decide
+ // to rasterize something without caching it, in which case it should be
+ // cleaned up when the reference to it expires.
+ class SurfaceReference {
+ public:
+ // The |transform| parameter should be the transform that the node will
+ // ultimately have applied to it, and is used for ensuring that the rendered
+ // result is sub-pixel aligned properly.
+ SurfaceReference(CachedSoftwareRasterizer* rasterizer,
+ render_tree::Node* node, const Transform& transform)
+ : surface_(rasterizer->GetSurface(node, transform)) {}
+ ~SurfaceReference() {
+ // If the surface returned to us was not actually cached, then destroy
+ // it here when the surface reference is destroyed.
+ if (!surface_.cached && SbBlitterIsSurfaceValid(surface_.surface)) {
+ SbBlitterDestroySurface(surface_.surface);
+ }
+ }
+ const Surface& surface() const { return surface_; }
+
+ private:
+ Surface surface_;
+
+ DISALLOW_COPY_AND_ASSIGN(SurfaceReference);
+ };
+
+ Surface GetSurface(render_tree::Node* node, const Transform& transform);
+
+ render_tree::ResourceProvider* GetResourceProvider() {
+ return software_rasterizer_.GetResourceProvider();
+ }
+
+ private:
+ // The cache, mapping input render_tree::Node references to cached surfaces.
+ typedef base::hash_map<render_tree::Node*, Surface> CacheMap;
+ CacheMap surface_map_;
+
+ const int cache_capacity_;
+
+ // The Blitter device/context that all image allocation/blitting operations
+ // will occur within.
+ SbBlitterDevice device_;
+ SbBlitterContext context_;
+
+ // The internal rasterizer used to actually render nodes.
+ skia::SoftwareRasterizer software_rasterizer_;
+
+ // The amount of memory currently consumed by the surfaces populating the
+ // cache.
+ base::CVal<base::cval::SizeInBytes> cache_memory_usage_;
+};
+
+} // namespace blitter
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // #if SB_HAS(BLITTER)
+
+#endif // COBALT_RENDERER_RASTERIZER_BLITTER_CACHED_SOFTWARE_RASTERIZER_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
index 2868926..dee6372 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
@@ -16,11 +16,17 @@
#include "cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h"
+#include <string>
+
#include "base/debug/trace_event.h"
#include "base/threading/thread_checker.h"
+#if defined(ENABLE_DEBUG_CONSOLE)
+#include "cobalt/base/console_commands.h"
+#endif
#include "cobalt/render_tree/resource_provider_stub.h"
#include "cobalt/renderer/backend/blitter/graphics_context.h"
#include "cobalt/renderer/backend/blitter/render_target.h"
+#include "cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h"
#include "cobalt/renderer/rasterizer/blitter/render_state.h"
#include "cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h"
#include "cobalt/renderer/rasterizer/blitter/resource_provider.h"
@@ -39,7 +45,8 @@
public:
explicit Impl(backend::GraphicsContext* graphics_context,
int scratch_surface_size_in_bytes,
- int surface_cache_size_in_bytes);
+ int surface_cache_size_in_bytes,
+ int software_surface_cache_size_in_bytes);
~Impl();
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
@@ -49,11 +56,14 @@
render_tree::ResourceProvider* GetResourceProvider();
private:
+#if defined(ENABLE_DEBUG_CONSOLE)
+ void OnToggleHighlightSoftwareDraws(const std::string& message);
+#endif
+
base::ThreadChecker thread_checker_;
backend::GraphicsContextBlitter* context_;
- skia::SoftwareRasterizer software_rasterizer_;
scoped_ptr<render_tree::ResourceProvider> resource_provider_;
int64 submit_count_;
@@ -61,21 +71,47 @@
ScratchSurfaceCache scratch_surface_cache_;
base::optional<SurfaceCacheDelegate> surface_cache_delegate_;
base::optional<common::SurfaceCache> surface_cache_;
+
+ CachedSoftwareRasterizer software_surface_cache_;
+
+#if defined(ENABLE_DEBUG_CONSOLE)
+ // Debug command to toggle cache highlights to help visualize which nodes
+ // are being cached.
+ bool toggle_highlight_software_draws_;
+ base::ConsoleCommandManager::CommandHandler
+ toggle_highlight_software_draws_command_handler_;
+#endif
};
HardwareRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
int scratch_surface_size_in_bytes,
- int surface_cache_size_in_bytes)
+ int surface_cache_size_in_bytes,
+ int software_surface_cache_size_in_bytes)
: context_(base::polymorphic_downcast<backend::GraphicsContextBlitter*>(
graphics_context)),
- software_rasterizer_(0),
submit_count_(0),
scratch_surface_cache_(context_->GetSbBlitterDevice(),
context_->GetSbBlitterContext(),
- scratch_surface_size_in_bytes) {
+ scratch_surface_size_in_bytes),
+ software_surface_cache_(context_->GetSbBlitterDevice(),
+ context_->GetSbBlitterContext(),
+ software_surface_cache_size_in_bytes)
+#if defined(ENABLE_DEBUG_CONSOLE)
+ ,
+ toggle_highlight_software_draws_(false),
+ toggle_highlight_software_draws_command_handler_(
+ "toggle_highlight_software_draws",
+ base::Bind(&HardwareRasterizer::Impl::OnToggleHighlightSoftwareDraws,
+ base::Unretained(this)),
+ "Highlights regions where software rasterization is occurring.",
+ "Toggles whether all software rasterized elements will appear as a "
+ "green rectangle or not. This can be used to identify where in a "
+ "scene software rasterization is occurring.")
+#endif // defined(ENABLE_DEBUG_CONSOLE)
+{
resource_provider_ = scoped_ptr<render_tree::ResourceProvider>(
new ResourceProvider(context_->GetSbBlitterDevice(),
- software_rasterizer_.GetResourceProvider()));
+ software_surface_cache_.GetResourceProvider()));
if (surface_cache_size_in_bytes > 0) {
surface_cache_delegate_.emplace(context_->GetSbBlitterDevice(),
@@ -88,6 +124,14 @@
HardwareRasterizer::Impl::~Impl() {}
+#if defined(ENABLE_DEBUG_CONSOLE)
+void HardwareRasterizer::Impl::OnToggleHighlightSoftwareDraws(
+ const std::string& message) {
+ UNREFERENCED_PARAMETER(message);
+ toggle_highlight_software_draws_ = !toggle_highlight_software_draws_;
+}
+#endif
+
void HardwareRasterizer::Impl::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target, int options) {
@@ -112,6 +156,8 @@
surface_cache_->Frame();
}
+ software_surface_cache_.OnStartNewFrame();
+
// Clear the background before proceeding if the clear option is set.
// We also clear if this is one of the first 3 submits. This is for security
// purposes, so that despite the Blitter API implementation, we ensure that
@@ -127,14 +173,20 @@
TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
// Visit the render tree with our Blitter API visitor.
+ RenderState initial_render_state(
+ render_target_blitter->GetSbRenderTarget(), Transform(),
+ BoundsStack(context_->GetSbBlitterContext(),
+ math::Rect(render_target_blitter->GetSize())));
+#if defined(ENABLE_DEBUG_CONSOLE)
+ initial_render_state.highlight_software_draws =
+ toggle_highlight_software_draws_;
+#endif // defined(ENABLE_DEBUG_CONSOLE)
RenderTreeNodeVisitor visitor(
context_->GetSbBlitterDevice(), context_->GetSbBlitterContext(),
- RenderState(render_target_blitter->GetSbRenderTarget(), Transform(),
- BoundsStack(context_->GetSbBlitterContext(),
- math::Rect(render_target_blitter->GetSize()))),
- &software_rasterizer_, &scratch_surface_cache_,
+ initial_render_state, &scratch_surface_cache_,
surface_cache_delegate_ ? &surface_cache_delegate_.value() : NULL,
- surface_cache_ ? &surface_cache_.value() : NULL);
+ surface_cache_ ? &surface_cache_.value() : NULL,
+ &software_surface_cache_);
render_tree->Accept(&visitor);
}
@@ -151,9 +203,11 @@
HardwareRasterizer::HardwareRasterizer(
backend::GraphicsContext* graphics_context,
- int scratch_surface_size_in_bytes, int surface_cache_size_in_bytes)
+ int scratch_surface_size_in_bytes, int surface_cache_size_in_bytes,
+ int software_surface_cache_size_in_bytes)
: impl_(new Impl(graphics_context, scratch_surface_size_in_bytes,
- surface_cache_size_in_bytes)) {}
+ surface_cache_size_in_bytes,
+ software_surface_cache_size_in_bytes)) {}
HardwareRasterizer::~HardwareRasterizer() {}
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
index abf4d4e..6a48124 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
@@ -39,7 +39,8 @@
public:
explicit HardwareRasterizer(backend::GraphicsContext* graphics_context,
int scratch_surface_size_in_bytes,
- int surface_cache_size_in_bytes);
+ int surface_cache_size_in_bytes,
+ int software_surface_cache_size_in_bytes);
virtual ~HardwareRasterizer();
// Consume the render tree and output the results to the render target passed
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.cc b/src/cobalt/renderer/rasterizer/blitter/image.cc
index 5932ddd..bd82e11 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/image.cc
@@ -38,7 +38,8 @@
RenderTreePixelFormatToBlitter(pixel_format))),
descriptor_(size, pixel_format, alpha_format,
SbBlitterGetPixelDataPitchInBytes(pixel_data_)) {
- CHECK_EQ(render_tree::kAlphaFormatPremultiplied, alpha_format);
+ CHECK(alpha_format == render_tree::kAlphaFormatPremultiplied ||
+ alpha_format == render_tree::kAlphaFormatOpaque);
CHECK(SbBlitterIsPixelDataValid(pixel_data_));
}
@@ -63,6 +64,9 @@
surface_ = SbBlitterCreateSurfaceFromPixelData(image_data->device(),
image_data->TakePixelData());
CHECK(SbBlitterIsSurfaceValid(surface_));
+
+ is_opaque_ = image_data->GetDescriptor().alpha_format ==
+ render_tree::kAlphaFormatOpaque;
}
bool SinglePlaneImage::EnsureInitialized() { return false; }
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.h b/src/cobalt/renderer/rasterizer/blitter/image.h
index 605cabe..949e3a2 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.h
+++ b/src/cobalt/renderer/rasterizer/blitter/image.h
@@ -84,12 +84,16 @@
// surface into a SkBitmap.
const SkBitmap& GetBitmap() const OVERRIDE;
+ bool IsOpaque() const OVERRIDE { return is_opaque_; }
+
private:
~SinglePlaneImage() OVERRIDE;
math::Size size_;
SbBlitterSurface surface_;
+ bool is_opaque_;
+
// This field is populated when GetBitmap() is called for the first time, and
// after that is never modified.
mutable base::optional<SkBitmap> bitmap_;
diff --git a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
index 8c75fd1..07f6947 100644
--- a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
@@ -38,6 +38,7 @@
'type': 'static_library',
'sources': [
+ 'cached_software_rasterizer.cc',
'cobalt_blitter_conversions.cc',
'hardware_rasterizer.cc',
'image.cc',
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_state.h b/src/cobalt/renderer/rasterizer/blitter/render_state.h
index 5b6f683..8b05887 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_state.h
+++ b/src/cobalt/renderer/rasterizer/blitter/render_state.h
@@ -118,11 +118,26 @@
const BoundsStack& bounds_stack)
: render_target(render_target),
transform(transform),
- bounds_stack(bounds_stack) {}
+ bounds_stack(bounds_stack),
+ opacity(1.0f)
+#if defined(ENABLE_DEBUG_CONSOLE)
+ ,
+ highlight_software_draws(false)
+#endif
+ {
+ }
SbBlitterRenderTarget render_target;
Transform transform;
BoundsStack bounds_stack;
+ float opacity;
+
+#if defined(ENABLE_DEBUG_CONSOLE)
+ // If true, all software rasterization results are replaced with a green
+ // fill rectangle. Thus, if the software cache is used, one will see a flash
+ // of green every time something is registered with the cache.
+ bool highlight_software_draws;
+#endif // defined(ENABLE_DEBUG_CONSOLE)
};
} // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
index d0f5377..8726b2e 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
@@ -28,9 +28,6 @@
#include "cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h"
#include "cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h"
#include "starboard/blitter.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkImageInfo.h"
#if SB_HAS(BLITTER)
@@ -52,18 +49,17 @@
RenderTreeNodeVisitor::RenderTreeNodeVisitor(
SbBlitterDevice device, SbBlitterContext context,
- const RenderState& render_state,
- skia::SoftwareRasterizer* software_rasterizer,
- ScratchSurfaceCache* scratch_surface_cache,
+ const RenderState& render_state, ScratchSurfaceCache* scratch_surface_cache,
SurfaceCacheDelegate* surface_cache_delegate,
- common::SurfaceCache* surface_cache)
- : software_rasterizer_(software_rasterizer),
- device_(device),
+ common::SurfaceCache* surface_cache,
+ CachedSoftwareRasterizer* software_surface_cache)
+ : device_(device),
context_(context),
render_state_(render_state),
scratch_surface_cache_(scratch_surface_cache),
surface_cache_delegate_(surface_cache_delegate),
- surface_cache_(surface_cache) {
+ surface_cache_(surface_cache),
+ software_surface_cache_(software_surface_cache) {
DCHECK_EQ(surface_cache_delegate_ == NULL, surface_cache_ == NULL);
if (surface_cache_delegate_) {
// Update our surface cache delegate to point to this render tree node
@@ -77,9 +73,6 @@
void RenderTreeNodeVisitor::Visit(
render_tree::CompositionNode* composition_node) {
- common::SurfaceCache::Block cache_block(surface_cache_, composition_node);
- if (cache_block.Cached()) return;
-
const render_tree::CompositionNode::Children& children =
composition_node->data().children();
@@ -99,6 +92,27 @@
render_state_.transform.ApplyOffset(-composition_node->data().offset());
}
+namespace {
+bool SourceCanRenderWithOpacity(render_tree::Node* source) {
+ if (source->GetTypeId() == base::GetTypeId<render_tree::ImageNode>() ||
+ source->GetTypeId() == base::GetTypeId<render_tree::RectNode>()) {
+ return true;
+ } else if (source->GetTypeId() ==
+ base::GetTypeId<render_tree::CompositionNode>()) {
+ // If we are a composition of valid sources, then we also allow
+ // rendering through a viewport here.
+ render_tree::CompositionNode* composition_node =
+ base::polymorphic_downcast<render_tree::CompositionNode*>(source);
+ typedef render_tree::CompositionNode::Children Children;
+ const Children& children = composition_node->data().children();
+ if (children.size() == 1 && SourceCanRenderWithOpacity(children[0].get())) {
+ return true;
+ }
+ }
+ return false;
+}
+} // namespace
+
void RenderTreeNodeVisitor::Visit(render_tree::FilterNode* filter_node) {
if (filter_node->data().blur_filter) {
// The Starboard Blitter API does not support blur filters, so we fallback
@@ -135,6 +149,16 @@
// we know that opacity is in the range (0, 1), exclusive.
float opacity = filter_node->data().opacity_filter->opacity();
+ if (SourceCanRenderWithOpacity(source)) {
+ float original_opacity = render_state_.opacity;
+ render_state_.opacity *= opacity;
+
+ source->Accept(this);
+
+ render_state_.opacity = original_opacity;
+ return;
+ }
+
// Render our source subtree to an offscreen surface, and then we will
// re-render it to our main render target with an alpha value applied to it.
scoped_ptr<OffscreenRender> offscreen_render =
@@ -202,8 +226,18 @@
-local_matrix.Get(1, 2) * image_size.height()));
// Render the image.
- SbBlitterSetBlending(context_, true);
- SbBlitterSetModulateBlitsWithColor(context_, false);
+ if (render_state_.opacity < 1.0f) {
+ SbBlitterSetBlending(context_, true);
+ SbBlitterSetModulateBlitsWithColor(context_, true);
+ SbBlitterSetColor(
+ context_,
+ SbBlitterColorFromRGBA(255, 255, 255,
+ static_cast<int>(255 * render_state_.opacity)));
+ } else {
+ SbBlitterSetBlending(context_, !skia_image->IsOpaque());
+ SbBlitterSetModulateBlitsWithColor(context_, false);
+ }
+
SbBlitterBlitRectToRectTiled(
context_, blitter_image->surface(),
RectFToBlitterRect(local_transform.TransformRect(RectF(image_size))),
@@ -213,15 +247,12 @@
void RenderTreeNodeVisitor::Visit(
render_tree::MatrixTransformNode* matrix_transform_node) {
- common::SurfaceCache::Block cache_block(surface_cache_,
- matrix_transform_node);
- if (cache_block.Cached()) return;
-
const Matrix3F& transform = matrix_transform_node->data().transform;
- if (transform.Get(1, 0) != 0 || transform.Get(0, 1) != 0) {
- // The Starboard Blitter API does not support rotations/shears, so we must
- // fallback to software in order to render the entire subtree.
+ if (transform.Get(1, 0) != 0 || transform.Get(0, 1) != 0 ||
+ transform.Get(0, 0) < 0 || transform.Get(1, 1) < 0) {
+ // The Starboard Blitter API does not support rotations/shears/flips, so we
+ // must fallback to software in order to render the entire subtree.
RenderWithSoftwareRenderer(matrix_transform_node);
return;
}
@@ -277,8 +308,14 @@
void RenderRectNodeBorder(SbBlitterContext context, ColorRGBA color, float left,
float right, float top, float bottom,
const RectF& rect) {
- SbBlitterSetColor(context, RenderTreeToBlitterColor(color));
- SbBlitterSetBlending(context, true);
+ SbBlitterColor blitter_color = RenderTreeToBlitterColor(color);
+ SbBlitterSetColor(context, blitter_color);
+
+ if (SbBlitterAFromColor(blitter_color) < 255) {
+ SbBlitterSetBlending(context, true);
+ } else {
+ SbBlitterSetBlending(context, false);
+ }
// We draw four rectangles, one for each border edge. They have the following
// layout:
@@ -342,13 +379,16 @@
// Render the solid color fill, if a brush exists.
if (rect_node->data().background_brush) {
- SbBlitterSetBlending(context_, true);
-
SolidColorBrush* solid_color_brush =
base::polymorphic_downcast<SolidColorBrush*>(
rect_node->data().background_brush.get());
ColorRGBA color = solid_color_brush->color();
+ if (render_state_.opacity < 1.0f) {
+ color.set_a(color.a() * render_state_.opacity);
+ }
+
+ SbBlitterSetBlending(context_, color.a() < 1.0f);
SbBlitterSetColor(context_, RenderTreeToBlitterColor(color));
SbBlitterFillRect(context_, RectFToBlitterRect(transformed_rect));
@@ -370,8 +410,12 @@
float bottom_width =
border.bottom.width * render_state_.transform.scale().y();
- RenderRectNodeBorder(context_, border.left.color, left_width, right_width,
- top_width, bottom_width, transformed_rect);
+ ColorRGBA color = border.left.color;
+ if (render_state_.opacity < 1.0f) {
+ color.set_a(color.a() * render_state_.opacity);
+ }
+ RenderRectNodeBorder(context_, color, left_width, right_width, top_width,
+ bottom_width, transformed_rect);
}
}
}
@@ -387,82 +431,51 @@
void RenderTreeNodeVisitor::RenderWithSoftwareRenderer(
render_tree::Node* node) {
- common::SurfaceCache::Block cache_block(surface_cache_, node);
- if (cache_block.Cached()) return;
-
- common::OffscreenRenderCoordinateMapping coord_mapping =
- common::GetOffscreenRenderCoordinateMapping(
- node->GetBounds(), render_state_.transform.ToMatrix(),
- render_state_.bounds_stack.Top());
- if (coord_mapping.output_bounds.IsEmpty()) {
- // There's nothing to render if the bounds are 0.
+ CachedSoftwareRasterizer::SurfaceReference software_surface_reference(
+ software_surface_cache_, node, render_state_.transform);
+ CachedSoftwareRasterizer::Surface software_surface =
+ software_surface_reference.surface();
+ if (!SbBlitterIsSurfaceValid(software_surface.surface)) {
return;
}
- DCHECK_GE(0.001f, std::abs(1.0f -
- render_state_.transform.scale().x() *
- coord_mapping.output_post_scale.x()));
- DCHECK_GE(0.001f, std::abs(1.0f -
- render_state_.transform.scale().y() *
- coord_mapping.output_post_scale.y()));
+ Transform apply_transform(render_state_.transform);
+ apply_transform.ApplyScale(software_surface.coord_mapping.output_post_scale);
+ math::RectF output_rectf = apply_transform.TransformRect(
+ software_surface.coord_mapping.output_bounds);
+ // We can simulate a "pre-multiply" by translation by offsetting the final
+ // output rectangle by the pre-translate, effectively resulting in the
+ // translation being applied last, as intended.
+ output_rectf.Offset(software_surface.coord_mapping.output_pre_translate);
+ SbBlitterRect output_blitter_rect = RectFToBlitterRect(output_rectf);
- SkImageInfo output_image_info = SkImageInfo::MakeN32(
- coord_mapping.output_bounds.width(), coord_mapping.output_bounds.height(),
- kPremul_SkAlphaType);
-
- // Allocate the pixels for the output image.
- SbBlitterPixelDataFormat blitter_pixel_data_format =
- SkiaToBlitterPixelFormat(output_image_info.colorType());
- DCHECK(SbBlitterIsPixelFormatSupportedByPixelData(device_,
- blitter_pixel_data_format));
- SbBlitterPixelData pixel_data = SbBlitterCreatePixelData(
- device_, coord_mapping.output_bounds.width(),
- coord_mapping.output_bounds.height(), blitter_pixel_data_format);
- CHECK(SbBlitterIsPixelDataValid(pixel_data));
-
- SkBitmap bitmap;
- bitmap.installPixels(output_image_info,
- SbBlitterGetPixelDataPointer(pixel_data),
- SbBlitterGetPixelDataPitchInBytes(pixel_data));
-
- // Setup our Skia canvas that we will be using as the target for all CPU Skia
- // output.
- SkCanvas canvas(bitmap);
- canvas.clear(SkColorSetARGB(0, 0, 0, 0));
-
- Transform sub_render_transform(coord_mapping.sub_render_transform);
-
- // Now setup our canvas so that the render tree will be rendered to the top
- // left corner instead of at node->GetBounds().origin().
- canvas.translate(sub_render_transform.translate().x(),
- sub_render_transform.translate().y());
- // And finally set the scale on our target canvas to match that of the current
- // |render_state_.transform|.
- canvas.scale(sub_render_transform.scale().x(),
- sub_render_transform.scale().y());
-
- // Use the Skia software rasterizer to render our subtree.
- software_rasterizer_->Submit(node, &canvas);
-
- // Create a surface out of the now populated pixel data.
- SbBlitterSurface surface =
- SbBlitterCreateSurfaceFromPixelData(device_, pixel_data);
-
- math::RectF output_rect = coord_mapping.output_bounds;
- output_rect.Offset(coord_mapping.output_pre_translate);
- output_rect.Offset(render_state_.transform.translate());
-
- // Finally blit the resulting surface to our actual render target.
SbBlitterSetBlending(context_, true);
- SbBlitterSetModulateBlitsWithColor(context_, false);
- SbBlitterBlitRectToRect(
- context_, surface,
- SbBlitterMakeRect(0, 0, coord_mapping.output_bounds.width(),
- coord_mapping.output_bounds.height()),
- RectFToBlitterRect(output_rect));
- // Clean up our temporary surface.
- SbBlitterDestroySurface(surface);
+ if (render_state_.opacity < 1.0f) {
+ SbBlitterSetModulateBlitsWithColor(context_, true);
+ SbBlitterSetColor(
+ context_,
+ SbBlitterColorFromRGBA(255, 255, 255,
+ static_cast<int>(255 * render_state_.opacity)));
+ } else {
+ SbBlitterSetModulateBlitsWithColor(context_, false);
+ }
+
+// Blit the software rasterized surface to our actual render target.
+#if defined(ENABLE_DEBUG_CONSOLE)
+ if (render_state_.highlight_software_draws && software_surface.created) {
+ SbBlitterSetColor(context_, SbBlitterColorFromRGBA(0, 255, 0, 255));
+ SbBlitterFillRect(context_, output_blitter_rect);
+ } else // NOLINT(readability/braces)
+#endif // defined(ENABLE_DEBUG_CONSOLE)
+ {
+ SbBlitterBlitRectToRect(
+ context_, software_surface.surface,
+ SbBlitterMakeRect(
+ 0, 0, software_surface.coord_mapping.output_bounds.width(),
+ software_surface.coord_mapping.output_bounds.height()),
+ output_blitter_rect);
+ }
}
scoped_ptr<RenderTreeNodeVisitor::OffscreenRender>
@@ -496,8 +509,8 @@
RenderState(
render_target, Transform(coord_mapping.sub_render_transform),
BoundsStack(context_, Rect(coord_mapping.output_bounds.size()))),
- software_rasterizer_, scratch_surface_cache_, surface_cache_delegate_,
- surface_cache_);
+ scratch_surface_cache_, surface_cache_delegate_, surface_cache_,
+ software_surface_cache_);
node->Accept(&sub_visitor);
// Restore our original render target.
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
index 4677308..4d9e0ee 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
@@ -32,11 +32,11 @@
#include "cobalt/render_tree/rect_node.h"
#include "cobalt/render_tree/rect_shadow_node.h"
#include "cobalt/render_tree/text_node.h"
+#include "cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h"
#include "cobalt/renderer/rasterizer/blitter/render_state.h"
#include "cobalt/renderer/rasterizer/blitter/scratch_surface_cache.h"
#include "cobalt/renderer/rasterizer/blitter/surface_cache_delegate.h"
#include "cobalt/renderer/rasterizer/common/surface_cache.h"
-#include "cobalt/renderer/rasterizer/skia/software_rasterizer.h"
#include "starboard/blitter.h"
@@ -62,10 +62,10 @@
public:
RenderTreeNodeVisitor(SbBlitterDevice device, SbBlitterContext context,
const RenderState& render_state,
- skia::SoftwareRasterizer* software_rasterizer,
ScratchSurfaceCache* scratch_surface_cache,
SurfaceCacheDelegate* surface_cache_delegate,
- common::SurfaceCache* surface_cache);
+ common::SurfaceCache* surface_cache,
+ CachedSoftwareRasterizer* software_surface_cache);
void Visit(render_tree::animations::AnimateNode* animate_node) OVERRIDE {
NOTREACHED();
@@ -99,10 +99,6 @@
};
scoped_ptr<OffscreenRender> RenderToOffscreenSurface(render_tree::Node* node);
- // We maintain an instance of a software skia rasterizer which is used to
- // render anything that we cannot render via the Blitter API directly.
- skia::SoftwareRasterizer* software_rasterizer_;
-
SbBlitterDevice device_;
SbBlitterContext context_;
@@ -118,6 +114,10 @@
base::optional<SurfaceCacheDelegate::ScopedContext>
surface_cache_scoped_context_;
+ // We fallback to software rasterization in order to render anything that we
+ // cannot render via the Blitter API directly. We cache the results.
+ CachedSoftwareRasterizer* software_surface_cache_;
+
DISALLOW_COPY_AND_ASSIGN(RenderTreeNodeVisitor);
};
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
index ae7cbd9..f518c52 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -48,7 +48,8 @@
bool ResourceProvider::AlphaFormatSupported(
render_tree::AlphaFormat alpha_format) {
- return alpha_format == render_tree::kAlphaFormatPremultiplied;
+ return alpha_format == render_tree::kAlphaFormatPremultiplied ||
+ alpha_format == render_tree::kAlphaFormatOpaque;
}
scoped_ptr<render_tree::ImageData> ResourceProvider::AllocateImageData(
diff --git a/src/cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h b/src/cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h
index 4c114b1..c55f7cc 100644
--- a/src/cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h
+++ b/src/cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h
@@ -35,6 +35,9 @@
namespace common {
struct OffscreenRenderCoordinateMapping {
+ OffscreenRenderCoordinateMapping()
+ : sub_render_transform(math::Matrix3F::Zeros()) {}
+
OffscreenRenderCoordinateMapping(const math::Rect& output_bounds,
const math::Vector2dF& output_pre_translate,
const math::Vector2dF& output_post_scale,
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index 6f821ed..aaadf4b 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -436,15 +436,21 @@
return resource_provider->CreateImage(image_data.Pass());
}
-scoped_refptr<Image> CreateColoredCheckersImage(
- ResourceProvider* resource_provider, const SizeF& dimensions) {
+scoped_refptr<Image> CreateColoredCheckersImageForAlphaFormat(
+ ResourceProvider* resource_provider, const SizeF& dimensions,
+ render_tree::AlphaFormat alpha_format) {
std::vector<RGBAWord> row_colors;
row_colors.push_back(RGBAWord(255, 0, 0, 255));
row_colors.push_back(RGBAWord(0, 255, 0, 255));
row_colors.push_back(RGBAWord(0, 0, 255, 255));
return CreateAdditiveColorGridImage(resource_provider, dimensions, row_colors,
- row_colors,
- render_tree::kAlphaFormatPremultiplied);
+ row_colors, alpha_format);
+}
+
+scoped_refptr<Image> CreateColoredCheckersImage(
+ ResourceProvider* resource_provider, const SizeF& dimensions) {
+ return CreateColoredCheckersImageForAlphaFormat(
+ resource_provider, dimensions, render_tree::kAlphaFormatPremultiplied);
}
} // namespace
@@ -457,6 +463,24 @@
TestTree(new ImageNode(image));
}
+TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatNone) {
+ scoped_refptr<Image> image = CreateColoredCheckersImageForAlphaFormat(
+ GetResourceProvider(), output_surface_size(),
+ render_tree::kAlphaFormatOpaque);
+
+ TestTree(new ImageNode(image));
+}
+
+TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners) {
+ scoped_refptr<Image> image = CreateColoredCheckersImageForAlphaFormat(
+ GetResourceProvider(), output_surface_size(),
+ render_tree::kAlphaFormatOpaque);
+
+ TestTree(new FilterNode(
+ ViewportFilter(RectF(25, 25, 150, 150), RoundedCorners(75, 75)),
+ new ImageNode(image)));
+}
+
TEST_F(PixelTest, SingleRGBAImageLargerThanRenderTarget) {
// Tests that rasterizing images that are larger than the render target
// work as expected (e.g. they are cropped).
diff --git a/src/cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.cc b/src/cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.cc
index 1ca29c1..4b6e10c 100644
--- a/src/cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.cc
+++ b/src/cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.cc
@@ -49,6 +49,8 @@
return kPremul_SkAlphaType;
case render_tree::kAlphaFormatUnpremultiplied:
return kUnpremul_SkAlphaType;
+ case render_tree::kAlphaFormatOpaque:
+ return kOpaque_SkAlphaType;
default: DLOG(FATAL) << "Unknown render tree alpha format!";
}
return kUnpremul_SkAlphaType;
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_image.cc b/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
index 0300dd6..2c8db59 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
@@ -164,7 +164,9 @@
scoped_ptr<HardwareImageData> image_data,
backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context,
MessageLoop* rasterizer_message_loop)
- : size_(image_data->GetDescriptor().size),
+ : is_opaque_(image_data->GetDescriptor().alpha_format ==
+ render_tree::kAlphaFormatOpaque),
+ size_(image_data->GetDescriptor().size),
rasterizer_message_loop_(rasterizer_message_loop) {
TRACE_EVENT0("cobalt::renderer",
"HardwareFrontendImage::HardwareFrontendImage()");
@@ -180,7 +182,8 @@
intptr_t offset, const render_tree::ImageDataDescriptor& descriptor,
backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context,
MessageLoop* rasterizer_message_loop)
- : size_(descriptor.size),
+ : is_opaque_(descriptor.alpha_format == render_tree::kAlphaFormatOpaque),
+ size_(descriptor.size),
rasterizer_message_loop_(rasterizer_message_loop) {
TRACE_EVENT0("cobalt::renderer",
"HardwareFrontendImage::HardwareFrontendImage()");
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_image.h b/src/cobalt/renderer/rasterizer/skia/hardware_image.h
index 1590567..bb44b1a 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_image.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_image.h
@@ -102,6 +102,8 @@
bool EnsureInitialized() OVERRIDE;
+ bool IsOpaque() const OVERRIDE { return is_opaque_; }
+
private:
~HardwareFrontendImage() OVERRIDE;
@@ -116,6 +118,10 @@
intptr_t offset, const render_tree::ImageDataDescriptor& descriptor,
backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context);
+ // Track if we have any alpha or not, which can enable optimizations in the
+ // case that alpha is not present.
+ bool is_opaque_;
+
// We shadow the image dimensions so they can be quickly looked up from just
// the frontend image object.
const math::Size size_;
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index 1f97820..a17b893 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -68,7 +68,8 @@
bool HardwareResourceProvider::AlphaFormatSupported(
render_tree::AlphaFormat alpha_format) {
- return alpha_format == render_tree::kAlphaFormatPremultiplied;
+ return alpha_format == render_tree::kAlphaFormatPremultiplied ||
+ alpha_format == render_tree::kAlphaFormatOpaque;
}
scoped_ptr<ImageData> HardwareResourceProvider::AllocateImageData(
@@ -96,7 +97,8 @@
const render_tree::ImageDataDescriptor& descriptor =
skia_hardware_source_data->GetDescriptor();
- DCHECK_EQ(render_tree::kAlphaFormatPremultiplied, descriptor.alpha_format);
+ DCHECK(descriptor.alpha_format == render_tree::kAlphaFormatPremultiplied ||
+ descriptor.alpha_format == render_tree::kAlphaFormatOpaque);
#if defined(COBALT_BUILD_TYPE_DEBUG)
Image::DCheckForPremultipliedAlpha(descriptor.size, descriptor.pitch_in_bytes,
descriptor.pixel_format,
diff --git a/src/cobalt/renderer/rasterizer/skia/image.h b/src/cobalt/renderer/rasterizer/skia/image.h
index bb696aa..da4a5dc 100644
--- a/src/cobalt/renderer/rasterizer/skia/image.h
+++ b/src/cobalt/renderer/rasterizer/skia/image.h
@@ -79,6 +79,9 @@
base::TypeId GetTypeId() const OVERRIDE {
return base::GetTypeId<MultiPlaneImage>();
}
+
+ // All currently supported MultiPlaneImage formats do not feature alpha.
+ bool IsOpaque() const OVERRIDE { return true; }
};
} // namespace skia
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 e7225dc..06ed04b 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -505,12 +505,14 @@
}
SkPaint CreateSkPaintForImageRendering(
- const RenderTreeNodeVisitorDrawState& draw_state) {
+ const RenderTreeNodeVisitorDrawState& draw_state, bool is_opaque) {
SkPaint paint;
paint.setFilterLevel(SkPaint::kLow_FilterLevel);
if (draw_state.opacity < 1.0f) {
paint.setAlpha(draw_state.opacity * 255);
+ } else if (is_opaque) {
+ paint.setXfermodeMode(SkXfermode::kSrc_Mode);
}
return paint;
@@ -520,7 +522,8 @@
RenderTreeNodeVisitorDrawState* draw_state,
const math::RectF& destination_rect,
const math::Matrix3F* local_transform) {
- SkPaint paint = CreateSkPaintForImageRendering(*draw_state);
+ SkPaint paint = CreateSkPaintForImageRendering(
+ *draw_state, single_plane_image->IsOpaque());
// In the most frequent by far case where the normalized transformed image
// texture coordinates lie within the unit square, then we must ensure NOT
@@ -608,7 +611,8 @@
}
}
- SkPaint paint = CreateSkPaintForImageRendering(*draw_state);
+ SkPaint paint = CreateSkPaintForImageRendering(*draw_state,
+ multi_plane_image->IsOpaque());
paint.setShader(yuv2rgb_shader);
draw_state->render_target->drawRect(CobaltRectFToSkiaRect(destination_rect),
paint);
diff --git a/src/cobalt/renderer/rasterizer/skia/software_image.cc b/src/cobalt/renderer/rasterizer/skia/software_image.cc
index 5f68cde..9c05a86 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_image.cc
+++ b/src/cobalt/renderer/rasterizer/skia/software_image.cc
@@ -79,6 +79,7 @@
void SoftwareImage::Initialize(
uint8_t* source_data, const render_tree::ImageDataDescriptor& descriptor) {
+ is_opaque_ = (descriptor.alpha_format == render_tree::kAlphaFormatOpaque);
SkAlphaType skia_alpha_format =
RenderTreeAlphaFormatToSkia(descriptor.alpha_format);
DCHECK_EQ(kPremul_SkAlphaType, skia_alpha_format);
diff --git a/src/cobalt/renderer/rasterizer/skia/software_image.h b/src/cobalt/renderer/rasterizer/skia/software_image.h
index 63afe6a..d01f212 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_image.h
+++ b/src/cobalt/renderer/rasterizer/skia/software_image.h
@@ -57,6 +57,8 @@
bool EnsureInitialized() OVERRIDE { return false; }
+ bool IsOpaque() const OVERRIDE { return is_opaque_; }
+
private:
void Initialize(uint8_t* source_data,
const render_tree::ImageDataDescriptor& descriptor);
@@ -64,6 +66,7 @@
scoped_array<uint8_t> owned_pixel_data_;
SkBitmap bitmap_;
math::Size size_;
+ bool is_opaque_;
};
class SoftwareRawImageMemory : public render_tree::RawImageMemory {
@@ -97,6 +100,10 @@
bool EnsureInitialized() OVERRIDE { return false; }
+ // Currently, all supported multiplane images (e.g. mostly YUV) do not
+ // support alpha, so multiplane images will always be opaque.
+ bool IsOpaque() const OVERRIDE { return true; }
+
private:
math::Size size_;
render_tree::MultiPlaneImageFormat format_;
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
index be05ccf..d3af607 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
@@ -47,7 +47,8 @@
bool SoftwareResourceProvider::AlphaFormatSupported(
render_tree::AlphaFormat alpha_format) {
- return alpha_format == render_tree::kAlphaFormatPremultiplied;
+ return alpha_format == render_tree::kAlphaFormatPremultiplied ||
+ alpha_format == render_tree::kAlphaFormatOpaque;
}
scoped_ptr<ImageData> SoftwareResourceProvider::AllocateImageData(
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNone-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNone-expected.png
new file mode 100644
index 0000000..b8638eb
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNone-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners-expected.png
new file mode 100644
index 0000000..2076087
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/renderer_module.h b/src/cobalt/renderer/renderer_module.h
index 44099d6..96226ca 100644
--- a/src/cobalt/renderer/renderer_module.h
+++ b/src/cobalt/renderer/renderer_module.h
@@ -55,6 +55,12 @@
// using the hardware-accelerated Skia rasterizer.
int skia_cache_size_in_bytes;
+ // Only relevant if you are using the Blitter API.
+ // Determines the capacity of the software surface cache, which is used to
+ // cache all surfaces that are rendered via a software rasterizer to avoid
+ // re-rendering them.
+ int software_surface_cache_size_in_bytes;
+
// Determines the capacity of the surface cache. The surface cache tracks
// which render tree nodes are being re-used across frames and stores the
// nodes that are most CPU-expensive to render into surfaces.
diff --git a/src/cobalt/renderer/renderer_module_default_options_starboard.cc b/src/cobalt/renderer/renderer_module_default_options_starboard.cc
index 7a00bd5..7ee0e3e 100644
--- a/src/cobalt/renderer/renderer_module_default_options_starboard.cc
+++ b/src/cobalt/renderer/renderer_module_default_options_starboard.cc
@@ -54,7 +54,8 @@
return scoped_ptr<rasterizer::Rasterizer>(
new rasterizer::blitter::HardwareRasterizer(
graphics_context, options.scratch_surface_cache_size_in_bytes,
- options.surface_cache_size_in_bytes));
+ options.surface_cache_size_in_bytes,
+ options.software_surface_cache_size_in_bytes));
#endif // COBALT_FORCE_SOFTWARE_RASTERIZER
#else
#error "Either GLES2 or the Starboard Blitter API must be available."
@@ -70,6 +71,8 @@
skia_cache_size_in_bytes = COBALT_SKIA_CACHE_SIZE_IN_BYTES;
scratch_surface_cache_size_in_bytes =
COBALT_SCRATCH_SURFACE_CACHE_SIZE_IN_BYTES;
+ software_surface_cache_size_in_bytes =
+ COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES;
// If there is no need to frequently flip the display buffer, then enable
// support for an optimization where the scene is not re-rasterized each frame
diff --git a/src/cobalt/renderer/renderer_parameters_setup.gypi b/src/cobalt/renderer/renderer_parameters_setup.gypi
index 95724f6..ed0313b 100644
--- a/src/cobalt/renderer/renderer_parameters_setup.gypi
+++ b/src/cobalt/renderer/renderer_parameters_setup.gypi
@@ -17,6 +17,7 @@
'COBALT_SKIA_CACHE_SIZE_IN_BYTES=<(skia_cache_size_in_bytes)',
'COBALT_SCRATCH_SURFACE_CACHE_SIZE_IN_BYTES=<(scratch_surface_cache_size_in_bytes)',
'COBALT_SURFACE_CACHE_SIZE_IN_BYTES=<(surface_cache_size_in_bytes)',
+ 'COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES=<(software_surface_cache_size_in_bytes)',
],
'conditions': [
['rasterizer_type == "software"', {
diff --git a/src/cobalt/renderer/test/png_utils/png_decode.cc b/src/cobalt/renderer/test/png_utils/png_decode.cc
index 4bf217b..b65618b 100644
--- a/src/cobalt/renderer/test/png_utils/png_decode.cc
+++ b/src/cobalt/renderer/test/png_utils/png_decode.cc
@@ -209,6 +209,9 @@
}
}
} break;
+ case render_tree::kAlphaFormatOpaque: {
+ NOTREACHED() << "kAlphaFormatOpaque not supported.";
+ } break;
}
// End transformations. Get the updated info, and then verify.
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.cc b/src/cobalt/script/mozjs/mozjs_global_environment.cc
index 1cf31fd..89ddeb2 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.cc
@@ -230,10 +230,8 @@
JSAutoCompartment auto_compartment(context_, global_object_proxy_);
JSExceptionState* previous_exception_state = JS_SaveExceptionState(context_);
JS::RootedValue result_value(context_);
- if (!EvaluateScriptInternal(source_code, &result_value)) {
- return false;
- }
- if (out_opaque_handle) {
+ bool success = EvaluateScriptInternal(source_code, &result_value);
+ if (success && out_opaque_handle) {
JS::RootedObject js_object(context_);
JS_ValueToObject(context_, result_value, js_object.address());
MozjsObjectHandleHolder mozjs_object_holder(js_object, context_,
@@ -241,7 +239,7 @@
out_opaque_handle->emplace(owning_object.get(), mozjs_object_holder);
}
JS_RestoreExceptionState(context_, previous_exception_state);
- return true;
+ return success;
}
bool MozjsGlobalEnvironment::EvaluateScriptInternal(
diff --git a/src/cobalt/speech/endpointer/endpointer.cc b/src/cobalt/speech/endpointer/endpointer.cc
index b0c09e9..0b19a22 100644
--- a/src/cobalt/speech/endpointer/endpointer.cc
+++ b/src/cobalt/speech/endpointer/endpointer.cc
@@ -9,7 +9,10 @@
using base::Time;
namespace {
-const int kFrameRate = 50; // 1 frame = 20ms of audio.
+// Only send |kFrameSize| of audio data to energy endpointer each time.
+// It should be smaller than the samples of audio bus which is passed in
+// |ProcessAudio|.
+const int kFrameSize = 160;
}
namespace cobalt {
@@ -20,11 +23,9 @@
speech_input_complete_silence_length_us_(-1),
audio_frame_time_us_(0),
sample_rate_(sample_rate),
- frame_size_(0) {
+ frame_rate_(sample_rate / kFrameSize) {
Reset();
- frame_size_ = static_cast<int>(sample_rate / static_cast<float>(kFrameRate));
-
speech_input_minimum_length_us_ =
static_cast<int64_t>(1.7 * Time::kMicrosecondsPerSecond);
speech_input_complete_silence_length_us_ =
@@ -36,8 +37,8 @@
// Set the default configuration for Push To Talk mode.
EnergyEndpointerParams ep_config;
- ep_config.set_frame_period(1.0f / static_cast<float>(kFrameRate));
- ep_config.set_frame_duration(1.0f / static_cast<float>(kFrameRate));
+ ep_config.set_frame_period(1.0f / static_cast<float>(frame_rate_));
+ ep_config.set_frame_duration(1.0f / static_cast<float>(frame_rate_));
ep_config.set_endpoint_margin(0.2f);
ep_config.set_onset_window(0.15f);
ep_config.set_speech_on_window(0.4f);
@@ -92,6 +93,8 @@
EpStatus Endpointer::ProcessAudio(
const ShellAudioBus& audio_bus, float* rms_out) {
DCHECK_EQ(audio_bus.channels(), 1);
+ DCHECK_LE(kFrameSize, static_cast<int>(audio_bus.frames()));
+
const size_t num_samples = audio_bus.frames();
const int16_t* audio_data = NULL;
@@ -111,19 +114,17 @@
EpStatus ep_status = EP_PRE_SPEECH;
- // Process the input data in blocks of frame_size_, dropping any incomplete
+ // Process the input data in blocks of kFrameSize, dropping any incomplete
// frames at the end (which is ok since typically the caller will be recording
// audio in multiples of our frame size).
int sample_index = 0;
- while (static_cast<size_t>(sample_index + frame_size_) <= num_samples) {
+ while (static_cast<size_t>(sample_index + kFrameSize) <= num_samples) {
// Have the endpointer process the frame.
- energy_endpointer_.ProcessAudioFrame(audio_frame_time_us_,
- audio_data + sample_index,
- frame_size_,
- rms_out);
- sample_index += frame_size_;
- audio_frame_time_us_ += (frame_size_ * Time::kMicrosecondsPerSecond) /
- sample_rate_;
+ energy_endpointer_.ProcessAudioFrame(
+ audio_frame_time_us_, audio_data + sample_index, kFrameSize, rms_out);
+ sample_index += kFrameSize;
+ audio_frame_time_us_ +=
+ (kFrameSize * Time::kMicrosecondsPerSecond) / sample_rate_;
// Get the status of the endpointer.
int64_t ep_time;
diff --git a/src/cobalt/speech/endpointer/endpointer.h b/src/cobalt/speech/endpointer/endpointer.h
index b8d46f5..f6c818f 100644
--- a/src/cobalt/speech/endpointer/endpointer.h
+++ b/src/cobalt/speech/endpointer/endpointer.h
@@ -100,6 +100,8 @@
return speech_input_complete_;
}
+ int sample_rate() const { return sample_rate_; }
+
// RMS background noise level in dB.
float NoiseLevelDb() const { return energy_endpointer_.GetNoiseLevelDb(); }
@@ -147,7 +149,8 @@
bool speech_input_complete_;
EnergyEndpointer energy_endpointer_;
int sample_rate_;
- int32_t frame_size_;
+ // 1 frame = (1 / frame_rate_) second of audio.
+ int frame_rate_;
};
} // namespace speech
diff --git a/src/cobalt/speech/endpointer_delegate.cc b/src/cobalt/speech/endpointer_delegate.cc
new file mode 100644
index 0000000..a3d0444
--- /dev/null
+++ b/src/cobalt/speech/endpointer_delegate.cc
@@ -0,0 +1,75 @@
+/*
+ * 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/speech/endpointer_delegate.h"
+
+#include "base/time.h"
+
+namespace cobalt {
+namespace speech {
+
+namespace {
+const int kEndPointerEstimationTimeInMillisecond = 300;
+// If used in noisy conditions, the endpointer should be started and run in the
+// EnvironmentEstimation mode, for at least 200ms, before switching to
+// UserInputMode.
+COMPILE_ASSERT(kEndPointerEstimationTimeInMillisecond >= 200,
+ in_environment_estimation_mode_for_at_least_200ms);
+} // namespace
+
+EndPointerDelegate::EndPointerDelegate(int sample_rate)
+ : endpointer_(sample_rate),
+ num_samples_recorded_(0),
+ is_first_time_sound_started_(false) {}
+
+EndPointerDelegate::~EndPointerDelegate() {}
+
+void EndPointerDelegate::Start() {
+ num_samples_recorded_ = 0;
+ is_first_time_sound_started_ = false;
+
+ endpointer_.StartSession();
+ endpointer_.SetEnvironmentEstimationMode();
+}
+
+void EndPointerDelegate::Stop() { endpointer_.EndSession(); }
+
+bool EndPointerDelegate::IsFirstTimeSoundStarted(
+ const ShellAudioBus& audio_bus) {
+ if (is_first_time_sound_started_) {
+ return false;
+ }
+
+ num_samples_recorded_ += static_cast<int>(audio_bus.frames());
+ if (endpointer_.IsEstimatingEnvironment() &&
+ num_samples_recorded_ * 1000 / endpointer_.sample_rate() >
+ kEndPointerEstimationTimeInMillisecond) {
+ // Switch to user input mode.
+ endpointer_.SetUserInputMode();
+ }
+
+ endpointer_.ProcessAudio(audio_bus, NULL);
+ int64_t ep_time;
+ EpStatus status = endpointer_.Status(&ep_time);
+ if (status == EP_POSSIBLE_ONSET) {
+ is_first_time_sound_started_ = true;
+ }
+
+ return is_first_time_sound_started_;
+}
+
+} // namespace speech
+} // namespace cobalt
diff --git a/src/cobalt/speech/endpointer_delegate.h b/src/cobalt/speech/endpointer_delegate.h
new file mode 100644
index 0000000..8a3aafa
--- /dev/null
+++ b/src/cobalt/speech/endpointer_delegate.h
@@ -0,0 +1,59 @@
+/*
+ * 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_SPEECH_ENDPOINTER_DELEGATE_H_
+#define COBALT_SPEECH_ENDPOINTER_DELEGATE_H_
+
+#include "cobalt/speech/endpointer/endpointer.h"
+#include "media/base/audio_bus.h"
+
+namespace cobalt {
+namespace speech {
+
+// Delegate of endpointer for detecting the first time sound started in one
+// speech session (from start speaking to end of speaking).
+class EndPointerDelegate {
+ public:
+ typedef ::media::ShellAudioBus ShellAudioBus;
+
+ explicit EndPointerDelegate(int sample_rate);
+ ~EndPointerDelegate();
+
+ // Start endpointer session for sound processing.
+ void Start();
+ // Stop endpointer session.
+ void Stop();
+
+ // Return true if it is the first time that the sound started.
+ bool IsFirstTimeSoundStarted(const ShellAudioBus& audio_bus);
+
+ private:
+ // Used for detecting sound start event.
+ Endpointer endpointer_;
+ // Used for recording the number of samples before notifying that it is the
+ // first time the sound started. The |endpointer_| should be started and run
+ // in the EnvironmentEstimation mode if used in noisy conditions for at least
+ // 200ms before switching to UserInputMode.
+ int num_samples_recorded_;
+
+ // Record if it is the first time that the sound started.
+ bool is_first_time_sound_started_;
+};
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // COBALT_SPEECH_ENDPOINTER_DELEGATE_H_
diff --git a/src/cobalt/speech/mic.h b/src/cobalt/speech/mic.h
deleted file mode 100644
index 506632a..0000000
--- a/src/cobalt/speech/mic.h
+++ /dev/null
@@ -1,70 +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_SPEECH_MIC_H_
-#define COBALT_SPEECH_MIC_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "media/base/shell_audio_bus.h"
-
-namespace cobalt {
-namespace speech {
-
-// An abstract class is used for interacting platform specific microphone.
-class Mic {
- public:
- typedef ::media::ShellAudioBus ShellAudioBus;
- typedef base::Callback<void(scoped_ptr<ShellAudioBus>)> DataReceivedCallback;
- typedef base::Callback<void(void)> CompletionCallback;
- typedef base::Callback<void(void)> ErrorCallback;
-
- virtual ~Mic() {}
-
- static scoped_ptr<Mic> Create(int sample_rate,
- const DataReceivedCallback& data_received,
- const CompletionCallback& completion,
- const ErrorCallback& error);
-
- // Multiple calls to Start/Stop are allowed, the implementation should take
- // care of multiple calls. The Start/Stop methods are required to be called in
- // the same thread.
- // Start microphone to receive voice.
- virtual void Start() = 0;
- // Stop microphone from receiving voice.
- virtual void Stop() = 0;
-
- protected:
- Mic(int sample_rate, const DataReceivedCallback& data_received,
- const CompletionCallback& completion, const ErrorCallback& error)
- : sample_rate_(sample_rate),
- data_received_callback_(data_received),
- completion_callback_(completion),
- error_callback_(error) {}
-
- int sample_rate_;
- const DataReceivedCallback data_received_callback_;
- const CompletionCallback completion_callback_;
- const ErrorCallback error_callback_;
-};
-
-std::string GetSpeechAPIKey();
-
-} // namespace speech
-} // namespace cobalt
-
-#endif // COBALT_SPEECH_MIC_H_
diff --git a/src/cobalt/speech/mic_linux.cc b/src/cobalt/speech/mic_linux.cc
deleted file mode 100644
index d7f6809..0000000
--- a/src/cobalt/speech/mic_linux.cc
+++ /dev/null
@@ -1,44 +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/speech/mic.h"
-
-namespace cobalt {
-namespace speech {
-
-class MicLinux : public Mic {
- public:
- MicLinux(int sample_rate, const DataReceivedCallback& data_received,
- const CompletionCallback& completion, const ErrorCallback& error)
- : Mic(sample_rate, data_received, completion, error) {}
-
- void Start() OVERRIDE { NOTIMPLEMENTED(); }
- void Stop() OVERRIDE { NOTIMPLEMENTED(); }
-};
-
-// static
-scoped_ptr<Mic> Mic::Create(int sample_rate,
- const DataReceivedCallback& data_received,
- const CompletionCallback& completion,
- const ErrorCallback& error) {
- return make_scoped_ptr<Mic>(
- new MicLinux(sample_rate, data_received, completion, error));
-}
-
-std::string GetSpeechAPIKey() { return ""; }
-
-} // namespace speech
-} // namespace cobalt
diff --git a/src/cobalt/speech/mic_starboard.cc b/src/cobalt/speech/mic_starboard.cc
deleted file mode 100644
index c818dab..0000000
--- a/src/cobalt/speech/mic_starboard.cc
+++ /dev/null
@@ -1,44 +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/speech/mic.h"
-
-namespace cobalt {
-namespace speech {
-
-class MicStarboard : public Mic {
- public:
- MicStarboard(int sample_rate, const DataReceivedCallback& data_received,
- const CompletionCallback& completion, const ErrorCallback& error)
- : Mic(sample_rate, data_received, completion, error) {}
-
- void Start() OVERRIDE { NOTIMPLEMENTED(); }
- void Stop() OVERRIDE { NOTIMPLEMENTED(); }
-};
-
-// static
-scoped_ptr<Mic> Mic::Create(int sample_rate,
- const DataReceivedCallback& data_received,
- const CompletionCallback& completion,
- const ErrorCallback& error) {
- return make_scoped_ptr<Mic>(
- new MicStarboard(sample_rate, data_received, completion, error));
-}
-
-std::string GetSpeechAPIKey() { return ""; }
-
-} // namespace speech
-} // namespace cobalt
diff --git a/src/cobalt/speech/mic_win.cc b/src/cobalt/speech/mic_win.cc
deleted file mode 100644
index 80c49fc..0000000
--- a/src/cobalt/speech/mic_win.cc
+++ /dev/null
@@ -1,44 +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/speech/mic.h"
-
-namespace cobalt {
-namespace speech {
-
-class MicWin : public Mic {
- public:
- MicWin(int sample_rate, const DataReceivedCallback& data_received,
- const CompletionCallback& completion, const ErrorCallback& error)
- : Mic(sample_rate, data_received, completion, error) {}
-
- void Start() OVERRIDE { NOTIMPLEMENTED(); }
- void Stop() OVERRIDE { NOTIMPLEMENTED(); }
-};
-
-// static
-scoped_ptr<Mic> Mic::Create(int sample_rate,
- const DataReceivedCallback& data_received,
- const CompletionCallback& completion,
- const ErrorCallback& error) {
- return make_scoped_ptr<Mic>(
- new MicWin(sample_rate, data_received, completion, error));
-}
-
-std::string GetSpeechAPIKey() { return ""; }
-
-} // namespace speech
-} // namespace cobalt
diff --git a/src/cobalt/speech/microphone.h b/src/cobalt/speech/microphone.h
new file mode 100644
index 0000000..3b9e6ae
--- /dev/null
+++ b/src/cobalt/speech/microphone.h
@@ -0,0 +1,52 @@
+/*
+ * 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_SPEECH_MICROPHONE_H_
+#define COBALT_SPEECH_MICROPHONE_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace cobalt {
+namespace speech {
+
+// An abstract class is used for interacting platform specific microphone.
+class Microphone {
+ public:
+ virtual ~Microphone() {}
+
+ // Opens microphone port and starts recording audio.
+ virtual bool Open() = 0;
+ // Reads audio data from microphone. The return value is the bytes that were
+ // read from microphone. Negative value indicates a read error. |data_size| is
+ // the requested read bytes.
+ virtual int Read(char* out_data, int data_size) = 0;
+ // Closes microphone port and stops recording audio.
+ virtual bool Close() = 0;
+ // Returns the minimum requested bytes per microphone read.
+ virtual int MinMicrophoneReadInBytes() = 0;
+ // Returns true if the microphone is valid.
+ virtual bool IsValid() = 0;
+
+ protected:
+ Microphone() {}
+};
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // COBALT_SPEECH_MICROPHONE_H_
diff --git a/src/cobalt/speech/microphone_fake.cc b/src/cobalt/speech/microphone_fake.cc
new file mode 100644
index 0000000..7ee601c
--- /dev/null
+++ b/src/cobalt/speech/microphone_fake.cc
@@ -0,0 +1,137 @@
+/*
+ * 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/speech/microphone_fake.h"
+
+#if defined(ENABLE_FAKE_MICROPHONE)
+
+#include <algorithm>
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/rand_util.h"
+#include "starboard/file.h"
+#include "starboard/memory.h"
+#include "starboard/time.h"
+
+namespace cobalt {
+namespace speech {
+
+namespace {
+const int kMaxBufferSize = 512 * 1024;
+const int kMinMicrophoneReadInBytes = 1024;
+// The possiblity of microphone creation failed is 1/20.
+const int kCreationRange = 20;
+// The possiblity of microphone open failed is 1/20.
+const int kOpenRange = 20;
+// The possiblity of microphone read failed is 1/200.
+const int kReadRange = 200;
+// The possiblity of microphone close failed is 1/20.
+const int kCloseRange = 20;
+const int kFailureNumber = 5;
+
+bool ShouldFail(int range) {
+ return base::RandGenerator(range) == kFailureNumber;
+}
+
+} // namespace
+
+MicrophoneFake::MicrophoneFake()
+ : Microphone(),
+ file_length_(-1),
+ read_index_(0),
+ is_valid_(!ShouldFail(kCreationRange)) {
+ if (!is_valid_) {
+ SB_DLOG(WARNING) << "Mocking microphone creation failed.";
+ return;
+ }
+
+ FilePath audio_files_path;
+ SB_CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &audio_files_path));
+ audio_files_path = audio_files_path.Append(FILE_PATH_LITERAL("cobalt"))
+ .Append(FILE_PATH_LITERAL("speech"))
+ .Append(FILE_PATH_LITERAL("testdata"));
+
+ file_util::FileEnumerator file_enumerator(audio_files_path,
+ false /* Not recursive */,
+ file_util::FileEnumerator::FILES);
+ for (FilePath next = file_enumerator.Next(); !next.empty();
+ next = file_enumerator.Next()) {
+ file_paths_.push_back(next);
+ }
+}
+
+bool MicrophoneFake::Open() {
+ if (ShouldFail(kOpenRange)) {
+ SB_DLOG(WARNING) << "Mocking microphone open failed.";
+ return false;
+ }
+
+ SB_DCHECK(file_paths_.size() != 0);
+ uint64 random_index = base::RandGenerator(file_paths_.size());
+ starboard::ScopedFile file(file_paths_[random_index].value().c_str(),
+ kSbFileOpenOnly | kSbFileRead, NULL, NULL);
+ SB_DCHECK(file.IsValid());
+ int file_buffer_size =
+ std::min(static_cast<int>(file.GetSize()), kMaxBufferSize);
+ SB_DCHECK(file_buffer_size > 0);
+ file_buffer_.reset(new char[file_buffer_size]);
+ int read_bytes = file.ReadAll(file_buffer_.get(), file_buffer_size);
+ if (read_bytes < 0) {
+ return false;
+ }
+
+ file_length_ = read_bytes;
+ return true;
+}
+
+int MicrophoneFake::Read(char* out_data, int data_size) {
+ if (ShouldFail(kReadRange)) {
+ SB_DLOG(WARNING) << "Mocking microphone read failed.";
+ return -1;
+ }
+
+ int copy_bytes = std::min(file_length_ - read_index_, data_size);
+ SbMemoryCopy(out_data, file_buffer_.get() + read_index_, copy_bytes);
+ read_index_ += copy_bytes;
+ if (read_index_ == file_length_) {
+ read_index_ = 0;
+ }
+
+ return copy_bytes;
+}
+
+bool MicrophoneFake::Close() {
+ file_buffer_.reset();
+ file_length_ = -1;
+ read_index_ = 0;
+
+ if (ShouldFail(kCloseRange)) {
+ SB_DLOG(WARNING) << "Mocking microphone close failed.";
+ return false;
+ }
+
+ return true;
+}
+
+int MicrophoneFake::MinMicrophoneReadInBytes() {
+ return kMinMicrophoneReadInBytes;
+}
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // defined(ENABLE_FAKE_MICROPHONE)
diff --git a/src/cobalt/speech/microphone_fake.h b/src/cobalt/speech/microphone_fake.h
new file mode 100644
index 0000000..dd17c1c
--- /dev/null
+++ b/src/cobalt/speech/microphone_fake.h
@@ -0,0 +1,59 @@
+/*
+ * 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_SPEECH_MICROPHONE_FAKE_H_
+#define COBALT_SPEECH_MICROPHONE_FAKE_H_
+
+#include "cobalt/speech/speech_configuration.h"
+
+#if defined(ENABLE_FAKE_MICROPHONE)
+
+#include <string>
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/speech/microphone.h"
+
+namespace cobalt {
+namespace speech {
+
+// Fake microphone to mock the speech input by reading from the pre-recorded
+// audio.
+class MicrophoneFake : public Microphone {
+ public:
+ MicrophoneFake();
+ ~MicrophoneFake() SB_OVERRIDE {}
+
+ bool Open() SB_OVERRIDE;
+ int Read(char* out_data, int data_size) SB_OVERRIDE;
+ bool Close() SB_OVERRIDE;
+ int MinMicrophoneReadInBytes() SB_OVERRIDE;
+ bool IsValid() SB_OVERRIDE { return is_valid_; }
+
+ private:
+ std::vector<FilePath> file_paths_;
+ scoped_array<char> file_buffer_;
+ int file_length_;
+ int read_index_;
+ bool is_valid_;
+};
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+#endif // COBALT_SPEECH_MICROPHONE_FAKE_H_
diff --git a/src/cobalt/speech/microphone_manager.cc b/src/cobalt/speech/microphone_manager.cc
new file mode 100644
index 0000000..7b491d9
--- /dev/null
+++ b/src/cobalt/speech/microphone_manager.cc
@@ -0,0 +1,191 @@
+/*
+ * 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/speech/microphone_manager.h"
+
+#if defined(ENABLE_FAKE_MICROPHONE)
+#include "cobalt/speech/microphone_fake.h"
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+#if defined(SB_USE_SB_MICROPHONE)
+#include "cobalt/speech/microphone_starboard.h"
+#endif // defined(SB_USE_SB_MICROPHONE)
+#include "cobalt/speech/speech_recognition_error.h"
+
+namespace cobalt {
+namespace speech {
+
+namespace {
+// Size of an audio buffer.
+const int kBufferSizeInBytes = 8 * 1024;
+// The frequency which we read the data from devices.
+const float kMicReadRateInHertz = 60.0f;
+} // namespace
+
+MicrophoneManager::MicrophoneManager(int sample_rate,
+ const DataReceivedCallback& data_received,
+ const CompletionCallback& completion,
+ const ErrorCallback& error,
+ bool enable_fake_microphone)
+ : sample_rate_(sample_rate),
+ data_received_callback_(data_received),
+ completion_callback_(completion),
+ error_callback_(error),
+#if defined(ENABLE_FAKE_MICROPHONE)
+ enable_fake_microphone_(enable_fake_microphone),
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+ state_(kStopped),
+ thread_("microphone_thread") {
+ UNREFERENCED_PARAMETER(sample_rate_);
+#if defined(ENABLE_FAKE_MICROPHONE)
+ UNREFERENCED_PARAMETER(enable_fake_microphone_);
+#else
+ UNREFERENCED_PARAMETER(enable_fake_microphone);
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+ thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0));
+}
+
+MicrophoneManager::~MicrophoneManager() {
+ thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&MicrophoneManager::DestroyInternal, base::Unretained(this)));
+}
+
+void MicrophoneManager::Open() {
+ thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&MicrophoneManager::OpenInternal, base::Unretained(this)));
+}
+
+void MicrophoneManager::Close() {
+ thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&MicrophoneManager::CloseInternal, base::Unretained(this)));
+}
+
+bool MicrophoneManager::CreateIfNecessary() {
+ DCHECK(thread_.message_loop_proxy()->BelongsToCurrentThread());
+
+ if (microphone_) {
+ return true;
+ }
+
+#if defined(SB_USE_SB_MICROPHONE)
+#if defined(ENABLE_FAKE_MICROPHONE)
+ if (enable_fake_microphone_) {
+ microphone_.reset(new MicrophoneFake());
+ } else {
+ microphone_.reset(
+ new MicrophoneStarboard(sample_rate_, kBufferSizeInBytes));
+ }
+#else
+ microphone_.reset(new MicrophoneStarboard(sample_rate_, kBufferSizeInBytes));
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+#endif // defined(SB_USE_SB_MICROPHONE)
+
+ if (microphone_ && microphone_->IsValid()) {
+ state_ = kStopped;
+ return true;
+ } else {
+ DLOG(WARNING) << "Microphone creation failed.";
+ microphone_.reset();
+ state_ = kError;
+ error_callback_.Run(new SpeechRecognitionError(
+ SpeechRecognitionError::kAudioCapture, "No microphone available."));
+ return false;
+ }
+}
+
+void MicrophoneManager::OpenInternal() {
+ DCHECK(thread_.message_loop_proxy()->BelongsToCurrentThread());
+
+ // Try to create a valid microphone if necessary.
+ if (state_ == kStarted || !CreateIfNecessary()) {
+ return;
+ }
+
+ DCHECK(microphone_);
+ if (!microphone_->Open()) {
+ state_ = kError;
+ error_callback_.Run(new SpeechRecognitionError(
+ SpeechRecognitionError::kAborted, "Microphone open failed."));
+ return;
+ }
+
+ poll_mic_events_timer_.emplace();
+ // Setup a timer to poll for input events.
+ poll_mic_events_timer_->Start(
+ FROM_HERE, base::TimeDelta::FromMicroseconds(static_cast<int64>(
+ base::Time::kMicrosecondsPerSecond / kMicReadRateInHertz)),
+ this, &MicrophoneManager::Read);
+ state_ = kStarted;
+}
+
+void MicrophoneManager::CloseInternal() {
+ DCHECK(thread_.message_loop_proxy()->BelongsToCurrentThread());
+
+ if (state_ == kStopped) {
+ return;
+ }
+
+ if (poll_mic_events_timer_) {
+ poll_mic_events_timer_->Stop();
+ }
+
+ if (microphone_) {
+ if (!microphone_->Close()) {
+ state_ = kError;
+ error_callback_.Run(new SpeechRecognitionError(
+ SpeechRecognitionError::kAborted, "Microphone close failed."));
+ return;
+ }
+ completion_callback_.Run();
+ state_ = kStopped;
+ }
+}
+
+void MicrophoneManager::Read() {
+ DCHECK(thread_.message_loop_proxy()->BelongsToCurrentThread());
+
+ DCHECK(state_ == kStarted);
+ DCHECK(microphone_);
+ DCHECK(microphone_->MinMicrophoneReadInBytes() <= kBufferSizeInBytes);
+
+ static int16_t samples[kBufferSizeInBytes / sizeof(int16_t)];
+ int read_bytes =
+ microphone_->Read(reinterpret_cast<char*>(samples), kBufferSizeInBytes);
+ if (read_bytes > 0) {
+ size_t frames = read_bytes / sizeof(int16_t);
+ scoped_ptr<ShellAudioBus> output_audio_bus(new ShellAudioBus(
+ 1, frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+ output_audio_bus->Assign(ShellAudioBus(1, frames, samples));
+ data_received_callback_.Run(output_audio_bus.Pass());
+ } else if (read_bytes < 0) {
+ state_ = kError;
+ error_callback_.Run(new SpeechRecognitionError(
+ SpeechRecognitionError::kAborted, "Microphone read failed."));
+ poll_mic_events_timer_->Stop();
+ }
+}
+
+void MicrophoneManager::DestroyInternal() {
+ DCHECK(thread_.message_loop_proxy()->BelongsToCurrentThread());
+
+ microphone_.reset();
+ state_ = kStopped;
+}
+
+} // namespace speech
+} // namespace cobalt
diff --git a/src/cobalt/speech/microphone_manager.h b/src/cobalt/speech/microphone_manager.h
new file mode 100644
index 0000000..e91ca8c
--- /dev/null
+++ b/src/cobalt/speech/microphone_manager.h
@@ -0,0 +1,89 @@
+/*
+ * 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_SPEECH_MICROPHONE_MANAGER_H_
+#define COBALT_SPEECH_MICROPHONE_MANAGER_H_
+
+#include "cobalt/speech/speech_configuration.h"
+
+#include "base/callback.h"
+#include "base/optional.h"
+#include "base/threading/thread.h"
+#include "base/timer.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/speech/microphone.h"
+#include "media/base/shell_audio_bus.h"
+
+namespace cobalt {
+namespace speech {
+
+// This class is used for microphone creation, control and destruction. It has
+// a self-managed poller to fetch audio data from microphone.
+class MicrophoneManager {
+ public:
+ typedef ::media::ShellAudioBus ShellAudioBus;
+ typedef base::Callback<void(scoped_ptr<ShellAudioBus>)> DataReceivedCallback;
+ typedef base::Callback<void(void)> CompletionCallback;
+ typedef base::Callback<void(const scoped_refptr<dom::Event>&)> ErrorCallback;
+
+ MicrophoneManager(int sample_rate, const DataReceivedCallback& data_received,
+ const CompletionCallback& completion,
+ const ErrorCallback& error, bool enable_fake_microphone);
+
+ ~MicrophoneManager();
+
+ // Open microphone to receive voice and start a timer to fetch audio data from
+ // microphone.
+ void Open();
+ // Close microphone and stop the timer from receiving audio data.
+ void Close();
+
+ private:
+ enum State { kStarted, kStopped, kError };
+
+ // Returns true if the creation succeeded or the microphone is already a valid
+ // one.
+ bool CreateIfNecessary();
+ void OpenInternal();
+ void CloseInternal();
+ void DestroyInternal();
+
+ // Timer callback for fetching audio data.
+ void Read();
+
+ int sample_rate_;
+ const DataReceivedCallback data_received_callback_;
+ const CompletionCallback completion_callback_;
+ const ErrorCallback error_callback_;
+
+ scoped_ptr<Microphone> microphone_;
+#if defined(ENABLE_FAKE_MICROPHONE)
+ bool enable_fake_microphone_;
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+
+ // Microphone state.
+ State state_;
+ // Repeat timer to poll mic events.
+ base::optional<base::RepeatingTimer<MicrophoneManager> >
+ poll_mic_events_timer_;
+ // Microphone thread.
+ base::Thread thread_;
+};
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // COBALT_SPEECH_MICROPHONE_MANAGER_H_
diff --git a/src/cobalt/speech/microphone_starboard.cc b/src/cobalt/speech/microphone_starboard.cc
new file mode 100644
index 0000000..676c0aa
--- /dev/null
+++ b/src/cobalt/speech/microphone_starboard.cc
@@ -0,0 +1,79 @@
+/*
+ * 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/speech/microphone_starboard.h"
+
+#if defined(SB_USE_SB_MICROPHONE)
+
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace speech {
+
+namespace {
+// The maximum of microphones which can be supported. Currently only supports
+// one microphone.
+const int kNumberOfMicrophones = 1;
+} // namespace
+
+MicrophoneStarboard::MicrophoneStarboard(int sample_rate, int buffer_size_bytes)
+ : Microphone(),
+ min_microphone_read_in_bytes_(-1),
+ microphone_(kSbMicrophoneInvalid) {
+ SbMicrophoneInfo info[kNumberOfMicrophones];
+ int microphone_num = SbMicrophoneGetAvailable(info, kNumberOfMicrophones);
+
+ // Loop all the available microphones and create a valid one.
+ for (int index = 0; index < microphone_num; ++index) {
+ if (!SbMicrophoneIsSampleRateSupported(info[index].id, sample_rate)) {
+ continue;
+ }
+
+ microphone_ =
+ SbMicrophoneCreate(info[index].id, sample_rate, buffer_size_bytes);
+ if (!SbMicrophoneIsValid(microphone_)) {
+ continue;
+ }
+
+ // Created a microphone successfully.
+ min_microphone_read_in_bytes_ = info[index].min_read_size;
+ return;
+ }
+}
+
+MicrophoneStarboard::~MicrophoneStarboard() {
+ SbMicrophoneDestroy(microphone_);
+}
+
+bool MicrophoneStarboard::Open() {
+ SB_DCHECK(SbMicrophoneIsValid(microphone_));
+ return SbMicrophoneOpen(microphone_);
+}
+
+int MicrophoneStarboard::Read(char* out_data, int data_size) {
+ SB_DCHECK(SbMicrophoneIsValid(microphone_));
+ return SbMicrophoneRead(microphone_, out_data, data_size);
+}
+
+bool MicrophoneStarboard::Close() {
+ SB_DCHECK(SbMicrophoneIsValid(microphone_));
+ return SbMicrophoneClose(microphone_);
+}
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // defined(SB_USE_SB_MICROPHONE)
diff --git a/src/cobalt/speech/microphone_starboard.h b/src/cobalt/speech/microphone_starboard.h
new file mode 100644
index 0000000..9df841a
--- /dev/null
+++ b/src/cobalt/speech/microphone_starboard.h
@@ -0,0 +1,53 @@
+/*
+ * 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_SPEECH_MICROPHONE_STARBOARD_H_
+#define COBALT_SPEECH_MICROPHONE_STARBOARD_H_
+
+#include "cobalt/speech/speech_configuration.h"
+
+#if defined(SB_USE_SB_MICROPHONE)
+
+#include "cobalt/speech/microphone.h"
+#include "starboard/microphone.h"
+
+namespace cobalt {
+namespace speech {
+
+class MicrophoneStarboard : public Microphone {
+ public:
+ MicrophoneStarboard(int sample_rate, int buffer_size_bytes);
+ ~MicrophoneStarboard() OVERRIDE;
+
+ bool Open() OVERRIDE;
+ int Read(char* out_data, int data_size) OVERRIDE;
+ bool Close() OVERRIDE;
+ int MinMicrophoneReadInBytes() OVERRIDE {
+ return min_microphone_read_in_bytes_;
+ }
+ bool IsValid() OVERRIDE { return SbMicrophoneIsValid(microphone_); }
+
+ private:
+ // Minimum requested bytes per microphone read.
+ int min_microphone_read_in_bytes_;
+ SbMicrophone microphone_;
+};
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // defined(SB_USE_SB_MICROPHONE)
+#endif // COBALT_SPEECH_MICROPHONE_STARBOARD_H_
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp
index eb0ffcb..aa933d3 100644
--- a/src/cobalt/speech/speech.gyp
+++ b/src/cobalt/speech/speech.gyp
@@ -30,6 +30,8 @@
'audio_encoder_flac.h',
'chunked_byte_buffer.cc',
'chunked_byte_buffer.h',
+ 'endpointer_delegate.cc',
+ 'endpointer_delegate.h',
'endpointer/endpointer.cc',
'endpointer/endpointer.h',
@@ -41,7 +43,10 @@
'google_streaming_api.pb.cc',
'google_streaming_api.pb.h',
'google_streaming_api.pb.proto',
- 'mic.h',
+ 'microphone.h',
+ 'microphone_manager.cc',
+ 'microphone_manager.h',
+ 'speech_configuration.h',
'speech_recognition.cc',
'speech_recognition.h',
'speech_recognition_alternative.cc',
@@ -73,14 +78,38 @@
'conditions': [
['OS=="starboard"', {
'sources': [
- 'mic_starboard.cc',
+ 'microphone_starboard.cc',
+ 'microphone_starboard.h',
],
}],
- ['OS!="starboard" and actual_target_arch in ["linux", "ps3", "win"]', {
+ ['OS=="starboard" and enable_fake_microphone == 1', {
'sources': [
- 'mic_<(actual_target_arch).cc',
+ 'microphone_fake.cc',
+ 'microphone_fake.h',
],
+ 'defines': [
+ 'ENABLE_FAKE_MICROPHONE',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [ 'ENABLE_FAKE_MICROPHONE', ],
+ },
}],
+ ['cobalt_copy_test_data == 1', {
+ 'actions': [
+ {
+ 'action_name': 'speech_copy_test_data',
+ 'variables': {
+ 'input_files': [
+ '<(DEPTH)/cobalt/speech/testdata/',
+ ],
+ 'output_dir': 'cobalt/speech/testdata/',
+ },
+ 'includes': [ '../build/copy_test_data.gypi' ],
+ },
+ ],
+ }
+
+ ],
],
},
{
diff --git a/src/cobalt/speech/speech_configuration.h b/src/cobalt/speech/speech_configuration.h
new file mode 100644
index 0000000..97ab76f
--- /dev/null
+++ b/src/cobalt/speech/speech_configuration.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SPEECH_SPEECH_CONFIGURATION_H_
+#define COBALT_SPEECH_SPEECH_CONFIGURATION_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/configuration.h"
+#if SB_HAS(MICROPHONE) && SB_VERSION(2)
+#define SB_USE_SB_MICROPHONE 1
+#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // defined(OS_STARBOARD)
+
+#endif // COBALT_SPEECH_SPEECH_CONFIGURATION_H_
diff --git a/src/cobalt/speech/speech_recognition.cc b/src/cobalt/speech/speech_recognition.cc
index 761b2e5..8ea7c49 100644
--- a/src/cobalt/speech/speech_recognition.cc
+++ b/src/cobalt/speech/speech_recognition.cc
@@ -31,7 +31,9 @@
->fetcher_factory()
->network_module(),
base::Bind(&SpeechRecognition::OnEventAvailable,
- base::Unretained(this)))),
+ base::Unretained(this)),
+ base::polymorphic_downcast<dom::DOMSettings*>(settings)
+ ->enable_fake_microphone())),
config_("" /*lang*/, false /*continuous*/, false /*interim_results*/,
1 /*max alternatives*/) {}
diff --git a/src/cobalt/speech/speech_recognition_manager.cc b/src/cobalt/speech/speech_recognition_manager.cc
index e531a77..114d374 100644
--- a/src/cobalt/speech/speech_recognition_manager.cc
+++ b/src/cobalt/speech/speech_recognition_manager.cc
@@ -17,6 +17,7 @@
#include "cobalt/speech/speech_recognition_manager.h"
#include "base/bind.h"
+#include "cobalt/base/tokens.h"
#include "cobalt/dom/dom_exception.h"
namespace cobalt {
@@ -28,7 +29,8 @@
} // namespace
SpeechRecognitionManager::SpeechRecognitionManager(
- network::NetworkModule* network_module, const EventCallback& event_callback)
+ network::NetworkModule* network_module, const EventCallback& event_callback,
+ bool enable_fake_microphone)
: ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
weak_this_(weak_ptr_factory_.GetWeakPtr()),
main_message_loop_(base::MessageLoopProxy::current()),
@@ -37,13 +39,15 @@
recognizer_(network_module,
base::Bind(&SpeechRecognitionManager::OnRecognizerEvent,
base::Unretained(this)))),
- ALLOW_THIS_IN_INITIALIZER_LIST(mic_(Mic::Create(
+ ALLOW_THIS_IN_INITIALIZER_LIST(microphone_manager_(
kSampleRate, base::Bind(&SpeechRecognitionManager::OnDataReceived,
base::Unretained(this)),
base::Bind(&SpeechRecognitionManager::OnDataCompletion,
base::Unretained(this)),
base::Bind(&SpeechRecognitionManager::OnMicError,
- base::Unretained(this))))),
+ base::Unretained(this)),
+ enable_fake_microphone)),
+ endpointer_delegate_(kSampleRate),
state_(kStopped) {}
SpeechRecognitionManager::~SpeechRecognitionManager() { Stop(); }
@@ -61,7 +65,8 @@
}
recognizer_.Start(config, kSampleRate);
- mic_->Start();
+ microphone_manager_.Open();
+ endpointer_delegate_.Start();
state_ = kStarted;
}
@@ -74,9 +79,11 @@
return;
}
- mic_->Stop();
+ endpointer_delegate_.Stop();
+ microphone_manager_.Close();
recognizer_.Stop();
state_ = kStopped;
+ event_callback_.Run(new dom::Event(base::Tokens::soundend()));
}
void SpeechRecognitionManager::Abort() {
@@ -88,9 +95,11 @@
return;
}
- mic_->Stop();
+ endpointer_delegate_.Stop();
+ microphone_manager_.Close();
recognizer_.Stop();
state_ = kAborted;
+ event_callback_.Run(new dom::Event(base::Tokens::soundend()));
}
void SpeechRecognitionManager::OnDataReceived(
@@ -105,6 +114,9 @@
// Stop recognizing if in the abort state.
if (state_ != kAborted) {
+ if (endpointer_delegate_.IsFirstTimeSoundStarted(*audio_bus)) {
+ event_callback_.Run(new dom::Event(base::Tokens::soundstart()));
+ }
recognizer_.RecognizeAudio(audio_bus.Pass(), false);
}
}
@@ -125,7 +137,7 @@
size_t dummy_frames =
static_cast<size_t>(kSampleRate * kAudioPacketDurationInSeconds);
scoped_ptr<ShellAudioBus> dummy_audio_bus(new ShellAudioBus(
- 1, dummy_frames, ShellAudioBus::kInt16, ShellAudioBus::kPlanar));
+ 1, dummy_frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
dummy_audio_bus->ZeroAllFrames();
recognizer_.RecognizeAudio(dummy_audio_bus.Pass(), true);
}
@@ -147,22 +159,23 @@
}
}
-void SpeechRecognitionManager::OnMicError() {
+void SpeechRecognitionManager::OnMicError(
+ const scoped_refptr<dom::Event>& event) {
if (!main_message_loop_->BelongsToCurrentThread()) {
// Called from mic thread.
main_message_loop_->PostTask(
FROM_HERE,
- base::Bind(&SpeechRecognitionManager::OnMicError, weak_this_));
+ base::Bind(&SpeechRecognitionManager::OnMicError, weak_this_, event));
return;
}
- event_callback_.Run(
- scoped_refptr<SpeechRecognitionError>(new SpeechRecognitionError(
- SpeechRecognitionError::kAborted, "Mic Disconnected.")));
+ event_callback_.Run(event);
- // An error is occured in Mic, so stopping the recognizer.
+ // An error is occured in Mic, so stop the energy endpointer and recognizer.
+ endpointer_delegate_.Stop();
recognizer_.Stop();
state_ = kAborted;
+ event_callback_.Run(new dom::Event(base::Tokens::soundend()));
}
} // namespace speech
diff --git a/src/cobalt/speech/speech_recognition_manager.h b/src/cobalt/speech/speech_recognition_manager.h
index 852ede2..9367a10 100644
--- a/src/cobalt/speech/speech_recognition_manager.h
+++ b/src/cobalt/speech/speech_recognition_manager.h
@@ -21,7 +21,9 @@
#include "cobalt/network/network_module.h"
#include "cobalt/script/exception_state.h"
-#include "cobalt/speech/mic.h"
+#include "cobalt/speech/endpointer_delegate.h"
+#include "cobalt/speech/microphone_manager.h"
+#include "cobalt/speech/speech_configuration.h"
#include "cobalt/speech/speech_recognition_config.h"
#include "cobalt/speech/speech_recognition_error.h"
#include "cobalt/speech/speech_recognition_event.h"
@@ -42,7 +44,8 @@
typedef base::Callback<bool(const scoped_refptr<dom::Event>&)> EventCallback;
SpeechRecognitionManager(network::NetworkModule* network_module,
- const EventCallback& event_callback);
+ const EventCallback& event_callback,
+ bool enable_fake_microphone);
~SpeechRecognitionManager();
// Start/Stop speech recognizer and microphone. Multiple calls would be
@@ -62,7 +65,7 @@
// Callbacks from mic.
void OnDataReceived(scoped_ptr<ShellAudioBus> audio_bus);
void OnDataCompletion();
- void OnMicError();
+ void OnMicError(const scoped_refptr<dom::Event>& event);
// Callbacks from recognizer.
void OnRecognizerEvent(const scoped_refptr<dom::Event>& event);
@@ -76,7 +79,12 @@
// Callback for sending dom events if available.
EventCallback event_callback_;
SpeechRecognizer recognizer_;
- scoped_ptr<Mic> mic_;
+
+ MicrophoneManager microphone_manager_;
+
+ // Delegate of endpointer which is used for detecting sound energy.
+ EndPointerDelegate endpointer_delegate_;
+
State state_;
};
diff --git a/src/cobalt/speech/speech_recognizer.cc b/src/cobalt/speech/speech_recognizer.cc
index d5c1ce1..d7ad5dd 100644
--- a/src/cobalt/speech/speech_recognizer.cc
+++ b/src/cobalt/speech/speech_recognizer.cc
@@ -25,11 +25,16 @@
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/network/network_module.h"
#include "cobalt/speech/google_streaming_api.pb.h"
-#include "cobalt/speech/mic.h"
+#include "cobalt/speech/microphone.h"
+#include "cobalt/speech/speech_configuration.h"
#include "cobalt/speech/speech_recognition_error.h"
#include "net/base/escape.h"
#include "net/url_request/url_fetcher.h"
+#if defined(SB_USE_SB_MICROPHONE)
+#include "starboard/microphone.h"
+#endif // defined(SB_USE_SB_MICROPHONE)
+
namespace cobalt {
namespace speech {
@@ -75,16 +80,23 @@
SpeechRecognitionResultList::SpeechRecognitionResults results;
for (int i = 0; i < event.result_size(); ++i) {
SpeechRecognitionResult::SpeechRecognitionAlternatives alternatives;
- const proto::SpeechRecognitionResult& kProtoResult = event.result(i);
- for (int j = 0; j < kProtoResult.alternative_size(); ++j) {
- const proto::SpeechRecognitionAlternative& kAlternative =
- kProtoResult.alternative(j);
+ const proto::SpeechRecognitionResult& proto_result = event.result(i);
+ for (int j = 0; j < proto_result.alternative_size(); ++j) {
+ const proto::SpeechRecognitionAlternative& proto_alternative =
+ proto_result.alternative(j);
+ float confidence = 0.0f;
+ if (proto_alternative.has_confidence()) {
+ confidence = proto_alternative.confidence();
+ } else if (proto_result.has_stability()) {
+ confidence = proto_result.stability();
+ }
scoped_refptr<SpeechRecognitionAlternative> alternative(
- new SpeechRecognitionAlternative(kAlternative.transcript(),
- kAlternative.confidence()));
+ new SpeechRecognitionAlternative(proto_alternative.transcript(),
+ confidence));
alternatives.push_back(alternative);
}
- bool final = kProtoResult.has_final() && kProtoResult.final();
+
+ bool final = proto_result.has_final() && proto_result.final();
scoped_refptr<SpeechRecognitionResult> recognition_result(
new SpeechRecognitionResult(alternatives, final));
results.push_back(recognition_result);
@@ -139,6 +151,16 @@
event_callback.Run(error_event);
}
+bool IsResponseCodeSuccess(int response_code) {
+ // NetFetcher only considers success to be if the network request
+ // was successful *and* we get a 2xx response back.
+ // TODO: 304s are unexpected since we don't enable the HTTP cache,
+ // meaning we don't add the If-Modified-Since header to our request.
+ // However, it's unclear what would happen if we did, so DCHECK.
+ DCHECK_NE(response_code, 304) << "Unsupported status code";
+ return response_code / 100 == 2;
+}
+
} // namespace
SpeechRecognizer::SpeechRecognizer(network::NetworkModule* network_module,
@@ -182,23 +204,31 @@
const net::URLFetcher* source, scoped_ptr<std::string> download_data) {
DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+ const net::URLRequestStatus& status = source->GetStatus();
+ const int response_code = source->GetResponseCode();
+
if (source == downstream_fetcher_.get()) {
- chunked_byte_buffer_.Append(*download_data);
- while (chunked_byte_buffer_.HasChunks()) {
- scoped_ptr<std::vector<uint8_t> > chunk =
- chunked_byte_buffer_.PopChunk().Pass();
+ if (status.is_success() && IsResponseCodeSuccess(response_code)) {
+ chunked_byte_buffer_.Append(*download_data);
+ while (chunked_byte_buffer_.HasChunks()) {
+ scoped_ptr<std::vector<uint8_t> > chunk =
+ chunked_byte_buffer_.PopChunk().Pass();
- proto::SpeechRecognitionEvent event;
- if (!event.ParseFromString(std::string(chunk->begin(), chunk->end()))) {
- DLOG(WARNING) << "Parse proto string error.";
- return;
- }
+ proto::SpeechRecognitionEvent event;
+ if (!event.ParseFromString(std::string(chunk->begin(), chunk->end()))) {
+ DLOG(WARNING) << "Parse proto string error.";
+ return;
+ }
- if (event.status() == proto::SpeechRecognitionEvent::STATUS_SUCCESS) {
- ProcessAndFireSuccessEvent(ProcessProtoSuccessResults(event));
- } else {
- ProcessAndFireErrorEvent(event, event_callback_);
+ if (event.status() == proto::SpeechRecognitionEvent::STATUS_SUCCESS) {
+ ProcessAndFireSuccessEvent(ProcessProtoSuccessResults(event));
+ } else {
+ ProcessAndFireErrorEvent(event, event_callback_);
+ }
}
+ } else {
+ event_callback_.Run(new SpeechRecognitionError(
+ SpeechRecognitionError::kNetwork, "Network response failure."));
}
}
}
@@ -243,7 +273,14 @@
up_url = AppendQueryParameter(up_url, "client", kClient);
up_url = AppendQueryParameter(up_url, "pair", pair);
up_url = AppendQueryParameter(up_url, "output", "pb");
- up_url = AppendQueryParameter(up_url, "key", GetSpeechAPIKey());
+
+ const char* speech_api_key = NULL;
+#if defined(SB_USE_SB_MICROPHONE)
+ speech_api_key = SbMicrophoneGetSpeechApiKey();
+#else
+ speech_api_key = "";
+#endif
+ up_url = AppendQueryParameter(up_url, "key", speech_api_key);
// Language is required. If no language is specified, use the system language.
if (!config.lang.empty()) {
diff --git a/src/cobalt/speech/testdata/audio1.raw b/src/cobalt/speech/testdata/audio1.raw
new file mode 100644
index 0000000..5ebf79d
--- /dev/null
+++ b/src/cobalt/speech/testdata/audio1.raw
Binary files differ
diff --git a/src/cobalt/speech/testdata/audio2.raw b/src/cobalt/speech/testdata/audio2.raw
new file mode 100644
index 0000000..35413b7
--- /dev/null
+++ b/src/cobalt/speech/testdata/audio2.raw
Binary files differ
diff --git a/src/cobalt/speech/testdata/audio3.raw b/src/cobalt/speech/testdata/audio3.raw
new file mode 100644
index 0000000..c503c9e
--- /dev/null
+++ b/src/cobalt/speech/testdata/audio3.raw
Binary files differ
diff --git a/src/cobalt/speech/testdata/quit.raw b/src/cobalt/speech/testdata/quit.raw
new file mode 100644
index 0000000..a01dfc4
--- /dev/null
+++ b/src/cobalt/speech/testdata/quit.raw
Binary files differ
diff --git a/src/cobalt/trace_event/json_file_outputter.cc b/src/cobalt/trace_event/json_file_outputter.cc
index f17f2d3..1f7a30b 100644
--- a/src/cobalt/trace_event/json_file_outputter.cc
+++ b/src/cobalt/trace_event/json_file_outputter.cc
@@ -18,12 +18,38 @@
#include <string>
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+#include "base/command_line.h"
+#endif
#include "base/logging.h"
#include "base/platform_file.h"
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+#include "base/string_piece.h"
+#include "cobalt/trace_event/switches.h"
+#endif
namespace cobalt {
namespace trace_event {
+// Returns true if and only if log_timed_trace == "on", and
+// ENABLE_DEBUG_COMMAND_LINE_SWITCHES is set.
+bool ShouldLogTimedTrace() {
+ bool isTimedTraceSet = false;
+
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kLogTimedTrace) &&
+ command_line->GetSwitchValueASCII(switches::kLogTimedTrace) ==
+ "on") {
+ isTimedTraceSet = true;
+ }
+
+#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
+
+ return isTimedTraceSet;
+}
+
JSONFileOutputter::JSONFileOutputter(const FilePath& output_path)
: output_path_(output_path),
output_trace_event_call_count_(0),
@@ -72,6 +98,14 @@
return;
}
+ if (ShouldLogTimedTrace()) {
+ // These markers assist in locating the trace data in the log, and can be
+ // used by scripts to help extract the trace data.
+ LOG(INFO) << "BEGIN_TRACELOG_MARKER" << base::StringPiece(buffer, length)
+ << "END_TRACELOG_MARKER";
+ return;
+ }
+
int count = base::WritePlatformFileAtCurrentPos(file_, buffer, length);
if (count < 0) {
Close();
diff --git a/src/cobalt/trace_event/switches.cc b/src/cobalt/trace_event/switches.cc
new file mode 100644
index 0000000..6cf65e6
--- /dev/null
+++ b/src/cobalt/trace_event/switches.cc
@@ -0,0 +1,33 @@
+/*
+ * 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/trace_event/switches.h"
+
+namespace cobalt {
+namespace trace_event {
+namespace switches {
+
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+// If this flag is set, then the contents of the timed_trace is sent to the log
+// such that it can be collected by examining console output. This may be
+// useful on devices where it is difficult to gain access to files written by
+// Cobalt. log_timed_trace: on | off.
+const char kLogTimedTrace[] = "log_timed_trace";
+#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
+
+} // namespace switches
+} // namespace trace_event
+} // namespace cobalt
diff --git a/src/cobalt/trace_event/switches.h b/src/cobalt/trace_event/switches.h
new file mode 100644
index 0000000..98fc0ec
--- /dev/null
+++ b/src/cobalt/trace_event/switches.h
@@ -0,0 +1,32 @@
+/*
+ * 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_TRACE_EVENT_SWITCHES_H_
+#define COBALT_TRACE_EVENT_SWITCHES_H_
+
+namespace cobalt {
+namespace trace_event {
+namespace switches {
+
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+extern const char kLogTimedTrace[];
+#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
+
+} // namespace switches
+} // namespace trace_event
+} // namespace cobalt
+
+#endif // COBALT_TRACE_EVENT_SWITCHES_H_
diff --git a/src/cobalt/trace_event/trace_event.gyp b/src/cobalt/trace_event/trace_event.gyp
index 75d5c82..4e4279e 100644
--- a/src/cobalt/trace_event/trace_event.gyp
+++ b/src/cobalt/trace_event/trace_event.gyp
@@ -27,6 +27,8 @@
'scoped_event_parser_trace.h',
'scoped_trace_to_file.cc',
'scoped_trace_to_file.h',
+ 'switches.cc',
+ 'switches.h',
],
'dependencies': [
'<(DEPTH)/base/base.gyp:base',
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index aa9b2a5..3c57db9 100644
--- a/src/cobalt/version.h
+++ b/src/cobalt/version.h
@@ -17,6 +17,6 @@
#define COBALT_VERSION_H_
// Cobalt release number.
-#define COBALT_VERSION "4"
+#define COBALT_VERSION "6"
#endif // COBALT_VERSION_H_
diff --git a/src/cobalt/webdriver/ScriptExecutor.idl b/src/cobalt/webdriver/ScriptExecutor.idl
index d82d27a..1b77e2f 100644
--- a/src/cobalt/webdriver/ScriptExecutor.idl
+++ b/src/cobalt/webdriver/ScriptExecutor.idl
@@ -25,4 +25,4 @@
};
callback ExecuteFunctionCallback =
- DOMString(DOMString functionBody, DOMString json_args);
+ void(ScriptExecutorParams params, ScriptExecutorResult resultHandler);
diff --git a/src/cobalt/webdriver/ScriptExecutorParams.idl b/src/cobalt/webdriver/ScriptExecutorParams.idl
new file mode 100644
index 0000000..cda45c5
--- /dev/null
+++ b/src/cobalt/webdriver/ScriptExecutorParams.idl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+[
+ NoInterfaceObject,
+ Conditional=ENABLE_WEBDRIVER
+]
+interface ScriptExecutorParams {
+ readonly attribute object functionObject;
+ readonly attribute DOMString jsonArgs;
+ readonly attribute long? asyncTimeout;
+};
diff --git a/src/cobalt/webdriver/ScriptExecutorResult.idl b/src/cobalt/webdriver/ScriptExecutorResult.idl
new file mode 100644
index 0000000..718059c
--- /dev/null
+++ b/src/cobalt/webdriver/ScriptExecutorResult.idl
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+[
+ NoInterfaceObject,
+ Conditional=ENABLE_WEBDRIVER
+]
+interface ScriptExecutorResult {
+ void onResult(DOMString result);
+ void onTimeout();
+};
diff --git a/src/cobalt/webdriver/content/webdriver-init.js b/src/cobalt/webdriver/content/webdriver-init.js
index 2a0b120..03c5099 100644
--- a/src/cobalt/webdriver/content/webdriver-init.js
+++ b/src/cobalt/webdriver/content/webdriver-init.js
@@ -14,71 +14,84 @@
* limitations under the License.
*/
+// Run the JSON deserialize algorithm on the value.
+// https://www.w3.org/TR/webdriver/#dfn-json-deserialize
+webdriverExecutor.deserialize = function(value) {
+ if (value instanceof Array) {
+ // Deserialize each of the array's values;
+ var deserializedArray = [];
+ var numArgs = value.length;
+ for (var i = 0; i < numArgs; i++) {
+ deserializedArray.push(webdriverExecutor.deserialize(value[i]));
+ }
+ return deserializedArray;
+ } else if (value instanceof Object && value.ELEMENT) {
+ // The argument is a WebElement, as denoted by the presence of the
+ // ELEMENT property, so get the actual Element the id is mapped to.
+ return webdriverExecutor.idToElement(value.ELEMENT);
+ } else if (value instanceof Object) {
+ // Deserialize each of the object's values.
+ var deserializedObject = {};
+ for (var key in value) {
+ deserializedObject[key] = webdriverExecutor.deserialize(value[key]);
+ }
+ return deserializedObject;
+ }
+ return value;
+};
+
+// Run the JSON clone algorithm on the value.
+// https://www.w3.org/TR/webdriver/#dfn-internal-json-clone-algorithm
+webdriverExecutor.serialize = function(value, seen) {
+ if (value === undefined || value === null) {
+ return null;
+ } else if (value instanceof Element) {
+ var webElementId = webdriverExecutor.elementToId(value);
+ return { "ELEMENT": webElementId };
+ } else if (value instanceof Object) {
+ if (seen.indexOf(value) != -1) {
+ throw "Reference cycle found trying to serialize.";
+ }
+ seen.push(value);
+ if (value instanceof Array || value instanceof NodeList
+ || value instanceof HTMLCollection) {
+ var serializedArray = [];
+ var length = value.length;
+ for (var i = 0; i < length; i++) {
+ serializedArray.push(webdriverExecutor.serialize(value[i], seen));
+ }
+ return serializedArray;
+ } else {
+ // Deserialize each of the object's its values.
+ var serializedObject = {};
+ for (var key in value) {
+ serializedObject[key] = webdriverExecutor.serialize(value[key], seen);
+ }
+ return serializedObject;
+ }
+ }
+ return value;
+};
+
// Set the executeScriptHarness callback function.
// The function takes a functionObject and a JSON string representing an array
// of arguments. The arguments are applied to the function |functionObject|.
-webdriverExecutor.executeScriptHarness = function(functionObject, jsonArgString) {
-
- // Run the JSON deserialize algorithm on the value.
- // https://www.w3.org/TR/webdriver/#dfn-json-deserialize
- var deserialize = function(value) {
- if (value instanceof Array) {
- // Deserialize each of the array's values;
- var deserializedArray = [];
- var numArgs = value.length;
- for (var i = 0; i < numArgs; i++) {
- deserializedArray.push(deserialize(value[i]));
- }
- return deserializedArray;
- } else if (value instanceof Object && value.ELEMENT) {
- // The argument is a WebElement, as denoted by the presence of the
- // ELEMENT property, so get the actual Element the id is mapped to.
- return webdriverExecutor.idToElement(value.ELEMENT);
- } else if (value instanceof Object) {
- // Deserialize each of the object's values.
- var deserializedObject = {};
- for (var key in value) {
- deserializedObject[key] = deserialize(value[key]);
- }
- return deserializedObject;
+webdriverExecutor.executeScriptHarness = function(params, resultHandler) {
+ var parameters = webdriverExecutor.deserialize(JSON.parse(params.jsonArgs));
+ if (params.asyncTimeout === null) {
+ var result = params.functionObject.apply(window, parameters);
+ resultHandler.onResult(JSON.stringify(webdriverExecutor.serialize(result, [])));
+ } else {
+ var timeoutCallback = function() {
+ resultHandler.onTimeout();
}
- return value;
- };
+ var timerId = window.setTimeout(timeoutCallback, params.asyncTimeout);
- // Run the JSON clone algorithm on the value.
- // https://www.w3.org/TR/webdriver/#dfn-internal-json-clone-algorithm
- var serialize = function(value, seen) {
- if (value === undefined || value === null) {
- return null;
- } else if (value instanceof Element) {
- var webElementId = webdriverExecutor.elementToId(value);
- return { "ELEMENT": webElementId };
- } else if (value instanceof Object) {
- if (seen.indexOf(value) != -1) {
- throw "Reference cycle found trying to serialize.";
- }
- seen.push(value);
- if (value instanceof Array || value instanceof NodeList
- || value instanceof HTMLCollection) {
- var serializedArray = [];
- var length = value.length;
- for (var i = 0; i < length; i++) {
- serializedArray.push(serialize(value[i], seen));
- }
- return serializedArray;
- } else {
- // Deserialize each of the object's its values.
- var serializedObject = {};
- for (var key in value) {
- serializedObject[key] = serialize(value[key], seen);
- }
- return serializedObject;
- }
+ var resultCallback = function(result) {
+ resultHandler.onResult(JSON.stringify(webdriverExecutor.serialize(result, [])));
+ window.clearTimeout(timerId);
}
- return value;
- };
- var parameters = deserialize(JSON.parse(jsonArgString));
- var result = functionObject.apply(window, parameters);
- return JSON.stringify(serialize(result, []));
-
+ parameters.push(resultCallback);
+ params.functionObject.apply(window, parameters);
+ }
};
diff --git a/src/cobalt/webdriver/execute_test.cc b/src/cobalt/webdriver/execute_test.cc
new file mode 100644
index 0000000..6edec33
--- /dev/null
+++ b/src/cobalt/webdriver/execute_test.cc
@@ -0,0 +1,304 @@
+/*
+ * 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 <algorithm>
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/run_loop.h"
+#include "cobalt/dom/document.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/javascript_engine.h"
+#include "cobalt/webdriver/script_executor.h"
+#include "cobalt/webdriver/testing/stub_window.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::DefaultValue;
+using ::testing::Return;
+
+namespace cobalt {
+namespace webdriver {
+namespace {
+
+class MockElementMapping : public ElementMapping {
+ public:
+ MOCK_METHOD1(ElementToId,
+ protocol::ElementId(const scoped_refptr<dom::Element>&));
+ MOCK_METHOD1(IdToElement,
+ scoped_refptr<dom::Element>(const protocol::ElementId& id));
+};
+
+class MockScriptExecutorResult : public ScriptExecutorResult::ResultHandler {
+ public:
+ MOCK_METHOD1(OnResult, void(const std::string&));
+ MOCK_METHOD0(OnTimeout, void());
+};
+
+class JSONScriptExecutorResult : public ScriptExecutorResult::ResultHandler {
+ public:
+ void OnResult(const std::string& result) {
+ json_result_.reset(base::JSONReader::Read(result.c_str()));
+ }
+ void OnTimeout() { NOTREACHED(); }
+ base::Value* json_result() { return json_result_.get(); }
+
+ private:
+ scoped_ptr<base::Value> json_result_;
+};
+
+class ScriptExecutorTest : public ::testing::Test {
+ protected:
+ void SetUp() OVERRIDE {
+ stub_window_.reset(new testing::StubWindow());
+ script_executor_ =
+ ScriptExecutor::Create(&element_mapping_, global_environment());
+
+ ON_CALL(element_mapping_, IdToElement(_))
+ .WillByDefault(Return(scoped_refptr<dom::Element>()));
+ ON_CALL(element_mapping_, ElementToId(_))
+ .WillByDefault(Return(protocol::ElementId("bad-id")));
+ }
+
+ scoped_refptr<dom::Window> window() { return stub_window_->window(); }
+ scoped_refptr<script::GlobalEnvironment> global_environment() {
+ return stub_window_->global_environment();
+ }
+
+ protected:
+ scoped_ptr<testing::StubWindow> stub_window_;
+ MockElementMapping element_mapping_;
+ scoped_refptr<ScriptExecutor> script_executor_;
+};
+
+} // namespace
+
+TEST_F(ScriptExecutorTest, CreateSyncScript) {
+ scoped_refptr<ScriptExecutorParams> params =
+ ScriptExecutorParams::Create(global_environment(), "return 5;", "[]");
+ ASSERT_TRUE(params);
+ EXPECT_NE(reinterpret_cast<intptr_t>(params->function_object()), NULL);
+ EXPECT_STREQ(params->json_args().c_str(), "[]");
+ EXPECT_EQ(params->async_timeout(), base::nullopt);
+}
+
+TEST_F(ScriptExecutorTest, CreateAsyncScript) {
+ scoped_refptr<ScriptExecutorParams> params =
+ ScriptExecutorParams::Create(global_environment(), "return 5;", "[]",
+ base::TimeDelta::FromMilliseconds(5));
+ ASSERT_TRUE(params);
+ EXPECT_NE(reinterpret_cast<intptr_t>(params->function_object()), NULL);
+ EXPECT_STREQ(params->json_args().c_str(), "[]");
+ EXPECT_EQ(params->async_timeout(), 5);
+}
+
+TEST_F(ScriptExecutorTest, CreateInvalidScript) {
+ scoped_refptr<ScriptExecutorParams> params =
+ ScriptExecutorParams::Create(global_environment(), "retarn 5ish;", "[]");
+ ASSERT_TRUE(params);
+ EXPECT_EQ(reinterpret_cast<intptr_t>(params->function_object()), NULL);
+}
+
+TEST_F(ScriptExecutorTest, ExecuteSync) {
+ scoped_refptr<ScriptExecutorParams> params = ScriptExecutorParams::Create(
+ global_environment(), "return \"retval\";", "[]");
+ ASSERT_TRUE(params);
+ MockScriptExecutorResult result_handler;
+ EXPECT_CALL(result_handler, OnResult(std::string("\"retval\"")));
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+}
+
+TEST_F(ScriptExecutorTest, ExecuteAsync) {
+ // Create a script that will call the async callback after 50 ms, with
+ // an async timeout of 100 ms.
+ scoped_refptr<ScriptExecutorParams> params = ScriptExecutorParams::Create(
+ global_environment(),
+ "var callback = arguments[0];"
+ "window.setTimeout(function() { callback(72); }, 50);",
+ "[]", base::TimeDelta::FromMilliseconds(100));
+ ASSERT_TRUE(params);
+ MockScriptExecutorResult result_handler;
+ EXPECT_CALL(result_handler, OnResult(std::string("72")));
+ EXPECT_CALL(result_handler, OnTimeout()).Times(0);
+
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+
+ // Let the message loop run for 200ms to allow enough time for the async
+ // script to fire the callback.
+ base::RunLoop run_loop;
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(200));
+ run_loop.Run();
+}
+
+TEST_F(ScriptExecutorTest, AsyncTimeout) {
+ // Create a script that will call the async callback after 10 seconds, with
+ // an async timeout of 100 ms.
+ scoped_refptr<ScriptExecutorParams> params = ScriptExecutorParams::Create(
+ global_environment(),
+ "var callback = arguments[0];"
+ "window.setTimeout(function() { callback(72); }, 10000);",
+ "[]", base::TimeDelta::FromMilliseconds(100));
+ ASSERT_TRUE(params);
+ MockScriptExecutorResult result_handler;
+ EXPECT_CALL(result_handler, OnResult(_)).Times(0);
+ EXPECT_CALL(result_handler, OnTimeout()).Times(1);
+
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+
+ // Let the message loop run for 200ms to allow enough time for the async
+ // timeout to fire.
+ base::RunLoop run_loop;
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(),
+ base::TimeDelta::FromMilliseconds(200));
+ run_loop.Run();
+}
+
+TEST_F(ScriptExecutorTest, ScriptThrowsException) {
+ scoped_refptr<ScriptExecutorParams> params =
+ ScriptExecutorParams::Create(global_environment(), "throw Error()", "[]");
+ ASSERT_TRUE(params);
+ MockScriptExecutorResult result_handler;
+ EXPECT_FALSE(script_executor_->Execute(params, &result_handler));
+}
+
+TEST_F(ScriptExecutorTest, ConvertBoolean) {
+ scoped_refptr<ScriptExecutorParams> params = ScriptExecutorParams::Create(
+ global_environment(), "return arguments[0];", "[true]");
+ ASSERT_TRUE(params);
+ MockScriptExecutorResult result_handler;
+ EXPECT_CALL(result_handler, OnResult(std::string("true")));
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+
+ params = ScriptExecutorParams::Create(global_environment(),
+ "return arguments[0];", "[false]");
+ ASSERT_TRUE(params);
+ EXPECT_CALL(result_handler, OnResult(std::string("false")));
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+}
+
+TEST_F(ScriptExecutorTest, ConvertNull) {
+ scoped_refptr<ScriptExecutorParams> params = ScriptExecutorParams::Create(
+ global_environment(), "return arguments[0];", "[null]");
+ ASSERT_TRUE(params);
+ MockScriptExecutorResult result_handler;
+ EXPECT_CALL(result_handler, OnResult(std::string("null")));
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+}
+
+TEST_F(ScriptExecutorTest, ConvertNumericType) {
+ scoped_refptr<ScriptExecutorParams> params = ScriptExecutorParams::Create(
+ global_environment(), "return arguments[0];", "[6]");
+ ASSERT_TRUE(params);
+ MockScriptExecutorResult result_handler;
+ EXPECT_CALL(result_handler, OnResult(std::string("6")));
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+
+ params = ScriptExecutorParams::Create(global_environment(),
+ "return arguments[0];", "[-6.4]");
+ ASSERT_TRUE(params);
+ EXPECT_CALL(result_handler, OnResult(std::string("-6.4")));
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+}
+
+TEST_F(ScriptExecutorTest, ConvertString) {
+ scoped_refptr<ScriptExecutorParams> params = ScriptExecutorParams::Create(
+ global_environment(), "return arguments[0];", "[\"Mr. T\"]");
+ ASSERT_TRUE(params);
+ MockScriptExecutorResult result_handler;
+ EXPECT_CALL(result_handler, OnResult(std::string("\"Mr. T\"")));
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+}
+
+TEST_F(ScriptExecutorTest, ConvertWebElement) {
+ // Create a dom::Element for the MockElementMapping to return.
+ scoped_refptr<dom::Element> element =
+ window()->document()->CreateElement("p");
+ EXPECT_CALL(element_mapping_, IdToElement(protocol::ElementId("id123")))
+ .WillRepeatedly(Return(element));
+ EXPECT_CALL(element_mapping_, ElementToId(element))
+ .WillRepeatedly(Return(protocol::ElementId("id123")));
+
+ // Create a script that will pass a web element argument as a parameter, and
+ // return it back. This will invoke the lookup to and from an id.
+ scoped_refptr<ScriptExecutorParams> params =
+ ScriptExecutorParams::Create(global_environment(), "return arguments[0];",
+ "[ {\"ELEMENT\": \"id123\"} ]");
+ ASSERT_TRUE(params);
+
+ // Execute the script and parse the result as JSON, ensuring we got the same
+ // web element.
+ JSONScriptExecutorResult result_handler;
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+ ASSERT_TRUE(result_handler.json_result());
+
+ std::string element_id;
+ base::DictionaryValue* dictionary_value;
+ ASSERT_TRUE(result_handler.json_result()->GetAsDictionary(&dictionary_value));
+ EXPECT_TRUE(dictionary_value->GetString(protocol::ElementId::kElementKey,
+ &element_id));
+ EXPECT_STREQ(element_id.c_str(), "id123");
+}
+
+TEST_F(ScriptExecutorTest, ConvertArray) {
+ // Create a script that takes an array of numbers as input, and returns an
+ // array of those numbers incremented by one.
+ scoped_refptr<ScriptExecutorParams> params = ScriptExecutorParams::Create(
+ global_environment(),
+ "return [ (arguments[0][0]+1), (arguments[0][1]+1) ];", "[ [5, 6] ]");
+ ASSERT_TRUE(params);
+
+ JSONScriptExecutorResult result_handler;
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+ ASSERT_TRUE(result_handler.json_result());
+
+ base::ListValue* list_value;
+ ASSERT_TRUE(result_handler.json_result()->GetAsList(&list_value));
+ ASSERT_EQ(list_value->GetSize(), 2);
+
+ int value;
+ EXPECT_TRUE(list_value->GetInteger(0, &value));
+ EXPECT_EQ(value, 6);
+ EXPECT_TRUE(list_value->GetInteger(1, &value));
+ EXPECT_EQ(value, 7);
+}
+
+TEST_F(ScriptExecutorTest, ConvertObject) {
+ // Create a script that takes an Object with two properties as input, and
+ // returns an Object with one property that is the sum of the other Object's
+ // properties.
+ scoped_refptr<ScriptExecutorParams> params = ScriptExecutorParams::Create(
+ global_environment(),
+ "return {\"sum\": arguments[0].a + arguments[0].b};",
+ "[ {\"a\":5, \"b\":6} ]");
+ ASSERT_TRUE(params);
+
+ JSONScriptExecutorResult result_handler;
+ EXPECT_TRUE(script_executor_->Execute(params, &result_handler));
+ ASSERT_TRUE(result_handler.json_result());
+
+ int value;
+ base::DictionaryValue* dictionary_value;
+ ASSERT_TRUE(result_handler.json_result()->GetAsDictionary(&dictionary_value));
+ EXPECT_TRUE(dictionary_value->GetInteger("sum", &value));
+ EXPECT_EQ(value, 11);
+}
+
+} // namespace webdriver
+} // namespace cobalt
diff --git a/src/cobalt/webdriver/protocol/element_id.cc b/src/cobalt/webdriver/protocol/element_id.cc
index 8891cc7..9d03cf9 100644
--- a/src/cobalt/webdriver/protocol/element_id.cc
+++ b/src/cobalt/webdriver/protocol/element_id.cc
@@ -19,9 +19,8 @@
namespace cobalt {
namespace webdriver {
namespace protocol {
-namespace {
-const char kElementKey[] = "ELEMENT";
-}
+
+const char ElementId::kElementKey[] = "ELEMENT";
scoped_ptr<base::Value> ElementId::ToValue(const ElementId& element_id) {
scoped_ptr<base::DictionaryValue> element_object(new base::DictionaryValue());
diff --git a/src/cobalt/webdriver/protocol/element_id.h b/src/cobalt/webdriver/protocol/element_id.h
index c4e0f58..c19dfb5 100644
--- a/src/cobalt/webdriver/protocol/element_id.h
+++ b/src/cobalt/webdriver/protocol/element_id.h
@@ -30,6 +30,8 @@
// Opaque type that uniquely identifies an Element from a WebDriver session.
class ElementId {
public:
+ static const char kElementKey[];
+
// Convert the ElementId to a WebElement JSON object:
// https://code.google.com/p/selenium/wiki/JsonWireProtocol#WebElement_JSON_Object
static scoped_ptr<base::Value> ToValue(const ElementId& element_id);
diff --git a/src/cobalt/webdriver/protocol/response.h b/src/cobalt/webdriver/protocol/response.h
index 28ffe9a..8cfc549 100644
--- a/src/cobalt/webdriver/protocol/response.h
+++ b/src/cobalt/webdriver/protocol/response.h
@@ -62,6 +62,9 @@
// An error occurred while executing user supplied JavaScript.
kJavaScriptError = 17,
+ // An operation did not complete before its timeout expired.
+ kTimeOut = 21,
+
// The specified window has been closed, or otherwise couldn't be found.
kNoSuchWindow = 23,
diff --git a/src/cobalt/webdriver/script_executor.cc b/src/cobalt/webdriver/script_executor.cc
index ab7a9c8..cd3d63f 100644
--- a/src/cobalt/webdriver/script_executor.cc
+++ b/src/cobalt/webdriver/script_executor.cc
@@ -16,40 +16,107 @@
#include "cobalt/webdriver/script_executor.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/path_service.h"
+#include "cobalt/script/source_code.h"
+
namespace cobalt {
namespace webdriver {
+namespace {
+// Path to the script to initialize the script execution harness.
+const char kWebDriverInitScriptPath[] = "webdriver/webdriver-init.js";
+
+// Wrapper around a scoped_refptr<script::SourceCode> instance. The script
+// at kWebDriverInitScriptPath will be loaded from disk and a new
+// script::SourceCode will be created.
+class LazySourceLoader {
+ public:
+ LazySourceLoader() {
+ FilePath exe_path;
+ if (!PathService::Get(base::DIR_EXE, &exe_path)) {
+ NOTREACHED() << "Failed to get EXE path.";
+ return;
+ }
+ FilePath script_path = exe_path.Append(kWebDriverInitScriptPath);
+ std::string script_contents;
+ if (!file_util::ReadFileToString(script_path, &script_contents)) {
+ NOTREACHED() << "Failed to read script contents.";
+ return;
+ }
+ source_code_ = script::SourceCode::CreateSourceCode(
+ script_contents.c_str(),
+ base::SourceLocation(kWebDriverInitScriptPath, 1, 1));
+ }
+ const scoped_refptr<script::SourceCode>& source_code() {
+ return source_code_;
+ }
+
+ private:
+ scoped_refptr<script::SourceCode> source_code_;
+};
+
+// The script only needs to be loaded once, so allow it to persist as a
+// LazyInstance and be shared amongst different WindowDriver instances.
+base::LazyInstance<LazySourceLoader> lazy_source_loader =
+ LAZY_INSTANCE_INITIALIZER;
+} // namespace
+
+void ScriptExecutor::LoadExecutorSourceCode() { lazy_source_loader.Get(); }
+
+scoped_refptr<ScriptExecutor> ScriptExecutor::Create(
+ ElementMapping* element_mapping,
+ const scoped_refptr<script::GlobalEnvironment>& global_environment) {
+ // This could be NULL if there was an error loading the harness source from
+ // disk.
+ scoped_refptr<script::SourceCode> source =
+ lazy_source_loader.Get().source_code();
+ if (!source) {
+ return NULL;
+ }
+
+ // Create a new ScriptExecutor and bind it to the global object.
+ scoped_refptr<ScriptExecutor> script_executor =
+ new ScriptExecutor(element_mapping);
+ global_environment->Bind("webdriverExecutor", script_executor);
+
+ // Evaluate the harness initialization script.
+ std::string result;
+ if (!global_environment->EvaluateScript(source, &result)) {
+ return NULL;
+ }
+
+ // The initialization script should have set this.
+ DCHECK(script_executor->execute_script_harness());
+ return script_executor;
+}
+
+bool ScriptExecutor::Execute(
+ const scoped_refptr<ScriptExecutorParams>& params,
+ ScriptExecutorResult::ResultHandler* result_handler) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ scoped_refptr<ScriptExecutorResult> executor_result(
+ new ScriptExecutorResult(result_handler));
+ return ExecuteInternal(params, executor_result);
+}
void ScriptExecutor::set_execute_script_harness(
const ExecuteFunctionCallbackHolder& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
- callback_.emplace(this, callback);
+ execute_callback_.emplace(this, callback);
}
const ScriptExecutor::ExecuteFunctionCallbackHolder*
ScriptExecutor::execute_script_harness() {
DCHECK(thread_checker_.CalledOnValidThread());
- if (callback_) {
- return &(callback_->referenced_object());
+ if (execute_callback_) {
+ return &(execute_callback_->referenced_object());
} else {
return NULL;
}
}
-base::optional<std::string> ScriptExecutor::Execute(
- const script::OpaqueHandleHolder* function_object,
- const std::string& json_arguments) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(callback_);
-
- ExecuteFunctionCallback::ReturnValue callback_result =
- callback_->value().Run(function_object, json_arguments);
- if (callback_result.exception) {
- return base::nullopt;
- } else {
- return callback_result.result;
- }
-}
-
scoped_refptr<dom::Element> ScriptExecutor::IdToElement(const std::string& id) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(element_mapping_);
@@ -63,5 +130,16 @@
return element_mapping_->ElementToId(element).id();
}
+bool ScriptExecutor::ExecuteInternal(
+ const scoped_refptr<ScriptExecutorParams>& params,
+ const scoped_refptr<ScriptExecutorResult>& result_handler) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(params);
+ DCHECK(result_handler);
+ ExecuteFunctionCallback::ReturnValue callback_result =
+ execute_callback_->value().Run(params, result_handler);
+ return !callback_result.exception;
+}
+
} // namespace webdriver
} // namespace cobalt
diff --git a/src/cobalt/webdriver/script_executor.h b/src/cobalt/webdriver/script_executor.h
index 494cd5e..a46f1f2 100644
--- a/src/cobalt/webdriver/script_executor.h
+++ b/src/cobalt/webdriver/script_executor.h
@@ -31,6 +31,8 @@
#include "cobalt/script/wrappable.h"
#include "cobalt/webdriver/element_mapping.h"
#include "cobalt/webdriver/protocol/element_id.h"
+#include "cobalt/webdriver/script_executor_params.h"
+#include "cobalt/webdriver/script_executor_result.h"
namespace cobalt {
namespace webdriver {
@@ -40,14 +42,32 @@
public base::SupportsWeakPtr<ScriptExecutor>,
public script::Wrappable {
public:
- typedef script::CallbackFunction<std::string(
- const script::OpaqueHandleHolder*, const std::string&)>
- ExecuteFunctionCallback;
+ typedef script::CallbackFunction<void(
+ const scoped_refptr<ScriptExecutorParams>&,
+ const scoped_refptr<ScriptExecutorResult>&)> ExecuteFunctionCallback;
typedef script::ScriptObject<ExecuteFunctionCallback>
ExecuteFunctionCallbackHolder;
- explicit ScriptExecutor(ElementMapping* mapping)
- : element_mapping_(mapping) {}
+ // This can be called on any thread to preload the webdriver javascript code.
+ // If this is not called, then it will be lazily loaded the first time a
+ // ScriptExecutor is created, which will be the Web Module thread in most
+ // cases.
+ static void LoadExecutorSourceCode();
+
+ // Create a new ScriptExecutor instance.
+ static scoped_refptr<ScriptExecutor> Create(
+ ElementMapping* element_mapping,
+ const scoped_refptr<script::GlobalEnvironment>& global_environment);
+
+ // Calls the function set to the executeScriptHarness property with the
+ // provided |param|. The result of script execution will be passed to the
+ // caller through |result_handler|.
+ // Returns false on execution failure.
+ bool Execute(const scoped_refptr<ScriptExecutorParams>& params,
+ ScriptExecutorResult::ResultHandler* result_handler);
+
+ // ScriptExecutor bindings implementation.
+ //
void set_execute_script_harness(
const ExecuteFunctionCallbackHolder& callback);
@@ -56,19 +76,19 @@
scoped_refptr<dom::Element> IdToElement(const std::string& id);
std::string ElementToId(const scoped_refptr<dom::Element>& id);
- // Calls the function set to the executeScriptHarness property with the
- // provided |function_body| and |json_arguments|. Returns a JSON
- // serialized result string, of base::nullopt on execution failure.
- base::optional<std::string> Execute(
- const script::OpaqueHandleHolder* function_object,
- const std::string& json_arguments);
-
DEFINE_WRAPPABLE_TYPE(ScriptExecutor);
private:
+ explicit ScriptExecutor(ElementMapping* mapping)
+ : element_mapping_(mapping) {}
+
+ bool ExecuteInternal(
+ const scoped_refptr<ScriptExecutorParams>& params,
+ const scoped_refptr<ScriptExecutorResult>& result_handler);
+
base::ThreadChecker thread_checker_;
ElementMapping* element_mapping_;
- base::optional<ExecuteFunctionCallbackHolder::Reference> callback_;
+ base::optional<ExecuteFunctionCallbackHolder::Reference> execute_callback_;
};
} // namespace webdriver
diff --git a/src/cobalt/webdriver/script_executor_params.cc b/src/cobalt/webdriver/script_executor_params.cc
new file mode 100644
index 0000000..d96a325
--- /dev/null
+++ b/src/cobalt/webdriver/script_executor_params.cc
@@ -0,0 +1,50 @@
+/*
+ * 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/webdriver/script_executor_params.h"
+
+#include "base/stringprintf.h"
+#include "cobalt/script/source_code.h"
+
+namespace cobalt {
+namespace webdriver {
+
+scoped_refptr<ScriptExecutorParams> ScriptExecutorParams::Create(
+ const scoped_refptr<script::GlobalEnvironment>& global_environment,
+ const std::string& function_body, const std::string& json_args,
+ base::optional<base::TimeDelta> async_timeout) {
+ scoped_refptr<ScriptExecutorParams> params(new ScriptExecutorParams());
+ params->json_args_ = json_args;
+
+ if (async_timeout) {
+ int async_timeout_ms = async_timeout->InMilliseconds();
+ params->async_timeout_ = async_timeout_ms;
+ }
+
+ std::string function =
+ StringPrintf("(function() {\n%s\n})", function_body.c_str());
+ scoped_refptr<script::SourceCode> function_source =
+ script::SourceCode::CreateSourceCode(
+ function.c_str(), base::SourceLocation("[webdriver]", 1, 1));
+
+ if (!global_environment->EvaluateScript(function_source, params.get(),
+ ¶ms->function_object_)) {
+ DLOG(ERROR) << "Failed to create Function object";
+ }
+ return params;
+}
+} // namespace webdriver
+} // namespace cobalt
diff --git a/src/cobalt/webdriver/script_executor_params.h b/src/cobalt/webdriver/script_executor_params.h
new file mode 100644
index 0000000..2256beb
--- /dev/null
+++ b/src/cobalt/webdriver/script_executor_params.h
@@ -0,0 +1,70 @@
+/*
+ * 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_WEBDRIVER_SCRIPT_EXECUTOR_PARAMS_H_
+#define COBALT_WEBDRIVER_SCRIPT_EXECUTOR_PARAMS_H_
+
+#if defined(ENABLE_WEBDRIVER)
+
+#include <string>
+
+#include "base/optional.h"
+#include "base/threading/thread_checker.h"
+#include "base/time.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/opaque_handle.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace webdriver {
+
+// An instance of the ScriptExecutorResult is passed to the webdriver script
+// execution harness to collect the results of running a script via webdriver.
+// The results are forwarded to a ResultHandler instance.
+class ScriptExecutorParams : public script::Wrappable {
+ public:
+ static scoped_refptr<ScriptExecutorParams> Create(
+ const scoped_refptr<script::GlobalEnvironment>& global_environment,
+ const std::string& function_body, const std::string& json_args) {
+ return Create(global_environment, function_body, json_args, base::nullopt);
+ }
+
+ static scoped_refptr<ScriptExecutorParams> Create(
+ const scoped_refptr<script::GlobalEnvironment>& global_environment,
+ const std::string& function_body, const std::string& json_args,
+ base::optional<base::TimeDelta> async_timeout);
+
+ const script::OpaqueHandleHolder* function_object() {
+ return function_object_ ? &function_object_->referenced_object() : NULL;
+ }
+ const std::string& json_args() { return json_args_; }
+ base::optional<int32_t> async_timeout() { return async_timeout_; }
+
+ DEFINE_WRAPPABLE_TYPE(ScriptExecutorParams);
+
+ private:
+ std::string function_body_;
+ base::optional<script::OpaqueHandleHolder::Reference> function_object_;
+ std::string json_args_;
+ base::optional<int32_t> async_timeout_;
+};
+
+} // namespace webdriver
+} // namespace cobalt
+
+#endif // defined(ENABLE_WEBDRIVER)
+
+#endif // COBALT_WEBDRIVER_SCRIPT_EXECUTOR_PARAMS_H_
diff --git a/src/cobalt/webdriver/script_executor_result.h b/src/cobalt/webdriver/script_executor_result.h
new file mode 100644
index 0000000..aa68d80
--- /dev/null
+++ b/src/cobalt/webdriver/script_executor_result.h
@@ -0,0 +1,79 @@
+/*
+ * 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_WEBDRIVER_SCRIPT_EXECUTOR_RESULT_H_
+#define COBALT_WEBDRIVER_SCRIPT_EXECUTOR_RESULT_H_
+
+#if defined(ENABLE_WEBDRIVER)
+
+#include <string>
+
+#include "base/threading/thread_checker.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace webdriver {
+
+// An instance of the ScriptExecutorResult is passed to the webdriver script
+// execution harness to collect the results of running a script via webdriver.
+// The results are forwarded to a ResultHandler instance.
+class ScriptExecutorResult : public script::Wrappable {
+ public:
+ // The ScriptExecutorResult class must only be accessed from the main thread,
+ // so use an instance of the ResultHandler class to access the results of
+ // execution from another thread.
+ // Exactly one of OnResult and OnTimeout will be called. After one of these
+ // is called, the ScriptExectutorResult object will no longer hold a pointer
+ // to the ResultHandler instance, so it's safe for the owning thread to
+ // destroy it.
+ class ResultHandler {
+ public:
+ virtual void OnResult(const std::string& result) = 0;
+ virtual void OnTimeout() = 0;
+ };
+
+ explicit ScriptExecutorResult(ScriptExecutorResult::ResultHandler* handler)
+ : result_handler_(handler) {}
+
+ void OnResult(const std::string& result) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (result_handler_) {
+ result_handler_->OnResult(result);
+ result_handler_ = NULL;
+ }
+ }
+
+ void OnTimeout() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (result_handler_) {
+ result_handler_->OnTimeout();
+ result_handler_ = NULL;
+ }
+ }
+
+ DEFINE_WRAPPABLE_TYPE(ScriptExecutorResult);
+
+ private:
+ base::ThreadChecker thread_checker_;
+ ResultHandler* result_handler_;
+};
+
+} // namespace webdriver
+} // namespace cobalt
+
+#endif // defined(ENABLE_WEBDRIVER)
+
+#endif // COBALT_WEBDRIVER_SCRIPT_EXECUTOR_RESULT_H_
diff --git a/src/cobalt/webdriver/server.cc b/src/cobalt/webdriver/server.cc
index 6573cd7..94ad4ca 100644
--- a/src/cobalt/webdriver/server.cc
+++ b/src/cobalt/webdriver/server.cc
@@ -179,7 +179,7 @@
WebDriverServer::WebDriverServer(int port,
const HandleRequestCallback& callback)
: handle_request_callback_(callback) {
- DLOG(INFO) << "Starting WebDriver server on port " << port;
+ LOG(INFO) << "Starting WebDriver server on port " << port;
// Create http server
factory_.reset(new net::TCPListenSocketFactory("0.0.0.0", port));
server_ = new net::HttpServer(*factory_, this);
diff --git a/src/cobalt/webdriver/testing/stub_window.h b/src/cobalt/webdriver/testing/stub_window.h
new file mode 100644
index 0000000..aa6c912
--- /dev/null
+++ b/src/cobalt/webdriver/testing/stub_window.h
@@ -0,0 +1,93 @@
+/*
+ * 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_WEBDRIVER_TESTING_STUB_WINDOW_H_
+#define COBALT_WEBDRIVER_TESTING_STUB_WINDOW_H_
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/message_loop.h"
+#include "cobalt/css_parser/parser.h"
+#include "cobalt/dom/local_storage_database.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/dom_parser/parser.h"
+#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/media/media_module_stub.h"
+#include "cobalt/network/network_module.h"
+#include "googleurl/src/gurl.h"
+
+namespace cobalt {
+namespace webdriver {
+namespace testing {
+
+// A helper class for WebDriver tests that brings up a dom::Window with a number
+// of parts stubbed out.
+class StubWindow {
+ public:
+ StubWindow()
+ : message_loop_(MessageLoop::TYPE_DEFAULT),
+ css_parser_(css_parser::Parser::Create()),
+ dom_parser_(new dom_parser::Parser(base::Bind(&StubErrorCallback))),
+ fetcher_factory_(new loader::FetcherFactory(&network_module_)),
+ local_storage_database_(NULL),
+ stub_media_module_(new media::MediaModuleStub()),
+ url_("about:blank"),
+ dom_stat_tracker_(new dom::DomStatTracker("StubWindow")) {
+ 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,
+ &local_storage_database_, stub_media_module_.get(),
+ stub_media_module_.get(), NULL, NULL, NULL, dom_stat_tracker_.get(),
+ url_, "", "en-US", base::Callback<void(const GURL&)>(),
+ base::Bind(&StubErrorCallback), NULL, network_bridge::PostSender(),
+ std::string() /* default security policy */, dom::kCspEnforcementEnable,
+ base::Closure() /* csp_policy_changed */,
+ base::Closure() /* window_close */);
+ global_environment_->CreateGlobalObject(window_, &environment_settings_);
+ }
+
+ scoped_refptr<dom::Window> window() { return window_; }
+ scoped_refptr<script::GlobalEnvironment> global_environment() {
+ return global_environment_;
+ }
+
+ private:
+ static void StubErrorCallback(const std::string& /*error*/) {}
+
+ MessageLoop message_loop_;
+ scoped_ptr<css_parser::Parser> css_parser_;
+ scoped_ptr<dom_parser::Parser> dom_parser_;
+ network::NetworkModule network_module_;
+ scoped_ptr<loader::FetcherFactory> fetcher_factory_;
+ dom::LocalStorageDatabase local_storage_database_;
+ scoped_ptr<media::MediaModule> stub_media_module_;
+ GURL url_;
+ scoped_ptr<dom::DomStatTracker> dom_stat_tracker_;
+ script::EnvironmentSettings environment_settings_;
+ scoped_ptr<script::JavaScriptEngine> engine_;
+ scoped_refptr<script::GlobalEnvironment> global_environment_;
+ scoped_refptr<dom::Window> window_;
+};
+
+} // namespace testing
+} // namespace webdriver
+} // namespace cobalt
+
+#endif // COBALT_WEBDRIVER_TESTING_STUB_WINDOW_H_
diff --git a/src/cobalt/webdriver/web_driver_module.cc b/src/cobalt/webdriver/web_driver_module.cc
index 46a77e8..3d510cb 100644
--- a/src/cobalt/webdriver/web_driver_module.cc
+++ b/src/cobalt/webdriver/web_driver_module.cc
@@ -294,6 +294,11 @@
base::Bind(&WindowDriver::Execute)));
webdriver_dispatcher_->RegisterCommand(
WebDriverServer::kPost,
+ StringPrintf("/session/%s/execute_async", kSessionIdVariable),
+ current_window_command_factory->GetCommandHandler(
+ base::Bind(&WindowDriver::ExecuteAsync)));
+ webdriver_dispatcher_->RegisterCommand(
+ WebDriverServer::kPost,
StringPrintf("/session/%s/element", kSessionIdVariable),
current_window_command_factory->GetCommandHandler(
base::Bind(&WindowDriver::FindElement)));
diff --git a/src/cobalt/webdriver/webdriver.gyp b/src/cobalt/webdriver/webdriver.gyp
index 558a0ee..1463a01 100644
--- a/src/cobalt/webdriver/webdriver.gyp
+++ b/src/cobalt/webdriver/webdriver.gyp
@@ -61,6 +61,9 @@
'protocol/window_id.h',
'script_executor.cc',
'script_executor.h',
+ 'script_executor_params.cc',
+ 'script_executor_params.h',
+ 'script_executor_result.h',
'search.cc',
'search.h',
'server.cc',
@@ -86,53 +89,7 @@
],
},
- {
- 'target_name': 'webdriver_test',
- 'type': '<(gtest_target_type)',
- 'conditions': [
- ['enable_webdriver==1', {
- 'sources': [
- 'get_element_text_test.cc',
- 'is_displayed_test.cc',
- 'keyboard_test.cc',
- ],
- }],
- ],
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:run_all_unittests',
- '<(DEPTH)/cobalt/base/base.gyp:base',
- '<(DEPTH)/cobalt/css_parser/css_parser.gyp:css_parser',
- '<(DEPTH)/cobalt/dom/dom.gyp:dom',
- '<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:dom_parser',
- '<(DEPTH)/testing/gmock.gyp:gmock',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- 'webdriver',
- ],
- 'actions': [
- {
- 'action_name': 'webdriver_test_copy_test_data',
- 'variables': {
- 'input_files': [
- '<(DEPTH)/cobalt/webdriver/testdata/',
- ],
- 'output_dir': 'cobalt/webdriver_test',
- },
- 'includes': ['../build/copy_test_data.gypi'],
- }
- ],
- },
- {
- 'target_name': 'webdriver_test_deploy',
- 'type': 'none',
- 'dependencies': [
- 'webdriver_test',
- ],
- 'variables': {
- 'executable_name': 'webdriver_test',
- },
- 'includes': [ '../../starboard/build/deploy.gypi' ],
- },
{
'target_name': 'copy_webdriver_data',
diff --git a/src/cobalt/webdriver/webdriver_test.gyp b/src/cobalt/webdriver/webdriver_test.gyp
new file mode 100644
index 0000000..d01bef5
--- /dev/null
+++ b/src/cobalt/webdriver/webdriver_test.gyp
@@ -0,0 +1,48 @@
+{
+ 'targets': [
+ {
+ 'target_name': 'webdriver_test',
+ 'type': '<(gtest_target_type)',
+ 'conditions': [
+ ['enable_webdriver==1', {
+ 'sources': [
+ 'get_element_text_test.cc',
+ 'is_displayed_test.cc',
+ 'keyboard_test.cc',
+ 'execute_test.cc',
+ ],
+ }],
+ ],
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:run_all_unittests',
+ '<(DEPTH)/cobalt/browser/browser.gyp:browser',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'webdriver_test_copy_test_data',
+ 'variables': {
+ 'input_files': [
+ '<(DEPTH)/cobalt/webdriver/testdata/',
+ ],
+ 'output_dir': 'cobalt/webdriver_test',
+ },
+ 'includes': ['../build/copy_test_data.gypi'],
+ }
+ ],
+ },
+
+ {
+ 'target_name': 'webdriver_test_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'webdriver_test',
+ ],
+ 'variables': {
+ 'executable_name': 'webdriver_test',
+ },
+ 'includes': [ '../../starboard/build/deploy.gypi' ],
+ },
+ ]
+}
diff --git a/src/cobalt/webdriver/window_driver.cc b/src/cobalt/webdriver/window_driver.cc
index a21d3b6..57b0467 100644
--- a/src/cobalt/webdriver/window_driver.cc
+++ b/src/cobalt/webdriver/window_driver.cc
@@ -18,16 +18,10 @@
#include <utility>
-#include "base/file_path.h"
-#include "base/file_util.h"
-#include "base/lazy_instance.h"
-#include "base/path_service.h"
-#include "base/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "cobalt/dom/document.h"
#include "cobalt/dom/location.h"
#include "cobalt/script/global_environment.h"
-#include "cobalt/script/source_code.h"
#include "cobalt/webdriver/keyboard.h"
#include "cobalt/webdriver/search.h"
#include "cobalt/webdriver/util/call_on_message_loop.h"
@@ -35,87 +29,45 @@
namespace cobalt {
namespace webdriver {
namespace {
-// Path to the script to initialize the script execution harness.
-const char kWebDriverInitScriptPath[] = "webdriver/webdriver-init.js";
-// Wrapper around a scoped_refptr<script::SourceCode> instance. The script
-// at kWebDriverInitScriptPath will be loaded from disk and a new
-// script::SourceCode will be created.
-class LazySourceLoader {
+class SyncExecuteResultHandler : public ScriptExecutorResult::ResultHandler {
public:
- LazySourceLoader() {
- FilePath exe_path;
- if (!PathService::Get(base::DIR_EXE, &exe_path)) {
- NOTREACHED() << "Failed to get EXE path.";
- return;
- }
- FilePath script_path = exe_path.Append(kWebDriverInitScriptPath);
- std::string script_contents;
- if (!file_util::ReadFileToString(script_path, &script_contents)) {
- NOTREACHED() << "Failed to read script contents.";
- return;
- }
- source_code_ = script::SourceCode::CreateSourceCode(
- script_contents.c_str(),
- base::SourceLocation(kWebDriverInitScriptPath, 1, 1));
+ void OnResult(const std::string& result) OVERRIDE {
+ DCHECK(!result_);
+ result_ = result;
}
- const scoped_refptr<script::SourceCode>& source_code() {
- return source_code_;
+ void OnTimeout() OVERRIDE { NOTREACHED(); }
+ std::string result() const {
+ DCHECK(result_);
+ return result_.value_or(std::string());
}
private:
- scoped_refptr<script::SourceCode> source_code_;
+ base::optional<std::string> result_;
};
-// The script only needs to be loaded once, so allow it to persist as a
-// LazyInstance and be shared amongst different WindowDriver instances.
-base::LazyInstance<LazySourceLoader> lazy_source_loader =
- LAZY_INSTANCE_INITIALIZER;
+class AsyncExecuteResultHandler : public ScriptExecutorResult::ResultHandler {
+ public:
+ AsyncExecuteResultHandler() : timed_out_(false), event_(true, false) {}
-scoped_refptr<ScriptExecutor> CreateScriptExecutor(
- ElementMapping* element_mapping,
- const scoped_refptr<script::GlobalEnvironment>& global_environment) {
- // This could be NULL if there was an error loading the harness source from
- // disk.
- scoped_refptr<script::SourceCode> source =
- lazy_source_loader.Get().source_code();
- if (!source) {
- return NULL;
+ void WaitForResult() { event_.Wait(); }
+ bool timed_out() const { return timed_out_; }
+ const std::string& result() const { return result_; }
+
+ private:
+ void OnResult(const std::string& result) OVERRIDE {
+ result_ = result;
+ event_.Signal();
+ }
+ void OnTimeout() OVERRIDE {
+ timed_out_ = true;
+ event_.Signal();
}
- // Create a new ScriptExecutor and bind it to the global object.
- scoped_refptr<ScriptExecutor> script_executor =
- new ScriptExecutor(element_mapping);
- global_environment->Bind("webdriverExecutor", script_executor);
-
- // Evaluate the harness initialization script.
- std::string result;
- if (!global_environment->EvaluateScript(source, &result)) {
- return NULL;
- }
-
- // The initialization script should have set this.
- DCHECK(script_executor->execute_script_harness());
- return script_executor;
-}
-
-void CreateFunction(
- const std::string& function_body,
- const scoped_refptr<script::GlobalEnvironment>& global_environment,
- scoped_refptr<ScriptExecutor> script_executor,
- base::optional<script::OpaqueHandleHolder::Reference>* out_opaque_handle) {
- std::string function =
- StringPrintf("(function() {\n%s\n})", function_body.c_str());
- scoped_refptr<script::SourceCode> function_source =
- script::SourceCode::CreateSourceCode(
- function.c_str(), base::SourceLocation("[webdriver]", 1, 1));
-
- if (!global_environment->EvaluateScript(
- function_source, make_scoped_refptr(script_executor.get()),
- out_opaque_handle)) {
- DLOG(ERROR) << "Failed to create Function object";
- }
-}
+ std::string result_;
+ bool timed_out_;
+ base::WaitableEvent event_;
+};
std::string GetCurrentUrl(dom::Window* window) {
DCHECK(window);
@@ -258,12 +210,51 @@
util::CommandResult<protocol::ScriptResult> WindowDriver::Execute(
const protocol::Script& script) {
+ typedef util::CommandResult<protocol::ScriptResult> CommandResult;
DCHECK(thread_checker_.CalledOnValidThread());
- // Poke the lazy loader so we don't hit the disk on window_message_loop_.
- lazy_source_loader.Get();
- return util::CallOnMessageLoop(
- window_message_loop_, base::Bind(&WindowDriver::ExecuteScriptInternal,
- base::Unretained(this), script));
+ // Pre-load the ScriptExecutor source so we don't hit the disk on
+ // window_message_loop_.
+ ScriptExecutor::LoadExecutorSourceCode();
+
+ SyncExecuteResultHandler result_handler;
+
+ CommandResult result = util::CallOnMessageLoop(
+ window_message_loop_,
+ base::Bind(&WindowDriver::ExecuteScriptInternal, base::Unretained(this),
+ script, base::nullopt, &result_handler));
+ if (result.is_success()) {
+ return CommandResult(protocol::ScriptResult(result_handler.result()));
+ } else {
+ return result;
+ }
+}
+
+util::CommandResult<protocol::ScriptResult> WindowDriver::ExecuteAsync(
+ const protocol::Script& script) {
+ typedef util::CommandResult<protocol::ScriptResult> CommandResult;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // Pre-load the ScriptExecutor source so we don't hit the disk on
+ // window_message_loop_.
+ ScriptExecutor::LoadExecutorSourceCode();
+
+ const base::TimeDelta kDefaultAsyncTimeout =
+ base::TimeDelta::FromMilliseconds(0);
+ AsyncExecuteResultHandler result_handler;
+ CommandResult result = util::CallOnMessageLoop(
+ window_message_loop_,
+ base::Bind(&WindowDriver::ExecuteScriptInternal, base::Unretained(this),
+ script, kDefaultAsyncTimeout, &result_handler));
+
+ if (!result.is_success()) {
+ return result;
+ }
+
+ result_handler.WaitForResult();
+ if (result_handler.timed_out()) {
+ return CommandResult(protocol::Response::kTimeOut);
+ } else {
+ return CommandResult(protocol::ScriptResult(result_handler.result()));
+ }
}
util::CommandResult<void> WindowDriver::SendKeys(const protocol::Keys& keys) {
@@ -376,7 +367,9 @@
}
util::CommandResult<protocol::ScriptResult> WindowDriver::ExecuteScriptInternal(
- const protocol::Script& script) {
+ const protocol::Script& script,
+ base::optional<base::TimeDelta> async_timeout,
+ ScriptExecutorResult::ResultHandler* async_handler) {
typedef util::CommandResult<protocol::ScriptResult> CommandResult;
DCHECK_EQ(base::MessageLoopProxy::current(), window_message_loop_);
if (!window_) {
@@ -393,7 +386,7 @@
// global object, thus with the WindowDriver.
if (!script_executor_) {
scoped_refptr<ScriptExecutor> script_executor =
- CreateScriptExecutor(this, global_environment);
+ ScriptExecutor::Create(this, global_environment);
if (!script_executor) {
DLOG(INFO) << "Failed to create ScriptExecutor.";
return CommandResult(protocol::Response::kUnknownError);
@@ -404,20 +397,16 @@
DLOG(INFO) << "Executing: " << script.function_body();
DLOG(INFO) << "Arguments: " << script.argument_array();
- base::optional<script::OpaqueHandleHolder::Reference> function_object;
- CreateFunction(script.function_body(), global_environment,
- make_scoped_refptr(script_executor_.get()), &function_object);
- if (!function_object) {
- return CommandResult(protocol::Response::kJavaScriptError);
- }
+ scoped_refptr<ScriptExecutorParams> params =
+ ScriptExecutorParams::Create(global_environment, script.function_body(),
+ script.argument_array(), async_timeout);
- base::optional<std::string> script_result = script_executor_->Execute(
- &(function_object->referenced_object()), script.argument_array());
- if (script_result) {
- return CommandResult(protocol::ScriptResult(script_result.value()));
- } else {
- return CommandResult(protocol::Response::kJavaScriptError);
+ if (params->function_object()) {
+ if (script_executor_->Execute(params, async_handler)) {
+ return CommandResult(protocol::Response::kSuccess);
+ }
}
+ return CommandResult(protocol::Response::kJavaScriptError);
}
util::CommandResult<void> WindowDriver::SendKeysInternal(
diff --git a/src/cobalt/webdriver/window_driver.h b/src/cobalt/webdriver/window_driver.h
index f67d272..e68f06d 100644
--- a/src/cobalt/webdriver/window_driver.h
+++ b/src/cobalt/webdriver/window_driver.h
@@ -82,6 +82,8 @@
util::CommandResult<std::string> GetSource();
util::CommandResult<protocol::ScriptResult> Execute(
const protocol::Script& script);
+ util::CommandResult<protocol::ScriptResult> ExecuteAsync(
+ const protocol::Script& script);
util::CommandResult<void> SendKeys(const protocol::Keys& keys);
util::CommandResult<protocol::ElementId> GetActiveElement();
util::CommandResult<void> SwitchFrame(const protocol::FrameId& frame_id);
@@ -117,7 +119,9 @@
const protocol::SearchStrategy& strategy);
util::CommandResult<protocol::ScriptResult> ExecuteScriptInternal(
- const protocol::Script& script);
+ const protocol::Script& script,
+ base::optional<base::TimeDelta> async_timeout,
+ ScriptExecutorResult::ResultHandler* result_handler);
util::CommandResult<void> SendKeysInternal(
scoped_ptr<Keyboard::KeyboardEventVector> keyboard_events);
diff --git a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
index fa2642d..cc7547c 100755
--- a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
+++ b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
@@ -15,6 +15,7 @@
import re
import socket
import sys
+import thread
import threading
import unittest
@@ -49,6 +50,10 @@
# opened.
RE_WEBDRIVER_LISTEN = re.compile(r"Starting WebDriver server on port (\d+)$")
+STARTUP_TIMEOUT_SECONDS = 2 * 60
+
+COBALT_EXIT_TIMEOUT_SECONDS = 5
+
COBALT_WEBDRIVER_CAPABILITIES = {
"browserName": "cobalt",
"javascriptEnabled": True,
@@ -96,15 +101,20 @@
return module
+class TimeoutException(Exception):
+ pass
+
+
class CobaltRunner(object):
"""Runs a Cobalt browser w/ a WebDriver client attached."""
test_script_started = threading.Event()
- should_exit = threading.Event()
selenium_webdriver_module = None
webdriver = None
launcher = None
log_file_path = None
thread = None
+ failed = False
+ should_exit = threading.Event()
def __init__(self, platform, executable, devkit_name, log_file_path):
self.selenium_webdriver_module = ImportSeleniumModule("webdriver")
@@ -126,31 +136,41 @@
def __enter__(self):
self.thread = threading.Thread(target=self.Run)
self.thread.start()
- self.WaitForStart()
+ try:
+ self.WaitForStart()
+ except KeyboardInterrupt:
+ # potentially from thread.interrupt_main(). We will treat as
+ # a timeout regardless
+ raise TimeoutException
def __exit__(self, exc_type, exc_value, traceback):
- self.SetShouldExit()
- self.thread.join()
+ # The unittest module terminates with a SystemExit
+ # If this is a successful exit, then this is a successful run
+ success = exc_type is None or (exc_type is SystemExit
+ and not exc_value.code)
+ self.SetShouldExit(failed=not success)
+ self.thread.join(COBALT_EXIT_TIMEOUT_SECONDS)
def _HandleLine(self, line):
"""Internal log line callback."""
- done = self.should_exit.is_set()
# Wait for WebDriver port here then connect
if self.test_script_started.is_set():
- return done
+ return
match = RE_WEBDRIVER_LISTEN.search(line)
if not match:
- return done
+ return
port = match.group(1)
+ sys.stderr.write("WebDriver port opened:" + port + "\n")
self._StartWebdriver(port)
- return done
- def SetShouldExit(self):
- """Indicates cobalt process should exit. Done at next log line output."""
+ def SetShouldExit(self, failed=False):
+ """Indicates cobalt process should exit."""
+ self.failed = failed
self.should_exit.set()
+ self.launcher.SendKill()
def _GetIPAddress(self):
return self.launcher.GetIPAddress()
@@ -160,12 +180,16 @@
url = "http://{}:{}/".format(self._GetIPAddress(), port)
self.webdriver = self.selenium_webdriver_module.Remote(
url, COBALT_WEBDRIVER_CAPABILITIES)
+ sys.stderr.write("Selenium Connected\n")
_webdriver = self.webdriver
self.test_script_started.set()
def WaitForStart(self):
"""Waits for the webdriver client to attach to cobalt."""
- self.test_script_started.wait()
+ if not self.test_script_started.wait(STARTUP_TIMEOUT_SECONDS):
+ self.SetShouldExit(failed=True)
+ raise TimeoutException
+ sys.stderr.write("Cobalt started\n")
def Run(self):
"""Thread run routine."""
@@ -181,12 +205,21 @@
self.log_file = sys.stdout
self.launcher.SetOutputFile(self.log_file)
+ sys.stderr.write("Running launcher \n")
self.launcher.Run()
+ sys.stderr.write("Cobalt terminated. failed: " + str(self.failed) + "\n")
# This is watched for in webdriver_benchmark_test.py
- sys.stdout.write("partial_layout_benchmark TEST COMPLETE\n")
+ if not self.failed:
+ sys.stdout.write("partial_layout_benchmark TEST COMPLETE\n")
+ except Exception as ex:
+ print("Exception running Cobalt " + str(ex), file=sys.stderr)
finally:
if to_close:
to_close.close()
+ if not self.should_exit.is_set():
+ # If the main thread is not expecting us to exit,
+ # we must interrupt it.
+ thread.interrupt_main()
return 0
@@ -226,9 +259,13 @@
if devkit_name is None:
devkit_name = socket.gethostname()
- with CobaltRunner(platform, executable, devkit_name, args.log_file):
- unittest.main()
- return 0
+ try:
+ with CobaltRunner(platform, executable, devkit_name, args.log_file):
+ unittest.main()
+ return 0
+ except TimeoutException:
+ print("Timeout waiting for Cobalt to start", file=sys.stderr)
+ return 1
if __name__ == "__main__":
diff --git a/src/cobalt/webdriver_benchmarks/tests/guide.py b/src/cobalt/webdriver_benchmarks/tests/guide.py
index 2ebf58e..324daef 100755
--- a/src/cobalt/webdriver_benchmarks/tests/guide.py
+++ b/src/cobalt/webdriver_benchmarks/tests/guide.py
@@ -9,8 +9,7 @@
import sys
# The parent directory is a module
-sys.path.insert(0, os.path.dirname(os.path.dirname(
- os.path.realpath(__file__))))
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
# pylint: disable=C6204,C6203
import tv
@@ -36,9 +35,7 @@
self.assert_displayed(tv.FOCUSED_GUIDE)
self.wait_for_layout_complete()
self.send_keys(tv.FOCUSED_GUIDE, keys.Keys.ARROW_RIGHT)
- self.poll_until_found(tv.FOCUSED_SHELF)
- self.assert_displayed(tv.FOCUSED_SHELF_TITLE)
- self.wait_for_layout_complete()
+ self.wait_for_layout_complete_after_focused_shelf()
layout_times_us.append(self.get_keyup_layout_duration_us())
self.record_results("GuideTest.test_simple", layout_times_us)
diff --git a/src/cobalt/webdriver_benchmarks/tests/shelf.py b/src/cobalt/webdriver_benchmarks/tests/shelf.py
index 7372e55..ce058e0 100755
--- a/src/cobalt/webdriver_benchmarks/tests/shelf.py
+++ b/src/cobalt/webdriver_benchmarks/tests/shelf.py
@@ -9,8 +9,7 @@
import sys
# The parent directory is a module
-sys.path.insert(0, os.path.dirname(os.path.dirname(
- os.path.realpath(__file__))))
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
# pylint: disable=C6204,C6203
import tv
@@ -33,16 +32,12 @@
for _ in xrange(DEFAULT_SHELVES_COUNT):
self.send_keys(tv.FOCUSED_SHELF, keys.Keys.ARROW_DOWN)
- self.poll_until_found(tv.FOCUSED_SHELF)
- self.assert_displayed(tv.FOCUSED_SHELF_TITLE)
- self.wait_for_layout_complete()
+ self.wait_for_layout_complete_after_focused_shelf()
layout_times_us.append(self.get_keyup_layout_duration_us())
for _ in xrange(SHELF_ITEMS_COUNT):
self.send_keys(tv.FOCUSED_TILE, keys.Keys.ARROW_RIGHT)
- self.poll_until_found(tv.FOCUSED_TILE)
- self.assert_displayed(tv.FOCUSED_SHELF_TITLE)
- self.wait_for_layout_complete()
+ self.wait_for_layout_complete_after_focused_shelf()
layout_times_us.append(self.get_keyup_layout_duration_us())
self.record_results("ShelfTest.test_simple", layout_times_us)
diff --git a/src/cobalt/webdriver_benchmarks/tests/test_median.py b/src/cobalt/webdriver_benchmarks/tests/test_median.py
new file mode 100755
index 0000000..f265b15
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/tests/test_median.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python2
+# 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.
+# ==============================================================================
+"""Tests the functionality median function."""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import sys
+# The parent directory is a module
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
+
+# pylint: disable=C6204,C6203
+from tv_testcase import _median as median
+import unittest
+
+
+class MedianTest(unittest.TestCase):
+
+ def test_empty_case(self):
+ self.assertEqual(median([]), None)
+
+ def test_one_item(self):
+ self.assertEqual(median([1]), 1)
+
+ def test_two_items(self):
+ self.assertAlmostEqual(median([4, 1]), 2.5)
+
+ def test_three_items(self):
+ self.assertAlmostEqual(median([4, -4, -2]), -2)
+
+ def test_four_items(self):
+ self.assertAlmostEqual(median([4, 0, 1, -2]), 0.5)
+
+
+if __name__ == '__main__':
+ sys.exit(unittest.main())
diff --git a/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py b/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
new file mode 100755
index 0000000..b1ede63
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python2
+# 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.
+# ==============================================================================
+"""Calculate time to download YouTube TV app and initial layout of shelves."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import sys
+
+# The parent directory is a module
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
+
+# pylint: disable=C6204,C6203
+import tv_testcase
+
+
+class TimeToShelf(tv_testcase.TvTestCase):
+
+ def test_simple(self):
+ """This test tries to measure the startup time for the YouTube TV page.
+
+ Specifically, this test uses the Cobalt CVal Cobalt.Lifetime, which gets
+ updated ~60Hz on a best effort basis. This value is in microseconds.
+
+ Note: t0 is defined after Cobalt starts up, but has not navigated to a page.
+ If that true startup time metric is desired, perhaps a separate should be
+ used.
+ """
+ metrics_array = []
+ blank_startup_time_microseconds = self.get_int_cval('Cobalt.Lifetime')
+ for _ in range(10):
+ t0 = self.get_int_cval('Cobalt.Lifetime')
+ self.load_tv()
+ self.wait_for_layout_complete_after_focused_shelf()
+ t1 = self.get_int_cval('Cobalt.Lifetime')
+ startup_time_microseconds = t1 - t0
+ metrics_array.append(startup_time_microseconds)
+
+ self.record_results('TimeToShelf.test_time_shelf_display', metrics_array)
+ self.record_results('TimeToShelf.blank_startup_time',
+ blank_startup_time_microseconds)
+
+
+if __name__ == '__main__':
+ tv_testcase.main()
diff --git a/src/cobalt/webdriver_benchmarks/timer.py b/src/cobalt/webdriver_benchmarks/timer.py
new file mode 100644
index 0000000..0ee08dc
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/timer.py
@@ -0,0 +1,94 @@
+# 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_testcase.py b/src/cobalt/webdriver_benchmarks/tv_testcase.py
index 3189523..cbb5903 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase.py
@@ -9,7 +9,16 @@
import sys
import time
import unittest
-import urlparse
+
+# pylint: disable=C6204
+try:
+ # This code works for Python 2.
+ import urlparse
+ from urllib import urlencode
+except ImportError:
+ # This code works for Python 3.
+ import urllib.parse as urlparse
+ from urllib.parse import urlencode
# This directory is a package
sys.path.insert(0, os.path.abspath("."))
@@ -22,15 +31,48 @@
WebDriverWait = partial_layout_benchmark.ImportSeleniumModule(
submodule="webdriver.support.ui").WebDriverWait
-ElementNotVisibleException = (
- partial_layout_benchmark.ImportSeleniumModule(
- submodule="common.exceptions").ElementNotVisibleException)
+ElementNotVisibleException = (partial_layout_benchmark.ImportSeleniumModule(
+ submodule="common.exceptions").ElementNotVisibleException)
-BASE_URL = "https://www.youtube.com/tv?env_forcedOffAllExperiments=true"
+BASE_URL = "https://www.youtube.com/"
+TV_APP_PATH = "/tv"
+BASE_PARAMS = {"env_forcedOffAllExperiments": True}
PAGE_LOAD_WAIT_SECONDS = 30
LAYOUT_TIMEOUT_SECONDS = 5
+def _median(l):
+ """Returns median value of items in a list.
+
+ Note: This function is setup for convieniance, and might not be performant
+ for large datasets. Also, no care has been taken to deal with nans, or
+ infinities, so please expand the functionality and tests if that is desired.
+ The other option is to use median.
+
+ Running time: O(n^2)
+ Space complexity: O(n)
+
+ Args:
+ l: List containing sortable items.
+
+ Returns:
+ Median of the items in a list. None if |l| is empty.
+ """
+ if not l:
+ return None
+ l_length = len(l)
+ if l_length == 0:
+ return l[0]
+
+ l_sorted = sorted(l)
+ middle_index = l_length // 2
+ if len(l) % 2 == 1:
+ return l_sorted[middle_index]
+
+ # If there are even number of items, take the average of two middle values.
+ return 0.5 * (l_sorted[middle_index] + l_sorted[middle_index - 1])
+
+
class TvTestCase(unittest.TestCase):
"""Base class for WebDriver tests.
@@ -44,23 +86,66 @@
def get_webdriver(self):
return partial_layout_benchmark.GetWebDriver()
- def goto(self, path):
+ def get_cval(self, cval_name):
+ """Returns value of a cval.
+
+ Args:
+ cval_name: Name of the cval.
+ Returns:
+ Value of the cval.
+ """
+ javascript_code = "return h5vcc.cVal.getValue('{}')".format(cval_name)
+ return self.get_webdriver().execute_script(javascript_code)
+
+ def get_int_cval(self, cval_name):
+ """Returns int value of a cval.
+
+ The cval value must be an integer, if it is a float, then this function will
+ throw a ValueError.
+
+ Args:
+ cval_name: Name of the cval.
+ Returns:
+ Value of the cval.
+ Raises:
+ ValueError if the cval is cannot be converted to int.
+ """
+ answer = self.get_cval(cval_name)
+ if answer is None:
+ return answer
+ return int(answer)
+
+ def goto(self, path, query_params=None):
"""Goes to a path off of BASE_URL.
Args:
- path: URL path
+ path: URL path without the hostname.
+ query_params: Dictionary of parameter names and values.
Raises:
Underlying WebDriver exceptions
"""
- self.get_webdriver().get(urlparse.urljoin(BASE_URL, path))
+ parsed_url = list(urlparse.urlparse(BASE_URL))
+ parsed_url[2] = path
+ query_dict = BASE_PARAMS.copy()
+ if query_params:
+ query_dict.update(urlparse.parse_qsl(parsed_url[4]))
+ query_dict.update(query_params)
+ parsed_url[4] = urlencode(query_dict)
+ final_url = urlparse.urlunparse(parsed_url)
+ self.get_webdriver().get(final_url)
- def load_tv(self):
+ def load_tv(self, label=None):
"""Loads the main TV page and waits for it to display.
+ Args:
+ label: A value for the label query parameter.
Raises:
Underlying WebDriver exceptions
"""
- self.goto("")
+ query_params = None
+ if label is not None:
+ query_params = {"label": label}
+ self.goto(TV_APP_PATH, query_params)
# Note that the internal tests use "expect_transition" which is
# a mechanism that sets a maximum timeout for a "@with_retries"
# decorator-driven success retry loop for subsequent webdriver requests.
@@ -156,8 +241,7 @@
def wait_for_layout_complete(self):
"""Waits for Cobalt to complete pending layouts."""
start_time = time.time()
- while int(self.get_webdriver().execute_script(
- "return h5vcc.cVal.getValue('Event.MainWebModule.IsProcessing')")):
+ while self.get_int_cval("Event.MainWebModule.IsProcessing"):
if time.time() - start_time > LAYOUT_TIMEOUT_SECONDS:
raise TvTestCase.LayoutTimeoutException()
@@ -165,8 +249,13 @@
time.sleep(0.1)
def get_keyup_layout_duration_us(self):
- return int(self.get_webdriver().execute_script(
- "return h5vcc.cVal.getValue('Event.Duration.MainWebModule.KeyUp')"))
+ return self.get_int_cval("Event.Duration.MainWebModule.KeyUp")
+
+ def wait_for_layout_complete_after_focused_shelf(self):
+ """Waits for Cobalt to focus on a shelf and complete pending layouts."""
+ self.poll_until_found(tv.FOCUSED_SHELF)
+ self.assert_displayed(tv.FOCUSED_SHELF_TITLE)
+ self.wait_for_layout_complete()
def record_results(self, name, results):
"""Records results of benchmark.
@@ -177,8 +266,13 @@
name: name of test case
results: Test results. Must be JSON encodable
"""
- print("tv_testcase RESULT: " + name + " "
- + json.JSONEncoder().encode(results))
+ if isinstance(results, list):
+ value_to_record = _median(results)
+ else:
+ value_to_record = results
+
+ string_value_to_record = json.JSONEncoder().encode(value_to_record)
+ print("tv_testcase RESULT: {} {}".format(name, string_value_to_record))
def main():
diff --git a/src/cobalt/xhr/xml_http_request_test.cc b/src/cobalt/xhr/xml_http_request_test.cc
index e39fe5f..ae2f187 100644
--- a/src/cobalt/xhr/xml_http_request_test.cc
+++ b/src/cobalt/xhr/xml_http_request_test.cc
@@ -93,7 +93,7 @@
class FakeSettings : public dom::DOMSettings {
public:
FakeSettings()
- : dom::DOMSettings(0, NULL, NULL, NULL, NULL, NULL, NULL, NULL) {}
+ : dom::DOMSettings(0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) {}
GURL base_url() const OVERRIDE { return GURL("http://example.com"); }
};
diff --git a/src/content/browser/speech/chunked_byte_buffer.cc b/src/content/browser/speech/chunked_byte_buffer.cc
new file mode 100644
index 0000000..a43e40a
--- /dev/null
+++ b/src/content/browser/speech/chunked_byte_buffer.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/speech/chunked_byte_buffer.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+
+namespace {
+
+static const size_t kHeaderLength = sizeof(uint32_t);
+
+static_assert(sizeof(size_t) >= kHeaderLength,
+ "chunked byte buffer not supported on this architecture");
+
+uint32_t ReadBigEndian32(const uint8_t* buffer) {
+ return (static_cast<uint32_t>(buffer[3])) |
+ (static_cast<uint32_t>(buffer[2]) << 8) |
+ (static_cast<uint32_t>(buffer[1]) << 16) |
+ (static_cast<uint32_t>(buffer[0]) << 24);
+}
+
+} // namespace
+
+namespace content {
+
+ChunkedByteBuffer::ChunkedByteBuffer()
+ : partial_chunk_(new Chunk()),
+ total_bytes_stored_(0) {
+}
+
+ChunkedByteBuffer::~ChunkedByteBuffer() {
+ Clear();
+}
+
+void ChunkedByteBuffer::Append(const uint8_t* start, size_t length) {
+ size_t remaining_bytes = length;
+ const uint8_t* next_data = start;
+
+ while (remaining_bytes > 0) {
+ DCHECK(partial_chunk_ != NULL);
+ size_t insert_length = 0;
+ bool header_completed = false;
+ bool content_completed = false;
+ std::vector<uint8_t>* insert_target;
+
+ if (partial_chunk_->header.size() < kHeaderLength) {
+ const size_t bytes_to_complete_header =
+ kHeaderLength - partial_chunk_->header.size();
+ insert_length = std::min(bytes_to_complete_header, remaining_bytes);
+ insert_target = &partial_chunk_->header;
+ header_completed = (remaining_bytes >= bytes_to_complete_header);
+ } else {
+ DCHECK_LT(partial_chunk_->content->size(),
+ partial_chunk_->ExpectedContentLength());
+ const size_t bytes_to_complete_chunk =
+ partial_chunk_->ExpectedContentLength() -
+ partial_chunk_->content->size();
+ insert_length = std::min(bytes_to_complete_chunk, remaining_bytes);
+ insert_target = partial_chunk_->content.get();
+ content_completed = (remaining_bytes >= bytes_to_complete_chunk);
+ }
+
+ DCHECK_GT(insert_length, 0U);
+ DCHECK_LE(insert_length, remaining_bytes);
+ DCHECK_LE(next_data + insert_length, start + length);
+ insert_target->insert(insert_target->end(),
+ next_data,
+ next_data + insert_length);
+ next_data += insert_length;
+ remaining_bytes -= insert_length;
+
+ if (header_completed) {
+ DCHECK_EQ(partial_chunk_->header.size(), kHeaderLength);
+ if (partial_chunk_->ExpectedContentLength() == 0) {
+ // Handle zero-byte chunks.
+ chunks_.push_back(partial_chunk_.release());
+ partial_chunk_.reset(new Chunk());
+ } else {
+ partial_chunk_->content->reserve(
+ partial_chunk_->ExpectedContentLength());
+ }
+ } else if (content_completed) {
+ DCHECK_EQ(partial_chunk_->content->size(),
+ partial_chunk_->ExpectedContentLength());
+ chunks_.push_back(partial_chunk_.release());
+ partial_chunk_.reset(new Chunk());
+ }
+ }
+ DCHECK_EQ(next_data, start + length);
+ total_bytes_stored_ += length;
+}
+
+void ChunkedByteBuffer::Append(const std::string& string) {
+ Append(reinterpret_cast<const uint8_t*>(string.data()), string.size());
+}
+
+bool ChunkedByteBuffer::HasChunks() const {
+ return !chunks_.empty();
+}
+
+std::unique_ptr<std::vector<uint8_t>> ChunkedByteBuffer::PopChunk() {
+ if (chunks_.empty())
+ return std::unique_ptr<std::vector<uint8_t>>();
+ std::unique_ptr<Chunk> chunk(*chunks_.begin());
+ chunks_.weak_erase(chunks_.begin());
+ DCHECK_EQ(chunk->header.size(), kHeaderLength);
+ DCHECK_EQ(chunk->content->size(), chunk->ExpectedContentLength());
+ total_bytes_stored_ -= chunk->content->size();
+ total_bytes_stored_ -= kHeaderLength;
+ return std::move(chunk->content);
+}
+
+void ChunkedByteBuffer::Clear() {
+ chunks_.clear();
+ partial_chunk_.reset(new Chunk());
+ total_bytes_stored_ = 0;
+}
+
+ChunkedByteBuffer::Chunk::Chunk() : content(new std::vector<uint8_t>()) {}
+
+ChunkedByteBuffer::Chunk::~Chunk() {
+}
+
+size_t ChunkedByteBuffer::Chunk::ExpectedContentLength() const {
+ DCHECK_EQ(header.size(), kHeaderLength);
+ return static_cast<size_t>(ReadBigEndian32(&header[0]));
+}
+
+} // namespace content
diff --git a/src/content/browser/speech/chunked_byte_buffer.h b/src/content/browser/speech/chunked_byte_buffer.h
new file mode 100644
index 0000000..41e273d
--- /dev/null
+++ b/src/content/browser/speech/chunked_byte_buffer.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SPEECH_CHUNKED_BYTE_BUFFER_H_
+#define CONTENT_BROWSER_SPEECH_CHUNKED_BYTE_BUFFER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// Models a chunk-oriented byte buffer. The term chunk is herein defined as an
+// arbitrary sequence of bytes that is preceeded by N header bytes, indicating
+// its size. Data may be appended to the buffer with no particular respect of
+// chunks boundaries. However, chunks can be extracted (FIFO) only when their
+// content (according to their header) is fully available in the buffer.
+// The current implementation support only 4 byte Big Endian headers.
+// Empty chunks (i.e. the sequence 00 00 00 00) are NOT allowed.
+//
+// E.g. 00 00 00 04 xx xx xx xx 00 00 00 02 yy yy 00 00 00 04 zz zz zz zz
+// [----- CHUNK 1 -------] [--- CHUNK 2 ---] [------ CHUNK 3 ------]
+class CONTENT_EXPORT ChunkedByteBuffer {
+ public:
+ ChunkedByteBuffer();
+ ~ChunkedByteBuffer();
+
+ // Appends |length| bytes starting from |start| to the buffer.
+ void Append(const uint8_t* start, size_t length);
+
+ // Appends bytes contained in the |string| to the buffer.
+ void Append(const std::string& string);
+
+ // Checks whether one or more complete chunks are available in the buffer.
+ bool HasChunks() const;
+
+ // If enough data is available, reads and removes the first complete chunk
+ // from the buffer. Returns a NULL pointer if no complete chunk is available.
+ std::unique_ptr<std::vector<uint8_t>> PopChunk();
+
+ // Clears all the content of the buffer.
+ void Clear();
+
+ // Returns the number of raw bytes (including headers) present.
+ size_t GetTotalLength() const { return total_bytes_stored_; }
+
+ private:
+ struct Chunk {
+ Chunk();
+ ~Chunk();
+
+ std::vector<uint8_t> header;
+ std::unique_ptr<std::vector<uint8_t>> content;
+ size_t ExpectedContentLength() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Chunk);
+ };
+
+ ScopedVector<Chunk> chunks_;
+ std::unique_ptr<Chunk> partial_chunk_;
+ size_t total_bytes_stored_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChunkedByteBuffer);
+};
+
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SPEECH_CHUNKED_BYTE_BUFFER_H_
diff --git a/src/content/browser/speech/chunked_byte_buffer_unittest.cc b/src/content/browser/speech/chunked_byte_buffer_unittest.cc
new file mode 100644
index 0000000..d8a5cb2
--- /dev/null
+++ b/src/content/browser/speech/chunked_byte_buffer_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "content/browser/speech/chunked_byte_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+typedef std::vector<uint8_t> ByteVector;
+
+TEST(ChunkedByteBufferTest, BasicTest) {
+ ChunkedByteBuffer buffer;
+
+ const uint8_t kChunks[] = {
+ 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, // Chunk 1: 4 bytes
+ 0x00, 0x00, 0x00, 0x02, 0x05, 0x06, // Chunk 2: 2 bytes
+ 0x00, 0x00, 0x00, 0x01, 0x07 // Chunk 3: 1 bytes
+ };
+
+ EXPECT_EQ(0U, buffer.GetTotalLength());
+ EXPECT_FALSE(buffer.HasChunks());
+
+ // Append partially chunk 1.
+ buffer.Append(kChunks, 2);
+ EXPECT_EQ(2U, buffer.GetTotalLength());
+ EXPECT_FALSE(buffer.HasChunks());
+
+ // Complete chunk 1.
+ buffer.Append(kChunks + 2, 6);
+ EXPECT_EQ(8U, buffer.GetTotalLength());
+ EXPECT_TRUE(buffer.HasChunks());
+
+ // Append fully chunk 2.
+ buffer.Append(kChunks + 8, 6);
+ EXPECT_EQ(14U, buffer.GetTotalLength());
+ EXPECT_TRUE(buffer.HasChunks());
+
+ // Remove and check chunk 1.
+ std::unique_ptr<ByteVector> chunk;
+ chunk = buffer.PopChunk();
+ EXPECT_TRUE(chunk != NULL);
+ EXPECT_EQ(4U, chunk->size());
+ EXPECT_EQ(0, std::char_traits<uint8_t>::compare(kChunks + 4, &(*chunk)[0],
+ chunk->size()));
+ EXPECT_EQ(6U, buffer.GetTotalLength());
+ EXPECT_TRUE(buffer.HasChunks());
+
+ // Read and check chunk 2.
+ chunk = buffer.PopChunk();
+ EXPECT_TRUE(chunk != NULL);
+ EXPECT_EQ(2U, chunk->size());
+ EXPECT_EQ(0, std::char_traits<uint8_t>::compare(kChunks + 12, &(*chunk)[0],
+ chunk->size()));
+ EXPECT_EQ(0U, buffer.GetTotalLength());
+ EXPECT_FALSE(buffer.HasChunks());
+
+ // Append fully chunk 3.
+ buffer.Append(kChunks + 14, 5);
+ EXPECT_EQ(5U, buffer.GetTotalLength());
+
+ // Remove and check chunk 3.
+ chunk = buffer.PopChunk();
+ EXPECT_TRUE(chunk != NULL);
+ EXPECT_EQ(1U, chunk->size());
+ EXPECT_EQ((*chunk)[0], kChunks[18]);
+ EXPECT_EQ(0U, buffer.GetTotalLength());
+ EXPECT_FALSE(buffer.HasChunks());
+}
+
+} // namespace content
diff --git a/src/content/browser/speech/endpointer/endpointer.cc b/src/content/browser/speech/endpointer/endpointer.cc
new file mode 100644
index 0000000..1758970
--- /dev/null
+++ b/src/content/browser/speech/endpointer/endpointer.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/speech/endpointer/endpointer.h"
+
+#include "base/time/time.h"
+#include "content/browser/speech/audio_buffer.h"
+
+using base::Time;
+
+namespace {
+const int kFrameRate = 50; // 1 frame = 20ms of audio.
+}
+
+namespace content {
+
+Endpointer::Endpointer(int sample_rate)
+ : speech_input_possibly_complete_silence_length_us_(-1),
+ speech_input_complete_silence_length_us_(-1),
+ audio_frame_time_us_(0),
+ sample_rate_(sample_rate),
+ frame_size_(0) {
+ Reset();
+
+ frame_size_ = static_cast<int>(sample_rate / static_cast<float>(kFrameRate));
+
+ speech_input_minimum_length_us_ =
+ static_cast<int64_t>(1.7 * Time::kMicrosecondsPerSecond);
+ speech_input_complete_silence_length_us_ =
+ static_cast<int64_t>(0.5 * Time::kMicrosecondsPerSecond);
+ long_speech_input_complete_silence_length_us_ = -1;
+ long_speech_length_us_ = -1;
+ speech_input_possibly_complete_silence_length_us_ =
+ 1 * Time::kMicrosecondsPerSecond;
+
+ // Set the default configuration for Push To Talk mode.
+ EnergyEndpointerParams ep_config;
+ ep_config.set_frame_period(1.0f / static_cast<float>(kFrameRate));
+ ep_config.set_frame_duration(1.0f / static_cast<float>(kFrameRate));
+ ep_config.set_endpoint_margin(0.2f);
+ ep_config.set_onset_window(0.15f);
+ ep_config.set_speech_on_window(0.4f);
+ ep_config.set_offset_window(0.15f);
+ ep_config.set_onset_detect_dur(0.09f);
+ ep_config.set_onset_confirm_dur(0.075f);
+ ep_config.set_on_maintain_dur(0.10f);
+ ep_config.set_offset_confirm_dur(0.12f);
+ ep_config.set_decision_threshold(1000.0f);
+ ep_config.set_min_decision_threshold(50.0f);
+ ep_config.set_fast_update_dur(0.2f);
+ ep_config.set_sample_rate(static_cast<float>(sample_rate));
+ ep_config.set_min_fundamental_frequency(57.143f);
+ ep_config.set_max_fundamental_frequency(400.0f);
+ ep_config.set_contamination_rejection_period(0.25f);
+ energy_endpointer_.Init(ep_config);
+}
+
+void Endpointer::Reset() {
+ old_ep_status_ = EP_PRE_SPEECH;
+ waiting_for_speech_possibly_complete_timeout_ = false;
+ waiting_for_speech_complete_timeout_ = false;
+ speech_previously_detected_ = false;
+ speech_input_complete_ = false;
+ audio_frame_time_us_ = 0; // Reset time for packets sent to endpointer.
+ speech_end_time_us_ = -1;
+ speech_start_time_us_ = -1;
+}
+
+void Endpointer::StartSession() {
+ Reset();
+ energy_endpointer_.StartSession();
+}
+
+void Endpointer::EndSession() {
+ energy_endpointer_.EndSession();
+}
+
+void Endpointer::SetEnvironmentEstimationMode() {
+ Reset();
+ energy_endpointer_.SetEnvironmentEstimationMode();
+}
+
+void Endpointer::SetUserInputMode() {
+ energy_endpointer_.SetUserInputMode();
+}
+
+EpStatus Endpointer::Status(int64_t* time) {
+ return energy_endpointer_.Status(time);
+}
+
+EpStatus Endpointer::ProcessAudio(const AudioChunk& raw_audio, float* rms_out) {
+ const int16_t* audio_data = raw_audio.SamplesData16();
+ const int num_samples = raw_audio.NumSamples();
+ EpStatus ep_status = EP_PRE_SPEECH;
+
+ // Process the input data in blocks of frame_size_, dropping any incomplete
+ // frames at the end (which is ok since typically the caller will be recording
+ // audio in multiples of our frame size).
+ int sample_index = 0;
+ while (sample_index + frame_size_ <= num_samples) {
+ // Have the endpointer process the frame.
+ energy_endpointer_.ProcessAudioFrame(audio_frame_time_us_,
+ audio_data + sample_index,
+ frame_size_,
+ rms_out);
+ sample_index += frame_size_;
+ audio_frame_time_us_ += (frame_size_ * Time::kMicrosecondsPerSecond) /
+ sample_rate_;
+
+ // Get the status of the endpointer.
+ int64_t ep_time;
+ ep_status = energy_endpointer_.Status(&ep_time);
+
+ // Handle state changes.
+ if ((EP_SPEECH_PRESENT == ep_status) &&
+ (EP_POSSIBLE_ONSET == old_ep_status_)) {
+ speech_end_time_us_ = -1;
+ waiting_for_speech_possibly_complete_timeout_ = false;
+ waiting_for_speech_complete_timeout_ = false;
+ // Trigger SpeechInputDidStart event on first detection.
+ if (false == speech_previously_detected_) {
+ speech_previously_detected_ = true;
+ speech_start_time_us_ = ep_time;
+ }
+ }
+ if ((EP_PRE_SPEECH == ep_status) &&
+ (EP_POSSIBLE_OFFSET == old_ep_status_)) {
+ speech_end_time_us_ = ep_time;
+ waiting_for_speech_possibly_complete_timeout_ = true;
+ waiting_for_speech_complete_timeout_ = true;
+ }
+ if (ep_time > speech_input_minimum_length_us_) {
+ // Speech possibly complete timeout.
+ if ((waiting_for_speech_possibly_complete_timeout_) &&
+ (ep_time - speech_end_time_us_ >
+ speech_input_possibly_complete_silence_length_us_)) {
+ waiting_for_speech_possibly_complete_timeout_ = false;
+ }
+ if (waiting_for_speech_complete_timeout_) {
+ // The length of the silence timeout period can be held constant, or it
+ // can be changed after a fixed amount of time from the beginning of
+ // speech.
+ bool has_stepped_silence =
+ (long_speech_length_us_ > 0) &&
+ (long_speech_input_complete_silence_length_us_ > 0);
+ int64_t requested_silence_length;
+ if (has_stepped_silence &&
+ (ep_time - speech_start_time_us_) > long_speech_length_us_) {
+ requested_silence_length =
+ long_speech_input_complete_silence_length_us_;
+ } else {
+ requested_silence_length =
+ speech_input_complete_silence_length_us_;
+ }
+
+ // Speech complete timeout.
+ if ((ep_time - speech_end_time_us_) > requested_silence_length) {
+ waiting_for_speech_complete_timeout_ = false;
+ speech_input_complete_ = true;
+ }
+ }
+ }
+ old_ep_status_ = ep_status;
+ }
+ return ep_status;
+}
+
+} // namespace content
diff --git a/src/content/browser/speech/endpointer/endpointer.h b/src/content/browser/speech/endpointer/endpointer.h
new file mode 100644
index 0000000..5790672
--- /dev/null
+++ b/src/content/browser/speech/endpointer/endpointer.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SPEECH_ENDPOINTER_ENDPOINTER_H_
+#define CONTENT_BROWSER_SPEECH_ENDPOINTER_ENDPOINTER_H_
+
+#include <stdint.h>
+
+#include "content/browser/speech/endpointer/energy_endpointer.h"
+#include "content/common/content_export.h"
+
+class EpStatus;
+
+namespace content {
+
+class AudioChunk;
+
+// A simple interface to the underlying energy-endpointer implementation, this
+// class lets callers provide audio as being recorded and let them poll to find
+// when the user has stopped speaking.
+//
+// There are two events that may trigger the end of speech:
+//
+// speechInputPossiblyComplete event:
+//
+// Signals that silence/noise has been detected for a *short* amount of
+// time after some speech has been detected. It can be used for low latency
+// UI feedback. To disable it, set it to a large amount.
+//
+// speechInputComplete event:
+//
+// This event is intended to signal end of input and to stop recording.
+// The amount of time to wait after speech is set by
+// speech_input_complete_silence_length_ and optionally two other
+// parameters (see below).
+// This time can be held constant, or can change as more speech is detected.
+// In the latter case, the time changes after a set amount of time from the
+// *beginning* of speech. This is motivated by the expectation that there
+// will be two distinct types of inputs: short search queries and longer
+// dictation style input.
+//
+// Three parameters are used to define the piecewise constant timeout function.
+// The timeout length is speech_input_complete_silence_length until
+// long_speech_length, when it changes to
+// long_speech_input_complete_silence_length.
+class CONTENT_EXPORT Endpointer {
+ public:
+ explicit Endpointer(int sample_rate);
+
+ // Start the endpointer. This should be called at the beginning of a session.
+ void StartSession();
+
+ // Stop the endpointer.
+ void EndSession();
+
+ // Start environment estimation. Audio will be used for environment estimation
+ // i.e. noise level estimation.
+ void SetEnvironmentEstimationMode();
+
+ // Start user input. This should be called when the user indicates start of
+ // input, e.g. by pressing a button.
+ void SetUserInputMode();
+
+ // Process a segment of audio, which may be more than one frame.
+ // The status of the last frame will be returned.
+ EpStatus ProcessAudio(const AudioChunk& raw_audio, float* rms_out);
+
+ // Get the status of the endpointer.
+ EpStatus Status(int64_t* time_us);
+
+ // Returns true if the endpointer detected reasonable audio levels above
+ // background noise which could be user speech, false if not.
+ bool DidStartReceivingSpeech() const {
+ return speech_previously_detected_;
+ }
+
+ bool IsEstimatingEnvironment() const {
+ return energy_endpointer_.estimating_environment();
+ }
+
+ void set_speech_input_complete_silence_length(int64_t time_us) {
+ speech_input_complete_silence_length_us_ = time_us;
+ }
+
+ void set_long_speech_input_complete_silence_length(int64_t time_us) {
+ long_speech_input_complete_silence_length_us_ = time_us;
+ }
+
+ void set_speech_input_possibly_complete_silence_length(int64_t time_us) {
+ speech_input_possibly_complete_silence_length_us_ = time_us;
+ }
+
+ void set_long_speech_length(int64_t time_us) {
+ long_speech_length_us_ = time_us;
+ }
+
+ bool speech_input_complete() const {
+ return speech_input_complete_;
+ }
+
+ // RMS background noise level in dB.
+ float NoiseLevelDb() const { return energy_endpointer_.GetNoiseLevelDb(); }
+
+ private:
+ // Reset internal states. Helper method common to initial input utterance
+ // and following input utternaces.
+ void Reset();
+
+ // Minimum allowable length of speech input.
+ int64_t speech_input_minimum_length_us_;
+
+ // The speechInputPossiblyComplete event signals that silence/noise has been
+ // detected for a *short* amount of time after some speech has been detected.
+ // This proporty specifies the time period.
+ int64_t speech_input_possibly_complete_silence_length_us_;
+
+ // The speechInputComplete event signals that silence/noise has been
+ // detected for a *long* amount of time after some speech has been detected.
+ // This property specifies the time period.
+ int64_t speech_input_complete_silence_length_us_;
+
+ // Same as above, this specifies the required silence period after speech
+ // detection. This period is used instead of
+ // speech_input_complete_silence_length_ when the utterance is longer than
+ // long_speech_length_. This parameter is optional.
+ int64_t long_speech_input_complete_silence_length_us_;
+
+ // The period of time after which the endpointer should consider
+ // long_speech_input_complete_silence_length_ as a valid silence period
+ // instead of speech_input_complete_silence_length_. This parameter is
+ // optional.
+ int64_t long_speech_length_us_;
+
+ // First speech onset time, used in determination of speech complete timeout.
+ int64_t speech_start_time_us_;
+
+ // Most recent end time, used in determination of speech complete timeout.
+ int64_t speech_end_time_us_;
+
+ int64_t audio_frame_time_us_;
+ EpStatus old_ep_status_;
+ bool waiting_for_speech_possibly_complete_timeout_;
+ bool waiting_for_speech_complete_timeout_;
+ bool speech_previously_detected_;
+ bool speech_input_complete_;
+ EnergyEndpointer energy_endpointer_;
+ int sample_rate_;
+ int32_t frame_size_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SPEECH_ENDPOINTER_ENDPOINTER_H_
diff --git a/src/content/browser/speech/endpointer/endpointer_unittest.cc b/src/content/browser/speech/endpointer/endpointer_unittest.cc
new file mode 100644
index 0000000..53ec4d1
--- /dev/null
+++ b/src/content/browser/speech/endpointer/endpointer_unittest.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "content/browser/speech/audio_buffer.h"
+#include "content/browser/speech/endpointer/endpointer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+const int kFrameRate = 50; // 20 ms long frames for AMR encoding.
+const int kSampleRate = 8000; // 8 k samples per second for AMR encoding.
+
+// At 8 sample per second a 20 ms frame is 160 samples, which corrsponds
+// to the AMR codec.
+const int kFrameSize = kSampleRate / kFrameRate; // 160 samples.
+static_assert(kFrameSize == 160, "invalid frame size");
+}
+
+namespace content {
+
+class FrameProcessor {
+ public:
+ // Process a single frame of test audio samples.
+ virtual EpStatus ProcessFrame(int64_t time,
+ int16_t* samples,
+ int frame_size) = 0;
+};
+
+void RunEndpointerEventsTest(FrameProcessor* processor) {
+ int16_t samples[kFrameSize];
+
+ // We will create a white noise signal of 150 frames. The frames from 50 to
+ // 100 will have more power, and the endpointer should fire on those frames.
+ const int kNumFrames = 150;
+
+ // Create a random sequence of samples.
+ srand(1);
+ float gain = 0.0;
+ int64_t time = 0;
+ for (int frame_count = 0; frame_count < kNumFrames; ++frame_count) {
+ // The frames from 50 to 100 will have more power, and the endpointer
+ // should detect those frames as speech.
+ if ((frame_count >= 50) && (frame_count < 100)) {
+ gain = 2000.0;
+ } else {
+ gain = 1.0;
+ }
+ // Create random samples.
+ for (int i = 0; i < kFrameSize; ++i) {
+ float randNum = static_cast<float>(rand() - (RAND_MAX / 2)) /
+ static_cast<float>(RAND_MAX);
+ samples[i] = static_cast<int16_t>(gain * randNum);
+ }
+
+ EpStatus ep_status = processor->ProcessFrame(time, samples, kFrameSize);
+ time += static_cast<int64_t>(kFrameSize * (1e6 / kSampleRate));
+
+ // Log the status.
+ if (20 == frame_count)
+ EXPECT_EQ(EP_PRE_SPEECH, ep_status);
+ if (70 == frame_count)
+ EXPECT_EQ(EP_SPEECH_PRESENT, ep_status);
+ if (120 == frame_count)
+ EXPECT_EQ(EP_PRE_SPEECH, ep_status);
+ }
+}
+
+// This test instantiates and initializes a stand alone endpointer module.
+// The test creates FrameData objects with random noise and send them
+// to the endointer module. The energy of the first 50 frames is low,
+// followed by 500 high energy frames, and another 50 low energy frames.
+// We test that the correct start and end frames were detected.
+class EnergyEndpointerFrameProcessor : public FrameProcessor {
+ public:
+ explicit EnergyEndpointerFrameProcessor(EnergyEndpointer* endpointer)
+ : endpointer_(endpointer) {}
+
+ EpStatus ProcessFrame(int64_t time,
+ int16_t* samples,
+ int frame_size) override {
+ endpointer_->ProcessAudioFrame(time, samples, kFrameSize, NULL);
+ int64_t ep_time;
+ return endpointer_->Status(&ep_time);
+ }
+
+ private:
+ EnergyEndpointer* endpointer_;
+};
+
+TEST(EndpointerTest, TestEnergyEndpointerEvents) {
+ // Initialize endpointer and configure it. We specify the parameters
+ // here for a 20ms window, and a 20ms step size, which corrsponds to
+ // the narrow band AMR codec.
+ EnergyEndpointerParams ep_config;
+ ep_config.set_frame_period(1.0f / static_cast<float>(kFrameRate));
+ ep_config.set_frame_duration(1.0f / static_cast<float>(kFrameRate));
+ ep_config.set_endpoint_margin(0.2f);
+ ep_config.set_onset_window(0.15f);
+ ep_config.set_speech_on_window(0.4f);
+ ep_config.set_offset_window(0.15f);
+ ep_config.set_onset_detect_dur(0.09f);
+ ep_config.set_onset_confirm_dur(0.075f);
+ ep_config.set_on_maintain_dur(0.10f);
+ ep_config.set_offset_confirm_dur(0.12f);
+ ep_config.set_decision_threshold(100.0f);
+ EnergyEndpointer endpointer;
+ endpointer.Init(ep_config);
+
+ endpointer.StartSession();
+
+ EnergyEndpointerFrameProcessor frame_processor(&endpointer);
+ RunEndpointerEventsTest(&frame_processor);
+
+ endpointer.EndSession();
+};
+
+// Test endpointer wrapper class.
+class EndpointerFrameProcessor : public FrameProcessor {
+ public:
+ explicit EndpointerFrameProcessor(Endpointer* endpointer)
+ : endpointer_(endpointer) {}
+
+ EpStatus ProcessFrame(int64_t time,
+ int16_t* samples,
+ int frame_size) override {
+ scoped_refptr<AudioChunk> frame(
+ new AudioChunk(reinterpret_cast<uint8_t*>(samples), kFrameSize * 2, 2));
+ endpointer_->ProcessAudio(*frame.get(), NULL);
+ int64_t ep_time;
+ return endpointer_->Status(&ep_time);
+ }
+
+ private:
+ Endpointer* endpointer_;
+};
+
+TEST(EndpointerTest, TestEmbeddedEndpointerEvents) {
+ const int kSampleRate = 8000; // 8 k samples per second for AMR encoding.
+
+ Endpointer endpointer(kSampleRate);
+ const int64_t kMillisecondsPerMicrosecond = 1000;
+ const int64_t short_timeout = 300 * kMillisecondsPerMicrosecond;
+ endpointer.set_speech_input_possibly_complete_silence_length(short_timeout);
+ const int64_t long_timeout = 500 * kMillisecondsPerMicrosecond;
+ endpointer.set_speech_input_complete_silence_length(long_timeout);
+ endpointer.StartSession();
+
+ EndpointerFrameProcessor frame_processor(&endpointer);
+ RunEndpointerEventsTest(&frame_processor);
+
+ endpointer.EndSession();
+}
+
+} // namespace content
diff --git a/src/content/browser/speech/endpointer/energy_endpointer.cc b/src/content/browser/speech/endpointer/energy_endpointer.cc
new file mode 100644
index 0000000..fc1d871
--- /dev/null
+++ b/src/content/browser/speech/endpointer/energy_endpointer.cc
@@ -0,0 +1,379 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// To know more about the algorithm used and the original code which this is
+// based of, see
+// https://wiki.corp.google.com/twiki/bin/view/Main/ChromeGoogleCodeXRef
+
+#include "content/browser/speech/endpointer/energy_endpointer.h"
+
+#include <math.h>
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace {
+
+// Returns the RMS (quadratic mean) of the input signal.
+float RMS(const int16_t* samples, int num_samples) {
+ int64_t ssq_int64 = 0;
+ int64_t sum_int64 = 0;
+ for (int i = 0; i < num_samples; ++i) {
+ sum_int64 += samples[i];
+ ssq_int64 += samples[i] * samples[i];
+ }
+ // now convert to floats.
+ double sum = static_cast<double>(sum_int64);
+ sum /= num_samples;
+ double ssq = static_cast<double>(ssq_int64);
+ return static_cast<float>(sqrt((ssq / num_samples) - (sum * sum)));
+}
+
+int64_t Secs2Usecs(float seconds) {
+ return static_cast<int64_t>(0.5 + (1.0e6 * seconds));
+}
+
+float GetDecibel(float value) {
+ if (value > 1.0e-100)
+ return 20 * log10(value);
+ return -2000.0;
+}
+
+} // namespace
+
+namespace content {
+
+// Stores threshold-crossing histories for making decisions about the speech
+// state.
+class EnergyEndpointer::HistoryRing {
+ public:
+ HistoryRing() : insertion_index_(0) {}
+
+ // Resets the ring to |size| elements each with state |initial_state|
+ void SetRing(int size, bool initial_state);
+
+ // Inserts a new entry into the ring and drops the oldest entry.
+ void Insert(int64_t time_us, bool decision);
+
+ // Returns the time in microseconds of the most recently added entry.
+ int64_t EndTime() const;
+
+ // Returns the sum of all intervals during which 'decision' is true within
+ // the time in seconds specified by 'duration'. The returned interval is
+ // in seconds.
+ float RingSum(float duration_sec);
+
+ private:
+ struct DecisionPoint {
+ int64_t time_us;
+ bool decision;
+ };
+
+ std::vector<DecisionPoint> decision_points_;
+ int insertion_index_; // Index at which the next item gets added/inserted.
+
+ DISALLOW_COPY_AND_ASSIGN(HistoryRing);
+};
+
+void EnergyEndpointer::HistoryRing::SetRing(int size, bool initial_state) {
+ insertion_index_ = 0;
+ decision_points_.clear();
+ DecisionPoint init = { -1, initial_state };
+ decision_points_.resize(size, init);
+}
+
+void EnergyEndpointer::HistoryRing::Insert(int64_t time_us, bool decision) {
+ decision_points_[insertion_index_].time_us = time_us;
+ decision_points_[insertion_index_].decision = decision;
+ insertion_index_ = (insertion_index_ + 1) % decision_points_.size();
+}
+
+int64_t EnergyEndpointer::HistoryRing::EndTime() const {
+ int ind = insertion_index_ - 1;
+ if (ind < 0)
+ ind = decision_points_.size() - 1;
+ return decision_points_[ind].time_us;
+}
+
+float EnergyEndpointer::HistoryRing::RingSum(float duration_sec) {
+ if (decision_points_.empty())
+ return 0.0;
+
+ int64_t sum_us = 0;
+ int ind = insertion_index_ - 1;
+ if (ind < 0)
+ ind = decision_points_.size() - 1;
+ int64_t end_us = decision_points_[ind].time_us;
+ bool is_on = decision_points_[ind].decision;
+ int64_t start_us =
+ end_us - static_cast<int64_t>(0.5 + (1.0e6 * duration_sec));
+ if (start_us < 0)
+ start_us = 0;
+ size_t n_summed = 1; // n points ==> (n-1) intervals
+ while ((decision_points_[ind].time_us > start_us) &&
+ (n_summed < decision_points_.size())) {
+ --ind;
+ if (ind < 0)
+ ind = decision_points_.size() - 1;
+ if (is_on)
+ sum_us += end_us - decision_points_[ind].time_us;
+ is_on = decision_points_[ind].decision;
+ end_us = decision_points_[ind].time_us;
+ n_summed++;
+ }
+
+ return 1.0e-6f * sum_us; // Returns total time that was super threshold.
+}
+
+EnergyEndpointer::EnergyEndpointer()
+ : status_(EP_PRE_SPEECH),
+ offset_confirm_dur_sec_(0),
+ endpointer_time_us_(0),
+ fast_update_frames_(0),
+ frame_counter_(0),
+ max_window_dur_(4.0),
+ sample_rate_(0),
+ history_(new HistoryRing()),
+ decision_threshold_(0),
+ estimating_environment_(false),
+ noise_level_(0),
+ rms_adapt_(0),
+ start_lag_(0),
+ end_lag_(0),
+ user_input_start_time_us_(0) {
+}
+
+EnergyEndpointer::~EnergyEndpointer() {
+}
+
+int EnergyEndpointer::TimeToFrame(float time) const {
+ return static_cast<int32_t>(0.5 + (time / params_.frame_period()));
+}
+
+void EnergyEndpointer::Restart(bool reset_threshold) {
+ status_ = EP_PRE_SPEECH;
+ user_input_start_time_us_ = 0;
+
+ if (reset_threshold) {
+ decision_threshold_ = params_.decision_threshold();
+ rms_adapt_ = decision_threshold_;
+ noise_level_ = params_.decision_threshold() / 2.0f;
+ frame_counter_ = 0; // Used for rapid initial update of levels.
+ }
+
+ // Set up the memories to hold the history windows.
+ history_->SetRing(TimeToFrame(max_window_dur_), false);
+
+ // Flag that indicates that current input should be used for
+ // estimating the environment. The user has not yet started input
+ // by e.g. pressed the push-to-talk button. By default, this is
+ // false for backward compatibility.
+ estimating_environment_ = false;
+}
+
+void EnergyEndpointer::Init(const EnergyEndpointerParams& params) {
+ params_ = params;
+
+ // Find the longest history interval to be used, and make the ring
+ // large enough to accommodate that number of frames. NOTE: This
+ // depends upon ep_frame_period being set correctly in the factory
+ // that did this instantiation.
+ max_window_dur_ = params_.onset_window();
+ if (params_.speech_on_window() > max_window_dur_)
+ max_window_dur_ = params_.speech_on_window();
+ if (params_.offset_window() > max_window_dur_)
+ max_window_dur_ = params_.offset_window();
+ Restart(true);
+
+ offset_confirm_dur_sec_ = params_.offset_window() -
+ params_.offset_confirm_dur();
+ if (offset_confirm_dur_sec_ < 0.0)
+ offset_confirm_dur_sec_ = 0.0;
+
+ user_input_start_time_us_ = 0;
+
+ // Flag that indicates that current input should be used for
+ // estimating the environment. The user has not yet started input
+ // by e.g. pressed the push-to-talk button. By default, this is
+ // false for backward compatibility.
+ estimating_environment_ = false;
+ // The initial value of the noise and speech levels is inconsequential.
+ // The level of the first frame will overwrite these values.
+ noise_level_ = params_.decision_threshold() / 2.0f;
+ fast_update_frames_ =
+ static_cast<int64_t>(params_.fast_update_dur() / params_.frame_period());
+
+ frame_counter_ = 0; // Used for rapid initial update of levels.
+
+ sample_rate_ = params_.sample_rate();
+ start_lag_ = static_cast<int>(sample_rate_ /
+ params_.max_fundamental_frequency());
+ end_lag_ = static_cast<int>(sample_rate_ /
+ params_.min_fundamental_frequency());
+}
+
+void EnergyEndpointer::StartSession() {
+ Restart(true);
+}
+
+void EnergyEndpointer::EndSession() {
+ status_ = EP_POST_SPEECH;
+}
+
+void EnergyEndpointer::SetEnvironmentEstimationMode() {
+ Restart(true);
+ estimating_environment_ = true;
+}
+
+void EnergyEndpointer::SetUserInputMode() {
+ estimating_environment_ = false;
+ user_input_start_time_us_ = endpointer_time_us_;
+}
+
+void EnergyEndpointer::ProcessAudioFrame(int64_t time_us,
+ const int16_t* samples,
+ int num_samples,
+ float* rms_out) {
+ endpointer_time_us_ = time_us;
+ float rms = RMS(samples, num_samples);
+
+ // Check that this is user input audio vs. pre-input adaptation audio.
+ // Input audio starts when the user indicates start of input, by e.g.
+ // pressing push-to-talk. Audio received prior to that is used to update
+ // noise and speech level estimates.
+ if (!estimating_environment_) {
+ bool decision = false;
+ if ((endpointer_time_us_ - user_input_start_time_us_) <
+ Secs2Usecs(params_.contamination_rejection_period())) {
+ decision = false;
+ DVLOG(1) << "decision: forced to false, time: " << endpointer_time_us_;
+ } else {
+ decision = (rms > decision_threshold_);
+ }
+
+ history_->Insert(endpointer_time_us_, decision);
+
+ switch (status_) {
+ case EP_PRE_SPEECH:
+ if (history_->RingSum(params_.onset_window()) >
+ params_.onset_detect_dur()) {
+ status_ = EP_POSSIBLE_ONSET;
+ }
+ break;
+
+ case EP_POSSIBLE_ONSET: {
+ float tsum = history_->RingSum(params_.onset_window());
+ if (tsum > params_.onset_confirm_dur()) {
+ status_ = EP_SPEECH_PRESENT;
+ } else { // If signal is not maintained, drop back to pre-speech.
+ if (tsum <= params_.onset_detect_dur())
+ status_ = EP_PRE_SPEECH;
+ }
+ break;
+ }
+
+ case EP_SPEECH_PRESENT: {
+ // To induce hysteresis in the state residency, we allow a
+ // smaller residency time in the on_ring, than was required to
+ // enter the SPEECH_PERSENT state.
+ float on_time = history_->RingSum(params_.speech_on_window());
+ if (on_time < params_.on_maintain_dur())
+ status_ = EP_POSSIBLE_OFFSET;
+ break;
+ }
+
+ case EP_POSSIBLE_OFFSET:
+ if (history_->RingSum(params_.offset_window()) <=
+ offset_confirm_dur_sec_) {
+ // Note that this offset time may be beyond the end
+ // of the input buffer in a real-time system. It will be up
+ // to the RecognizerSession to decide what to do.
+ status_ = EP_PRE_SPEECH; // Automatically reset for next utterance.
+ } else { // If speech picks up again we allow return to SPEECH_PRESENT.
+ if (history_->RingSum(params_.speech_on_window()) >=
+ params_.on_maintain_dur())
+ status_ = EP_SPEECH_PRESENT;
+ }
+ break;
+
+ default:
+ LOG(WARNING) << "Invalid case in switch: " << status_;
+ break;
+ }
+
+ // If this is a quiet, non-speech region, slowly adapt the detection
+ // threshold to be about 6dB above the average RMS.
+ if ((!decision) && (status_ == EP_PRE_SPEECH)) {
+ decision_threshold_ = (0.98f * decision_threshold_) + (0.02f * 2 * rms);
+ rms_adapt_ = decision_threshold_;
+ } else {
+ // If this is in a speech region, adapt the decision threshold to
+ // be about 10dB below the average RMS. If the noise level is high,
+ // the threshold is pushed up.
+ // Adaptation up to a higher level is 5 times faster than decay to
+ // a lower level.
+ if ((status_ == EP_SPEECH_PRESENT) && decision) {
+ if (rms_adapt_ > rms) {
+ rms_adapt_ = (0.99f * rms_adapt_) + (0.01f * rms);
+ } else {
+ rms_adapt_ = (0.95f * rms_adapt_) + (0.05f * rms);
+ }
+ float target_threshold = 0.3f * rms_adapt_ + noise_level_;
+ decision_threshold_ = (.90f * decision_threshold_) +
+ (0.10f * target_threshold);
+ }
+ }
+
+ // Set a floor
+ if (decision_threshold_ < params_.min_decision_threshold())
+ decision_threshold_ = params_.min_decision_threshold();
+ }
+
+ // Update speech and noise levels.
+ UpdateLevels(rms);
+ ++frame_counter_;
+
+ if (rms_out)
+ *rms_out = GetDecibel(rms);
+}
+
+float EnergyEndpointer::GetNoiseLevelDb() const {
+ return GetDecibel(noise_level_);
+}
+
+void EnergyEndpointer::UpdateLevels(float rms) {
+ // Update quickly initially. We assume this is noise and that
+ // speech is 6dB above the noise.
+ if (frame_counter_ < fast_update_frames_) {
+ // Alpha increases from 0 to (k-1)/k where k is the number of time
+ // steps in the initial adaptation period.
+ float alpha = static_cast<float>(frame_counter_) /
+ static_cast<float>(fast_update_frames_);
+ noise_level_ = (alpha * noise_level_) + ((1 - alpha) * rms);
+ DVLOG(1) << "FAST UPDATE, frame_counter_ " << frame_counter_
+ << ", fast_update_frames_ " << fast_update_frames_;
+ } else {
+ // Update Noise level. The noise level adapts quickly downward, but
+ // slowly upward. The noise_level_ parameter is not currently used
+ // for threshold adaptation. It is used for UI feedback.
+ if (noise_level_ < rms)
+ noise_level_ = (0.999f * noise_level_) + (0.001f * rms);
+ else
+ noise_level_ = (0.95f * noise_level_) + (0.05f * rms);
+ }
+ if (estimating_environment_ || (frame_counter_ < fast_update_frames_)) {
+ decision_threshold_ = noise_level_ * 2; // 6dB above noise level.
+ // Set a floor
+ if (decision_threshold_ < params_.min_decision_threshold())
+ decision_threshold_ = params_.min_decision_threshold();
+ }
+}
+
+EpStatus EnergyEndpointer::Status(int64_t* status_time) const {
+ *status_time = history_->EndTime();
+ return status_;
+}
+
+} // namespace content
diff --git a/src/content/browser/speech/endpointer/energy_endpointer.h b/src/content/browser/speech/endpointer/energy_endpointer.h
new file mode 100644
index 0000000..7b0b292
--- /dev/null
+++ b/src/content/browser/speech/endpointer/energy_endpointer.h
@@ -0,0 +1,161 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The EnergyEndpointer class finds likely speech onset and offset points.
+//
+// The implementation described here is about the simplest possible.
+// It is based on timings of threshold crossings for overall signal
+// RMS. It is suitable for light weight applications.
+//
+// As written, the basic idea is that one specifies intervals that
+// must be occupied by super- and sub-threshold energy levels, and
+// defers decisions re onset and offset times until these
+// specifications have been met. Three basic intervals are tested: an
+// onset window, a speech-on window, and an offset window. We require
+// super-threshold to exceed some mimimum total durations in the onset
+// and speech-on windows before declaring the speech onset time, and
+// we specify a required sub-threshold residency in the offset window
+// before declaring speech offset. As the various residency requirements are
+// met, the EnergyEndpointer instance assumes various states, and can return the
+// ID of these states to the client (see EpStatus below).
+//
+// The levels of the speech and background noise are continuously updated. It is
+// important that the background noise level be estimated initially for
+// robustness in noisy conditions. The first frames are assumed to be background
+// noise and a fast update rate is used for the noise level. The duration for
+// fast update is controlled by the fast_update_dur_ paramter.
+//
+// If used in noisy conditions, the endpointer should be started and run in the
+// EnvironmentEstimation mode, for at least 200ms, before switching to
+// UserInputMode.
+// Audio feedback contamination can appear in the input audio, if not cut
+// out or handled by echo cancellation. Audio feedback can trigger a false
+// accept. The false accepts can be ignored by setting
+// ep_contamination_rejection_period.
+
+#ifndef CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_H_
+#define CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "content/browser/speech/endpointer/energy_endpointer_params.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// Endpointer status codes
+enum EpStatus {
+ EP_PRE_SPEECH = 10,
+ EP_POSSIBLE_ONSET,
+ EP_SPEECH_PRESENT,
+ EP_POSSIBLE_OFFSET,
+ EP_POST_SPEECH,
+};
+
+class CONTENT_EXPORT EnergyEndpointer {
+ public:
+ // The default construction MUST be followed by Init(), before any
+ // other use can be made of the instance.
+ EnergyEndpointer();
+ virtual ~EnergyEndpointer();
+
+ void Init(const EnergyEndpointerParams& params);
+
+ // Start the endpointer. This should be called at the beginning of a session.
+ void StartSession();
+
+ // Stop the endpointer.
+ void EndSession();
+
+ // Start environment estimation. Audio will be used for environment estimation
+ // i.e. noise level estimation.
+ void SetEnvironmentEstimationMode();
+
+ // Start user input. This should be called when the user indicates start of
+ // input, e.g. by pressing a button.
+ void SetUserInputMode();
+
+ // Computes the next input frame and modifies EnergyEndpointer status as
+ // appropriate based on the computation.
+ void ProcessAudioFrame(int64_t time_us,
+ const int16_t* samples,
+ int num_samples,
+ float* rms_out);
+
+ // Returns the current state of the EnergyEndpointer and the time
+ // corresponding to the most recently computed frame.
+ EpStatus Status(int64_t* status_time_us) const;
+
+ bool estimating_environment() const {
+ return estimating_environment_;
+ }
+
+ // Returns estimated noise level in dB.
+ float GetNoiseLevelDb() const;
+
+ private:
+ class HistoryRing;
+
+ // Resets the endpointer internal state. If reset_threshold is true, the
+ // state will be reset completely, including adaptive thresholds and the
+ // removal of all history information.
+ void Restart(bool reset_threshold);
+
+ // Update internal speech and noise levels.
+ void UpdateLevels(float rms);
+
+ // Returns the number of frames (or frame number) corresponding to
+ // the 'time' (in seconds).
+ int TimeToFrame(float time) const;
+
+ EpStatus status_; // The current state of this instance.
+ float offset_confirm_dur_sec_; // max on time allowed to confirm POST_SPEECH
+ int64_t
+ endpointer_time_us_; // Time of the most recently received audio frame.
+ int64_t
+ fast_update_frames_; // Number of frames for initial level adaptation.
+ int64_t
+ frame_counter_; // Number of frames seen. Used for initial adaptation.
+ float max_window_dur_; // Largest search window size (seconds)
+ float sample_rate_; // Sampling rate.
+
+ // Ring buffers to hold the speech activity history.
+ std::unique_ptr<HistoryRing> history_;
+
+ // Configuration parameters.
+ EnergyEndpointerParams params_;
+
+ // RMS which must be exceeded to conclude frame is speech.
+ float decision_threshold_;
+
+ // Flag to indicate that audio should be used to estimate environment, prior
+ // to receiving user input.
+ bool estimating_environment_;
+
+ // Estimate of the background noise level. Used externally for UI feedback.
+ float noise_level_;
+
+ // An adaptive threshold used to update decision_threshold_ when appropriate.
+ float rms_adapt_;
+
+ // Start lag corresponds to the highest fundamental frequency.
+ int start_lag_;
+
+ // End lag corresponds to the lowest fundamental frequency.
+ int end_lag_;
+
+ // Time when mode switched from environment estimation to user input. This
+ // is used to time forced rejection of audio feedback contamination.
+ int64_t user_input_start_time_us_;
+
+ DISALLOW_COPY_AND_ASSIGN(EnergyEndpointer);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_H_
diff --git a/src/content/browser/speech/endpointer/energy_endpointer_params.cc b/src/content/browser/speech/endpointer/energy_endpointer_params.cc
new file mode 100644
index 0000000..9cdf024
--- /dev/null
+++ b/src/content/browser/speech/endpointer/energy_endpointer_params.cc
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/speech/endpointer/energy_endpointer_params.h"
+
+namespace content {
+
+EnergyEndpointerParams::EnergyEndpointerParams() {
+ SetDefaults();
+}
+
+void EnergyEndpointerParams::SetDefaults() {
+ frame_period_ = 0.01f;
+ frame_duration_ = 0.01f;
+ endpoint_margin_ = 0.2f;
+ onset_window_ = 0.15f;
+ speech_on_window_ = 0.4f;
+ offset_window_ = 0.15f;
+ onset_detect_dur_ = 0.09f;
+ onset_confirm_dur_ = 0.075f;
+ on_maintain_dur_ = 0.10f;
+ offset_confirm_dur_ = 0.12f;
+ decision_threshold_ = 150.0f;
+ min_decision_threshold_ = 50.0f;
+ fast_update_dur_ = 0.2f;
+ sample_rate_ = 8000.0f;
+ min_fundamental_frequency_ = 57.143f;
+ max_fundamental_frequency_ = 400.0f;
+ contamination_rejection_period_ = 0.25f;
+}
+
+void EnergyEndpointerParams::operator=(const EnergyEndpointerParams& source) {
+ frame_period_ = source.frame_period();
+ frame_duration_ = source.frame_duration();
+ endpoint_margin_ = source.endpoint_margin();
+ onset_window_ = source.onset_window();
+ speech_on_window_ = source.speech_on_window();
+ offset_window_ = source.offset_window();
+ onset_detect_dur_ = source.onset_detect_dur();
+ onset_confirm_dur_ = source.onset_confirm_dur();
+ on_maintain_dur_ = source.on_maintain_dur();
+ offset_confirm_dur_ = source.offset_confirm_dur();
+ decision_threshold_ = source.decision_threshold();
+ min_decision_threshold_ = source.min_decision_threshold();
+ fast_update_dur_ = source.fast_update_dur();
+ sample_rate_ = source.sample_rate();
+ min_fundamental_frequency_ = source.min_fundamental_frequency();
+ max_fundamental_frequency_ = source.max_fundamental_frequency();
+ contamination_rejection_period_ = source.contamination_rejection_period();
+}
+
+} // namespace content
diff --git a/src/content/browser/speech/endpointer/energy_endpointer_params.h b/src/content/browser/speech/endpointer/energy_endpointer_params.h
new file mode 100644
index 0000000..1510435
--- /dev/null
+++ b/src/content/browser/speech/endpointer/energy_endpointer_params.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
+#define CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
+
+#include "content/common/content_export.h"
+
+namespace content {
+
+// Input parameters for the EnergyEndpointer class.
+class CONTENT_EXPORT EnergyEndpointerParams {
+ public:
+ EnergyEndpointerParams();
+
+ void SetDefaults();
+
+ void operator=(const EnergyEndpointerParams& source);
+
+ // Accessors and mutators
+ float frame_period() const { return frame_period_; }
+ void set_frame_period(float frame_period) {
+ frame_period_ = frame_period;
+ }
+
+ float frame_duration() const { return frame_duration_; }
+ void set_frame_duration(float frame_duration) {
+ frame_duration_ = frame_duration;
+ }
+
+ float endpoint_margin() const { return endpoint_margin_; }
+ void set_endpoint_margin(float endpoint_margin) {
+ endpoint_margin_ = endpoint_margin;
+ }
+
+ float onset_window() const { return onset_window_; }
+ void set_onset_window(float onset_window) { onset_window_ = onset_window; }
+
+ float speech_on_window() const { return speech_on_window_; }
+ void set_speech_on_window(float speech_on_window) {
+ speech_on_window_ = speech_on_window;
+ }
+
+ float offset_window() const { return offset_window_; }
+ void set_offset_window(float offset_window) {
+ offset_window_ = offset_window;
+ }
+
+ float onset_detect_dur() const { return onset_detect_dur_; }
+ void set_onset_detect_dur(float onset_detect_dur) {
+ onset_detect_dur_ = onset_detect_dur;
+ }
+
+ float onset_confirm_dur() const { return onset_confirm_dur_; }
+ void set_onset_confirm_dur(float onset_confirm_dur) {
+ onset_confirm_dur_ = onset_confirm_dur;
+ }
+
+ float on_maintain_dur() const { return on_maintain_dur_; }
+ void set_on_maintain_dur(float on_maintain_dur) {
+ on_maintain_dur_ = on_maintain_dur;
+ }
+
+ float offset_confirm_dur() const { return offset_confirm_dur_; }
+ void set_offset_confirm_dur(float offset_confirm_dur) {
+ offset_confirm_dur_ = offset_confirm_dur;
+ }
+
+ float decision_threshold() const { return decision_threshold_; }
+ void set_decision_threshold(float decision_threshold) {
+ decision_threshold_ = decision_threshold;
+ }
+
+ float min_decision_threshold() const { return min_decision_threshold_; }
+ void set_min_decision_threshold(float min_decision_threshold) {
+ min_decision_threshold_ = min_decision_threshold;
+ }
+
+ float fast_update_dur() const { return fast_update_dur_; }
+ void set_fast_update_dur(float fast_update_dur) {
+ fast_update_dur_ = fast_update_dur;
+ }
+
+ float sample_rate() const { return sample_rate_; }
+ void set_sample_rate(float sample_rate) { sample_rate_ = sample_rate; }
+
+ float min_fundamental_frequency() const { return min_fundamental_frequency_; }
+ void set_min_fundamental_frequency(float min_fundamental_frequency) {
+ min_fundamental_frequency_ = min_fundamental_frequency;
+ }
+
+ float max_fundamental_frequency() const { return max_fundamental_frequency_; }
+ void set_max_fundamental_frequency(float max_fundamental_frequency) {
+ max_fundamental_frequency_ = max_fundamental_frequency;
+ }
+
+ float contamination_rejection_period() const {
+ return contamination_rejection_period_;
+ }
+ void set_contamination_rejection_period(
+ float contamination_rejection_period) {
+ contamination_rejection_period_ = contamination_rejection_period;
+ }
+
+ private:
+ float frame_period_; // Frame period
+ float frame_duration_; // Window size
+ float onset_window_; // Interval scanned for onset activity
+ float speech_on_window_; // Inverval scanned for ongoing speech
+ float offset_window_; // Interval scanned for offset evidence
+ float offset_confirm_dur_; // Silence duration required to confirm offset
+ float decision_threshold_; // Initial rms detection threshold
+ float min_decision_threshold_; // Minimum rms detection threshold
+ float fast_update_dur_; // Period for initial estimation of levels.
+ float sample_rate_; // Expected sample rate.
+
+ // Time to add on either side of endpoint threshold crossings
+ float endpoint_margin_;
+ // Total dur within onset_window required to enter ONSET state
+ float onset_detect_dur_;
+ // Total on time within onset_window required to enter SPEECH_ON state
+ float onset_confirm_dur_;
+ // Minimum dur in SPEECH_ON state required to maintain ON state
+ float on_maintain_dur_;
+ // Minimum fundamental frequency for autocorrelation.
+ float min_fundamental_frequency_;
+ // Maximum fundamental frequency for autocorrelation.
+ float max_fundamental_frequency_;
+ // Period after start of user input that above threshold values are ignored.
+ // This is to reject audio feedback contamination.
+ float contamination_rejection_period_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
diff --git a/src/media/base/sbplayer_pipeline.cc b/src/media/base/sbplayer_pipeline.cc
index b9d2262..ff21883 100644
--- a/src/media/base/sbplayer_pipeline.cc
+++ b/src/media/base/sbplayer_pipeline.cc
@@ -201,8 +201,6 @@
scoped_refptr<SbPlayerSetBoundsHelper> set_bounds_helper_;
- bool flushing_;
-
// The following member variables can be accessed from WMPI thread but all
// modifications to them happens on the pipeline thread. So any access of
// them from the WMPI thread and any modification to them on the pipeline
@@ -232,7 +230,6 @@
has_video_(false),
audio_read_in_progress_(false),
video_read_in_progress_(false),
- flushing_(false),
set_bounds_helper_(new SbPlayerSetBoundsHelper),
suspended_(false) {}
@@ -327,19 +324,17 @@
seek_cb.Run(PIPELINE_ERROR_INVALID_STATE);
}
+ player_->PrepareForSeek();
+
DCHECK(seek_cb_.is_null());
DCHECK(!seek_cb.is_null());
- flushing_ = true;
-
if (audio_read_in_progress_ || video_read_in_progress_) {
message_loop_->PostTask(
FROM_HERE, base::Bind(&SbPlayerPipeline::Seek, this, time, seek_cb));
return;
}
- player_->PrepareForSeek();
-
{
base::AutoLock auto_lock(lock_);
seek_cb_ = seek_cb;
@@ -526,8 +521,8 @@
return;
}
- if (error != PIPELINE_OK && !error_cb_.is_null()) {
- base::ResetAndReturn(&error_cb_).Run(error);
+ if (error != PIPELINE_OK) {
+ ResetAndRunIfNotNull(&error_cb_, error);
}
}
@@ -600,9 +595,13 @@
DCHECK(message_loop_->BelongsToCurrentThread());
if (status != PIPELINE_OK) {
- if (!error_cb_.is_null()) {
- base::ResetAndReturn(&error_cb_).Run(status);
- }
+ ResetAndRunIfNotNull(&error_cb_, status);
+ return;
+ }
+
+ if (demuxer_->GetStream(DemuxerStream::AUDIO) == NULL ||
+ demuxer_->GetStream(DemuxerStream::VIDEO) == NULL) {
+ ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
return;
}
@@ -637,7 +636,6 @@
DCHECK(message_loop_->BelongsToCurrentThread());
if (status == PIPELINE_OK) {
- flushing_ = false;
player_->Seek(seek_time_);
}
}
@@ -682,7 +680,6 @@
video_read_in_progress_ = false;
}
if (!seek_cb_.is_null()) {
- buffering_state_cb_.Run(kPrerollCompleted);
PipelineStatusCB seek_cb;
{
base::AutoLock auto_lock(lock_);
@@ -718,10 +715,6 @@
return;
}
- if (flushing_) {
- return;
- }
-
if (type == DemuxerStream::AUDIO) {
if (audio_read_in_progress_) {
return;
@@ -768,9 +761,7 @@
case kSbPlayerStateDestroyed:
break;
case kSbPlayerStateError:
- if (!error_cb_.is_null()) {
- base::ResetAndReturn(&error_cb_).Run(PIPELINE_ERROR_DECODE);
- }
+ ResetAndRunIfNotNull(&error_cb_, PIPELINE_ERROR_DECODE);
break;
}
}
diff --git a/src/media/base/shell_audio_bus.cc b/src/media/base/shell_audio_bus.cc
index 86f4129..4b5ad5e 100644
--- a/src/media/base/shell_audio_bus.cc
+++ b/src/media/base/shell_audio_bus.cc
@@ -23,6 +23,9 @@
namespace {
+typedef ShellAudioBus::StorageType StorageType;
+typedef ShellAudioBus::SampleType SampleType;
+
const float kFloat32ToInt16Factor = 32768.f;
inline void ConvertSample(ShellAudioBus::SampleType src_type,
@@ -218,66 +221,86 @@
}
}
-template <ShellAudioBus::StorageType T>
-inline uint8* ShellAudioBus::GetSamplePtrForType(size_t channel,
- size_t frame) const {
- DCHECK_LT(channel, channels_);
- DCHECK_LT(frame, frames_);
-
- if (T == kInterleaved) {
- return channel_data_[0] + sizeof(float) * (channels_ * frame + channel);
- } else if (T == kPlanar) {
- return channel_data_[channel] + sizeof(float) * frame;
- } else {
- NOTREACHED();
- }
-
- return NULL;
-}
-
-template <ShellAudioBus::StorageType T>
-inline float ShellAudioBus::GetFloat32SampleForType(size_t channel,
- size_t frame) const {
- return *reinterpret_cast<const float*>(
- GetSamplePtrForType<T>(channel, frame));
-}
-
-template <ShellAudioBus::StorageType SourceStorageType,
- ShellAudioBus::StorageType DestStorageType>
-void ShellAudioBus::MixForType(const ShellAudioBus& source) {
+template <typename SourceSampleType,
+ typename DestSampleType,
+ StorageType SourceStorageType,
+ StorageType DestStorageType>
+void ShellAudioBus::MixForTypes(const ShellAudioBus& source) {
const size_t frames = std::min(frames_, source.frames_);
for (size_t channel = 0; channel < channels_; ++channel) {
for (size_t frame = 0; frame < frames; ++frame) {
- *reinterpret_cast<float*>(
- GetSamplePtrForType<DestStorageType>(channel, frame)) +=
- source.GetFloat32SampleForType<SourceStorageType>(channel, frame);
+ *reinterpret_cast<DestSampleType*>(
+ GetSamplePtrForType<DestSampleType, DestStorageType>(channel,
+ frame)) +=
+ source.GetSampleForType<SourceSampleType, SourceStorageType>(channel,
+ frame);
}
}
}
void ShellAudioBus::Mix(const ShellAudioBus& source) {
DCHECK_EQ(channels_, source.channels_);
- DCHECK_EQ(sample_type_, kFloat32);
- DCHECK_EQ(source.sample_type_, kFloat32);
- if (channels_ != source.channels_ || sample_type_ != kFloat32 ||
- source.sample_type_ != kFloat32) {
+
+ if (channels_ != source.channels_) {
ZeroAllFrames();
return;
}
// Profiling has identified this area of code as hot, so instead of calling
- // GetSamplePtr, which branches on storage_type_ each time it is called, we
- // branch once before we loop and inline the branch of the function we want.
- DCHECK_EQ(GetSampleSizeInBytes(), sizeof(float));
- if (source.storage_type_ == kInterleaved && storage_type_ == kInterleaved) {
- MixForType<kInterleaved, kInterleaved>(source);
- } else if (source.storage_type_ == kInterleaved && storage_type_ == kPlanar) {
- MixForType<kInterleaved, kPlanar>(source);
- } else if (source.storage_type_ == kPlanar && storage_type_ == kInterleaved) {
- MixForType<kPlanar, kInterleaved>(source);
- } else if (source.storage_type_ == kPlanar && storage_type_ == kPlanar) {
- MixForType<kPlanar, kPlanar>(source);
+ // GetSamplePtr, which branches each time it is called, we branch once
+ // before we loop and inline the branch of the function we want.
+ if (source.sample_type_ == kInt16 && sample_type_ == kInt16 &&
+ source.storage_type_ == kInterleaved && storage_type_ == kInterleaved) {
+ MixForTypes<int16, int16, kInterleaved, kInterleaved>(source);
+ } else if (source.sample_type_ == kInt16 && sample_type_ == kInt16 &&
+ source.storage_type_ == kInterleaved && storage_type_ == kPlanar) {
+ MixForTypes<int16, int16, kInterleaved, kPlanar>(source);
+ } else if (source.sample_type_ == kInt16 && sample_type_ == kInt16 &&
+ source.storage_type_ == kPlanar && storage_type_ == kInterleaved) {
+ MixForTypes<int16, int16, kPlanar, kInterleaved>(source);
+ } else if (source.sample_type_ == kInt16 && sample_type_ == kInt16 &&
+ source.storage_type_ == kPlanar && storage_type_ == kPlanar) {
+ MixForTypes<int16, int16, kPlanar, kPlanar>(source);
+ } else if (source.sample_type_ == kInt16 && sample_type_ == kFloat32 &&
+ source.storage_type_ == kInterleaved &&
+ storage_type_ == kInterleaved) {
+ MixForTypes<int16, float, kInterleaved, kInterleaved>(source);
+ } else if (source.sample_type_ == kInt16 && sample_type_ == kFloat32 &&
+ source.storage_type_ == kInterleaved && storage_type_ == kPlanar) {
+ MixForTypes<int16, float, kInterleaved, kPlanar>(source);
+ } else if (source.sample_type_ == kInt16 && sample_type_ == kFloat32 &&
+ source.storage_type_ == kPlanar && storage_type_ == kInterleaved) {
+ MixForTypes<int16, float, kPlanar, kInterleaved>(source);
+ } else if (source.sample_type_ == kInt16 && sample_type_ == kFloat32 &&
+ source.storage_type_ == kPlanar && storage_type_ == kPlanar) {
+ MixForTypes<int16, float, kPlanar, kPlanar>(source);
+ } else if (source.sample_type_ == kFloat32 && sample_type_ == kInt16 &&
+ source.storage_type_ == kInterleaved &&
+ storage_type_ == kInterleaved) {
+ MixForTypes<float, int16, kInterleaved, kInterleaved>(source);
+ } else if (source.sample_type_ == kFloat32 && sample_type_ == kInt16 &&
+ source.storage_type_ == kInterleaved && storage_type_ == kPlanar) {
+ MixForTypes<float, int16, kInterleaved, kPlanar>(source);
+ } else if (source.sample_type_ == kFloat32 && sample_type_ == kInt16 &&
+ source.storage_type_ == kPlanar && storage_type_ == kInterleaved) {
+ MixForTypes<float, int16, kPlanar, kInterleaved>(source);
+ } else if (source.sample_type_ == kFloat32 && sample_type_ == kInt16 &&
+ source.storage_type_ == kPlanar && storage_type_ == kPlanar) {
+ MixForTypes<float, int16, kPlanar, kPlanar>(source);
+ } else if (source.sample_type_ == kFloat32 && sample_type_ == kFloat32 &&
+ source.storage_type_ == kInterleaved &&
+ storage_type_ == kInterleaved) {
+ MixForTypes<float, float, kInterleaved, kInterleaved>(source);
+ } else if (source.sample_type_ == kFloat32 && sample_type_ == kFloat32 &&
+ source.storage_type_ == kInterleaved && storage_type_ == kPlanar) {
+ MixForTypes<float, float, kInterleaved, kPlanar>(source);
+ } else if (source.sample_type_ == kFloat32 && sample_type_ == kFloat32 &&
+ source.storage_type_ == kPlanar && storage_type_ == kInterleaved) {
+ MixForTypes<float, float, kPlanar, kInterleaved>(source);
+ } else if (source.sample_type_ == kFloat32 && sample_type_ == kFloat32 &&
+ source.storage_type_ == kPlanar && storage_type_ == kPlanar) {
+ MixForTypes<float, float, kPlanar, kPlanar>(source);
} else {
NOTREACHED();
}
diff --git a/src/media/base/shell_audio_bus.h b/src/media/base/shell_audio_bus.h
index 16c7012..5beaf2c 100644
--- a/src/media/base/shell_audio_bus.h
+++ b/src/media/base/shell_audio_bus.h
@@ -60,6 +60,7 @@
size_t channels() const { return channels_; }
size_t frames() const { return frames_; }
SampleType sample_type() const { return sample_type_; }
+ StorageType storage_type() const { return storage_type_; }
size_t GetSampleSizeInBytes() const;
const uint8* interleaved_data() const;
const uint8* planar_data(size_t channel) const;
@@ -102,6 +103,40 @@
void Mix(const ShellAudioBus& source);
void Mix(const ShellAudioBus& source, const std::vector<float>& matrix);
+ public:
+ // The .*ForTypes? functions below are optimized versions that assume what
+ // storage type the bus is using. They are meant to be called after
+ // checking what storage type the bus is once, and then performing a batch
+ // of operations, where it is known that the type will not change.
+ template <typename SampleTypeName, StorageType T>
+ inline uint8* GetSamplePtrForType(size_t channel, size_t frame) const {
+ DCHECK_LT(channel, channels_);
+ DCHECK_LT(frame, frames_);
+
+ if (T == kInterleaved) {
+ return channel_data_[0] +
+ sizeof(SampleTypeName) * (channels_ * frame + channel);
+ } else if (T == kPlanar) {
+ return channel_data_[channel] + sizeof(SampleTypeName) * frame;
+ } else {
+ NOTREACHED();
+ }
+
+ return NULL;
+ }
+
+ template <typename SampleTypeName, StorageType T>
+ inline SampleTypeName GetSampleForType(size_t channel, size_t frame) const {
+ return *reinterpret_cast<const SampleTypeName*>(
+ GetSamplePtrForType<SampleTypeName, T>(channel, frame));
+ }
+
+ template <typename SourceSampleType,
+ typename DestSampleType,
+ StorageType SourceStorageType,
+ StorageType DestStorageType>
+ void MixForTypes(const ShellAudioBus& source);
+
private:
void SetFloat32Sample(size_t channel, size_t frame, float sample) {
DCHECK_EQ(sample_type_, kFloat32);
@@ -110,20 +145,6 @@
uint8* GetSamplePtr(size_t channel, size_t frame);
const uint8* GetSamplePtr(size_t channel, size_t frame) const;
- // The *ForType functions below are optimized versions that assume what
- // storage type the bus is using. They are meant to be called after checking
- // what storage type the bus is once, and then performing a batch of
- // operations, where it is known that the type will not change.
- // Note: The bus must have storage type of kFloat32.
- template <StorageType T>
- inline uint8* GetSamplePtrForType(size_t channel, size_t frame) const;
-
- template <StorageType T>
- inline float GetFloat32SampleForType(size_t channel, size_t frame) const;
-
- template <StorageType SourceStorageType, StorageType DestStorageType>
- void MixForType(const ShellAudioBus& source);
-
// Contiguous block of channel memory if the memory is owned by this object.
scoped_ptr_malloc<uint8, base::ScopedPtrAlignedFree> data_;
diff --git a/src/media/base/starboard_player.cc b/src/media/base/starboard_player.cc
index ce5197c..2708468 100644
--- a/src/media/base/starboard_player.cc
+++ b/src/media/base/starboard_player.cc
@@ -40,6 +40,7 @@
ticket_(SB_PLAYER_INITIAL_TICKET),
volume_(1.0),
paused_(true),
+ seek_pending_(false),
state_(kPlaying) {
DCHECK(audio_config.IsValidConfig());
DCHECK(video_config.IsValidConfig());
@@ -131,6 +132,8 @@
void StarboardPlayer::PrepareForSeek() {
DCHECK(message_loop_->BelongsToCurrentThread());
++ticket_;
+ SbPlayerSetPause(player_, true);
+ seek_pending_ = true;
}
void StarboardPlayer::Seek(base::TimeDelta time) {
@@ -154,6 +157,10 @@
++ticket_;
SbPlayerSeek(player_, TimeDeltaToSbMediaTime(time), ticket_);
+ seek_pending_ = false;
+ if (!paused_) {
+ SbPlayerSetPause(player_, false);
+ }
}
void StarboardPlayer::SetVolume(float volume) {
@@ -174,8 +181,10 @@
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(SbPlayerIsValid(player_));
- SbPlayerSetPause(player_, pause);
paused_ = pause;
+ if (!seek_pending_) {
+ SbPlayerSetPause(player_, pause);
+ }
}
void StarboardPlayer::GetInfo(uint32* video_frames_decoded,
diff --git a/src/media/base/starboard_player.h b/src/media/base/starboard_player.h
index 0f71acb..2d9dc58 100644
--- a/src/media/base/starboard_player.h
+++ b/src/media/base/starboard_player.h
@@ -131,6 +131,7 @@
int ticket_;
float volume_;
bool paused_;
+ bool seek_pending_;
DecoderBufferCache decoder_buffer_cache_;
// The following variables can be accessed from GetInfo(), which can be called
diff --git a/src/media/filters/source_buffer_stream.cc b/src/media/filters/source_buffer_stream.cc
index 7f3678d..25bcfe2 100644
--- a/src/media/filters/source_buffer_stream.cc
+++ b/src/media/filters/source_buffer_stream.cc
@@ -1682,7 +1682,7 @@
}
base::TimeDelta SourceBufferRange::GetStartTimestamp() const {
- DCHECK(!buffers_.empty());
+ DLOG_IF(WARNING, buffers_.empty()) << "|buffers_| cannot be empty.";
base::TimeDelta start_timestamp = media_segment_start_time_;
if (start_timestamp == kNoTimestamp())
start_timestamp = buffers_.front()->GetDecodeTimestamp();
diff --git a/src/nb/atomic.h b/src/nb/atomic.h
new file mode 100644
index 0000000..9bd5c61
--- /dev/null
+++ b/src/nb/atomic.h
@@ -0,0 +1,281 @@
+/*
+ * 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 NB_ATOMIC_H_
+#define NB_ATOMIC_H_
+
+#include "starboard/mutex.h"
+#include "starboard/types.h"
+
+namespace nb {
+
+// 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<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<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;
+
+// Subtype of atomic<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<T> may be instantiated with any TriviallyCopyable type T.
+// atomic<T> is neither copyable nor movable.
+template <typename T>
+class atomic {
+ public:
+ typedef T ValueType;
+
+ // C++11 forbids a copy constructor for std::atomic<T>, it also forbids
+ // a move operation.
+ atomic() : value_() {}
+ explicit atomic(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<T> that adds fetch_add(...) and fetch_sub(...).
+template <typename T>
+class atomic_number : public atomic<T> {
+ public:
+ typedef atomic<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<T> {
+ public:
+ typedef atomic<T> Super;
+ atomic_pointer() : Super() {}
+ explicit atomic_pointer(T initial_val) : Super(initial_val) {}
+};
+
+// Simple atomic bool class. This could be optimized for speed using
+// compiler intrinsics for concurrent integer modification.
+class atomic_bool : public atomic<bool> {
+ public:
+ typedef atomic<bool> Super;
+ atomic_bool() : Super() {}
+ explicit atomic_bool(bool initial_val) : Super(initial_val) {}
+};
+
+// Simple atomic int class. This could be optimized for speed using
+// compiler intrinsics for concurrent integer modification.
+class atomic_int32_t : public atomic_integral<int32_t> {
+ public:
+ typedef atomic_integral<int32_t> Super;
+ atomic_int32_t() : Super() {}
+ explicit atomic_int32_t(int32_t initial_val) : Super(initial_val) {}
+};
+
+// 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 nb
+
+#endif // NB_ATOMIC_H_
diff --git a/src/nb/atomic_test.cc b/src/nb/atomic_test.cc
new file mode 100644
index 0000000..fc76703
--- /dev/null
+++ b/src/nb/atomic_test.cc
@@ -0,0 +1,317 @@
+/*
+ * 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 "nb/atomic.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "nb/test_thread.h"
+#include "starboard/configuration.h"
+#include "starboard/mutex.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace nb {
+namespace {
+
+///////////////////////////////////////////////////////////////////////////////
+// Boilerplate for test setup.
+///////////////////////////////////////////////////////////////////////////////
+
+// Defines a typelist for all atomic types.
+typedef ::testing::Types<atomic_int32_t, atomic_int64_t,
+ atomic_float, atomic_double,
+ atomic_bool,
+ atomic_pointer<int*> > AllAtomicTypes;
+
+// Defines a typelist for just atomic number types.
+typedef ::testing::Types<atomic_int32_t, atomic_int64_t,
+ atomic_float, atomic_double> AtomicNumberTypes;
+
+// Defines test type that will be instantiated using each type in
+// AllAtomicTypes type list.
+template <typename T>
+class AtomicTest : public ::testing::Test {};
+TYPED_TEST_CASE(AtomicTest, AllAtomicTypes); // Registration.
+
+// Defines test type that will be instantiated using each type in
+// AtomicNumberTypes type list.
+template <typename T>
+class AtomicNumberTest : public ::testing::Test {};
+TYPED_TEST_CASE(AtomicNumberTest, AtomicNumberTypes); // Registration.
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Singlethreaded tests.
+///////////////////////////////////////////////////////////////////////////////
+
+// Tests default constructor and single-argument constructor.
+TYPED_TEST(AtomicTest, Constructors) {
+ typedef TypeParam AtomicT;
+ typedef typename AtomicT::ValueType T;
+
+ const T zero(0);
+ const T one = zero + 1; // Allows AtomicPointer<T*>.
+
+ AtomicT atomic_default;
+
+ // Tests that default value is zero.
+ ASSERT_EQ(atomic_default.load(), zero);
+ AtomicT atomic_val(one);
+ ASSERT_EQ(one, atomic_val.load());
+}
+
+// Tests load() and exchange().
+TYPED_TEST(AtomicTest, Load_Exchange_SingleThread) {
+ typedef TypeParam AtomicT;
+ typedef typename AtomicT::ValueType T;
+
+ const T zero(0);
+ const T one = zero + 1; // Allows AtomicPointer<T*>.
+
+ AtomicT atomic;
+ ASSERT_EQ(atomic.load(), zero); // Default is 0.
+ ASSERT_EQ(zero, atomic.exchange(one)); // Old value was 0.
+
+ // Tests that AtomicType has const get function.
+ const AtomicT& const_atomic = atomic;
+ ASSERT_EQ(one, const_atomic.load());
+}
+
+// Tests compare_exchange_strong().
+TYPED_TEST(AtomicNumberTest, CompareExchangeStrong_SingleThread) {
+ typedef TypeParam AtomicT;
+ typedef typename AtomicT::ValueType T;
+
+ const T zero(0);
+ const T one = zero + 1; // Allows AtomicPointer<T*>.
+
+ AtomicT atomic;
+ ASSERT_EQ(atomic.load(), zero); // Default is 0.
+ T expected_value = zero;
+ // Should succeed.
+ ASSERT_TRUE(atomic.compare_exchange_strong(&expected_value,
+ one)); // New value.
+
+ ASSERT_EQ(zero, expected_value);
+ ASSERT_EQ(one, atomic.load()); // Expect that value was set.
+
+ expected_value = zero;
+ // Asserts that when the expected and actual value is mismatched that the
+ // compare_exchange_strong() fails.
+ ASSERT_FALSE(atomic.compare_exchange_strong(&expected_value, // Mismatched.
+ zero)); // New value.
+
+ // Failed and this means that expected_value should be set to what the
+ // internal value was. In this case, one.
+ ASSERT_EQ(expected_value, one);
+ ASSERT_EQ(one, atomic.load());
+
+ ASSERT_TRUE(atomic.compare_exchange_strong(&expected_value, // Matches.
+ zero));
+ ASSERT_EQ(expected_value, one);
+}
+
+// Tests atomic fetching and adding.
+TYPED_TEST(AtomicNumberTest, FetchAdd_SingleThread) {
+ typedef TypeParam AtomicT;
+ typedef typename AtomicT::ValueType T;
+
+ const T zero(0);
+ const T one = zero + 1; // Allows atomic_pointer<T*>.
+ const T two = zero + 2;
+
+ AtomicT atomic;
+ ASSERT_EQ(atomic.load(), zero); // Default is 0.
+ ASSERT_EQ(zero, atomic.fetch_add(one)); // Prev value was 0.
+ ASSERT_EQ(one, atomic.load()); // Now value is this.
+ ASSERT_EQ(one, atomic.fetch_add(one)); // Prev value was 1.
+ ASSERT_EQ(two, atomic.exchange(one)); // Old value was 2.
+}
+
+// Tests atomic fetching and subtracting.
+TYPED_TEST(AtomicNumberTest, FetchSub_SingleThread) {
+ typedef TypeParam AtomicT;
+ typedef typename AtomicT::ValueType T;
+
+ const T zero(0);
+ const T one = zero + 1; // Allows AtomicPointer<T*>.
+ const T two = zero + 2;
+ const T neg_two(zero-2);
+
+ AtomicT atomic;
+ ASSERT_EQ(atomic.load(), zero); // Default is 0.
+ atomic.exchange(two);
+ ASSERT_EQ(two, atomic.fetch_sub(one)); // Prev value was 2.
+ ASSERT_EQ(one, atomic.load()); // New value.
+ ASSERT_EQ(one, atomic.fetch_sub(one)); // Prev value was one.
+ ASSERT_EQ(zero, atomic.load()); // New 0.
+ ASSERT_EQ(zero, atomic.fetch_sub(two));
+ ASSERT_EQ(neg_two, atomic.load()); // 0-2 = -2
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Multithreaded tests.
+///////////////////////////////////////////////////////////////////////////////
+
+// A thread that will execute compare_exhange_strong() and write out a result
+// to a shared output.
+template <typename AtomicT>
+class CompareExchangeThread : public TestThread {
+ public:
+ typedef typename AtomicT::ValueType T;
+ CompareExchangeThread(int start_num,
+ int end_num,
+ AtomicT* atomic_value,
+ std::vector<T>* output,
+ starboard::Mutex* output_mutex)
+ : start_num_(start_num), end_num_(end_num),
+ atomic_value_(atomic_value), output_(output),
+ output_mutex_(output_mutex) {
+ }
+
+ virtual void Run() {
+ std::vector<T> output_buffer;
+ output_buffer.reserve(end_num_ - start_num_);
+ for (int i = start_num_; i < end_num_; ++i) {
+ T new_value = T(i);
+ while (true) {
+ if (std::rand() % 3 == 0) {
+ // 1 in 3 chance of yeilding.
+ // Attempt to cause more contention by giving other threads a chance
+ // to run.
+ SbThreadYield();
+ }
+
+ const T prev_value = atomic_value_->load();
+ T expected_value = prev_value;
+ const bool success =
+ atomic_value_->compare_exchange_strong(&expected_value,
+ new_value);
+ if (success) {
+ output_buffer.push_back(prev_value);
+ break;
+ }
+ }
+ }
+
+ // Lock the output to append this output buffer.
+ starboard::ScopedLock lock(*output_mutex_);
+ output_->insert(output_->end(),
+ output_buffer.begin(),
+ output_buffer.end());
+ }
+ private:
+ const int start_num_;
+ const int end_num_;
+ AtomicT*const atomic_value_;
+ std::vector<T>*const output_;
+ starboard::Mutex*const output_mutex_;
+};
+
+// Tests Atomic<T>::compare_exchange_strong(). Each thread has a unique
+// sequential range [0,1,2,3 ... ), [5,6,8, ...) that it will generate.
+// The numbers are sequentially written to the shared Atomic type and then
+// exposed to other threads:
+//
+// Generates [0,1,2,...,n/2)
+// +------+ Thread A <--------+ (Write Exchanged Value)
+// | |
+// | compare_exchange() +---> exchanged? ---+
+// +----> +------------+ +----+ v
+// | AtomicType | Output vector
+// +----> +------------+ +----+ ^
+// | compare_exchange() +---> exchanged? ---+
+// | |
+// +------+ Thread B <--------+
+// Generates [n/2,n/2+1,...,n)
+//
+// By repeatedly calling atomic<T>::compare_exchange_strong() by each of the
+// threads, each will see the previous value of the shared variable when their
+// exchange (atomic swap) operation is successful. If all of the swapped out
+// values are recombined then it will form the original generated sequence from
+// all threads.
+//
+// TEST PHASE
+// sort( output vector ) AND TEST THAT
+// output vector Contains [0,1,2,...,n)
+//
+// The test passes when the output array is tested that it contains every
+// expected generated number from all threads. If compare_exchange_strong() is
+// written incorrectly for an atomic type then the output array will have
+// duplicates or otherwise not be equal to the expected natural number set.
+TYPED_TEST(AtomicNumberTest, Test_CompareExchange_MultiThreaded) {
+ typedef TypeParam AtomicT;
+ typedef typename AtomicT::ValueType T;
+
+ static const int kNumElements = 1000;
+ static const int kNumThreads = 4;
+
+ AtomicT atomic_value(T(-1));
+ std::vector<TestThread*> threads;
+ std::vector<T> output_values;
+ starboard::Mutex output_mutex;
+
+ for (int i = 0; i < kNumThreads; ++i) {
+ const int start_num = (kNumElements * i) / kNumThreads;
+ const int end_num = (kNumElements * (i + 1)) / kNumThreads;
+ threads.push_back(
+ new CompareExchangeThread<AtomicT>(
+ start_num, // defines the number range to generate.
+ end_num,
+ &atomic_value,
+ &output_values,
+ &output_mutex));
+ }
+
+ // These threads will generate unique numbers in their range and then
+ // write them to the output array.
+ for (int i = 0; i < kNumThreads; ++i) {
+ threads[i]->Start();
+ }
+
+ for (int i = 0; i < kNumThreads; ++i) {
+ threads[i]->Join();
+ }
+ // Cleanup threads.
+ for (int i = 0; i < kNumThreads; ++i) {
+ delete threads[i];
+ }
+ threads.clear();
+
+ // Final value needs to be written out. The last thread to join doesn't
+ // know it's the last and therefore the final value in the shared
+ // has not be pushed to the output array.
+ output_values.push_back(atomic_value.load());
+ std::sort(output_values.begin(), output_values.end());
+
+ // We expect the -1 value because it was the explicit initial value of the
+ // shared atomic.
+ ASSERT_EQ(T(-1), output_values[0]);
+ ASSERT_EQ(T(0), output_values[1]);
+ output_values.erase(output_values.begin()); // Chop off the -1 at the front.
+
+ // Finally, assert that the output array is equal to the natural numbers
+ // after it has been sorted.
+ ASSERT_EQ(output_values.size(), kNumElements);
+ // All of the elements should be equal too.
+ for (int i = 0; i < output_values.size(); ++i) {
+ ASSERT_EQ(output_values[i], T(i));
+ }
+}
+
+} // namespace
+} // namespace nb
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index 3780eb9..f87d42a 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -24,6 +24,7 @@
'allocator.h',
'allocator_decorator.cc',
'allocator_decorator.h',
+ 'atomic.h',
'fixed_no_free_allocator.cc',
'fixed_no_free_allocator.h',
'memory_pool.cc',
@@ -38,6 +39,7 @@
'scoped_ptr.h',
'thread_collision_warner.cc',
'thread_collision_warner.h',
+ 'thread_local_object.h',
],
'dependencies': [
@@ -60,9 +62,11 @@
'target_name': 'nb_test',
'type': '<(gtest_target_type)',
'sources': [
+ 'atomic_test.cc',
'fixed_no_free_allocator_test.cc',
'reuse_allocator_test.cc',
'run_all_unittests.cc',
+ 'test_thread.h',
'thread_local_object_test.cc',
],
'dependencies': [
diff --git a/src/nb/test_thread.h b/src/nb/test_thread.h
new file mode 100644
index 0000000..0ea5768
--- /dev/null
+++ b/src/nb/test_thread.h
@@ -0,0 +1,73 @@
+// 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 NB_TEST_THREAD_H_
+#define NB_TEST_THREAD_H_
+
+#include "starboard/configuration.h"
+#include "starboard/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace nb {
+
+// TestThread that is a bare bones class wrapper around Starboard
+// thread. Subclasses must override Run().
+class TestThread {
+ public:
+ TestThread() : thread_(kSbThreadInvalid) {}
+ virtual ~TestThread() {}
+
+ // Subclasses should override the Run method.
+ virtual void Run() = 0;
+
+ // Calls SbThreadCreate() with default parameters.
+ void Start() {
+ SbThreadEntryPoint entry_point = ThreadEntryPoint;
+
+ thread_ = SbThreadCreate(
+ 0, // default stack_size.
+ kSbThreadNoPriority, // default priority.
+ kSbThreadNoAffinity, // default affinity.
+ true, // joinable.
+ "TestThread",
+ entry_point,
+ this);
+
+ if (kSbThreadInvalid == thread_) {
+ ADD_FAILURE_AT(__FILE__, __LINE__) << "Invalid thread.";
+ }
+ return;
+ }
+
+ void Join() {
+ if (!SbThreadJoin(thread_, NULL)) {
+ ADD_FAILURE_AT(__FILE__, __LINE__) << "Could not join thread.";
+ }
+ }
+
+ private:
+ static void* ThreadEntryPoint(void* ptr) {
+ TestThread* this_ptr = static_cast<TestThread*>(ptr);
+ this_ptr->Run();
+ return NULL;
+ }
+
+ SbThread thread_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(TestThread);
+};
+
+} // namespace nb.
+
+#endif // NB_TEST_THREAD_H_
diff --git a/src/nb/thread_local_object.h b/src/nb/thread_local_object.h
index 4b6a7ab..a303f5a 100644
--- a/src/nb/thread_local_object.h
+++ b/src/nb/thread_local_object.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BASE_THREADING_THREAD_LOCAL_OBJECT_H_
-#define BASE_THREADING_THREAD_LOCAL_OBJECT_H_
+#ifndef NB_THREAD_LOCAL_OBJECT_H_
+#define NB_THREAD_LOCAL_OBJECT_H_
#include <set>
@@ -24,7 +24,7 @@
#include "starboard/mutex.h"
#include "starboard/thread.h"
-namespace base {
+namespace nb {
// Like base::ThreadLocalPointer<T> but destroys objects. This is important
// for using ThreadLocalObjects that aren't tied to a singleton, or for
@@ -75,12 +75,14 @@
// Thread Local Objects are destroyed after this call.
~ThreadLocalObject() {
CheckCurrentThreadAllowedToDestruct();
- SB_DCHECK(entry_set_.size() < 2)
+ if (SB_DLOG_IS_ON(FATAL)) {
+ SB_DCHECK(entry_set_.size() < 2)
<< "Logic error: Some threads may still be accessing the objects that "
<< "are about to be destroyed. Only one object is expected and that "
<< "should be for the main thread. The caller should ensure that "
<< "other threads that access this object are externally "
<< "synchronized.";
+ }
// No locking is done because the entries should not be accessed by
// different threads while this object is shutting down. If access is
// occuring then the caller has a race condition, external to this class.
@@ -209,6 +211,6 @@
SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalObject<Type>);
};
-} // namespace base
+} // namespace nb
-#endif // BASE_THREADING_THREAD_LOCAL_H_
+#endif // NB_THREAD_LOCAL_OBJECT_H_
diff --git a/src/nb/thread_local_object_test.cc b/src/nb/thread_local_object_test.cc
index 000c6f4..9fce6d6 100644
--- a/src/nb/thread_local_object_test.cc
+++ b/src/nb/thread_local_object_test.cc
@@ -15,172 +15,31 @@
#include <map>
#include <string>
+#include "nb/test_thread.h"
+#include "nb/atomic.h"
#include "nb/thread_local_object.h"
#include "nb/scoped_ptr.h"
#include "starboard/mutex.h"
#include "starboard/thread.h"
-
#include "testing/gtest/include/gtest/gtest.h"
-namespace base {
+namespace nb {
namespace {
-// Similar to C++11 std::atomic<T>.
-// Atomic<T> may be instantiated with any TriviallyCopyable type T.
-// Atomic<T> is neither copyable nor movable.
-// TODO: Lift this class out into the library.
-template <typename T>
-class Atomic {
- public:
- // C++11 forbids a copy constructor for std::atomic<T>, it also forbids
- // a move operation.
- Atomic() : value_() {}
- explicit Atomic(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.
- 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.
- T Swap(T new_val) {
- int old_value = -1;
- {
- starboard::ScopedLock lock(mutex_);
- old_value = value_;
- value_ = new_val;
- }
- return old_value;
- }
-
- // Atomically obtains the value of the atomic object.
- T Get() const {
- starboard::ScopedLock lock(mutex_);
- return value_;
- }
-
- // Returns the new updated value after the operation has been applied.
- T Add(T val) {
- starboard::ScopedLock lock(mutex_);
- value_ += val;
- return value_;
- }
-
- // TrySwap(...) sets the new value if and only if "expected_old_value"
- // matches the actual value during the atomic assignment operation. If this
- // succeeds then true is returned. If there is a mismatch then the value is
- // left unchanged and false is returned.
- // Inputs:
- // new_value: Attempt to set the value to this new value.
- // expected_old_value: A test condition for success. If the actual value
- // matches the expected_old_value then the swap will succeed.
- // optional_actual_value: If non-null, then the actual value at the time
- // of the attempted operation is set to this value.
- bool TrySwap(T new_value, T expected_old_value,
- T* optional_actual_value) {
- starboard::ScopedLock lock(mutex_);
- if (optional_actual_value) {
- *optional_actual_value = value_;
- }
- if (expected_old_value == value_) {
- value_ = new_value;
- return true;
- }
- return false;
- }
-
- private:
- T value_;
- starboard::Mutex mutex_;
-};
-
-// Simple atomic int class. This could be optimized for speed using
-// compiler intrinsics for concurrent integer modification.
-class AtomicInt : public Atomic<int> {
- public:
- AtomicInt() : Atomic<int>(0) {}
- explicit AtomicInt(int initial_val) : Atomic<int>(initial_val) {}
- void Increment() { Add(1); }
- void Decrement() { Add(-1); }
-};
-
-// Simple atomic bool class. This could be optimized for speed using
-// compiler intrinsics for concurrent integer modification.
-class AtomicBool : public Atomic<bool> {
- public:
- AtomicBool() : Atomic<bool>(false) {}
- explicit AtomicBool(bool initial_val) : Atomic<bool>(initial_val) {}
-};
-
-// AbstractTestThread that is a bare bones class wrapper around Starboard
-// thread. Subclasses must override Run().
-// TODO: Move this to nplb/thread_helpers.h
-class AbstractTestThread {
- public:
- explicit AbstractTestThread() : thread_(kSbThreadInvalid) {}
- virtual ~AbstractTestThread() {}
-
- // Subclasses should override the Run method.
- virtual void Run() = 0;
-
- // Calls SbThreadCreate() with default parameters.
- void Start() {
- SbThreadEntryPoint entry_point = ThreadEntryPoint;
-
- thread_ = SbThreadCreate(
- 0, // default stack_size.
- kSbThreadNoPriority, // default priority.
- kSbThreadNoAffinity, // default affinity.
- true, // joinable.
- "AbstractTestThread",
- entry_point,
- this);
-
- if (kSbThreadInvalid == thread_) {
- ADD_FAILURE_AT(__FILE__, __LINE__) << "Invalid thread.";
- }
- return;
- }
-
- void Join() {
- if (!SbThreadJoin(thread_, NULL)) {
- ADD_FAILURE_AT(__FILE__, __LINE__) << "Could not join thread.";
- }
- }
-
- private:
- static void* ThreadEntryPoint(void* ptr) {
- AbstractTestThread* this_ptr = static_cast<AbstractTestThread*>(ptr);
- this_ptr->Run();
- return NULL;
- }
-
- SbThread thread_;
-};
-
// Simple class that counts the number of instances alive.
struct CountsInstances {
- CountsInstances() { s_instances_.Increment(); }
- ~CountsInstances() { s_instances_.Decrement(); }
- static AtomicInt s_instances_;
- static int NumInstances() { return s_instances_.Get(); }
- static void ResetNumInstances() { s_instances_.Swap(0); }
+ CountsInstances() { s_instances_.increment(); }
+ ~CountsInstances() { s_instances_.decrement(); }
+ static atomic_int32_t s_instances_;
+ static int NumInstances() { return s_instances_.load(); }
+ static void ResetNumInstances() { s_instances_.exchange(0); }
};
-AtomicInt CountsInstances::s_instances_(0);
+nb::atomic_int32_t CountsInstances::s_instances_(0);
// A simple thread that just creates the an object from the supplied
// ThreadLocalObject<T> and then exits.
template <typename TYPE>
-class CreateThreadLocalObjectThenExit : public AbstractTestThread {
+class CreateThreadLocalObjectThenExit : public TestThread {
public:
explicit CreateThreadLocalObjectThenExit(
ThreadLocalObject<TYPE>* tlo) : tlo_(tlo) {}
@@ -196,7 +55,7 @@
// A simple thread that just deletes the object supplied on a thread and then
// exists.
template <typename TYPE>
-class DestroyTypeOnThread : public AbstractTestThread {
+class DestroyTypeOnThread : public TestThread {
public:
explicit DestroyTypeOnThread(TYPE* ptr)
: ptr_(ptr) {}
@@ -285,7 +144,7 @@
nb::scoped_ptr<TLO> tlo(new TLO);
{
- AbstractTestThread* thread =
+ TestThread* thread =
new CreateThreadLocalObjectThenExit<CountsInstances>(tlo.get());
thread->Start();
thread->Join();
@@ -318,7 +177,7 @@
// Thread will simply create the thread local object (CountsInstances)
// and then return.
- nb::scoped_ptr<AbstractTestThread> thread_ptr(
+ nb::scoped_ptr<TestThread> thread_ptr(
new CreateThreadLocalObjectThenExit<CountsInstances>(tlo));
thread_ptr->Start(); // Object is now created.
thread_ptr->Join(); // ...then destroyed.
@@ -342,5 +201,4 @@
}
} // anonymous namespace
-} // namespace base
-
+} // nb namespace
diff --git a/src/net/base/net_util.cc b/src/net/base/net_util.cc
index a5c6c64..7b406ba 100644
--- a/src/net/base/net_util.cc
+++ b/src/net/base/net_util.cc
@@ -63,6 +63,7 @@
#include "net/base/escape.h"
#include "net/base/mime_util.h"
#include "net/base/net_module.h"
+
#if defined(OS_WIN)
#include "net/base/winsock_init.h"
#endif
@@ -1870,7 +1871,16 @@
// TODO(jar): The following is a simple estimate of IPv6 support. We may need
// to do a test resolution, and a test connection, to REALLY verify support.
IPv6SupportResult TestIPv6SupportInternal() {
-#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#if defined(OS_STARBOARD)
+ SbSocket test_socket =
+ SbSocketCreate(kSbSocketAddressTypeIpv6, kSbSocketProtocolTcp);
+ if (!SbSocketIsValid(test_socket)) {
+ return IPv6SupportResult(false, IPV6_CANNOT_CREATE_SOCKETS,
+ SbSystemGetLastError());
+ }
+ SbSocketDestroy(test_socket);
+ return IPv6SupportResult(true, IPV6_SUPPORT_MAX, 0);
+#elif defined(OS_ANDROID) || defined(__LB_ANDROID__)
// TODO: We should fully implement IPv6 probe once 'getifaddrs' API available;
// Another approach is implementing the similar feature by
// java.net.NetworkInterface through JNI.
diff --git a/src/net/base/net_util_unittest.cc b/src/net/base/net_util_unittest.cc
index 8ec69ad..d6102b2 100644
--- a/src/net/base/net_util_unittest.cc
+++ b/src/net/base/net_util_unittest.cc
@@ -600,7 +600,7 @@
"%E9%A1%B5.doc"},
{L"D:\\plane1\\\xD835\xDC00\xD835\xDC01.txt", // Math alphabet "AB"
"file:///D:/plane1/%F0%9D%90%80%F0%9D%90%81.txt"},
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
{L"/foo/bar.txt", "file:///foo/bar.txt"},
{L"/foo/BAR.txt", "file:///foo/BAR.txt"},
{L"/C:/foo/bar.txt", "file:///C:/foo/bar.txt"},
@@ -647,7 +647,7 @@
{L"\\\\foo\\bar.txt", "file:/foo/bar.txt"},
{L"\\\\foo\\bar.txt", "file://foo\\bar.txt"},
{L"C:\\foo\\bar.txt", "file:\\\\\\c:/foo/bar.txt"},
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
{L"/c:/foo/bar.txt", "file:/c:/foo/bar.txt"},
{L"/c:/foo/bar.txt", "file:///c:/foo/bar.txt"},
{L"/foo/bar.txt", "file:/foo/bar.txt"},
diff --git a/src/net/http/http_pipelined_host_pool.h b/src/net/http/http_pipelined_host_pool.h
index 7a0a678..1f86618 100644
--- a/src/net/http/http_pipelined_host_pool.h
+++ b/src/net/http/http_pipelined_host_pool.h
@@ -81,7 +81,7 @@
base::Value* PipelineInfoToValue() const;
private:
-#if defined(__LB_SHELL__)
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
typedef std::map<HttpPipelinedHost::Key, HttpPipelinedHost*> HostMap;
#else
typedef std::map<const HttpPipelinedHost::Key, HttpPipelinedHost*> HostMap;
diff --git a/src/net/spdy/spdy_protocol.h b/src/net/spdy/spdy_protocol.h
index 30daee3..ab3bf3d 100644
--- a/src/net/spdy/spdy_protocol.h
+++ b/src/net/spdy/spdy_protocol.h
@@ -573,7 +573,7 @@
void set_flags(uint8 flags) { frame_->flags_length_.flags_[0] = flags; }
uint32 length() const {
- return ntohl(frame_->flags_length_.length_) & kLengthMask;
+ return base::NetToHost32(frame_->flags_length_.length_) & kLengthMask;
}
void set_length(uint32 length) {
@@ -583,13 +583,13 @@
| Flags (8) | Length (24 bits) |
+----------------------------------+
*/
- frame_->flags_length_.length_ = htonl((length & kLengthMask)
- | (flags() << 24));
+ frame_->flags_length_.length_ =
+ base::HostToNet32((length & kLengthMask) | (flags() << 24));
}
bool is_control_frame() const {
- return (ntohs(frame_->control_.version_) & kControlFlagMask) ==
- kControlFlagMask;
+ return (base::NetToHost16(frame_->control_.version_) & kControlFlagMask) ==
+ kControlFlagMask;
}
// The size of the SpdyFrameBlock structure.
@@ -614,7 +614,7 @@
: SpdyFrame(data, owns_buffer) {}
SpdyStreamId stream_id() const {
- return ntohl(frame_->data_.stream_id_) & kStreamIdMask;
+ return base::NetToHost32(frame_->data_.stream_id_) & kStreamIdMask;
}
// Note that setting the stream id sets the control bit to false.
@@ -622,7 +622,7 @@
// should always be set correctly.
void set_stream_id(SpdyStreamId id) {
DCHECK_EQ(0u, (id & ~kStreamIdMask));
- frame_->data_.stream_id_ = htonl(id & kStreamIdMask);
+ frame_->data_.stream_id_ = base::HostToNet32(id & kStreamIdMask);
}
// Returns the size of the SpdyFrameBlock structure.
@@ -648,7 +648,7 @@
// frame. Does not guarantee that there are no errors.
bool AppearsToBeAValidControlFrame() const {
// Right now we only check if the frame has an out-of-bounds type.
- uint16 type = ntohs(block()->control_.type_);
+ uint16 type = base::NetToHost16(block()->control_.type_);
// NOOP is not a 'valid' control frame in SPDY/3 and beyond.
return type >= SYN_STREAM &&
type < NUM_CONTROL_FRAME_TYPES &&
@@ -657,8 +657,8 @@
uint16 version() const {
const int kVersionMask = 0x7fff;
- return static_cast<uint16>(
- ntohs((block()->control_.version_)) & kVersionMask);
+ return static_cast<uint16>(base::NetToHost16((block()->control_.version_)) &
+ kVersionMask);
}
void set_version(uint16 version) {
@@ -668,11 +668,12 @@
+----------------------------------+
*/
DCHECK_EQ(0U, version & kControlFlagMask);
- mutable_block()->control_.version_ = htons(kControlFlagMask | version);
+ mutable_block()->control_.version_ =
+ base::HostToNet16(kControlFlagMask | version);
}
SpdyControlType type() const {
- uint16 type = ntohs(block()->control_.type_);
+ uint16 type = base::NetToHost16(block()->control_.type_);
LOG_IF(DFATAL, type < SYN_STREAM || type >= NUM_CONTROL_FRAME_TYPES)
<< "Invalid control frame type " << type;
return static_cast<SpdyControlType>(type);
@@ -680,7 +681,8 @@
void set_type(SpdyControlType type) {
DCHECK(type >= SYN_STREAM && type < NUM_CONTROL_FRAME_TYPES);
- mutable_block()->control_.type_ = htons(static_cast<uint16>(type));
+ mutable_block()->control_.type_ =
+ base::HostToNet16(static_cast<uint16>(type));
}
// Returns true if this control frame is of a type that has a header block,
@@ -707,19 +709,20 @@
: SpdyControlFrame(data, owns_buffer) {}
SpdyStreamId stream_id() const {
- return ntohl(block()->stream_id_) & kStreamIdMask;
+ return base::NetToHost32(block()->stream_id_) & kStreamIdMask;
}
void set_stream_id(SpdyStreamId id) {
- mutable_block()->stream_id_ = htonl(id & kStreamIdMask);
+ mutable_block()->stream_id_ = base::HostToNet32(id & kStreamIdMask);
}
SpdyStreamId associated_stream_id() const {
- return ntohl(block()->associated_stream_id_) & kStreamIdMask;
+ return base::NetToHost32(block()->associated_stream_id_) & kStreamIdMask;
}
void set_associated_stream_id(SpdyStreamId id) {
- mutable_block()->associated_stream_id_ = htonl(id & kStreamIdMask);
+ mutable_block()->associated_stream_id_ =
+ base::HostToNet32(id & kStreamIdMask);
}
SpdyPriority priority() const {
@@ -774,11 +777,11 @@
: SpdyControlFrame(data, owns_buffer) {}
SpdyStreamId stream_id() const {
- return ntohl(block()->stream_id_) & kStreamIdMask;
+ return base::NetToHost32(block()->stream_id_) & kStreamIdMask;
}
void set_stream_id(SpdyStreamId id) {
- mutable_block()->stream_id_ = htonl(id & kStreamIdMask);
+ mutable_block()->stream_id_ = base::HostToNet32(id & kStreamIdMask);
}
int header_block_len() const {
@@ -821,23 +824,23 @@
: SpdyControlFrame(data, owns_buffer) {}
SpdyStreamId stream_id() const {
- return ntohl(block()->stream_id_) & kStreamIdMask;
+ return base::NetToHost32(block()->stream_id_) & kStreamIdMask;
}
void set_stream_id(SpdyStreamId id) {
- mutable_block()->stream_id_ = htonl(id & kStreamIdMask);
+ mutable_block()->stream_id_ = base::HostToNet32(id & kStreamIdMask);
}
SpdyStatusCodes status() const {
SpdyStatusCodes status =
- static_cast<SpdyStatusCodes>(ntohl(block()->status_));
+ static_cast<SpdyStatusCodes>(base::NetToHost32(block()->status_));
if (status < INVALID || status >= NUM_STATUS_CODES) {
status = INVALID;
}
return status;
}
void set_status(SpdyStatusCodes status) {
- mutable_block()->status_ = htonl(static_cast<uint32>(status));
+ mutable_block()->status_ = base::HostToNet32(static_cast<uint32>(status));
}
// Returns the size of the SpdyRstStreamControlFrameBlock structure.
@@ -861,11 +864,11 @@
: SpdyControlFrame(data, owns_buffer) {}
uint32 num_entries() const {
- return ntohl(block()->num_entries_);
+ return base::NetToHost32(block()->num_entries_);
}
void set_num_entries(int val) {
- mutable_block()->num_entries_ = htonl(static_cast<uint32>(val));
+ mutable_block()->num_entries_ = base::HostToNet32(static_cast<uint32>(val));
}
int header_block_len() const {
@@ -896,12 +899,10 @@
SpdyPingControlFrame(char* data, bool owns_buffer)
: SpdyControlFrame(data, owns_buffer) {}
- uint32 unique_id() const {
- return ntohl(block()->unique_id_);
- }
+ uint32 unique_id() const { return base::NetToHost32(block()->unique_id_); }
void set_unique_id(uint32 unique_id) {
- mutable_block()->unique_id_ = htonl(unique_id);
+ mutable_block()->unique_id_ = base::HostToNet32(unique_id);
}
static size_t size() { return sizeof(SpdyPingControlFrameBlock); }
@@ -941,7 +942,7 @@
: SpdyControlFrame(data, owns_buffer) {}
SpdyStreamId last_accepted_stream_id() const {
- return ntohl(block()->last_accepted_stream_id_) & kStreamIdMask;
+ return base::NetToHost32(block()->last_accepted_stream_id_) & kStreamIdMask;
}
SpdyGoAwayStatus status() const {
@@ -949,7 +950,7 @@
LOG(DFATAL) << "Attempted to access status of SPDY 2 GOAWAY.";
return GOAWAY_INVALID;
} else {
- uint32 status = ntohl(block()->status_);
+ uint32 status = base::NetToHost32(block()->status_);
if (status >= GOAWAY_NUM_STATUS_CODES) {
return GOAWAY_INVALID;
} else {
@@ -959,7 +960,8 @@
}
void set_last_accepted_stream_id(SpdyStreamId id) {
- mutable_block()->last_accepted_stream_id_ = htonl(id & kStreamIdMask);
+ mutable_block()->last_accepted_stream_id_ =
+ base::HostToNet32(id & kStreamIdMask);
}
static size_t size() { return sizeof(SpdyGoAwayControlFrameBlock); }
@@ -982,11 +984,11 @@
: SpdyControlFrame(data, owns_buffer) {}
SpdyStreamId stream_id() const {
- return ntohl(block()->stream_id_) & kStreamIdMask;
+ return base::NetToHost32(block()->stream_id_) & kStreamIdMask;
}
void set_stream_id(SpdyStreamId id) {
- mutable_block()->stream_id_ = htonl(id & kStreamIdMask);
+ mutable_block()->stream_id_ = base::HostToNet32(id & kStreamIdMask);
}
// The number of bytes in the header block beyond the frame header length.
@@ -1030,19 +1032,19 @@
: SpdyControlFrame(data, owns_buffer) {}
SpdyStreamId stream_id() const {
- return ntohl(block()->stream_id_) & kStreamIdMask;
+ return base::NetToHost32(block()->stream_id_) & kStreamIdMask;
}
void set_stream_id(SpdyStreamId id) {
- mutable_block()->stream_id_ = htonl(id & kStreamIdMask);
+ mutable_block()->stream_id_ = base::HostToNet32(id & kStreamIdMask);
}
uint32 delta_window_size() const {
- return ntohl(block()->delta_window_size_);
+ return base::NetToHost32(block()->delta_window_size_);
}
void set_delta_window_size(uint32 delta_window_size) {
- mutable_block()->delta_window_size_ = htonl(delta_window_size);
+ mutable_block()->delta_window_size_ = base::HostToNet32(delta_window_size);
}
// Returns the size of the SpdyWindowUpdateControlFrameBlock structure.
diff --git a/src/starboard/audio_sink.h b/src/starboard/audio_sink.h
index 59f507b..790741a 100644
--- a/src/starboard/audio_sink.h
+++ b/src/starboard/audio_sink.h
@@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// An interface to output raw audio data.
+// Module Overview: Starboard Audio Sink API
+//
+// Provides an interface to output raw audio data.
#ifndef STARBOARD_AUDIO_SINK_H_
#define STARBOARD_AUDIO_SINK_H_
@@ -69,37 +71,49 @@
// --- Functions -------------------------------------------------------------
-// Returns whether the given audio sink handle is valid.
+// Indicates whether the given audio sink handle is valid.
+//
+// |audio_sink|: The audio sink handle to check.
SB_EXPORT bool SbAudioSinkIsValid(SbAudioSink audio_sink);
// Creates an audio sink for the specified |channels| and
-// |sampling_frequency_hz|, acquiring all resources needed to operate it, and
-// returning an opaque handle to it.
-//
-// |frame_buffers| is an array of pointers to sample data. If the sink is
-// operating in interleaved mode, the array contains only one element, which is
-// an array containing |frames_per_channel| * |channels| samples. If the sink
-// is operating in planar mode, the number of elements in the array will be the
-// same as |channels|, each of the elements will be an array of
-// |frames_per_channel| samples. The caller has to ensure that |frame_buffers|
-// is valid until SbAudioSinkDestroy is called.
-//
-// |update_source_status_func| cannot be NULL. The audio sink will call it on
-// an internal thread to query the status of the source.
-//
-// |consume_frames_func| cannot be NULL. The audio sink will call it on an
-// internal thread to report consumed frames.
-//
-// |context| will be passed back into all callbacks, and is generally used to
-// point at a class or struct that contains state associated with the audio
-// sink.
-//
-// The audio sink will start to call |update_source_status_func| immediately
-// after SbAudioSinkCreate is called, even before it returns. The caller has
-// to ensure that the above callbacks returns meaningful values in this case.
+// |sampling_frequency_hz|, acquires all resources needed to operate the
+// audio sink, and returns an opaque handle to the audio sink.
//
// If the particular platform doesn't support the requested audio sink, the
// function returns kSbAudioSinkInvalid without calling any of the callbacks.
+//
+// |channels|: The number of audio channels, such as left and right channels
+// in stereo audio.
+// |sampling_frequency_hz|: The sample frequency of the audio data being
+// streamed. For example, 22,000 Hz means 22,000 sample elements represents
+// one second of audio data.
+// |audio_sample_type|: The type of each sample of the audio data --
+// |int16|, |float32|, etc.
+// |audio_frame_storage_type|: Indicates whether frames are interleaved or
+// planar.
+// |frame_buffers|: An array of pointers to sample data.
+// - If the sink is operating in interleaved mode, the array contains only
+// one element, which is an array containing (|frames_per_channel| *
+// |channels|) samples.
+// - If the sink is operating in planar mode, the number of elements in the
+// array is the same as |channels|, and each element is an array of
+// |frames_per_channel| samples. The caller has to ensure that
+// |frame_buffers| is valid until SbAudioSinkDestroy is called.
+// |frames_per_channel|: The size of the frame buffers, in units of the
+// number of samples per channel. The frame, in this case, represents a
+// group of samples at the same media time, one for each channel.
+// |update_source_status_func|: The audio sink calls this function on an
+// internal thread to query the status of the source. The value cannot be NULL.
+// |consume_frames_func|: The audio sink calls this function on an internal
+// thread to report consumed frames. The value cannot be NULL.
+// |context|: A value that is passed back to all callbacks and is generally
+// used to point at a class or struct that contains state associated with the
+// audio sink.
+// |update_source_status_func|: A function that the audio sink starts to call
+// immediately after SbAudioSinkCreate is called, even before it returns.
+// The caller has to ensure that the callback functions above return
+// meaningful values in this case.
SB_EXPORT SbAudioSink
SbAudioSinkCreate(int channels,
int sampling_frequency_hz,
@@ -111,32 +125,36 @@
SbAudioSinkConsumeFramesFunc consume_frames_func,
void* context);
-// Destroys |audio_sink|, freeing all associated resources. It will wait until
-// all callbacks in progress is finished before returning. Upon returning of
-// this function, no callbacks passed into SbAudioSinkCreate will be called
-// further. This function can be called on any thread but cannot be called
+// Destroys |audio_sink|, freeing all associated resources. Before
+// returning, the function waits until all callbacks that are in progress
+// have finished. After the function returns, no further calls are made
+// callbacks passed into SbAudioSinkCreate. In addition, you can not pass
+// |audio_sink| to any other SbAudioSink functions after SbAudioSinkDestroy
+// has been called on it.
+//
+// This function can be called on any thread. However, it cannot be called
// within any of the callbacks passed into SbAudioSinkCreate.
-// It is not allowed to pass |audio_sink| into any other SbAudioSink function
-// once SbAudioSinkDestroy has been called on it.
+//
+// |audio_sink|: The audio sink to destroy.
SB_EXPORT void SbAudioSinkDestroy(SbAudioSink audio_sink);
-// Returns the maximum channel supported on the platform.
+// Returns the maximum number of channels supported on the platform. For
+// example, the number would be |2| if the platform only supports stereo.
SB_EXPORT int SbAudioSinkGetMaxChannels();
-// Returns the nearest supported sample rate of |sampling_frequency_hz|. On
-// platforms that don't support all sample rates, it is the caller's
+// Returns the supported sample rate closest to |sampling_frequency_hz|.
+// On platforms that don't support all sample rates, it is the caller's
// responsibility to resample the audio frames into the supported sample rate
// returned by this function.
SB_EXPORT int SbAudioSinkGetNearestSupportedSampleFrequency(
int sampling_frequency_hz);
-// Returns true if the particular SbMediaAudioSampleType is supported on this
-// platform.
+// Indicates whether |audio_sample_type| is supported on this platform.
SB_EXPORT bool SbAudioSinkIsAudioSampleTypeSupported(
SbMediaAudioSampleType audio_sample_type);
-// Returns true if the particular SbMediaAudioFrameStorageType is supported on
-// this platform.
+// Indicates whether |audio_frame_storage_type| is supported on this
+// platform.
SB_EXPORT bool SbAudioSinkIsAudioFrameStorageTypeSupported(
SbMediaAudioFrameStorageType audio_frame_storage_type);
diff --git a/src/starboard/client_porting/eztime/eztime.gyp b/src/starboard/client_porting/eztime/eztime.gyp
index 009c29f..70f51e6 100644
--- a/src/starboard/client_porting/eztime/eztime.gyp
+++ b/src/starboard/client_porting/eztime/eztime.gyp
@@ -32,9 +32,9 @@
'target_name': 'eztime_test',
'type': '<(gtest_target_type)',
'sources': [
+ '<(DEPTH)/starboard/common/test_main.cc',
'test_constants.h',
'eztime_test.cc',
- '<(DEPTH)/starboard/nplb/main.cc',
],
'dependencies': [
'<(DEPTH)/testing/gmock.gyp:gmock',
diff --git a/src/starboard/creator/ci20/thread_types_public.h b/src/starboard/common/common.cc
similarity index 65%
copy from src/starboard/creator/ci20/thread_types_public.h
copy to src/starboard/common/common.cc
index ec9eedf..86c63c2 100644
--- a/src/starboard/creator/ci20/thread_types_public.h
+++ b/src/starboard/common/common.cc
@@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#include "starboard/configuration.h"
-#include "starboard/linux/shared/thread_types_public.h"
-
-#endif // STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+// This audit is here so it is only displayed once every build.
+#if SB_API_VERSION == SB_EXPERIMENTAL_API_VERSION
+#pragma message "Your platform's SB_API_VERSION == SB_EXPERIMENTAL_API_VERSION."
+#pragma message "You are implementing the experimental SB API at your own risk!"
+#endif
diff --git a/src/starboard/common/common.gyp b/src/starboard/common/common.gyp
index 0503988..e354d5e 100644
--- a/src/starboard/common/common.gyp
+++ b/src/starboard/common/common.gyp
@@ -24,11 +24,18 @@
'includes_starboard': 1,
},
'sources': [
+ 'common.cc',
+ 'decode_target_provider.cc',
'memory.cc',
'move.h',
'reset_and_return.h',
'scoped_ptr.h',
],
+ 'defines': [
+ # This must be defined when building Starboard, and must not when
+ # building Starboard client code.
+ 'STARBOARD_IMPLEMENTATION',
+ ],
},
],
}
diff --git a/src/starboard/common/decode_target_provider.cc b/src/starboard/common/decode_target_provider.cc
new file mode 100644
index 0000000..673d091
--- /dev/null
+++ b/src/starboard/common/decode_target_provider.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/decode_target.h"
+#include "starboard/log.h"
+#include "starboard/mutex.h"
+
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+namespace {
+struct Registry {
+ SbMutex mutex;
+ SbDecodeTargetProvider* provider;
+ void* context;
+};
+
+Registry g_registry = {SB_MUTEX_INITIALIZER};
+} // namespace
+
+bool SbDecodeTargetRegisterProvider(SbDecodeTargetProvider* provider,
+ void* context) {
+ SbMutexAcquire(&(g_registry.mutex));
+ if (g_registry.provider || g_registry.context) {
+ SB_DLOG(WARNING) << __FUNCTION__ << ": "
+ << "Registering (P" << provider << ", C" << context
+ << ") while "
+ << "(P" << g_registry.provider << ", C"
+ << g_registry.context << ") is already registered.";
+ }
+ g_registry.provider = provider;
+ g_registry.context = context;
+ SbMutexRelease(&(g_registry.mutex));
+ return true;
+}
+
+void SbDecodeTargetUnregisterProvider(SbDecodeTargetProvider* provider,
+ void* context) {
+ SbMutexAcquire(&(g_registry.mutex));
+ if (g_registry.provider == provider && g_registry.context == context) {
+ g_registry.provider = NULL;
+ g_registry.context = NULL;
+ } else {
+ SB_DLOG(WARNING) << __FUNCTION__ << ": "
+ << "Unregistering (P" << provider << ", C" << context
+ << ") while "
+ << "(P" << g_registry.provider << ", C"
+ << g_registry.context << ") is what is registered.";
+ }
+ SbMutexRelease(&(g_registry.mutex));
+}
+
+SbDecodeTarget SbDecodeTargetAcquireFromProvider(SbDecodeTargetFormat format,
+ int width,
+ int height) {
+ SbDecodeTargetProvider* provider = NULL;
+ void* context = NULL;
+ SbMutexAcquire(&(g_registry.mutex));
+ provider = g_registry.provider;
+ context = g_registry.context;
+ SbMutexRelease(&(g_registry.mutex));
+ if (!provider) {
+ SB_DLOG(ERROR) << __FUNCTION__ << ": "
+ << "No registered provider.";
+ return kSbDecodeTargetInvalid;
+ }
+
+ return provider->acquire(context, format, width, height);
+}
+
+void SbDecodeTargetReleaseToProvider(SbDecodeTarget decode_target) {
+ SbDecodeTargetProvider* provider = NULL;
+ void* context = NULL;
+ SbMutexAcquire(&(g_registry.mutex));
+ provider = g_registry.provider;
+ context = g_registry.context;
+ SbMutexRelease(&(g_registry.mutex));
+ if (!provider) {
+ SB_DLOG(ERROR) << __FUNCTION__ << ": "
+ << "No registered provider.";
+ return;
+ }
+
+ provider->release(context, decode_target);
+}
+
+#endif // SB_VERSION(3) && SB_HAS(GRAPHICS)
diff --git a/src/starboard/nplb/main.cc b/src/starboard/common/test_main.cc
similarity index 100%
rename from src/starboard/nplb/main.cc
rename to src/starboard/common/test_main.cc
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index bfdf8d8..444cc9f 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -35,7 +35,12 @@
// The maximum API version allowed by this version of the Starboard headers,
// inclusive.
-#define SB_MAXIMUM_API_VERSION 2
+#define SB_MAXIMUM_API_VERSION 3
+
+// 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 3
// --- Common Detected Features ----------------------------------------------
@@ -458,6 +463,15 @@
#define SB_HAS_GLES2 !SB_GYP_GL_TYPE_IS_NONE
#endif
+// Specifies whether this platform has any kind of supported graphics system.
+#if !defined(SB_HAS_GRAPHICS)
+#if SB_HAS(GLES2) || SB_HAS(BLITTER)
+#define SB_HAS_GRAPHICS 1
+#else
+#define SB_HAS_GRAPHICS 0
+#endif
+#endif
+
// Specifies whether the starboard media pipeline components (SbPlayerPipeline
// and StarboardDecryptor) are used. Set to 0 means they are not used.
#define SB_CAN_MEDIA_USE_STARBOARD_PIPELINE \
diff --git a/src/starboard/creator/ci20/configuration_public.h b/src/starboard/creator/ci20/configuration_public.h
deleted file mode 100644
index cd85a36..0000000
--- a/src/starboard/creator/ci20/configuration_public.h
+++ /dev/null
@@ -1,95 +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.
-
-// The Starboard configuration for Creator Ci20 Debian.
-
-// Other source files should never include this header directly, but should
-// include the generic "starboard/configuration.h" instead.
-
-#ifndef STARBOARD_CREATOR_CI20_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_CONFIGURATION_PUBLIC_H_
-
-// --- Architecture Configuration --------------------------------------------
-
-// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
-// automatically set based on this.
-#define SB_IS_BIG_ENDIAN 0
-
-// Whether the current platform is an ARM architecture.
-#define SB_IS_ARCH_ARM 0
-
-// Whether the current platform is a MIPS architecture.
-#define SB_IS_ARCH_MIPS 1
-
-// Whether the current platform is a PPC architecture.
-#define SB_IS_ARCH_PPC 0
-
-// Whether the current platform is an x86 architecture.
-#define SB_IS_ARCH_X86 0
-
-// Whether the current platform is a 32-bit architecture.
-#define SB_IS_32_BIT 1
-
-// Whether the current platform is a 64-bit architecture.
-#define SB_IS_64_BIT 0
-
-// Whether the current platform's pointers are 32-bit.
-// Whether the current platform's longs are 32-bit.
-#if SB_IS(32_BIT)
-#define SB_HAS_32_BIT_POINTERS 1
-#define SB_HAS_32_BIT_LONG 1
-#else
-#define SB_HAS_32_BIT_POINTERS 0
-#define SB_HAS_32_BIT_LONG 0
-#endif
-
-// Whether the current platform's pointers are 64-bit.
-// Whether the current platform's longs are 64-bit.
-#if SB_IS(64_BIT)
-#define SB_HAS_64_BIT_POINTERS 1
-#define SB_HAS_64_BIT_LONG 1
-#else
-#define SB_HAS_64_BIT_POINTERS 0
-#define SB_HAS_64_BIT_LONG 0
-#endif
-
-// Configuration parameters that allow the application to make some general
-// compile-time decisions with respect to the the number of cores likely to be
-// available on this platform. For a definitive measure, the application should
-// still call SbSystemGetNumberOfProcessors at runtime.
-
-// Whether the current platform is expected to have many cores (> 6), or a
-// wildly varying number of cores.
-#define SB_HAS_MANY_CORES 0
-
-// Whether the current platform is expected to have exactly 1 core.
-#define SB_HAS_1_CORE 0
-
-// Whether the current platform is expected to have exactly 2 cores.
-#define SB_HAS_2_CORES 1
-
-// Whether the current platform is expected to have exactly 4 cores.
-#define SB_HAS_4_CORES 0
-
-// Whether the current platform is expected to have exactly 6 cores.
-#define SB_HAS_6_CORES 0
-
-// Whether the current platform's thread scheduler will automatically balance
-// threads between cores, as opposed to systems where threads will only ever run
-// on the specifically pinned core.
-#define SB_HAS_CROSS_CORE_SCHEDULER 1
-
-#include "starboard/creator/shared/configuration_public.h"
-
-#endif // STARBOARD_CREATOR_CI20_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/creator/ci20directfb/README.md b/src/starboard/creator/ci20directfb/README.md
new file mode 100644
index 0000000..e0c1251
--- /dev/null
+++ b/src/starboard/creator/ci20directfb/README.md
@@ -0,0 +1,4 @@
+# Setting up Starboard to use DirectFB on a Creator Ci20
+
+See `starboard/raspi/directfb/README.md`. The steps for getting directfb
+running on the Ci20 are identical.
diff --git a/src/starboard/creator/ci20/atomic_public.h b/src/starboard/creator/ci20directfb/atomic_public.h
similarity index 79%
copy from src/starboard/creator/ci20/atomic_public.h
copy to src/starboard/creator/ci20directfb/atomic_public.h
index 3b48d2e..2a182e7 100644
--- a/src/starboard/creator/ci20/atomic_public.h
+++ b/src/starboard/creator/ci20directfb/atomic_public.h
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_CREATOR_CI20DIRECTFB_ATOMIC_PUBLIC_H_
+#define STARBOARD_CREATOR_CI20DIRECTFB_ATOMIC_PUBLIC_H_
#include "starboard/linux/shared/atomic_public.h"
-#endif // STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#endif // STARBOARD_CREATOR_CI20DIRECTFB_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/creator/ci20directfb/configuration_public.h b/src/starboard/creator/ci20directfb/configuration_public.h
new file mode 100644
index 0000000..651c203
--- /dev/null
+++ b/src/starboard/creator/ci20directfb/configuration_public.h
@@ -0,0 +1,46 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_CREATOR_CI20DIRECTFB_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_CREATOR_CI20DIRECTFB_CONFIGURATION_PUBLIC_H_
+
+#include "starboard/creator/shared/configuration_public.h"
+
+// Indicates whether or not the given platform supports rendering of NV12
+// textures. These textures typically originate from video decoders.
+#undef SB_HAS_NV12_TEXTURE_SUPPORT
+#define SB_HAS_NV12_TEXTURE_SUPPORT 0
+
+// This configuration supports the blitter API (implemented via DirectFB).
+#undef SB_HAS_BLITTER
+#define SB_HAS_BLITTER 1
+
+// Unfortunately, DirectFB does not support bilinear filtering. According to
+// http://osdir.com/ml/graphics.directfb.user/2008-06/msg00028.html, "smooth
+// scaling is not supported in conjunction with blending", and we need blending
+// more.
+#undef SB_HAS_BILINEAR_FILTERING_SUPPORT
+#define SB_HAS_BILINEAR_FILTERING_SUPPORT 0
+
+// DirectFB's only 32-bit RGBA color format is word-order ARGB. This translates
+// to byte-order ARGB for big endian platforms and byte-order BGRA for
+// little-endian platforms.
+#undef SB_PREFERRED_RGBA_BYTE_ORDER
+#if SB_IS(BIG_ENDIAN)
+#define SB_PREFERRED_RGBA_BYTE_ORDER SB_PREFERRED_RGBA_BYTE_ORDER_ARGB
+#else
+#define SB_PREFERRED_RGBA_BYTE_ORDER SB_PREFERRED_RGBA_BYTE_ORDER_BGRA
+#endif
+
+#endif // STARBOARD_CREATOR_CI20DIRECTFB_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/creator/ci20directfb/gyp_configuration.gypi b/src/starboard/creator/ci20directfb/gyp_configuration.gypi
new file mode 100644
index 0000000..8d8c263
--- /dev/null
+++ b/src/starboard/creator/ci20directfb/gyp_configuration.gypi
@@ -0,0 +1,45 @@
+# 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.
+
+{
+ 'variables': {
+ 'platform_libraries': [
+ '-ldirectfb',
+ '-ldirect',
+ ],
+ 'gl_type': 'none',
+ },
+
+ 'target_defaults': {
+ 'default_configuration': 'creator-ci20directfb_debug',
+ 'configurations': {
+ 'creator-ci20directfb_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'creator-ci20directfb_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'creator-ci20directfb_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'creator-ci20directfb_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ '../shared/gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/creator/ci20directfb/gyp_configuration.py b/src/starboard/creator/ci20directfb/gyp_configuration.py
new file mode 100644
index 0000000..4e712f5
--- /dev/null
+++ b/src/starboard/creator/ci20directfb/gyp_configuration.py
@@ -0,0 +1,32 @@
+# 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.
+"""Starboard Creator Ci20 DirectFB platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+# Import the shared Creator platform configuration.
+sys.path.append(
+ os.path.realpath(
+ os.path.join(os.path.dirname(__file__), os.pardir, 'shared')))
+import gyp_configuration # pylint: disable=import-self,g-import-not-at-top
+
+
+def CreatePlatformConfig():
+ try:
+ return gyp_configuration.PlatformConfig('creator-ci20directfb')
+ except RuntimeError as e:
+ logging.critical(e)
+ return None
diff --git a/src/starboard/creator/ci20directfb/main.cc b/src/starboard/creator/ci20directfb/main.cc
new file mode 100644
index 0000000..68d7761
--- /dev/null
+++ b/src/starboard/creator/ci20directfb/main.cc
@@ -0,0 +1,29 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/shared/directfb/application_directfb.h"
+#include "starboard/shared/signal/crash_signals.h"
+#include "starboard/shared/signal/suspend_signals.h"
+
+int main(int argc, char** argv) {
+ tzset();
+ starboard::shared::signal::InstallCrashSignalHandlers();
+ starboard::shared::signal::InstallSuspendSignalHandlers();
+ starboard::ApplicationDirectFB application;
+ int result = application.Run(argc, argv);
+ starboard::shared::signal::UninstallSuspendSignalHandlers();
+ starboard::shared::signal::UninstallCrashSignalHandlers();
+ return result;
+}
diff --git a/src/starboard/creator/ci20/starboard_platform.gyp b/src/starboard/creator/ci20directfb/starboard_platform.gyp
similarity index 84%
copy from src/starboard/creator/ci20/starboard_platform.gyp
copy to src/starboard/creator/ci20directfb/starboard_platform.gyp
index c6a9f11..b695c91 100644
--- a/src/starboard/creator/ci20/starboard_platform.gyp
+++ b/src/starboard/creator/ci20directfb/starboard_platform.gyp
@@ -25,14 +25,14 @@
'target_name': 'starboard_platform',
'type': 'static_library',
'sources': [
- '<(DEPTH)/starboard/creator/ci20/configuration_public.h',
- '<(DEPTH)/starboard/creator/ci20/system_get_property.cc',
+ '<(DEPTH)/starboard/creator/ci20directfb/configuration_public.h',
+ '<(DEPTH)/starboard/creator/ci20directfb/main.cc',
+ '<(DEPTH)/starboard/creator/ci20directfb/system_get_property.cc',
'<(DEPTH)/starboard/linux/shared/atomic_public.h',
'<(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',
'<(DEPTH)/starboard/linux/shared/system_has_capability.cc',
- '<(DEPTH)/starboard/linux/x64x11/main.cc',
'<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.cc',
'<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.h',
'<(DEPTH)/starboard/shared/alsa/alsa_util.cc',
@@ -41,6 +41,43 @@
'<(DEPTH)/starboard/shared/alsa/audio_sink_get_nearest_supported_sample_frequency.cc',
'<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc',
'<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc',
+ '<(DEPTH)/starboard/shared/directfb/application_directfb.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_blit_rect_to_rect.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_create_context.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_create_default_device.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_create_pixel_data.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_create_render_target_surface.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_create_surface_from_pixel_data.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_create_swap_chain_from_window.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_destroy_context.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_destroy_device.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_destroy_pixel_data.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_destroy_surface.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_destroy_swap_chain.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_download_surface_pixels.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_fill_rect.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_flip_swap_chain.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_flush_context.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_get_max_contexts.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_get_pixel_data_pitch_in_bytes.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_get_pixel_data_pointer.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_get_render_target_from_surface.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_get_render_target_from_swap_chain.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_get_surface_info.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_internal.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_is_pixel_format_supported_by_download_surface_pixels.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_is_pixel_format_supported_by_pixel_data.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_is_surface_format_supported_by_render_target_surface.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_set_blending.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_set_color.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_set_modulate_blits_with_color.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_set_render_target.cc',
+ '<(DEPTH)/starboard/shared/directfb/blitter_set_scissor.cc',
+ '<(DEPTH)/starboard/shared/directfb/window_create.cc',
+ '<(DEPTH)/starboard/shared/directfb/window_destroy.cc',
+ '<(DEPTH)/starboard/shared/directfb/window_get_platform_handle.cc',
+ '<(DEPTH)/starboard/shared/directfb/window_get_size.cc',
+ '<(DEPTH)/starboard/shared/directfb/window_internal.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_unchecked.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
@@ -210,6 +247,8 @@
'<(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/blitter_blit_rect_to_rect_tiled.cc',
+ '<(DEPTH)/starboard/shared/starboard/blitter_blit_rects_to_rects.cc',
'<(DEPTH)/starboard/shared/starboard/directory_can_open.cc',
'<(DEPTH)/starboard/shared/starboard/event_cancel.cc',
'<(DEPTH)/starboard/shared/starboard/event_schedule.cc',
@@ -275,12 +314,9 @@
'<(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/x11/application_x11.cc',
- '<(DEPTH)/starboard/shared/x11/window_create.cc',
- '<(DEPTH)/starboard/shared/x11/window_destroy.cc',
- '<(DEPTH)/starboard/shared/x11/window_get_platform_handle.cc',
- '<(DEPTH)/starboard/shared/x11/window_get_size.cc',
- '<(DEPTH)/starboard/shared/x11/window_internal.cc',
+ ],
+ 'include_dirs': [
+ '<(sysroot)/mipsel-r2-hard/usr/include/directfb',
],
'defines': [
# This must be defined when building Starboard, and must not when
diff --git a/src/starboard/creator/ci20directfb/system_get_property.cc b/src/starboard/creator/ci20directfb/system_get_property.cc
new file mode 100644
index 0000000..db0f0e0
--- /dev/null
+++ b/src/starboard/creator/ci20directfb/system_get_property.cc
@@ -0,0 +1,69 @@
+// 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/system.h"
+
+#include "starboard/log.h"
+#include "starboard/string.h"
+
+namespace {
+
+const char* kFriendlyName = "Creator Ci20";
+const char* kPlatformName = "DirectFB; Creator Ci20 JZ4780";
+
+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;
+ }
+
+ switch (property_id) {
+ case kSbSystemPropertyBrandName:
+ case kSbSystemPropertyChipsetModelNumber:
+ case kSbSystemPropertyFirmwareVersion:
+ case kSbSystemPropertyModelName:
+ case kSbSystemPropertyModelYear:
+ case kSbSystemPropertyNetworkOperatorName:
+ return false;
+
+ case kSbSystemPropertyFriendlyName:
+ return CopyStringAndTestIfSuccess(out_value, value_length, kFriendlyName);
+
+ case kSbSystemPropertyPlatformName:
+ return CopyStringAndTestIfSuccess(out_value, value_length, kPlatformName);
+
+ 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/creator/ci20/thread_types_public.h b/src/starboard/creator/ci20directfb/thread_types_public.h
similarity index 77%
copy from src/starboard/creator/ci20/thread_types_public.h
copy to src/starboard/creator/ci20directfb/thread_types_public.h
index ec9eedf..b531032 100644
--- a/src/starboard/creator/ci20/thread_types_public.h
+++ b/src/starboard/creator/ci20directfb/thread_types_public.h
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_CREATOR_CI20DIRECTFB_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_CREATOR_CI20DIRECTFB_THREAD_TYPES_PUBLIC_H_
#include "starboard/linux/shared/thread_types_public.h"
-#endif // STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#endif // STARBOARD_CREATOR_CI20DIRECTFB_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/creator/ci20/atomic_public.h b/src/starboard/creator/ci20x11/atomic_public.h
similarity index 80%
rename from src/starboard/creator/ci20/atomic_public.h
rename to src/starboard/creator/ci20x11/atomic_public.h
index 3b48d2e..932fe9d 100644
--- a/src/starboard/creator/ci20/atomic_public.h
+++ b/src/starboard/creator/ci20x11/atomic_public.h
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_CREATOR_CI20X11_ATOMIC_PUBLIC_H_
+#define STARBOARD_CREATOR_CI20X11_ATOMIC_PUBLIC_H_
#include "starboard/linux/shared/atomic_public.h"
-#endif // STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#endif // STARBOARD_CREATOR_CI20X11_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/creator/ci20/thread_types_public.h b/src/starboard/creator/ci20x11/configuration_public.h
similarity index 71%
copy from src/starboard/creator/ci20/thread_types_public.h
copy to src/starboard/creator/ci20x11/configuration_public.h
index ec9eedf..4c00bad 100644
--- a/src/starboard/creator/ci20/thread_types_public.h
+++ b/src/starboard/creator/ci20x11/configuration_public.h
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_CREATOR_CI20X11_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_CREATOR_CI20X11_CONFIGURATION_PUBLIC_H_
-#include "starboard/linux/shared/thread_types_public.h"
+#include "starboard/creator/shared/configuration_public.h"
-#endif // STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#endif // STARBOARD_CREATOR_CI20X11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/creator/ci20x11/gyp_configuration.gypi b/src/starboard/creator/ci20x11/gyp_configuration.gypi
new file mode 100644
index 0000000..a2ea1b8
--- /dev/null
+++ b/src/starboard/creator/ci20x11/gyp_configuration.gypi
@@ -0,0 +1,48 @@
+# 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.
+
+{
+ 'variables': {
+ 'platform_libraries': [
+ '-lEGL',
+ '-lGLESv2',
+ '-lX11',
+ '-lXcomposite',
+ '-lXext',
+ '-lXrender',
+ ],
+ },
+
+ 'target_defaults': {
+ 'default_configuration': 'creator-ci20x11_debug',
+ 'configurations': {
+ 'creator-ci20x11_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'creator-ci20x11_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'creator-ci20x11_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'creator-ci20x11_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ '../shared/gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/creator/ci20x11/gyp_configuration.py b/src/starboard/creator/ci20x11/gyp_configuration.py
new file mode 100644
index 0000000..1c062df
--- /dev/null
+++ b/src/starboard/creator/ci20x11/gyp_configuration.py
@@ -0,0 +1,32 @@
+# 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.
+"""Starboard Creator Ci20 X11 platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+# Import the shared Creator platform configuration.
+sys.path.append(
+ os.path.realpath(
+ os.path.join(os.path.dirname(__file__), os.pardir, 'shared')))
+import gyp_configuration # pylint: disable=import-self,g-import-not-at-top
+
+
+def CreatePlatformConfig():
+ try:
+ return gyp_configuration.PlatformConfig('creator-ci20x11')
+ except RuntimeError as e:
+ logging.critical(e)
+ return None
diff --git a/src/starboard/creator/ci20/main.cc b/src/starboard/creator/ci20x11/main.cc
similarity index 100%
rename from src/starboard/creator/ci20/main.cc
rename to src/starboard/creator/ci20x11/main.cc
diff --git a/src/starboard/creator/ci20/starboard_platform.gyp b/src/starboard/creator/ci20x11/starboard_platform.gyp
similarity index 97%
rename from src/starboard/creator/ci20/starboard_platform.gyp
rename to src/starboard/creator/ci20x11/starboard_platform.gyp
index c6a9f11..bab02c6 100644
--- a/src/starboard/creator/ci20/starboard_platform.gyp
+++ b/src/starboard/creator/ci20x11/starboard_platform.gyp
@@ -25,14 +25,15 @@
'target_name': 'starboard_platform',
'type': 'static_library',
'sources': [
- '<(DEPTH)/starboard/creator/ci20/configuration_public.h',
- '<(DEPTH)/starboard/creator/ci20/system_get_property.cc',
+ '<(DEPTH)/starboard/creator/ci20x11/atomic_public.h',
+ '<(DEPTH)/starboard/creator/ci20x11/configuration_public.h',
+ '<(DEPTH)/starboard/creator/ci20x11/main.cc',
+ '<(DEPTH)/starboard/creator/ci20x11/system_get_property.cc',
'<(DEPTH)/starboard/linux/shared/atomic_public.h',
'<(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',
'<(DEPTH)/starboard/linux/shared/system_has_capability.cc',
- '<(DEPTH)/starboard/linux/x64x11/main.cc',
'<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.cc',
'<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.h',
'<(DEPTH)/starboard/shared/alsa/alsa_util.cc',
@@ -226,8 +227,8 @@
'<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_parser.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_parser.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/filter/audio_decoder_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
diff --git a/src/starboard/creator/ci20/system_get_property.cc b/src/starboard/creator/ci20x11/system_get_property.cc
similarity index 100%
rename from src/starboard/creator/ci20/system_get_property.cc
rename to src/starboard/creator/ci20x11/system_get_property.cc
diff --git a/src/starboard/creator/ci20/thread_types_public.h b/src/starboard/creator/ci20x11/thread_types_public.h
similarity index 79%
rename from src/starboard/creator/ci20/thread_types_public.h
rename to src/starboard/creator/ci20x11/thread_types_public.h
index ec9eedf..7437a70 100644
--- a/src/starboard/creator/ci20/thread_types_public.h
+++ b/src/starboard/creator/ci20x11/thread_types_public.h
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
#include "starboard/linux/shared/thread_types_public.h"
-#endif // STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#endif // STARBOARD_CREATOR_CI20X11_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/creator/shared/configuration_public.h b/src/starboard/creator/shared/configuration_public.h
index 8144c15..8898d5b 100644
--- a/src/starboard/creator/shared/configuration_public.h
+++ b/src/starboard/creator/shared/configuration_public.h
@@ -17,6 +17,76 @@
#ifndef STARBOARD_CREATOR_SHARED_CONFIGURATION_PUBLIC_H_
#define STARBOARD_CREATOR_SHARED_CONFIGURATION_PUBLIC_H_
+// --- Architecture Configuration --------------------------------------------
+
+// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
+// automatically set based on this.
+#define SB_IS_BIG_ENDIAN 0
+
+// Whether the current platform is an ARM architecture.
+#define SB_IS_ARCH_ARM 0
+
+// Whether the current platform is a MIPS architecture.
+#define SB_IS_ARCH_MIPS 1
+
+// Whether the current platform is a PPC architecture.
+#define SB_IS_ARCH_PPC 0
+
+// Whether the current platform is an x86 architecture.
+#define SB_IS_ARCH_X86 0
+
+// Whether the current platform is a 32-bit architecture.
+#define SB_IS_32_BIT 1
+
+// Whether the current platform is a 64-bit architecture.
+#define SB_IS_64_BIT 0
+
+// Whether the current platform's pointers are 32-bit.
+// Whether the current platform's longs are 32-bit.
+#if SB_IS(32_BIT)
+#define SB_HAS_32_BIT_POINTERS 1
+#define SB_HAS_32_BIT_LONG 1
+#else
+#define SB_HAS_32_BIT_POINTERS 0
+#define SB_HAS_32_BIT_LONG 0
+#endif
+
+// Whether the current platform's pointers are 64-bit.
+// Whether the current platform's longs are 64-bit.
+#if SB_IS(64_BIT)
+#define SB_HAS_64_BIT_POINTERS 1
+#define SB_HAS_64_BIT_LONG 1
+#else
+#define SB_HAS_64_BIT_POINTERS 0
+#define SB_HAS_64_BIT_LONG 0
+#endif
+
+// Configuration parameters that allow the application to make some general
+// compile-time decisions with respect to the the number of cores likely to be
+// available on this platform. For a definitive measure, the application should
+// still call SbSystemGetNumberOfProcessors at runtime.
+
+// Whether the current platform is expected to have many cores (> 6), or a
+// wildly varying number of cores.
+#define SB_HAS_MANY_CORES 0
+
+// Whether the current platform is expected to have exactly 1 core.
+#define SB_HAS_1_CORE 0
+
+// Whether the current platform is expected to have exactly 2 cores.
+#define SB_HAS_2_CORES 1
+
+// Whether the current platform is expected to have exactly 4 cores.
+#define SB_HAS_4_CORES 0
+
+// Whether the current platform is expected to have exactly 6 cores.
+#define SB_HAS_6_CORES 0
+
+// Whether the current platform's thread scheduler will automatically balance
+// threads between cores, as opposed to systems where threads will only ever run
+// on the specifically pinned core.
+#define SB_HAS_CROSS_CORE_SCHEDULER 1
+
// The API version implemented by this platform.
#define SB_API_VERSION 1
@@ -68,10 +138,8 @@
// --- Architecture Configuration --------------------------------------------
-// On the current version of Raspbian, real time thread scheduling seems to be
-// broken in that higher priority threads do not always have priority over lower
-// priority threads. It looks like the thread created last will always have the
-// highest priority.
+// On default Linux, you must be a superuser in order to set real time
+// scheduling on threads.
#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
// --- Attribute Configuration -----------------------------------------------
diff --git a/src/starboard/creator/ci20/gyp_configuration.gypi b/src/starboard/creator/shared/gyp_configuration.gypi
similarity index 86%
rename from src/starboard/creator/ci20/gyp_configuration.gypi
rename to src/starboard/creator/shared/gyp_configuration.gypi
index 045e41c..c6cb2f2 100644
--- a/src/starboard/creator/ci20/gyp_configuration.gypi
+++ b/src/starboard/creator/shared/gyp_configuration.gypi
@@ -19,7 +19,7 @@
'enable_webdriver': 0,
'in_app_dial%': 0,
- 'gl_type': 'system_gles2',
+ 'gl_type%': 'system_gles3',
'image_cache_size_in_bytes': 32 * 1024 * 1024,
'scratch_surface_cache_size_in_bytes' : 0,
@@ -39,6 +39,16 @@
'-U__linux__',
'--sysroot=<(sysroot)',
'-EL',
+
+ # Suppress some warnings that will be hard to fix.
+ '-Wno-unused-local-typedefs',
+ '-Wno-unused-result',
+ '-Wno-deprecated-declarations',
+ '-Wno-missing-field-initializers',
+ '-Wno-comment',
+ '-Wno-narrowing',
+ '-Wno-unknown-pragmas',
+ '-Wno-type-limits', # TODO: We should actually look into these.
],
'linker_flags': [
'--sysroot=<(sysroot)',
@@ -87,16 +97,10 @@
'-lavformat',
'-lavresample',
'-lavutil',
- '-lEGL',
- '-lGLESv2',
'-lm',
'-lpthread',
'-lpulse',
'-lrt',
- '-lX11',
- '-lXcomposite',
- '-lXext',
- '-lXrender',
],
'conditions': [
['cobalt_fastbuild==0', {
@@ -129,21 +133,6 @@
'-std=gnu++11',
'-Wno-literal-suffix',
],
- 'default_configuration': 'creator-ci20_debug',
- 'configurations': {
- 'creator-ci20_debug': {
- 'inherit_from': ['debug_base'],
- },
- 'creator-ci20_devel': {
- 'inherit_from': ['devel_base'],
- },
- 'creator-ci20_qa': {
- 'inherit_from': ['qa_base'],
- },
- 'creator-ci20_gold': {
- 'inherit_from': ['gold_base'],
- },
- }, # end of configurations
'target_conditions': [
['cobalt_code==1', {
'cflags': [
diff --git a/src/starboard/creator/ci20/gyp_configuration.py b/src/starboard/creator/shared/gyp_configuration.py
similarity index 73%
rename from src/starboard/creator/ci20/gyp_configuration.py
rename to src/starboard/creator/shared/gyp_configuration.py
index 10bb683..373a70e 100644
--- a/src/starboard/creator/ci20/gyp_configuration.py
+++ b/src/starboard/creator/shared/gyp_configuration.py
@@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Starboard ci20 platform configuration for gyp_cobalt."""
+"""Starboard Creator Ci20 platform configuration for gyp_cobalt."""
import logging
import os
@@ -20,19 +20,11 @@
import config.starboard
-def CreatePlatformConfig():
- try:
- return _PlatformConfig('creator-ci20')
- except RuntimeError as e:
- logging.critical(e)
- return None
-
-
-class _PlatformConfig(config.starboard.PlatformConfigStarboard):
+class PlatformConfig(config.starboard.PlatformConfigStarboard):
"""Starboard ci20 platform configuration."""
def __init__(self, platform):
- super(_PlatformConfig, self).__init__(platform)
+ super(PlatformConfig, self).__init__(platform)
def _GetCi20Home(self):
try:
@@ -46,16 +38,16 @@
def GetVariables(self, configuration):
ci20_home = self._GetCi20Home()
- sysroot = os.path.join(ci20_home, 'mips-mti-linux-gnu', '2016.05-03',
- 'sysroot')
+ relative_sysroot = os.path.join('mips-mti-linux-gnu', '2016.05-03',
+ 'sysroot')
+ sysroot = os.path.join(ci20_home, relative_sysroot)
if not os.path.isdir(sysroot):
logging.critical(
- 'ci20 builds require '
- '$CI20_HOME/mips-mti-linux-gnu/2016.05-03/sysroot to be a valid '
- 'directory.')
+ 'ci20 builds require $CI20_HOME/%s to be a valid directory.',
+ relative_sysroot)
sys.exit(1)
- variables = super(_PlatformConfig, self).GetVariables(configuration)
+ variables = super(PlatformConfig, self).GetVariables(configuration)
variables.update({
'clang': 0,
'sysroot': sysroot,
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h
new file mode 100644
index 0000000..6ea6951
--- /dev/null
+++ b/src/starboard/decode_target.h
@@ -0,0 +1,286 @@
+// 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.
+
+// Module Overview: Starboard Decode Target module
+//
+// A target for decoding image and video data into. This corresponds roughly to
+// an EGLImage, but that extension may not be fully supported on all GL
+// platforms. SbDecodeTarget supports multi-plane targets. We need a mechanism
+// for SbBlitter as well, and this is able to more-or-less unify the two.
+//
+// An SbDecodeTarget can be passed into any function which decodes video or
+// image data. This allows the application to allocate fast graphics memory, and
+// have decoding done directly into this memory, avoiding unnecessary memory
+// copies, and also avoiding pushing data between CPU and GPU memory
+// unnecessarily.
+//
+// #### SbDecodeTargetFormat
+//
+// SbDecodeTargets support several different formats that can be used to decode
+// into and render from. Some formats may be easier to decode into, and others
+// may be easier to render. Some may take less memory. Each decoder needs to
+// support the SbDecodeTargetFormat passed into it, or the decode will produce
+// an error. Each decoder provides a way to check if a given
+// SbDecodeTargetFormat is supported by that decoder.
+//
+// #### SbDecodeTargetProvider
+//
+// Some components may need to acquire SbDecodeTargets compatible with a certain
+// rendering context, which may need to be created on a particular thread. The
+// SbDecodeTargetProvider is a way for the primary rendering context to provide
+// an interface that can create SbDecodeTargets on demand by other system.
+//
+// The primary usage is likely to be the the SbPlayer implementation on some
+// platforms.
+//
+// #### SbDecodeTarget Example
+//
+// Let's say there's an image decoder for .foo files:
+//
+// bool SbImageDecodeFooSupportsFormat(SbDecodeTargetFormat format);
+// bool SbImageDecodeFoo(void *data, int data_size, SbDecodeTarget target);
+//
+// First, the client should enumerate which SbDecodeTargetFormats are supported
+// by that decoder.
+//
+// SbDecodeTargetFormat kPreferredFormats[] = {
+// kSbDecodeTargetFormat3PlaneYUVI420,
+// kSbDecodeTargetFormat1PlaneRGBA,
+// kSbDecodeTargetFormat1PlaneBGRA,
+// };
+//
+// SbDecodeTargetFormat format = kSbDecodeTargetFormatInvalid;
+// for (int i = 0; i < SB_ARRAY_SIZE_INT(kPreferredFormats); ++i) {
+// if (SbImageDecodeFooSupportsFormat(kPreferredFormats[i])) {
+// format = kPreferredFormats[i];
+// break;
+// }
+// }
+//
+// Now that the client has a format, it can create a decode target that it will
+// use to decode the .foo file into. Let's assume format is
+// kSbDecodeTargetFormat1PlaneRGBA, and that we are on an EGL/GLES2 platform.
+// Also, we won't do any error checking, to keep things even simpler.
+//
+// // Allocate a sized texture of the right format.
+// GLuint texture_handle;
+// glGenTextures(1, &texture_handle);
+// glBindTexture(GL_TEXTURE_2D, texture_handle);
+// glTexImage2D(GL_TEXTURE_2D, 0 /*level*/, GL_RGBA, width, height,
+// 0 /*border*/, GL_RGBA8, GL_UNSIGNED_BYTE, NULL /*data*/);
+// glBindTexture(GL_TEXTURE_2D, 0);
+//
+// // Create an SbDecodeTarget wrapping the new texture handle.
+// SbDecodeTarget target =
+// SbDecodeTargetCreate(display, context, format, &texture_handle);
+//
+// // Now pass the SbDecodeTarget into the decoder.
+// SbImageDecodeFoo(encoded_foo_data, encoded_foo_data_size, target);
+//
+// // If the decode works, you can get the texture out and render it.
+// GLuint texture =
+// SbDecodeTargetGetPlane(target, kSbDecodeTargetPlaneRGBA);
+//
+
+#ifndef STARBOARD_DECODE_TARGET_H_
+#define STARBOARD_DECODE_TARGET_H_
+
+#include "starboard/configuration.h"
+#include "starboard/export.h"
+#include "starboard/types.h"
+
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+#if SB_HAS(BLITTER)
+#include "starboard/blitter.h"
+#else
+#include <EGL/egl.h>
+#include <GL/gl.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// --- Types -----------------------------------------------------------------
+
+// Private structure representing a target for image data decoding.
+typedef struct SbDecodeTargetPrivate SbDecodeTargetPrivate;
+
+// A handle to a target for image data decoding.
+typedef SbDecodeTargetPrivate* SbDecodeTarget;
+
+// The list of all possible decoder target formats. An SbDecodeTarget consists
+// of one or more planes of data, each plane corresponding with a surface. For
+// some formats, different planes will be different sizes for the same
+// dimensions.
+//
+// NOTE: For enumeration entries with an alpha component, the alpha will always
+// be premultiplied unless otherwise explicitly specified.
+typedef enum SbDecodeTargetFormat {
+ // A decoder target format consisting of a single RGBA plane, in that channel
+ // order.
+ kSbDecodeTargetFormat1PlaneRGBA,
+
+ // A decoder target format consisting of a single BGRA plane, in that channel
+ // order.
+ kSbDecodeTargetFormat1PlaneBGRA,
+
+ // A decoder target format consisting of Y and interleaved UV planes, in that
+ // plane and channel order.
+ kSbDecodeTargetFormat2PlaneYUVNV12,
+
+ // A decoder target format consisting of Y, U, and V planes, in that order.
+ kSbDecodeTargetFormat3PlaneYUVI420,
+
+ // An invalid decode target format.
+ kSbDecodeTargetFormatInvalid,
+} SbDecodeTargetFormat;
+
+// All the planes supported by SbDecodeTarget.
+typedef enum SbDecodeTargetPlane {
+ // The RGBA plane for the RGBA format.
+ kSbDecodeTargetPlaneRGBA = 0,
+
+ // The BGRA plane for the BGRA format.
+ kSbDecodeTargetPlaneBGRA = 0,
+
+ // The Y plane for multi-plane YUV formats.
+ kSbDecodeTargetPlaneY = 0,
+
+ // The UV plane for 2-plane YUV formats.
+ kSbDecodeTargetPlaneUV = 1,
+
+ // The U plane for 3-plane YUV formats.
+ kSbDecodeTargetPlaneU = 1,
+
+ // The V plane for 3-plane YUV formats.
+ kSbDecodeTargetPlaneV = 2,
+} SbDecodeTargetPlane;
+
+// A function that can produce an SbDecodeTarget of the given |format|, |width|,
+// and |height|.
+typedef SbDecodeTarget (*SbDecodeTargetAcquireFunction)(
+ void* context,
+ SbDecodeTargetFormat format,
+ int width,
+ int height);
+
+// A function that can reclaim an SbDecodeTarget allocated with an
+// SbDecodeTargetAcquireFunction.
+typedef void (*SbDecodeTargetReleaseFunction)(void* context,
+ SbDecodeTarget decode_target);
+
+// An object that can acquire and release SbDecodeTarget instances.
+typedef struct SbDecodeTargetProvider {
+ // The function to acquire a new SbDecodeTarget from the provider.
+ SbDecodeTargetAcquireFunction acquire;
+
+ // The function to release an acquired SbDecodeTarget back to the provider.
+ SbDecodeTargetReleaseFunction release;
+} SbDecodeTargetProvider;
+
+// --- Constants -------------------------------------------------------------
+
+// Well-defined value for an invalid decode target handle.
+#define kSbDecodeTargetInvalid ((SbDecodeTarget)NULL)
+
+// --- Functions -------------------------------------------------------------
+
+// Returns whether the given file handle is valid.
+static SB_C_INLINE bool SbDecodeTargetIsValid(SbDecodeTarget handle) {
+ return handle != kSbDecodeTargetInvalid;
+}
+
+#if SB_HAS(BLITTER)
+// Creates a new SbBlitter-compatible SbDecodeTarget from one or more |planes|
+// created from |display|.
+//
+// |format|: The format of the decode target being created.
+// |planes|: An array of SbBlitterSurface handles to be bundled into an
+// SbDecodeTarget. Must not be NULL. Is expected to have the same number of
+// entries as the number of planes for |format|, in the order and size expected
+// by that format.
+SB_EXPORT SbDecodeTarget SbDecodeTargetCreate(SbDecodeTargetFormat format,
+ SbBlitterSurface* planes);
+
+// Gets the surface that represents the given plane.
+SB_EXPORT SbBlitterSurface SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
+ SbDecodeTargetPlane plane);
+#else // SB_HAS(BLITTER)
+// Creates a new EGL/GLES2-compatible SbDecodeTarget from one or more |planes|
+// owned by |context|, created from |display|. Must be called from a thread
+// where |context| is current.
+//
+// |display|: The EGLDisplay being targeted.
+// |context|: The EGLContext used for this operation, or EGL_NO_CONTEXT if a
+// context is not required.
+// |format|: The format of the decode target being created.
+// |planes|: An array of GLES Texture handles to be bundled into an
+// SbDecodeTarget. Must not be NULL. Is expected to have the same number of
+// entries as the number of planes for |format|, in the order and size expected
+// by that format.
+SB_EXPORT SbDecodeTarget SbDecodeTargetCreate(EGLDisplay display,
+ EGLContext context,
+ SbDecodeTargetFormat format,
+ GLuint* planes);
+
+// Gets the texture that represents the given plane.
+SB_EXPORT GLuint SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
+ SbDecodeTargetPlane plane);
+#endif // SB_HAS(BLITTER)
+
+// Destroys the given SbDecodeTarget and all associated surfaces.
+SB_EXPORT void SbDecodeTargetDestroy(SbDecodeTarget decode_target);
+
+// Gets the format that |decode_target| was created with.
+SB_EXPORT SbDecodeTargetFormat
+SbDecodeTargetGetFormat(SbDecodeTarget decode_target);
+
+// Registers |provider| as the SbDecodeTargetProvider with the given |context|,
+// displacing any previous registered provider. The provider is expected to be
+// kept alive by the caller until unregistered, so this function is NOT passing
+// ownership in any way. |context| will be passed into every call to
+// SbDecodeTargetProvider::acquire or SbDecodeTargetProvider::release. May be
+// called from any thread.
+SB_EXPORT bool SbDecodeTargetRegisterProvider(SbDecodeTargetProvider* provider,
+ void* context);
+
+// Unregisters |provider| as the SbDecodeTargetProvider, with |context|, if it
+// is the current registered provider-context. This is checked by comparing the
+// pointer values of both |provider| and |context| to the currently registered
+// provider. May be called from any thread.
+SB_EXPORT void SbDecodeTargetUnregisterProvider(
+ SbDecodeTargetProvider* provider,
+ void* context);
+
+// Acquires a decode target from the registered SbDecodeTargetProvider,
+// returning NULL if none is registered. May be called from any thread.
+SB_EXPORT SbDecodeTarget
+SbDecodeTargetAcquireFromProvider(SbDecodeTargetFormat format,
+ int width,
+ int height);
+
+// Releases |decode_target| back to the registered SbDecodeTargetProvider. If no
+// provider is registered, just calls SbDecodeTargetDestroy on |decode_target|.
+// May be called from any thread.
+SB_EXPORT void SbDecodeTargetReleaseToProvider(SbDecodeTarget decode_target);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+#endif // STARBOARD_DECODE_TARGET_H_
diff --git a/src/starboard/drm.h b/src/starboard/drm.h
index 26d99a9..f7f4544 100644
--- a/src/starboard/drm.h
+++ b/src/starboard/drm.h
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Definitions that allow for DRM support, common between Player and Decoder
-// interfaces.
+// Module Overview: Starboard DRM module
+//
+// Provides definitions that allow for DRM support, which are common
+// between Player and Decoder interfaces.
#ifndef STARBOARD_DRM_H_
#define STARBOARD_DRM_H_
@@ -104,26 +106,30 @@
// --- Functions -------------------------------------------------------------
-// Returns whether the |drm_system| is a valid SbDrmSystem.
+// Indicates whether |drm_system| is a valid SbDrmSystem.
static SB_C_FORCE_INLINE bool SbDrmSystemIsValid(SbDrmSystem drm) {
return drm != kSbDrmSystemInvalid;
}
-// Creates a new |key_system| DRM system that can be used when constructing an
-// SbPlayer or an SbDecoder. |key_system| should be in fhe form of
-// "com.example.somesystem" as suggested by
-// https://w3c.github.io/encrypted-media/#key-system. All letters in
-// |key_system| should be in lower case and will be matched exactly with known
-// DRM key systems of the platform.
-// |context| will be passed when any callback parameters of this function are
-// called.
-// |update_request_callback| is a callback that will be called every time after
-// SbDrmGenerateSessionUpdateRequest() is called.
-// |session_updated_callback| is a callback that will be called every time
-// after SbDrmUpdateSession() is called.
-// Please refer to the document of SbDrmGenerateSessionUpdateRequest() and
+// Creates a new DRM system that can be used when constructing an SbPlayer
+// or an SbDecoder.
+//
+// This function returns kSbDrmSystemInvalid if |key_system| is unsupported.
+//
+// Also see the documentation of SbDrmGenerateSessionUpdateRequest() and
// SbDrmUpdateSession() for more details.
-// Returns kSbDrmSystemInvalid if the |key_system| is unsupported.
+//
+// |key_system|: The DRM key system to be created. The value should be in the
+// form of "com.example.somesystem" as suggested by
+// https://w3c.github.io/encrypted-media/#key-system. All letters in the value
+// should be lowercase and will be matched exactly with known DRM key systems
+// of the platform.
+// |context|: A value passed when any of this function's callback parameters
+// are called.
+// |update_request_callback|: A function that is called every time after
+// SbDrmGenerateSessionUpdateRequest() is called.
+// |session_updated_callback|: A function that is called every time after
+// SbDrmUpdateSession() is called.
SB_EXPORT SbDrmSystem
SbDrmCreateSystem(const char* key_system,
void* context,
@@ -132,15 +138,28 @@
// Asynchronously generates a session update request payload for
// |initialization_data|, of |initialization_data_size|, in case sensitive
-// |type|, extracted from the media stream, in |drm_system|'s key system. Calls
-// |update_request_callback| with |context| and either a populated request, or
-// NULL |session_id| if an error occured. |context| may be used to distinguish
-// callbacks from multiple concurrent calls to
-// SbDrmGenerateSessionUpdateRequest(), and/or to route callbacks back to an
-// object instance.
+// |type|, extracted from the media stream, in |drm_system|'s key system.
//
-// Callbacks may called from another thread or from the current thread before
-// this function returns.
+// This function calls |drm_system|'s |update_request_callback| function,
+// which is defined when the DRM system is created by SbDrmCreateSystem. When
+// calling that function, this function either sends |context| (also from
+// |SbDrmCreateSystem|) and a populated request, or it sends NULL |session_id|
+// if an error occurred.
+//
+// |drm_system|'s |context| may be used to distinguish callbacks from
+// multiple concurrent calls to SbDrmGenerateSessionUpdateRequest(), and/or
+// to route callbacks back to an object instance.
+//
+// Callbacks may be called either from the current thread before this function
+// returns or from another thread.
+//
+// |drm_system|: The DRM system that defines the key system used for the
+// session update request payload as well as the callback function that is
+// called as a result of the function being called.
+// |type|: The case-sensitive type of the session update request payload.
+// |initialization_data|: The data for which the session update request payload
+// is created.
+// |initialization_data_size|: The size of the session update request payload.
SB_EXPORT void SbDrmGenerateSessionUpdateRequest(
SbDrmSystem drm_system,
const char* type,
@@ -155,26 +174,28 @@
// and/or to route callbacks back to an object instance.
//
// Once the session is successfully updated, an SbPlayer or SbDecoder associated
-// with that system will be able to decrypt samples encrypted.
+// with that DRM key system will be able to decrypt encrypted samples.
//
-// |session_updated_callback| may called from another thread or from the current
-// thread before this function returns.
+// |drm_system|'s |session_updated_callback| may called either from the
+// current thread before this function returns or from another thread.
SB_EXPORT void SbDrmUpdateSession(SbDrmSystem drm_system,
const void* key,
int key_size,
const void* session_id,
int session_id_size);
-// Clear any internal states/resources related to the particular |session_id|.
+// Clear any internal states/resources related to the specified |session_id|.
SB_EXPORT void SbDrmCloseSession(SbDrmSystem drm_system,
const void* session_id,
int session_id_size);
-// Gets the number of keys installed in the given |drm_system| system.
+// Returns the number of keys installed in |drm_system|.
+//
+// |drm_system|: The system for which the number of installed keys is retrieved.
SB_EXPORT int SbDrmGetKeyCount(SbDrmSystem drm_system);
-// Gets the |out_key|, |out_key_size|, and |out_status| for key with |index| in
-// the given |drm_system| system. Returns whether a key is installed at |index|.
+// Gets |out_key|, |out_key_size|, and |out_status| for the key with |index|
+// in |drm_system|. Returns whether a key is installed at |index|.
// If not, the output parameters, which all must not be NULL, will not be
// modified.
SB_EXPORT bool SbDrmGetKeyStatus(SbDrmSystem drm_system,
@@ -186,17 +207,21 @@
SbDrmKeyStatus* out_status);
// Removes all installed keys for |drm_system|. Any outstanding session update
-// requests will also be invalidated.
+// requests are also invalidated.
+//
+// |drm_system|: The DRM system for which keys should be removed.
SB_EXPORT void SbDrmRemoveAllKeys(SbDrmSystem drm_system);
-// Destroys |drm_system|, which implicitly removes all keys installed in it, and
+// Destroys |drm_system|, which implicitly removes all keys installed in it and
// invalidates all outstanding session update requests. A DRM system cannot be
// destroyed unless any associated SbPlayer or SbDecoder has first been
// destroyed.
//
-// All callbacks are guaranteed to be finished when this function returns. So
-// calling this function from a callback passed to SbDrmCreateSystem() will
-// result in deadlock.
+// All callbacks are guaranteed to be finished when this function returns.
+// As a result, if this function is called from a callback that is passed
+// to SbDrmCreateSystem(), a deadlock will occur.
+//
+// |drm_system|: The DRM system to be destroyed.
SB_EXPORT void SbDrmDestroySystem(SbDrmSystem drm_system);
#ifdef __cplusplus
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index 20643e6..b374b00 100644
--- a/src/starboard/linux/shared/configuration_public.h
+++ b/src/starboard/linux/shared/configuration_public.h
@@ -23,8 +23,9 @@
#ifndef STARBOARD_LINUX_SHARED_CONFIGURATION_PUBLIC_H_
#define STARBOARD_LINUX_SHARED_CONFIGURATION_PUBLIC_H_
-// The API version implemented by this platform.
+#ifndef SB_API_VERSION
#define SB_API_VERSION 1
+#endif
// --- System Header Configuration -------------------------------------------
diff --git a/src/starboard/creator/ci20/atomic_public.h b/src/starboard/linux/x64directfb/future/atomic_public.h
similarity index 78%
copy from src/starboard/creator/ci20/atomic_public.h
copy to src/starboard/linux/x64directfb/future/atomic_public.h
index 3b48d2e..e4ef8fd 100644
--- a/src/starboard/creator/ci20/atomic_public.h
+++ b/src/starboard/linux/x64directfb/future/atomic_public.h
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_LINUX_X64DIRECTFB_FUTURE_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64DIRECTFB_FUTURE_ATOMIC_PUBLIC_H_
#include "starboard/linux/shared/atomic_public.h"
-#endif // STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#endif // STARBOARD_LINUX_X64DIRECTFB_FUTURE_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/future/configuration_public.h b/src/starboard/linux/x64directfb/future/configuration_public.h
new file mode 100644
index 0000000..db7ef6e
--- /dev/null
+++ b/src/starboard/linux/x64directfb/future/configuration_public.h
@@ -0,0 +1,28 @@
+// 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.
+
+// The Starboard configuration for Desktop X64 Linux configured for the future
+// starboard API.
+
+#ifndef STARBOARD_LINUX_X64DIRECTFB_FUTURE_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64DIRECTFB_FUTURE_CONFIGURATION_PUBLIC_H_
+
+// Include the X64DIRECTFB Linux configuration.
+#include "starboard/linux/x64directfb/configuration_public.h"
+
+// The API version implemented by this platform.
+#undef SB_API_VERSION
+#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
+#endif // STARBOARD_LINUX_X64DIRECTFB_FUTURE_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/future/gyp_configuration.gypi b/src/starboard/linux/x64directfb/future/gyp_configuration.gypi
new file mode 100644
index 0000000..44b920d
--- /dev/null
+++ b/src/starboard/linux/x64directfb/future/gyp_configuration.gypi
@@ -0,0 +1,37 @@
+# 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.
+
+{
+ 'target_defaults': {
+ 'default_configuration': 'linux-x64directfb-future_debug',
+ 'configurations': {
+ 'linux-x64directfb-future_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'linux-x64directfb-future_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'linux-x64directfb-future_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'linux-x64directfb-future_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ '../gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/linux/x64directfb/future/gyp_configuration.py b/src/starboard/linux/x64directfb/future/gyp_configuration.py
new file mode 100644
index 0000000..2f7fe1b
--- /dev/null
+++ b/src/starboard/linux/x64directfb/future/gyp_configuration.py
@@ -0,0 +1,33 @@
+# 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.
+"""Starboard Linux X64 DirectFB future platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+# Import the shared Linux platform configuration.
+sys.path.append(
+ os.path.realpath(
+ os.path.join(
+ os.path.dirname(__file__), os.pardir, os.pardir, 'shared')))
+import gyp_configuration
+
+
+def CreatePlatformConfig():
+ try:
+ return gyp_configuration.PlatformConfig('linux-x64directfb-future')
+ except RuntimeError as e:
+ logging.critical(e)
+ return None
diff --git a/src/starboard/linux/x64directfb/future/starboard_platform.gyp b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
new file mode 100644
index 0000000..0f82c74
--- /dev/null
+++ b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
@@ -0,0 +1,40 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'starboard_platform',
+ 'product_name': 'starboard_platform_future',
+ 'type': 'static_library',
+ 'sources': [
+ '<(DEPTH)/starboard/shared/stub/decode_target_create_blitter.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_destroy.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_get_plane_blitter.cc',
+ ],
+ 'defines': [
+ # This must be defined when building Starboard, and must not when
+ # building Starboard client code.
+ 'STARBOARD_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/starboard/common/common.gyp:common',
+ '<(DEPTH)/starboard/linux/x64directfb/starboard_platform.gyp:starboard_platform',
+ '<(DEPTH)/starboard/linux/x64directfb/starboard_platform.gyp:starboard_base_symbolize',
+ '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
+ '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
+ ],
+ },
+ ],
+}
diff --git a/src/starboard/creator/ci20/thread_types_public.h b/src/starboard/linux/x64directfb/future/thread_types_public.h
similarity index 76%
copy from src/starboard/creator/ci20/thread_types_public.h
copy to src/starboard/linux/x64directfb/future/thread_types_public.h
index ec9eedf..19803be 100644
--- a/src/starboard/creator/ci20/thread_types_public.h
+++ b/src/starboard/linux/x64directfb/future/thread_types_public.h
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_LINUX_X64DIRECTFB_FUTURE_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64DIRECTFB_FUTURE_THREAD_TYPES_PUBLIC_H_
#include "starboard/linux/shared/thread_types_public.h"
-#endif // STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#endif // STARBOARD_LINUX_X64DIRECTFB_FUTURE_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/starboard_platform.gyp b/src/starboard/linux/x64directfb/starboard_platform.gyp
index f89ae85..2419586 100644
--- a/src/starboard/linux/x64directfb/starboard_platform.gyp
+++ b/src/starboard/linux/x64directfb/starboard_platform.gyp
@@ -265,8 +265,8 @@
'<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_parser.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_parser.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/filter/audio_decoder_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
diff --git a/src/starboard/linux/x64x11/configuration_public.h b/src/starboard/linux/x64x11/configuration_public.h
index 2291350..4aec879 100644
--- a/src/starboard/linux/x64x11/configuration_public.h
+++ b/src/starboard/linux/x64x11/configuration_public.h
@@ -22,6 +22,9 @@
#ifndef STARBOARD_LINUX_X64X11_CONFIGURATION_PUBLIC_H_
#define STARBOARD_LINUX_X64X11_CONFIGURATION_PUBLIC_H_
+// The API version implemented by this platform.
+#define SB_API_VERSION 2
+
// --- Architecture Configuration --------------------------------------------
// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
@@ -106,6 +109,8 @@
// textures. These textures typically originate from video decoders.
#define SB_HAS_NV12_TEXTURE_SUPPORT 1
+#define SB_HAS_MICROPHONE 0
+
// Include the Linux configuration that's common between all Desktop Linuxes.
#include "starboard/linux/shared/configuration_public.h"
diff --git a/src/starboard/creator/ci20/atomic_public.h b/src/starboard/linux/x64x11/future/atomic_public.h
similarity index 79%
copy from src/starboard/creator/ci20/atomic_public.h
copy to src/starboard/linux/x64x11/future/atomic_public.h
index 3b48d2e..e908f7d 100644
--- a/src/starboard/creator/ci20/atomic_public.h
+++ b/src/starboard/linux/x64x11/future/atomic_public.h
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#ifndef STARBOARD_LINUX_X64X11_FUTURE_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_FUTURE_ATOMIC_PUBLIC_H_
#include "starboard/linux/shared/atomic_public.h"
-#endif // STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#endif // STARBOARD_LINUX_X64X11_FUTURE_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/future/configuration_public.h b/src/starboard/linux/x64x11/future/configuration_public.h
new file mode 100644
index 0000000..91ab8df
--- /dev/null
+++ b/src/starboard/linux/x64x11/future/configuration_public.h
@@ -0,0 +1,28 @@
+// 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.
+
+// The Starboard configuration for Desktop X86 Linux configured for the future
+// starboard API.
+
+#ifndef STARBOARD_LINUX_X64X11_FUTURE_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_FUTURE_CONFIGURATION_PUBLIC_H_
+
+// Include the X64X11 Linux configuration.
+#include "starboard/linux/x64x11/configuration_public.h"
+
+// The API version implemented by this platform.
+#undef SB_API_VERSION
+#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
+#endif // STARBOARD_LINUX_X64X11_FUTURE_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/future/gyp_configuration.gypi b/src/starboard/linux/x64x11/future/gyp_configuration.gypi
new file mode 100644
index 0000000..0cb12cf
--- /dev/null
+++ b/src/starboard/linux/x64x11/future/gyp_configuration.gypi
@@ -0,0 +1,37 @@
+# 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.
+
+{
+ 'target_defaults': {
+ 'default_configuration': 'linux-x64x11-future_debug',
+ 'configurations': {
+ 'linux-x64x11-future_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'linux-x64x11-future_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'linux-x64x11-future_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'linux-x64x11-future_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ '../gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/linux/x64x11/future/gyp_configuration.py b/src/starboard/linux/x64x11/future/gyp_configuration.py
new file mode 100644
index 0000000..5f82ad7
--- /dev/null
+++ b/src/starboard/linux/x64x11/future/gyp_configuration.py
@@ -0,0 +1,33 @@
+# 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.
+"""Starboard Linux X64 X11 future platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+# Import the shared Linux platform configuration.
+sys.path.append(
+ os.path.realpath(
+ os.path.join(
+ os.path.dirname(__file__), os.pardir, os.pardir, 'shared')))
+import gyp_configuration
+
+
+def CreatePlatformConfig():
+ try:
+ return gyp_configuration.PlatformConfig('linux-x64x11-future')
+ except RuntimeError as e:
+ logging.critical(e)
+ return None
diff --git a/src/starboard/linux/x64x11/future/starboard_platform.gyp b/src/starboard/linux/x64x11/future/starboard_platform.gyp
new file mode 100644
index 0000000..9648c9d
--- /dev/null
+++ b/src/starboard/linux/x64x11/future/starboard_platform.gyp
@@ -0,0 +1,40 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'starboard_platform',
+ 'product_name': 'starboard_platform_future',
+ 'type': 'static_library',
+ 'sources': [
+ '<(DEPTH)/starboard/shared/stub/decode_target_create_egl.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_destroy.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_get_plane_egl.cc',
+ ],
+ 'defines': [
+ # This must be defined when building Starboard, and must not when
+ # building Starboard client code.
+ 'STARBOARD_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/starboard/common/common.gyp:common',
+ '<(DEPTH)/starboard/linux/x64x11/starboard_platform.gyp:starboard_platform',
+ '<(DEPTH)/starboard/linux/x64x11/starboard_platform.gyp:starboard_base_symbolize',
+ '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
+ '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
+ ],
+ },
+ ],
+}
diff --git a/src/starboard/creator/ci20/thread_types_public.h b/src/starboard/linux/x64x11/future/thread_types_public.h
similarity index 78%
copy from src/starboard/creator/ci20/thread_types_public.h
copy to src/starboard/linux/x64x11/future/thread_types_public.h
index ec9eedf..9ae5da8 100644
--- a/src/starboard/creator/ci20/thread_types_public.h
+++ b/src/starboard/linux/x64x11/future/thread_types_public.h
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#ifndef STARBOARD_LINUX_X64X11_FUTURE_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_FUTURE_THREAD_TYPES_PUBLIC_H_
#include "starboard/linux/shared/thread_types_public.h"
-#endif // STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#endif // STARBOARD_LINUX_X64X11_FUTURE_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sanitizer_options.cc b/src/starboard/linux/x64x11/sanitizer_options.cc
new file mode 100644
index 0000000..91d1d26
--- /dev/null
+++ b/src/starboard/linux/x64x11/sanitizer_options.cc
@@ -0,0 +1,39 @@
+// 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.
+
+// Removes gallium leak warnings from x11 GL code, but defines it as a weak
+// symbol, so other code can override it if they want to.
+
+#if defined(ADDRESS_SANITIZER)
+
+// Functions returning default options are declared weak in the tools' runtime
+// libraries. To make the linker pick the strong replacements for those
+// functions from this module, we explicitly force its inclusion by passing
+// -Wl,-u_sanitizer_options_link_helper
+extern "C" void _sanitizer_options_link_helper() { }
+
+#define SANITIZER_HOOK_ATTRIBUTE \
+ extern "C" \
+ __attribute__((no_sanitize_address)) \
+ __attribute__((no_sanitize_memory)) \
+ __attribute__((no_sanitize_thread)) \
+ __attribute__((visibility("default"))) \
+ __attribute__((weak)) \
+ __attribute__((used))
+
+SANITIZER_HOOK_ATTRIBUTE const char* __lsan_default_suppressions() {
+ return "leak:egl_gallium.so\n";
+}
+
+#endif // defined(ADDRESS_SANITIZER)
diff --git a/src/starboard/linux/x64x11/starboard_platform.gyp b/src/starboard/linux/x64x11/starboard_platform.gyp
index 6f9e3f8..228ceb0 100644
--- a/src/starboard/linux/x64x11/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/starboard_platform.gyp
@@ -32,6 +32,7 @@
'<(DEPTH)/starboard/linux/shared/system_get_path.cc',
'<(DEPTH)/starboard/linux/shared/system_has_capability.cc',
'<(DEPTH)/starboard/linux/x64x11/main.cc',
+ '<(DEPTH)/starboard/linux/x64x11/sanitizer_options.cc',
'<(DEPTH)/starboard/linux/x64x11/system_get_property.cc',
'<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.cc',
'<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.h',
@@ -226,8 +227,8 @@
'<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_parser.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_parser.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/filter/audio_decoder_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
diff --git a/src/starboard/linux/x64x11/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/starboard_platform_tests.gyp
new file mode 100644
index 0000000..0250125
--- /dev/null
+++ b/src/starboard/linux/x64x11/starboard_platform_tests.gyp
@@ -0,0 +1,41 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'starboard_platform_tests',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ '<(DEPTH)/starboard/common/test_main.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/mime_type_test.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/starboard/starboard.gyp:starboard',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ },
+ {
+ 'target_name': 'starboard_platform_tests_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ '<(DEPTH)/<(starboard_path)/starboard_platform_tests.gyp:starboard_platform_tests',
+ ],
+ 'variables': {
+ 'executable_name': 'starboard_platform_tests',
+ },
+ 'includes': [ '../../build/deploy.gypi' ],
+ },
+ ],
+}
diff --git a/src/starboard/microphone.h b/src/starboard/microphone.h
index 9ca8816..b742f71 100644
--- a/src/starboard/microphone.h
+++ b/src/starboard/microphone.h
@@ -85,6 +85,9 @@
// Microphone max supported sampling rate.
int max_sample_rate_hz;
+
+ // The minimum read size required for each read from microphone.
+ int min_read_size;
} SbMicrophoneInfo;
// An opaque handle to an implementation-private structure representing a
@@ -102,9 +105,11 @@
// Gets all currently-available microphone information and the results are
// stored in |out_info_array|. |info_array_size| is the size of
// |out_info_array|.
-// Return value is the number of the available microphones. A negative return
-// value indicates that either the |info_array_size| is too small or an internal
-// error is occurred.
+// Returns the number of the available microphones. A negative return
+// value indicates that an internal error has occurred. If the number of
+// available microphones is larger than |info_array_size|, |out_info_array| will
+// be filled up with as many available microphones as possible and the actual
+// number of available microphones will be returned.
SB_EXPORT int SbMicrophoneGetAvailable(SbMicrophoneInfo* out_info_array,
int info_array_size);
@@ -153,7 +158,9 @@
// an error. This function should be called frequently, otherwise microphone
// only buffers |buffer_size| bytes which is configured in |SbMicrophoneCreate|
// and the new audio data will be thrown out. No audio data will be read from a
-// stopped microphone.
+// stopped microphone. If |audio_data_size| is smaller than |min_read_size| of
+// |SbMicrophoneInfo|, the extra audio data which is already read from device
+// will be discarded.
SB_EXPORT int SbMicrophoneRead(SbMicrophone microphone,
void* out_audio_data,
int audio_data_size);
diff --git a/src/starboard/nplb/decode_target_create_test.cc b/src/starboard/nplb/decode_target_create_test.cc
new file mode 100644
index 0000000..9358c68
--- /dev/null
+++ b/src/starboard/nplb/decode_target_create_test.cc
@@ -0,0 +1,180 @@
+// 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/window.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This must come after gtest, because it includes GL, which can include X11,
+// which will define None to be 0L, which conflicts with gtest.
+#include "starboard/decode_target.h" // NOLINT(build/include_order)
+
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+#if SB_HAS(BLITTER)
+#include "starboard/blitter.h"
+#include "starboard/nplb/blitter_helpers.h"
+#endif
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+#if SB_HAS(BLITTER)
+const int kWidth = 128;
+const int kHeight = 128;
+
+TEST(SbDecodeTargetTest, SunnyDayCreate) {
+ ContextTestEnvironment env(kWidth, kHeight);
+
+ ASSERT_TRUE(SbBlitterSetRenderTarget(env.context(), env.render_target()));
+
+ SbBlitterSurface surface =
+ CreateArbitraryRenderTargetSurface(env.device(), kWidth, kHeight);
+
+ SbDecodeTarget target =
+ SbDecodeTargetCreate(kSbDecodeTargetFormat1PlaneRGBA, &surface);
+ EXPECT_TRUE(SbDecodeTargetIsValid(target));
+ if (SbDecodeTargetIsValid(target)) {
+ SbBlitterSurface plane =
+ SbDecodeTargetGetPlane(target, kSbDecodeTargetPlaneRGBA);
+ EXPECT_TRUE(SbBlitterIsSurfaceValid(plane));
+ }
+ SbDecodeTargetDestroy(target);
+ EXPECT_TRUE(SbBlitterDestroySurface(surface));
+}
+#else // SB_HAS(BLITTER)
+// clang-format off
+EGLint const kAttributeList[] = {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_STENCIL_SIZE, 0,
+ EGL_BUFFER_SIZE, 32,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+ EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
+ EGL_CONFORMANT, EGL_OPENGL_ES2_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+};
+// clang-format on
+
+class SbDecodeTargetTest : public testing::Test {
+ protected:
+ void SetUp() SB_OVERRIDE {
+ SbWindowOptions options;
+ SbWindowSetDefaultOptions(&options);
+ window_ = SbWindowCreate(&options);
+
+ display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_DISPLAY, display_);
+
+ eglInitialize(display_, NULL, NULL);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLint num_configs = 0;
+ eglChooseConfig(display_, kAttributeList, NULL, 0, &num_configs);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(0, num_configs);
+
+ // Allocate space to receive the matching configs and retrieve them.
+ EGLConfig* configs = new EGLConfig[num_configs];
+ eglChooseConfig(display_, kAttributeList, configs, num_configs,
+ &num_configs);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLNativeWindowType native_window =
+ (EGLNativeWindowType)SbWindowGetPlatformHandle(window_);
+ EGLConfig config;
+
+ // Find the first config that successfully allow a window surface to be
+ // created.
+ surface_ = EGL_NO_SURFACE;
+ for (int config_number = 0; config_number < num_configs; ++config_number) {
+ config = configs[config_number];
+ surface_ = eglCreateWindowSurface(display_, config, native_window, NULL);
+ if (EGL_SUCCESS == eglGetError())
+ break;
+ }
+ ASSERT_NE(EGL_NO_SURFACE, surface_);
+
+ delete[] configs;
+
+ // Create the GLES2 or GLEX3 Context.
+ context_ = EGL_NO_CONTEXT;
+ EGLint context_attrib_list[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE,
+ };
+#if defined(GLES3_SUPPORTED)
+ // Attempt to create an OpenGL ES 3.0 context.
+ context_ =
+ eglCreateContext(display_, config, EGL_NO_CONTEXT, context_attrib_list);
+#endif
+ if (context_ == EGL_NO_CONTEXT) {
+ // Create an OpenGL ES 2.0 context.
+ context_attrib_list[1] = 2;
+ context_ = eglCreateContext(display_, config, EGL_NO_CONTEXT,
+ context_attrib_list);
+ }
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_CONTEXT, context_);
+
+ // connect the context to the surface
+ eglMakeCurrent(display_, surface_, surface_, context_);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ }
+
+ void TearDown() SB_OVERRIDE {
+ eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+ eglDestroyContext(display_, context_);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+ eglDestroySurface(display_, surface_);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+ eglTerminate(display_);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+ SbWindowDestroy(window_);
+ }
+
+ EGLContext context_;
+ EGLDisplay display_;
+ EGLSurface surface_;
+ SbWindow window_;
+};
+
+TEST_F(SbDecodeTargetTest, SunnyDayCreate) {
+ // Generate a texture to put in the SbDecodeTarget.
+ GLuint texture_handle;
+ glGenTextures(1, &texture_handle);
+ glBindTexture(GL_TEXTURE_2D, texture_handle);
+ glTexImage2D(GL_TEXTURE_2D, 0 /*level*/, GL_RGBA, 256, 256, 0 /*border*/,
+ GL_RGBA8, GL_UNSIGNED_BYTE, NULL /*data*/);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ SbDecodeTarget target = SbDecodeTargetCreate(
+ display_, context_, kSbDecodeTargetFormat1PlaneRGBA, &texture_handle);
+ GLuint plane = SbDecodeTargetGetPlane(target, kSbDecodeTargetPlaneRGBA);
+ EXPECT_EQ(texture_handle, plane);
+ SbDecodeTargetDestroy(target);
+ glDeleteTextures(1, &texture_handle);
+}
+
+#endif // SB_HAS(BLITTER)
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
+
+#endif // SB_VERSION(3) && SB_HAS(GRAPHICS)
diff --git a/src/starboard/nplb/decode_target_provider_test.cc b/src/starboard/nplb/decode_target_provider_test.cc
new file mode 100644
index 0000000..a5fece5
--- /dev/null
+++ b/src/starboard/nplb/decode_target_provider_test.cc
@@ -0,0 +1,174 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+
+// This must come after gtest, because it includes GL, which can include X11,
+// which will define None to be 0L, which conflicts with gtest.
+#include "starboard/decode_target.h" // NOLINT(build/include_order)
+
+#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+struct ProviderContext {
+ static SbDecodeTarget Acquire(void* raw_context,
+ SbDecodeTargetFormat format,
+ int width,
+ int height) {
+ ProviderContext* context = reinterpret_cast<ProviderContext*>(raw_context);
+ ++context->called_acquire;
+ return kSbDecodeTargetInvalid;
+ }
+
+ static void Release(void* raw_context, SbDecodeTarget target) {
+ ProviderContext* context = reinterpret_cast<ProviderContext*>(raw_context);
+ ++context->called_release;
+ }
+
+ int called_acquire;
+ int called_release;
+};
+
+template <typename T>
+SbDecodeTargetProvider MakeProvider() {
+ return {&T::Acquire, &T::Release};
+}
+
+void AcquireFalse() {
+ SbDecodeTarget target = SbDecodeTargetAcquireFromProvider(
+ kSbDecodeTargetFormat1PlaneRGBA, 16, 16);
+ bool valid = SbDecodeTargetIsValid(target);
+ EXPECT_FALSE(valid);
+ if (valid) {
+ SbDecodeTargetDestroy(target);
+ }
+}
+
+void ReleaseInvalid() {
+ SbDecodeTargetReleaseToProvider(kSbDecodeTargetInvalid);
+}
+
+TEST(SbDecodeTargetProviderTest, SunnyDayRegisterUnregister) {
+ ProviderContext context = {};
+ SbDecodeTargetProvider provider = MakeProvider<ProviderContext>();
+ ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider, &context));
+ SbDecodeTargetUnregisterProvider(&provider, &context);
+}
+
+TEST(SbDecodeTargetProviderTest, SunnyDayCallsThroughToProvider) {
+ ProviderContext context = {};
+ SbDecodeTargetProvider provider = MakeProvider<ProviderContext>();
+ ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider, &context));
+ EXPECT_EQ(0, context.called_acquire);
+ EXPECT_EQ(0, context.called_release);
+
+ AcquireFalse();
+ EXPECT_EQ(1, context.called_acquire);
+ AcquireFalse();
+ EXPECT_EQ(2, context.called_acquire);
+
+ ReleaseInvalid();
+ EXPECT_EQ(1, context.called_release);
+ ReleaseInvalid();
+ EXPECT_EQ(2, context.called_release);
+
+ SbDecodeTargetUnregisterProvider(&provider, &context);
+}
+
+TEST(SbDecodeTargetProviderTest, SunnyDayRegisterOver) {
+ ProviderContext context = {};
+ SbDecodeTargetProvider provider = MakeProvider<ProviderContext>();
+ ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider, &context));
+
+ ProviderContext context2 = {};
+ SbDecodeTargetProvider provider2 = MakeProvider<ProviderContext>();
+ ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider2, &context2));
+
+ AcquireFalse();
+ EXPECT_EQ(1, context2.called_acquire);
+ EXPECT_EQ(0, context.called_acquire);
+ AcquireFalse();
+ EXPECT_EQ(2, context2.called_acquire);
+ EXPECT_EQ(0, context.called_acquire);
+
+ ReleaseInvalid();
+ EXPECT_EQ(1, context2.called_release);
+ EXPECT_EQ(0, context.called_release);
+ ReleaseInvalid();
+ EXPECT_EQ(2, context2.called_release);
+ EXPECT_EQ(0, context.called_release);
+
+ SbDecodeTargetUnregisterProvider(&provider2, &context2);
+}
+
+TEST(SbDecodeTargetProviderTest, RainyDayAcquireNoProvider) {
+ AcquireFalse();
+}
+
+TEST(SbDecodeTargetProviderTest, RainyDayReleaseNoProvider) {
+ ReleaseInvalid();
+}
+
+TEST(SbDecodeTargetProviderTest, RainyDayUnregisterUnregistered) {
+ ProviderContext context = {};
+ SbDecodeTargetProvider provider = MakeProvider<ProviderContext>();
+ SbDecodeTargetUnregisterProvider(&provider, &context);
+
+ ProviderContext context2 = {};
+ SbDecodeTargetProvider provider2 = MakeProvider<ProviderContext>();
+ ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider2, &context2));
+ SbDecodeTargetUnregisterProvider(&provider, &context);
+ SbDecodeTargetUnregisterProvider(&provider, &context2);
+ SbDecodeTargetUnregisterProvider(&provider2, &context);
+
+ AcquireFalse();
+ EXPECT_EQ(1, context2.called_acquire);
+ AcquireFalse();
+ EXPECT_EQ(2, context2.called_acquire);
+
+ ReleaseInvalid();
+ EXPECT_EQ(1, context2.called_release);
+ ReleaseInvalid();
+ EXPECT_EQ(2, context2.called_release);
+
+ SbDecodeTargetUnregisterProvider(&provider2, &context2);
+
+ AcquireFalse();
+ EXPECT_EQ(2, context2.called_acquire);
+ AcquireFalse();
+ EXPECT_EQ(2, context2.called_acquire);
+
+ ReleaseInvalid();
+ EXPECT_EQ(2, context2.called_release);
+ ReleaseInvalid();
+ EXPECT_EQ(2, context2.called_release);
+}
+
+TEST(SbDecodeTargetProviderTest, RainyDayUnregisterNull) {
+ ProviderContext context = {};
+ SbDecodeTargetUnregisterProvider(NULL, &context);
+}
+
+TEST(SbDecodeTargetProviderTest, RainyDayUnregisterNullNull) {
+ SbDecodeTargetUnregisterProvider(NULL, NULL);
+}
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
+
+#endif // SB_VERSION(3) && SB_HAS(GRAPHICS)
diff --git a/src/starboard/nplb/include_all.c b/src/starboard/nplb/include_all.c
index 74f0e84..5ac5aa1 100644
--- a/src/starboard/nplb/include_all.c
+++ b/src/starboard/nplb/include_all.c
@@ -21,6 +21,7 @@
#include "starboard/character.h"
#include "starboard/condition_variable.h"
#include "starboard/configuration.h"
+#include "starboard/decode_target.h"
#include "starboard/directory.h"
#include "starboard/double.h"
#include "starboard/drm.h"
diff --git a/src/starboard/nplb/microphone_get_available_test.cc b/src/starboard/nplb/microphone_get_available_test.cc
index 14a7ed0..232995b 100644
--- a/src/starboard/nplb/microphone_get_available_test.cc
+++ b/src/starboard/nplb/microphone_get_available_test.cc
@@ -32,14 +32,16 @@
SbMicrophoneInfo info_array[kMaxNumberOfMicrophone];
if (SbMicrophoneGetAvailable(info_array, kMaxNumberOfMicrophone) > 0) {
int available_microphones = SbMicrophoneGetAvailable(info_array, 0);
- EXPECT_LE(available_microphones, 0);
+ EXPECT_GT(available_microphones, 0);
}
}
TEST(SbMicrophoneGetAvailableTest, RainyDayNegativeNumberOfMicrophone) {
SbMicrophoneInfo info_array[kMaxNumberOfMicrophone];
- int available_microphones = SbMicrophoneGetAvailable(info_array, -10);
- EXPECT_LT(available_microphones, 0);
+ if (SbMicrophoneGetAvailable(info_array, kMaxNumberOfMicrophone) > 0) {
+ int available_microphones = SbMicrophoneGetAvailable(info_array, -10);
+ EXPECT_GT(available_microphones, 0);
+ }
}
TEST(SbMicrophoneGetAvailableTest, RainyDayNULLInfoArray) {
@@ -47,7 +49,7 @@
if (SbMicrophoneGetAvailable(info_array, kMaxNumberOfMicrophone) > 0) {
int available_microphones =
SbMicrophoneGetAvailable(NULL, kMaxNumberOfMicrophone);
- EXPECT_LT(available_microphones, 0);
+ EXPECT_GT(available_microphones, 0);
}
}
diff --git a/src/starboard/nplb/microphone_read_test.cc b/src/starboard/nplb/microphone_read_test.cc
index 6f80656..7df0260 100644
--- a/src/starboard/nplb/microphone_read_test.cc
+++ b/src/starboard/nplb/microphone_read_test.cc
@@ -35,16 +35,41 @@
info_array[0].id, info_array[0].max_sample_rate_hz, kBufferSize);
ASSERT_TRUE(SbMicrophoneIsValid(microphone));
- bool success = SbMicrophoneOpen(microphone);
- ASSERT_TRUE(success);
+ ASSERT_TRUE(SbMicrophoneOpen(microphone));
- void* audio_data[1024];
+ int requested_bytes = info_array[0].min_read_size;
+ std::vector<uint8_t> audio_data(requested_bytes, 0);
int read_bytes =
- SbMicrophoneRead(microphone, audio_data, sizeof(audio_data));
+ SbMicrophoneRead(microphone, &audio_data[0], audio_data.size());
EXPECT_GE(read_bytes, 0);
- success = SbMicrophoneClose(microphone);
- EXPECT_TRUE(success);
+ EXPECT_TRUE(SbMicrophoneClose(microphone));
+ SbMicrophoneDestroy(microphone);
+ }
+}
+
+TEST(SbMicrophoneReadTest, SunnyDayReadIsLargerThanMinReadSize) {
+ SbMicrophoneInfo info_array[kMaxNumberOfMicrophone];
+ int available_microphones =
+ SbMicrophoneGetAvailable(info_array, kMaxNumberOfMicrophone);
+ EXPECT_GE(available_microphones, 0);
+
+ if (available_microphones != 0) {
+ ASSERT_TRUE(SbMicrophoneIsSampleRateSupported(
+ info_array[0].id, info_array[0].max_sample_rate_hz));
+ SbMicrophone microphone = SbMicrophoneCreate(
+ info_array[0].id, info_array[0].max_sample_rate_hz, kBufferSize);
+ ASSERT_TRUE(SbMicrophoneIsValid(microphone));
+
+ ASSERT_TRUE(SbMicrophoneOpen(microphone));
+
+ int requested_bytes = info_array[0].min_read_size;
+ std::vector<uint8_t> audio_data(requested_bytes * 2, 0);
+ int read_bytes =
+ SbMicrophoneRead(microphone, &audio_data[0], audio_data.size());
+ EXPECT_GE(read_bytes, 0);
+
+ EXPECT_TRUE(SbMicrophoneClose(microphone));
SbMicrophoneDestroy(microphone);
}
}
@@ -62,22 +87,19 @@
info_array[0].id, info_array[0].max_sample_rate_hz, kBufferSize);
ASSERT_TRUE(SbMicrophoneIsValid(microphone));
- bool success = SbMicrophoneOpen(microphone);
- EXPECT_TRUE(success);
+ EXPECT_TRUE(SbMicrophoneOpen(microphone));
SbThreadSleep(50 * kSbTimeMillisecond);
- success = SbMicrophoneClose(microphone);
- EXPECT_TRUE(success);
+ EXPECT_TRUE(SbMicrophoneClose(microphone));
+ EXPECT_TRUE(SbMicrophoneOpen(microphone));
- success = SbMicrophoneOpen(microphone);
- EXPECT_TRUE(success);
-
- void* audio_data[16 * 1024];
+ int requested_bytes = info_array[0].min_read_size;
+ std::vector<uint8_t> audio_data(requested_bytes, 0);
int read_bytes =
- SbMicrophoneRead(microphone, audio_data, sizeof(audio_data));
+ SbMicrophoneRead(microphone, &audio_data[0], audio_data.size());
EXPECT_GE(read_bytes, 0);
- EXPECT_LT(read_bytes, sizeof(audio_data));
+ EXPECT_LT(read_bytes, audio_data.size());
SbMicrophoneDestroy(microphone);
}
@@ -96,14 +118,12 @@
info_array[0].id, info_array[0].max_sample_rate_hz, kBufferSize);
ASSERT_TRUE(SbMicrophoneIsValid(microphone));
- bool success = SbMicrophoneOpen(microphone);
- ASSERT_TRUE(success);
+ ASSERT_TRUE(SbMicrophoneOpen(microphone));
int read_bytes = SbMicrophoneRead(microphone, NULL, 0);
EXPECT_EQ(read_bytes, 0);
- success = SbMicrophoneClose(microphone);
- EXPECT_TRUE(success);
+ EXPECT_TRUE(SbMicrophoneClose(microphone));
SbMicrophoneDestroy(microphone);
}
}
@@ -121,14 +141,38 @@
info_array[0].id, info_array[0].max_sample_rate_hz, kBufferSize);
ASSERT_TRUE(SbMicrophoneIsValid(microphone));
- bool success = SbMicrophoneOpen(microphone);
- ASSERT_TRUE(success);
+ ASSERT_TRUE(SbMicrophoneOpen(microphone));
int read_bytes = SbMicrophoneRead(microphone, NULL, 1024);
EXPECT_LE(read_bytes, 0);
- success = SbMicrophoneClose(microphone);
- EXPECT_TRUE(success);
+ EXPECT_TRUE(SbMicrophoneClose(microphone));
+ SbMicrophoneDestroy(microphone);
+ }
+}
+
+TEST(SbMicrophoneReadTest, RainyDayAudioBufferSizeIsSmallerThanMinReadSize) {
+ SbMicrophoneInfo info_array[kMaxNumberOfMicrophone];
+ int available_microphones =
+ SbMicrophoneGetAvailable(info_array, kMaxNumberOfMicrophone);
+ EXPECT_GE(available_microphones, 0);
+
+ if (available_microphones != 0) {
+ ASSERT_TRUE(SbMicrophoneIsSampleRateSupported(
+ info_array[0].id, info_array[0].max_sample_rate_hz));
+ SbMicrophone microphone = SbMicrophoneCreate(
+ info_array[0].id, info_array[0].max_sample_rate_hz, kBufferSize);
+ ASSERT_TRUE(SbMicrophoneIsValid(microphone));
+
+ ASSERT_TRUE(SbMicrophoneOpen(microphone));
+
+ int requested_bytes = info_array[0].min_read_size;
+ std::vector<uint8_t> audio_data(requested_bytes / 2, 0);
+ int read_bytes =
+ SbMicrophoneRead(microphone, &audio_data[0], audio_data.size());
+ EXPECT_GE(read_bytes, 0);
+
+ EXPECT_TRUE(SbMicrophoneClose(microphone));
SbMicrophoneDestroy(microphone);
}
}
@@ -146,10 +190,12 @@
info_array[0].id, info_array[0].max_sample_rate_hz, kBufferSize);
ASSERT_TRUE(SbMicrophoneIsValid(microphone));
- void* audio_data[1024];
+ int requested_bytes = info_array[0].min_read_size;
+ std::vector<uint8_t> audio_data(requested_bytes, 0);
int read_bytes =
- SbMicrophoneRead(microphone, audio_data, sizeof(audio_data));
- EXPECT_EQ(read_bytes, 0);
+ SbMicrophoneRead(microphone, &audio_data[0], audio_data.size());
+ // An error should have occurred because open was not called.
+ EXPECT_LT(read_bytes, 0);
SbMicrophoneDestroy(microphone);
}
@@ -168,26 +214,24 @@
info_array[0].id, info_array[0].max_sample_rate_hz, kBufferSize);
ASSERT_TRUE(SbMicrophoneIsValid(microphone));
- bool success = SbMicrophoneOpen(microphone);
- EXPECT_TRUE(success);
+ EXPECT_TRUE(SbMicrophoneOpen(microphone));
+ EXPECT_TRUE(SbMicrophoneClose(microphone));
- success = SbMicrophoneClose(microphone);
- EXPECT_TRUE(success);
-
- void* audio_data[1024];
+ int requested_bytes = info_array[0].min_read_size;
+ std::vector<uint8_t> audio_data(requested_bytes, 0);
int read_bytes =
- SbMicrophoneRead(microphone, audio_data, sizeof(audio_data));
- // No data can be read.
- EXPECT_EQ(read_bytes, 0);
+ SbMicrophoneRead(microphone, &audio_data[0], audio_data.size());
+ // An error should have occurred because the microphone was closed.
+ EXPECT_LT(read_bytes, 0);
SbMicrophoneDestroy(microphone);
}
}
TEST(SbMicrophoneReadTest, RainyDayMicrophoneIsInvalid) {
- void* audio_data[1024];
+ std::vector<uint8_t> audio_data(1024, 0);
int read_bytes =
- SbMicrophoneRead(kSbMicrophoneInvalid, audio_data, sizeof(audio_data));
+ SbMicrophoneRead(kSbMicrophoneInvalid, &audio_data[0], audio_data.size());
EXPECT_LT(read_bytes, 0);
}
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 437cc92..cf31d23 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -21,6 +21,7 @@
'target_name': 'nplb',
'type': '<(gtest_target_type)',
'sources': [
+ '<(DEPTH)/starboard/common/test_main.cc',
'atomic_test.cc',
'audio_sink_create_test.cc',
'audio_sink_destroy_test.cc',
@@ -78,6 +79,8 @@
'condition_variable_wait_test.cc',
'condition_variable_wait_timed_test.cc',
'configuration_test.cc',
+ 'decode_target_create_test.cc',
+ 'decode_target_provider_test.cc',
'directory_can_open_test.cc',
'directory_close_test.cc',
'directory_create_test.cc',
@@ -107,7 +110,6 @@
'log_raw_dump_stack_test.cc',
'log_raw_test.cc',
'log_test.cc',
- 'main.cc',
'memory_align_to_page_size_test.cc',
'memory_allocate_aligned_test.cc',
'memory_allocate_test.cc',
diff --git a/src/starboard/nplb/socket_helpers.h b/src/starboard/nplb/socket_helpers.h
index 5b4e5d7..a01c02d 100644
--- a/src/starboard/nplb/socket_helpers.h
+++ b/src/starboard/nplb/socket_helpers.h
@@ -23,7 +23,7 @@
namespace starboard {
namespace nplb {
-const SbTime kSocketTimeout = kSbTimeSecond / 8;
+const SbTime kSocketTimeout = kSbTimeSecond / 5;
// Creates a plain TCP/IPv4 socket.
static inline SbSocket CreateTcpIpv4Socket() {
diff --git a/src/starboard/nplb/socket_waiter_create_test.cc b/src/starboard/nplb/socket_waiter_create_test.cc
index b14882a..6190e64 100644
--- a/src/starboard/nplb/socket_waiter_create_test.cc
+++ b/src/starboard/nplb/socket_waiter_create_test.cc
@@ -26,7 +26,7 @@
}
TEST(SbSocketWaiterCreateTest, ATon) {
- const int kATon = 4096;
+ const int kATon = 256;
for (int i = 0; i < kATon; ++i) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
diff --git a/src/starboard/raspi/1/starboard_platform.gyp b/src/starboard/raspi/1/starboard_platform.gyp
index b866ee7..c33f33d 100644
--- a/src/starboard/raspi/1/starboard_platform.gyp
+++ b/src/starboard/raspi/1/starboard_platform.gyp
@@ -33,6 +33,7 @@
'<(DEPTH)/starboard/linux/shared/system_has_capability.cc',
'<(DEPTH)/starboard/raspi/1/system_get_property.cc',
'<(DEPTH)/starboard/raspi/shared/application_dispmanx.cc',
+ '<(DEPTH)/starboard/raspi/shared/audio_sink_is_audio_sample_type_supported.cc',
'<(DEPTH)/starboard/raspi/shared/main.cc',
'<(DEPTH)/starboard/raspi/shared/window_create.cc',
'<(DEPTH)/starboard/raspi/shared/window_destroy.cc',
@@ -46,7 +47,6 @@
'<(DEPTH)/starboard/shared/alsa/audio_sink_get_max_channels.cc',
'<(DEPTH)/starboard/shared/alsa/audio_sink_get_nearest_supported_sample_frequency.cc',
'<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc',
- '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_unchecked.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
@@ -233,8 +233,8 @@
'<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_parser.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_parser.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/filter/audio_decoder_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
diff --git a/src/starboard/raspi/directfb/README.md b/src/starboard/raspi/directfb/README.md
index 4d9d9be..a4b4ec7 100644
--- a/src/starboard/raspi/directfb/README.md
+++ b/src/starboard/raspi/directfb/README.md
@@ -31,7 +31,7 @@
## Modifications to /etc/directfbrc
-Open or create 'etc/directfbrc' (as root) and add the lines:
+Open or create '/etc/directfbrc' (as root) and add the lines:
graphics-vt
hardware
diff --git a/src/starboard/creator/ci20/atomic_public.h b/src/starboard/raspi/shared/audio_sink_is_audio_sample_type_supported.cc
similarity index 74%
copy from src/starboard/creator/ci20/atomic_public.h
copy to src/starboard/raspi/shared/audio_sink_is_audio_sample_type_supported.cc
index 3b48d2e..f5026b8 100644
--- a/src/starboard/creator/ci20/atomic_public.h
+++ b/src/starboard/raspi/shared/audio_sink_is_audio_sample_type_supported.cc
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#include "starboard/audio_sink.h"
-#include "starboard/linux/shared/atomic_public.h"
-
-#endif // STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+bool SbAudioSinkIsAudioSampleTypeSupported(
+ SbMediaAudioSampleType audio_sample_type) {
+ return audio_sample_type == kSbMediaAudioSampleTypeInt16;
+}
diff --git a/src/starboard/shared/alsa/alsa_util.cc b/src/starboard/shared/alsa/alsa_util.cc
index c713015..0568156 100644
--- a/src/starboard/shared/alsa/alsa_util.cc
+++ b/src/starboard/shared/alsa/alsa_util.cc
@@ -35,6 +35,9 @@
namespace {
+const snd_pcm_uframes_t kSilenceThresholdInFrames = 256U;
+const snd_pcm_uframes_t kStartThresholdInFrames = 1024U;
+
template <typename T, typename CloseFunc>
class AutoClose {
public:
@@ -145,19 +148,20 @@
frames_per_request);
ALSA_CHECK(error, snd_pcm_sw_params_set_avail_min, NULL);
- error =
- snd_pcm_sw_params_set_silence_threshold(playback_handle, sw_params, 256U);
+ error = snd_pcm_sw_params_set_silence_threshold(playback_handle, sw_params,
+ kSilenceThresholdInFrames);
ALSA_CHECK(error, snd_pcm_sw_params_set_silence_threshold, NULL);
+ error = snd_pcm_sw_params_set_start_threshold(playback_handle, sw_params,
+ kStartThresholdInFrames);
+ ALSA_CHECK(error, snd_pcm_sw_params_set_start_threshold, NULL);
+
error = snd_pcm_sw_params(playback_handle, sw_params);
ALSA_CHECK(error, snd_pcm_sw_params, NULL);
error = snd_pcm_prepare(playback_handle);
ALSA_CHECK(error, snd_pcm_prepare, NULL);
- error = snd_pcm_start(playback_handle);
- ALSA_CHECK(error, snd_pcm_start, NULL);
-
return playback_handle.Detach();
}
diff --git a/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc b/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
index 9bfd9a7..0f417fd 100644
--- a/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
+++ b/src/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc
@@ -16,10 +16,10 @@
#include "starboard/character.h"
#include "starboard/log.h"
-#include "starboard/shared/starboard/media/mime_parser.h"
+#include "starboard/shared/starboard/media/mime_type.h"
#include "starboard/string.h"
-using starboard::shared::starboard::media::MimeParser;
+using starboard::shared::starboard::media::MimeType;
SbMediaSupportType SbMediaCanPlayMimeAndKeySystem(const char* mime,
const char* key_system) {
@@ -37,27 +37,32 @@
return kSbMediaSupportTypeNotSupported;
}
}
- MimeParser parser(mime);
- if (!parser.is_valid()) {
+ MimeType mime_type(mime);
+ if (!mime_type.is_valid()) {
SB_DLOG(WARNING) << mime << " is not a valid mime type";
return kSbMediaSupportTypeNotSupported;
}
- if (parser.mime() == "application/octet-stream") {
+ int codecs_index = mime_type.GetParamIndexByName("codecs");
+ if (codecs_index != MimeType::kInvalidParamIndex && codecs_index != 0) {
+ SB_DLOG(WARNING) << mime << " is not a valid mime type";
+ return kSbMediaSupportTypeNotSupported;
+ }
+ if (mime_type.type() == "application" &&
+ mime_type.subtype() == "octet-stream") {
return kSbMediaSupportTypeProbably;
}
- if (parser.mime() == "audio/mp4") {
+ if (mime_type.type() == "audio" && mime_type.subtype() == "mp4") {
return kSbMediaSupportTypeProbably;
}
- if (parser.mime() == "video/mp4") {
+ if (mime_type.type() == "video" && mime_type.subtype() == "mp4") {
return kSbMediaSupportTypeProbably;
}
#if SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
- if (parser.mime() == "video/webm") {
- if (!parser.HasParam("codecs")) {
+ if (mime_type.type() == "video" && mime_type.subtype() == "webm") {
+ if (codecs_index == MimeType::kInvalidParamIndex) {
return kSbMediaSupportTypeMaybe;
}
- std::string codecs = parser.GetParam("codecs");
- if (codecs == "vp9") {
+ if (mime_type.GetParamStringValue(0) == "vp9") {
return kSbMediaSupportTypeProbably;
}
return kSbMediaSupportTypeNotSupported;
diff --git a/src/starboard/shared/starboard/media/mime_parser.cc b/src/starboard/shared/starboard/media/mime_parser.cc
deleted file mode 100644
index 7db734a..0000000
--- a/src/starboard/shared/starboard/media/mime_parser.cc
+++ /dev/null
@@ -1,141 +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 "starboard/shared/starboard/media/mime_parser.h"
-
-#include <vector>
-
-#include "starboard/character.h"
-#include "starboard/log.h"
-#include "starboard/string.h"
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-namespace media {
-
-namespace {
-
-void ToLower(std::string* string) {
- SB_DCHECK(string);
- for (size_t i = 0; i < string->size(); ++i) {
- (*string)[i] = SbCharacterToLower((*string)[i]);
- }
-}
-
-void Trim(std::string* string) {
- SB_DCHECK(string);
- while (!string->empty() && SbCharacterIsSpace(*string->rbegin())) {
- string->resize(string->size() - 1);
- }
- while (!string->empty() && SbCharacterIsSpace(*string->begin())) {
- string->erase(string->begin());
- }
-}
-
-bool IsSeparator(char ch, std::string separator) {
- if (separator.empty()) {
- return SbCharacterIsSpace(ch);
- }
- return separator.find(ch) != separator.npos;
-}
-
-// For any given string, split it into substrings separated by any character in
-// the |separator| string. If |separator| is empty string, treat any white
-// space as separators.
-std::vector<std::string> SplitString(std::string string,
- std::string separator = "") {
- std::vector<std::string> result;
- while (!string.empty()) {
- Trim(&string);
- if (string.empty()) {
- break;
- }
- bool added = false;
- for (size_t i = 0; i < string.size(); ++i) {
- if (IsSeparator(string[i], separator)) {
- result.push_back(string.substr(0, i));
- Trim(&result.back());
- string = string.substr(i + 1);
- added = true;
- break;
- }
- }
- if (!added) {
- Trim(&string);
- result.push_back(string);
- break;
- }
- }
- return result;
-}
-
-} // namespace
-
-MimeParser::MimeParser(std::string mime_with_params) {
- Trim(&mime_with_params);
- ToLower(&mime_with_params);
-
- std::vector<std::string> splits = SplitString(mime_with_params, ";");
- if (splits.empty()) {
- return; // It is invalid, leave |mime_| as empty.
- }
- mime_ = splits[0];
- // Mime is in the form of "video/mp4".
- if (SplitString(mime_, "/").size() != 2) {
- mime_.clear();
- return;
- }
- splits.erase(splits.begin());
-
- while (!splits.empty()) {
- std::string params = *splits.begin();
- splits.erase(splits.begin());
-
- // Param is in the form of 'name=value' or 'name="value"'.
- std::vector<std::string> name_and_value = SplitString(params, "=");
- if (name_and_value.size() != 2) {
- mime_.clear();
- return;
- }
- std::string name = name_and_value[0];
- std::string value = name_and_value[1];
-
- if (value.size() >= 2 && value[0] == '\"' &&
- value[value.size() - 1] == '\"') {
- value.erase(value.begin());
- value.resize(value.size() - 1);
- Trim(&value);
- }
-
- params_[name] = value;
- }
-}
-
-bool MimeParser::HasParam(const std::string& name) const {
- return params_.find(name) != params_.end();
-}
-
-std::string MimeParser::GetParam(const std::string& name) const {
- std::map<std::string, std::string>::const_iterator iter = params_.find(name);
- if (iter == params_.end()) {
- return "";
- }
- return iter->second;
-}
-
-} // namespace media
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/starboard/media/mime_parser.h b/src/starboard/shared/starboard/media/mime_parser.h
deleted file mode 100644
index b896b2d..0000000
--- a/src/starboard/shared/starboard/media/mime_parser.h
+++ /dev/null
@@ -1,54 +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 STARBOARD_SHARED_STARBOARD_MEDIA_MIME_PARSER_H_
-#define STARBOARD_SHARED_STARBOARD_MEDIA_MIME_PARSER_H_
-
-#include <map>
-#include <string>
-
-#include "starboard/shared/internal_only.h"
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-namespace media {
-
-// This class can be used to parse media mime types with params. For example,
-// the following mime type 'video/mp4; codecs="avc1.4d401e"; width=640' will be
-// parsed into:
-// mime => video/mp4
-// param: codecs => avc1.4d401e
-// param: width => 640
-class MimeParser {
- public:
- explicit MimeParser(std::string mime_with_params);
-
- bool is_valid() const { return !mime_.empty(); }
- const std::string& mime() const { return mime_; }
-
- bool HasParam(const std::string& name) const;
- std::string GetParam(const std::string& name) const;
-
- private:
- std::string mime_;
- std::map<std::string, std::string> params_;
-};
-
-} // namespace media
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_STARBOARD_MEDIA_MIME_PARSER_H_
diff --git a/src/starboard/shared/starboard/media/mime_type.cc b/src/starboard/shared/starboard/media/mime_type.cc
new file mode 100644
index 0000000..d77b2d5
--- /dev/null
+++ b/src/starboard/shared/starboard/media/mime_type.cc
@@ -0,0 +1,217 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/mime_type.h"
+
+#include "starboard/character.h"
+#include "starboard/log.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+namespace {
+
+typedef std::vector<std::string> Strings;
+
+MimeType::ParamType GetParamTypeByValue(const std::string& value) {
+ int count;
+ int i;
+ if (SbStringScanF(value.c_str(), "%d%n", &i, &count) == 1 &&
+ count == value.size()) {
+ return MimeType::kParamTypeInteger;
+ }
+ float f;
+ if (SbStringScanF(value.c_str(), "%g%n", &f, &count) == 1 &&
+ count == value.size()) {
+ return MimeType::kParamTypeFloat;
+ }
+ return MimeType::kParamTypeString;
+}
+
+bool ContainsSpace(const std::string& str) {
+ for (size_t i = 0; i < str.size(); ++i) {
+ if (SbCharacterIsSpace(str[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Trim(std::string* str) {
+ while (!str->empty() && SbCharacterIsSpace(*str->begin())) {
+ str->erase(str->begin());
+ }
+ while (!str->empty() && SbCharacterIsSpace(*str->rbegin())) {
+ str->resize(str->size() - 1);
+ }
+}
+
+Strings SplitAndTrim(const std::string& str, char ch) {
+ Strings result;
+ size_t pos = 0;
+
+ for (;;) {
+ size_t next = str.find(ch, pos);
+ result.push_back(str.substr(pos, next - pos));
+ Trim(&result.back());
+ if (next == str.npos) {
+ break;
+ }
+ pos = next + 1;
+ }
+
+ return result;
+}
+
+} // namespace
+
+const int MimeType::kInvalidParamIndex = -1;
+
+MimeType::MimeType(const std::string& content_type) : is_valid_(false) {
+ Strings components = SplitAndTrim(content_type, ';');
+
+ SB_DCHECK(!components.empty());
+
+ // 1. Verify if there is a valid type/subtype in the very beginning.
+ if (ContainsSpace(components.front())) {
+ return;
+ }
+
+ std::vector<std::string> type_and_container =
+ SplitAndTrim(components.front(), '/');
+ if (type_and_container.size() != 2 || type_and_container[0].empty() ||
+ type_and_container[1].empty()) {
+ return;
+ }
+ type_ = type_and_container[0];
+ subtype_ = type_and_container[1];
+ components.erase(components.begin());
+
+ // 2. Verify the parameters have valid formats, we want to be strict here.
+ for (Strings::iterator iter = components.begin(); iter != components.end();
+ ++iter) {
+ std::vector<std::string> name_and_value = SplitAndTrim(*iter, '=');
+ if (name_and_value.size() != 2 || name_and_value[0].empty() ||
+ name_and_value[1].empty()) {
+ return;
+ }
+ Param param;
+ if (name_and_value[1].size() > 2 && name_and_value[1][0] == '\"' &&
+ *name_and_value[1].rbegin() == '\"') {
+ param.type = kParamTypeString;
+ param.value = name_and_value[1].substr(1, name_and_value[1].size() - 2);
+ } else {
+ param.type = GetParamTypeByValue(name_and_value[1]);
+ param.value = name_and_value[1];
+ }
+ param.name = name_and_value[0];
+ params_.push_back(param);
+ }
+
+ is_valid_ = true;
+}
+
+int MimeType::GetParamCount() const {
+ SB_DCHECK(is_valid());
+
+ return static_cast<int>(params_.size());
+}
+
+MimeType::ParamType MimeType::GetParamType(int index) const {
+ SB_DCHECK(is_valid());
+ SB_DCHECK(index < GetParamCount());
+
+ return params_[index].type;
+}
+
+const std::string& MimeType::GetParamName(int index) const {
+ SB_DCHECK(is_valid());
+ SB_DCHECK(index < GetParamCount());
+
+ return params_[index].name;
+}
+
+int MimeType::GetParamIntValue(int index) const {
+ SB_DCHECK(is_valid());
+ SB_DCHECK(index < GetParamCount());
+ SB_DCHECK(GetParamType(index) == kParamTypeInteger);
+
+ int i;
+ SbStringScanF(params_[index].value.c_str(), "%d", &i);
+ return i;
+}
+
+float MimeType::GetParamFloatValue(int index) const {
+ SB_DCHECK(is_valid());
+ SB_DCHECK(index < GetParamCount());
+ SB_DCHECK(GetParamType(index) == kParamTypeInteger ||
+ GetParamType(index) == kParamTypeFloat);
+
+ float f;
+ SbStringScanF(params_[index].value.c_str(), "%g", &f);
+
+ return f;
+}
+
+const std::string& MimeType::GetParamStringValue(int index) const {
+ SB_DCHECK(is_valid());
+ SB_DCHECK(index < GetParamCount());
+
+ return params_[index].value;
+}
+
+int MimeType::GetParamIntValue(const char* name, int default_value) const {
+ int index = GetParamIndexByName(name);
+ if (index != kInvalidParamIndex) {
+ return GetParamIntValue(index);
+ }
+ return default_value;
+}
+
+float MimeType::GetParamFloatValue(const char* name,
+ float default_value) const {
+ int index = GetParamIndexByName(name);
+ if (index != kInvalidParamIndex) {
+ return GetParamFloatValue(index);
+ }
+ return default_value;
+}
+
+const std::string& MimeType::GetParamStringValue(
+ const char* name,
+ const std::string& default_value) const {
+ int index = GetParamIndexByName(name);
+ if (index != kInvalidParamIndex) {
+ return GetParamStringValue(index);
+ }
+ return default_value;
+}
+
+int MimeType::GetParamIndexByName(const char* name) const {
+ for (size_t i = 0; i < params_.size(); ++i) {
+ if (SbStringCompareNoCase(params_[i].name.c_str(), name) == 0) {
+ return static_cast<int>(i);
+ }
+ }
+ return kInvalidParamIndex;
+}
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/media/mime_type.h b/src/starboard/shared/starboard/media/mime_type.h
new file mode 100644
index 0000000..eb5b2f2
--- /dev/null
+++ b/src/starboard/shared/starboard/media/mime_type.h
@@ -0,0 +1,95 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_MIME_TYPE_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_MIME_TYPE_H_
+
+#include <string>
+#include <vector>
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+// This class can be used to parse a content type for media in the form of
+// "type/subtype; param1=value1; param2="value2". For example, the content type
+// "video/webm; codecs="vp9"; width=1920; height=1080; framerate=59.96" will be
+// parsed into:
+// type: video
+// subtype: webm
+// codecs: vp9
+// width: 1920
+// height: 1080
+// framerate: 59.96
+//
+// The following are the restrictions on the components:
+// 1. Type/subtype has to be in the very beginning.
+// 2. String values may be double quoted.
+// 3. Numeric values cannot be double quoted.
+class MimeType {
+ public:
+ enum ParamType {
+ kParamTypeInteger,
+ kParamTypeFloat,
+ kParamTypeString,
+ };
+
+ static const int kInvalidParamIndex;
+
+ explicit MimeType(const std::string& content_type);
+
+ bool is_valid() const { return is_valid_; }
+
+ const std::string& type() const { return type_; }
+ const std::string& subtype() const { return subtype_; }
+
+ int GetParamIndexByName(const char* name) const;
+ int GetParamCount() const;
+ ParamType GetParamType(int index) const;
+ const std::string& GetParamName(int index) const;
+
+ int GetParamIntValue(int index) const;
+ float GetParamFloatValue(int index) const;
+ const std::string& GetParamStringValue(int index) const;
+
+ int GetParamIntValue(const char* name, int default_value) const;
+ float GetParamFloatValue(const char* name, float default_value) const;
+ const std::string& GetParamStringValue(
+ const char* name,
+ const std::string& default_value) const;
+
+ private:
+ struct Param {
+ ParamType type;
+ std::string name;
+ std::string value;
+ };
+
+ // Use std::vector as the number of components are usually small and we'd like
+ // to keep the order of components.
+ typedef std::vector<Param> Params;
+
+ bool is_valid_;
+ std::string type_;
+ std::string subtype_;
+ Params params_;
+};
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_MEDIA_MIME_TYPE_H_
diff --git a/src/starboard/shared/starboard/media/mime_type_test.cc b/src/starboard/shared/starboard/media/mime_type_test.cc
new file mode 100644
index 0000000..44d0371
--- /dev/null
+++ b/src/starboard/shared/starboard/media/mime_type_test.cc
@@ -0,0 +1,269 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/mime_type.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+namespace {
+
+TEST(MimeTypeTest, EmptyString) {
+ MimeType mime_type("");
+ EXPECT_FALSE(mime_type.is_valid());
+}
+
+// Valid mime type must have a type/subtype without space in between.
+TEST(MimeTypeTest, InvalidType) {
+ {
+ MimeType mime_type("video");
+ EXPECT_FALSE(mime_type.is_valid());
+ }
+
+ {
+ MimeType mime_type("video/");
+ EXPECT_FALSE(mime_type.is_valid());
+ }
+
+ {
+ MimeType mime_type("/mp4");
+ EXPECT_FALSE(mime_type.is_valid());
+ }
+
+ {
+ 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("video / mp4");
+ EXPECT_FALSE(mime_type.is_valid());
+ }
+}
+
+TEST(MimeTypeTest, ValidContentTypeWithTypeAndSubtypeOnly) {
+ {
+ MimeType mime_type("video/mp4");
+ EXPECT_TRUE(mime_type.is_valid());
+ EXPECT_EQ("video", mime_type.type());
+ EXPECT_EQ("mp4", mime_type.subtype());
+ }
+
+ {
+ MimeType mime_type("audio/mp4");
+ EXPECT_TRUE(mime_type.is_valid());
+ }
+
+ {
+ MimeType mime_type("abc/xyz");
+ EXPECT_TRUE(mime_type.is_valid());
+ }
+}
+
+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());
+ }
+}
+
+TEST(MimeTypeTest, ValidContentTypeWithParams) {
+ {
+ MimeType mime_type("video/mp4; name=123");
+ EXPECT_TRUE(mime_type.is_valid());
+ }
+
+ {
+ MimeType mime_type("audio/mp4;codecs=\"abc\"");
+ EXPECT_TRUE(mime_type.is_valid());
+ }
+
+ {
+ MimeType mime_type(" audio/mp4 ; codecs = \"abc\" ");
+ EXPECT_TRUE(mime_type.is_valid());
+ }
+}
+
+TEST(MimeTypeTest, GetParamIndexByName) {
+ MimeType mime_type("video/mp4; name=123");
+ EXPECT_EQ(MimeType::kInvalidParamIndex,
+ mime_type.GetParamIndexByName("video"));
+ EXPECT_EQ(MimeType::kInvalidParamIndex, mime_type.GetParamIndexByName("mp4"));
+ EXPECT_EQ(0, mime_type.GetParamIndexByName("name"));
+}
+
+TEST(MimeTypeTest, ParamCount) {
+ {
+ MimeType mime_type("video/mp4");
+ EXPECT_EQ(0, mime_type.GetParamCount());
+ }
+
+ {
+ MimeType mime_type("video/mp4; width=1920");
+ EXPECT_EQ(1, mime_type.GetParamCount());
+ }
+
+ {
+ MimeType mime_type("video/mp4; width=1920; height=1080");
+ EXPECT_EQ(2, mime_type.GetParamCount());
+ }
+}
+
+TEST(MimeTypeTest, GetParamType) {
+ {
+ MimeType mime_type("video/mp4; name0=123; name1=1.2; name2=xyz");
+ EXPECT_EQ(MimeType::kParamTypeInteger, mime_type.GetParamType(0));
+ EXPECT_EQ(MimeType::kParamTypeFloat, mime_type.GetParamType(1));
+ EXPECT_EQ(MimeType::kParamTypeString, mime_type.GetParamType(2));
+ }
+
+ {
+ MimeType mime_type("video/mp4; name0=\"123\"; name1=\"abc\"");
+ EXPECT_EQ(MimeType::kParamTypeString, mime_type.GetParamType(0));
+ EXPECT_EQ(MimeType::kParamTypeString, mime_type.GetParamType(1));
+ }
+
+ {
+ MimeType mime_type("video/mp4; name1=\" abc \"");
+ EXPECT_EQ(MimeType::kParamTypeString, mime_type.GetParamType(0));
+ }
+}
+
+TEST(MimeTypeTest, GetParamName) {
+ {
+ MimeType mime_type("video/mp4; name0=123; name1=1.2; name2=xyz");
+ EXPECT_EQ("name0", mime_type.GetParamName(0));
+ EXPECT_EQ("name1", mime_type.GetParamName(1));
+ EXPECT_EQ("name2", mime_type.GetParamName(2));
+ }
+}
+
+TEST(MimeTypeTest, GetParamIntValueWithIndex) {
+ {
+ MimeType mime_type("video/mp4; name=123");
+ EXPECT_EQ(123, mime_type.GetParamIntValue(0));
+ }
+
+ {
+ MimeType mime_type("video/mp4; width=1920; height=1080");
+ EXPECT_EQ(1920, mime_type.GetParamIntValue(0));
+ EXPECT_EQ(1080, mime_type.GetParamIntValue(1));
+ }
+
+ {
+ MimeType mime_type("audio/mp4; channels=6");
+ EXPECT_EQ(6, mime_type.GetParamIntValue(0));
+ }
+}
+
+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));
+ }
+}
+
+TEST(MimeTypeTest, GetParamStringValueWithIndex) {
+ {
+ MimeType mime_type("video/mp4; name0=123; name1=abc; name2=\"xyz\"");
+ EXPECT_EQ("123", mime_type.GetParamStringValue(0));
+ EXPECT_EQ("abc", mime_type.GetParamStringValue(1));
+ EXPECT_EQ("xyz", mime_type.GetParamStringValue(2));
+ }
+
+ {
+ MimeType mime_type("video/mp4; name=\" xyz \"");
+ EXPECT_EQ(" xyz ", mime_type.GetParamStringValue(0));
+ }
+}
+
+TEST(MimeTypeTest, GetParamIntValueWithName) {
+ {
+ MimeType mime_type("video/mp4; name=123");
+ EXPECT_EQ(123, mime_type.GetParamIntValue("name", 0));
+ EXPECT_EQ(6, mime_type.GetParamIntValue("channels", 6));
+ }
+
+ {
+ MimeType mime_type("video/mp4; width=1920; height=1080");
+ EXPECT_EQ(1920, mime_type.GetParamIntValue("width", 0));
+ EXPECT_EQ(1080, mime_type.GetParamIntValue("height", 0));
+ }
+
+ {
+ MimeType mime_type("audio/mp4; channels=6");
+ EXPECT_EQ(6, mime_type.GetParamIntValue("channels", 0));
+ }
+}
+
+TEST(MimeTypeTest, GetParamFloatValueWithName) {
+ {
+ MimeType mime_type("video/mp4; name0=123; name1=123.4");
+ EXPECT_FLOAT_EQ(123.f, mime_type.GetParamFloatValue("name0", 0.f));
+ EXPECT_FLOAT_EQ(123.4f, mime_type.GetParamFloatValue("name1", 0.f));
+ EXPECT_FLOAT_EQ(59.96f, mime_type.GetParamFloatValue("framerate", 59.96f));
+ }
+}
+
+TEST(MimeTypeTest, GetParamStringValueWithName) {
+ {
+ MimeType mime_type("video/mp4; name0=123; name1=abc; name2=\"xyz\"");
+ EXPECT_EQ("123", mime_type.GetParamStringValue("name0", ""));
+ EXPECT_EQ("abc", mime_type.GetParamStringValue("name1", ""));
+ EXPECT_EQ("xyz", mime_type.GetParamStringValue("name2", ""));
+ EXPECT_EQ("h263", mime_type.GetParamStringValue("codecs", "h263"));
+ }
+
+ {
+ MimeType mime_type("video/mp4; name=\" xyz \"");
+ EXPECT_EQ(" xyz ", mime_type.GetParamStringValue("name", ""));
+ }
+}
+
+} // namespace
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/creator/ci20/atomic_public.h b/src/starboard/shared/stub/decode_target_create_blitter.cc
similarity index 73%
copy from src/starboard/creator/ci20/atomic_public.h
copy to src/starboard/shared/stub/decode_target_create_blitter.cc
index 3b48d2e..019658d 100644
--- a/src/starboard/creator/ci20/atomic_public.h
+++ b/src/starboard/shared/stub/decode_target_create_blitter.cc
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#include "starboard/decode_target.h"
-#include "starboard/linux/shared/atomic_public.h"
-
-#endif // STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+SbDecodeTarget SbDecodeTargetCreate(SbDecodeTargetFormat /*format*/,
+ SbBlitterSurface* /*planes*/) {
+ return kSbDecodeTargetInvalid;
+}
diff --git a/src/starboard/creator/ci20/thread_types_public.h b/src/starboard/shared/stub/decode_target_create_egl.cc
similarity index 65%
copy from src/starboard/creator/ci20/thread_types_public.h
copy to src/starboard/shared/stub/decode_target_create_egl.cc
index ec9eedf..d43b91b 100644
--- a/src/starboard/creator/ci20/thread_types_public.h
+++ b/src/starboard/shared/stub/decode_target_create_egl.cc
@@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#include "starboard/decode_target.h"
-#include "starboard/linux/shared/thread_types_public.h"
-
-#endif // STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+SbDecodeTarget SbDecodeTargetCreate(EGLDisplay /*display*/,
+ EGLContext /*context*/,
+ SbDecodeTargetFormat /*format*/,
+ GLuint* /*planes*/) {
+ return kSbDecodeTargetInvalid;
+}
diff --git a/src/starboard/creator/ci20/atomic_public.h b/src/starboard/shared/stub/decode_target_destroy.cc
similarity index 74%
copy from src/starboard/creator/ci20/atomic_public.h
copy to src/starboard/shared/stub/decode_target_destroy.cc
index 3b48d2e..6762123 100644
--- a/src/starboard/creator/ci20/atomic_public.h
+++ b/src/starboard/shared/stub/decode_target_destroy.cc
@@ -12,9 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#include "starboard/decode_target.h"
-#include "starboard/linux/shared/atomic_public.h"
-
-#endif // STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+void SbDecodeTargetDestroy(SbDecodeTarget /*decode_target*/) {}
diff --git a/src/starboard/creator/ci20/atomic_public.h b/src/starboard/shared/stub/decode_target_get_format.cc
similarity index 74%
copy from src/starboard/creator/ci20/atomic_public.h
copy to src/starboard/shared/stub/decode_target_get_format.cc
index 3b48d2e..e8f9ea7 100644
--- a/src/starboard/creator/ci20/atomic_public.h
+++ b/src/starboard/shared/stub/decode_target_get_format.cc
@@ -12,9 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#include "starboard/decode_target.h"
-#include "starboard/linux/shared/atomic_public.h"
-
-#endif // STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+SbDecodeTargetFormat SbDecodeTargetGetFormat(SbDecodeTarget /*decode_target*/) {
+ return kSbDecodeTargetFormatInvalid;
+}
diff --git a/src/starboard/creator/ci20/thread_types_public.h b/src/starboard/shared/stub/decode_target_get_plane_blitter.cc
similarity index 72%
copy from src/starboard/creator/ci20/thread_types_public.h
copy to src/starboard/shared/stub/decode_target_get_plane_blitter.cc
index ec9eedf..7b3a7f8 100644
--- a/src/starboard/creator/ci20/thread_types_public.h
+++ b/src/starboard/shared/stub/decode_target_get_plane_blitter.cc
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+#include "starboard/decode_target.h"
-#include "starboard/linux/shared/thread_types_public.h"
-
-#endif // STARBOARD_CREATOR_CI20_THREAD_TYPES_PUBLIC_H_
+SbBlitterSurface SbDecodeTargetGetPlane(SbDecodeTarget /*decode_target*/,
+ SbDecodeTargetPlane /*plane*/) {
+ return kSbBlitterInvalidSurface;
+}
diff --git a/src/starboard/creator/ci20/atomic_public.h b/src/starboard/shared/stub/decode_target_get_plane_egl.cc
similarity index 74%
copy from src/starboard/creator/ci20/atomic_public.h
copy to src/starboard/shared/stub/decode_target_get_plane_egl.cc
index 3b48d2e..655ac40 100644
--- a/src/starboard/creator/ci20/atomic_public.h
+++ b/src/starboard/shared/stub/decode_target_get_plane_egl.cc
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
-#define STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+#include "starboard/decode_target.h"
-#include "starboard/linux/shared/atomic_public.h"
-
-#endif // STARBOARD_CREATOR_CI20_ATOMIC_PUBLIC_H_
+GLuint SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
+ SbDecodeTargetPlane plane) {
+ return 0;
+}
diff --git a/src/starboard/starboard.gyp b/src/starboard/starboard.gyp
index 5677568..1084b16 100644
--- a/src/starboard/starboard.gyp
+++ b/src/starboard/starboard.gyp
@@ -29,6 +29,7 @@
'character.h',
'condition_variable.h',
'configuration.h',
+ 'decode_target.h',
'directory.h',
'double.h',
'drm.h',
@@ -60,26 +61,13 @@
'window.h',
],
'dependencies': [
+ '<(DEPTH)/<(starboard_path)/starboard_platform.gyp:starboard_platform',
'common/common.gyp:common',
],
+ 'export_dependent_settings': [
+ '<(DEPTH)/<(starboard_path)/starboard_platform.gyp:starboard_platform',
+ ],
'conditions': [
- ['starboard_path == ""', {
- # TODO: Make starboard_path required. This legacy condition is only
- # here to support semi-starboard platforms while they still exist.
- 'dependencies': [
- '<(DEPTH)/starboard/<(target_arch)/starboard_platform.gyp:starboard_platform',
- ],
- 'export_dependent_settings': [
- '<(DEPTH)/starboard/<(target_arch)/starboard_platform.gyp:starboard_platform',
- ],
- }, {
- 'dependencies': [
- '<(DEPTH)/<(starboard_path)/starboard_platform.gyp:starboard_platform',
- ],
- 'export_dependent_settings': [
- '<(DEPTH)/<(starboard_path)/starboard_platform.gyp:starboard_platform',
- ],
- }],
['final_executable_type=="shared_library"', {
'all_dependent_settings': {
'target_conditions': [
diff --git a/src/starboard/starboard_all.gyp b/src/starboard/starboard_all.gyp
index b97230b..1eb3b02 100644
--- a/src/starboard/starboard_all.gyp
+++ b/src/starboard/starboard_all.gyp
@@ -16,6 +16,9 @@
# Starboard project.
{
+ 'variables': {
+ 'has_platform_tests%' : '<!(python -c "import os.path; print os.path.isfile(\'<(starboard_path)/starboard_platform_tests.gyp\') & 1 | 0")',
+ },
'targets': [
{
# Note that this target must be in a separate GYP file from starboard.gyp,
@@ -31,6 +34,13 @@
'<(DEPTH)/starboard/nplb/nplb.gyp:*',
'<(DEPTH)/starboard/starboard.gyp:*',
],
+ 'conditions': [
+ ['has_platform_tests==1', {
+ 'dependencies': [
+ '<(DEPTH)/<(starboard_path)/starboard_platform_tests.gyp:starboard_platform_tests',
+ ],
+ }],
+ ],
},
],
}
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h
index 90b83b7..75b5256 100644
--- a/src/starboard/stub/configuration_public.h
+++ b/src/starboard/stub/configuration_public.h
@@ -22,6 +22,9 @@
#ifndef STARBOARD_STUB_CONFIGURATION_PUBLIC_H_
#define STARBOARD_STUB_CONFIGURATION_PUBLIC_H_
+// The API version implemented by this platform.
+#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
// --- Architecture Configuration --------------------------------------------
// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
@@ -93,9 +96,6 @@
// on the specifically pinned core.
#define SB_HAS_CROSS_CORE_SCHEDULER 1
-// The API version implemented by this platform.
-#define SB_API_VERSION 2
-
// --- System Header Configuration -------------------------------------------
// Any system headers listed here that are not provided by the platform will be
diff --git a/src/starboard/time_zone.h b/src/starboard/time_zone.h
index ce67d09..25ac711 100644
--- a/src/starboard/time_zone.h
+++ b/src/starboard/time_zone.h
@@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Access to the system time zone information.
+// Module Overview: Starboard Time Zone module
+//
+// Provides access to the system time zone information.
#ifndef STARBOARD_TIME_ZONE_H_
#define STARBOARD_TIME_ZONE_H_
@@ -24,7 +26,7 @@
extern "C" {
#endif
-// The number of minutes west of the Greenwich Prime Meridian, NOT including any
+// The number of minutes west of the Greenwich Prime Meridian, NOT including
// Daylight Savings Time adjustments.
//
// For example: PST/PDT is 480 minutes (28800 seconds, 8 hours).
@@ -34,7 +36,7 @@
SB_EXPORT SbTimeZone SbTimeZoneGetCurrent();
// Gets the three-letter code of the current timezone in standard time,
-// regardless of current DST status. (e.g. "PST")
+// regardless of current Daylight Savings Time status. (e.g. "PST")
SB_EXPORT const char* SbTimeZoneGetName();
// Gets the three-letter code of the current timezone in Daylight Savings Time,
diff --git a/src/third_party/libevent/compat/sys/_libevent_time.h b/src/third_party/libevent/compat/sys/_libevent_time.h
index 6dec7d7..602d8b8 100644
--- a/src/third_party/libevent/compat/sys/_libevent_time.h
+++ b/src/third_party/libevent/compat/sys/_libevent_time.h
@@ -37,7 +37,6 @@
#ifndef STARBOARD
#include <sys/types.h>
-#endif
/*
* Structure returned by gettimeofday(2) system call,
@@ -55,6 +54,7 @@
time_t tv_sec; /* seconds */
long tv_nsec; /* and nanoseconds */
};
+#endif
#define TIMEVAL_TO_TIMESPEC(tv, ts) { \
(ts)->tv_sec = (tv)->tv_sec; \
diff --git a/src/third_party/libevent/event.c b/src/third_party/libevent/event.c
index 8564bed..aeda1c0 100644
--- a/src/third_party/libevent/event.c
+++ b/src/third_party/libevent/event.c
@@ -910,7 +910,7 @@
pev = base->timeheap.p;
size = base->timeheap.n;
for (; size-- > 0; ++pev) {
- struct timeval *ev_tv = &(**pev).ev_timeout;
+ struct evtimeval *ev_tv = &(**pev).ev_timeout;
evutil_timersub(ev_tv, &off, ev_tv);
}
/* Now remember what the new time turned out to be. */
diff --git a/src/third_party/libevent/event.h b/src/third_party/libevent/event.h
index 2ab00d1..0c29651 100644
--- a/src/third_party/libevent/event.h
+++ b/src/third_party/libevent/event.h
@@ -202,6 +202,20 @@
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10 /* Persistant event */
+#ifdef STARBOARD
+struct evtimeval {
+#if SB_IS(64_BIT)
+ int64_t tv_sec; /* seconds */
+ int64_t tv_usec; /* and microseconds */
+#else
+ int32_t tv_sec; /* seconds */
+ int32_t tv_usec; /* and microseconds */
+#endif
+};
+#else
+typedef struct timeval evtimeval;
+#endif
+
/* Fix so that ppl dont have to run with <sys/queue.h> */
#ifndef TAILQ_ENTRY
#define _EVENT_DEFINED_TQENTRY
@@ -227,7 +241,7 @@
short ev_ncalls;
short *ev_pncalls; /* Allows deletes in callback */
- struct timeval ev_timeout;
+ struct evtimeval ev_timeout;
int ev_pri; /* smaller numbers are higher priority */
diff --git a/src/third_party/libjpeg/jmemnobs.c b/src/third_party/libjpeg/jmemnobs.c
index a360de8..935ef6e 100644
--- a/src/third_party/libjpeg/jmemnobs.c
+++ b/src/third_party/libjpeg/jmemnobs.c
@@ -23,7 +23,7 @@
#if defined(NEED_STARBOARD_MEMORY)
#include "starboard/memory.h"
#define malloc SbMemoryAllocate
-#define free SbMemoryFree
+#define free SbMemoryDeallocate
#elif !defined(HAVE_STDLIB_H) /* <stdlib.h> should declare malloc(),free() */
extern void * malloc JPP((size_t size));
extern void free JPP((void *ptr));
diff --git a/src/third_party/libpng/pngmem.c b/src/third_party/libpng/pngmem.c
index d19179d..1b4b88f 100644
--- a/src/third_party/libpng/pngmem.c
+++ b/src/third_party/libpng/pngmem.c
@@ -24,7 +24,7 @@
#if defined(STARBOARD)
# include "starboard/memory.h"
# define malloc SbMemoryAllocate
-# define free SbMemoryFree
+# define free SbMemoryDeallocate
#endif
#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
diff --git a/src/third_party/openssl/openssl/crypto/LPdir_starboard.c b/src/third_party/openssl/openssl/crypto/LPdir_starboard.c
index f663a95..5daa624 100644
--- a/src/third_party/openssl/openssl/crypto/LPdir_starboard.c
+++ b/src/third_party/openssl/openssl/crypto/LPdir_starboard.c
@@ -52,7 +52,7 @@
(*ctx)->dir = SbDirectoryOpen(directory, NULL);
if (!SbDirectoryIsValid((*ctx)->dir)) {
- SbMemoryFree(*ctx);
+ SbMemoryDeallocate(*ctx);
*ctx = NULL;
return NULL;
}
@@ -70,7 +70,7 @@
{
if (ctx != NULL && *ctx != NULL) {
bool result = SbDirectoryClose((*ctx)->dir);
- SbMemoryFree(*ctx);
+ SbMemoryDeallocate(*ctx);
return (result ? 1 : 0);
}
return 0;
diff --git a/src/third_party/protobuf/src/google/protobuf/io/coded_stream.h b/src/third_party/protobuf/src/google/protobuf/io/coded_stream.h
index 9f2489a..d87a0a7 100644
--- a/src/third_party/protobuf/src/google/protobuf/io/coded_stream.h
+++ b/src/third_party/protobuf/src/google/protobuf/io/coded_stream.h
@@ -110,6 +110,13 @@
#define GOOGLE_PROTOBUF_IO_CODED_STREAM_H__
#include <string>
+#if defined(STARBOARD)
+#include "starboard/configuration.h"
+#if SB_IS(LITTLE_ENDIAN) && \
+ !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+#define PROTOBUF_LITTLE_ENDIAN 1
+#endif
+#else
#ifdef _MSC_VER
#if defined(_M_IX86) && \
!defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
@@ -127,6 +134,7 @@
#define PROTOBUF_LITTLE_ENDIAN 1
#endif
#endif
+#endif // defined(STARBOARD)
#include <google/protobuf/stubs/common.h>
#include "starboard/client_porting/poem/string_poem.h"
diff --git a/src/third_party/protobuf/src/google/protobuf/stubs/atomicops.h b/src/third_party/protobuf/src/google/protobuf/stubs/atomicops.h
index 3ace767..dcec550 100644
--- a/src/third_party/protobuf/src/google/protobuf/stubs/atomicops.h
+++ b/src/third_party/protobuf/src/google/protobuf/stubs/atomicops.h
@@ -56,12 +56,22 @@
// Don't include this file for people not concerned about thread safety.
#ifndef GOOGLE_PROTOBUF_NO_THREADSAFETY
+#if defined(STARBOARD)
+#include "starboard/atomic.h"
+#endif // defined(STARBOARD)
+
#include <google/protobuf/stubs/platform_macros.h>
namespace google {
namespace protobuf {
namespace internal {
+#if defined(STARBOARD)
+typedef SbAtomic32 Atomic32;
+#if defined(GOOGLE_PROTOBUF_ARCH_64_BIT)
+typedef SbAtomic64 Atomic64;
+#endif // defined(GOOGLE_PROTOBUF_ARCH_64_BIT)
+#else // defined(STARBOARD)
typedef int32 Atomic32;
#ifdef GOOGLE_PROTOBUF_ARCH_64_BIT
// We need to be able to go between Atomic64 and AtomicWord implicitly. This
@@ -74,6 +84,7 @@
typedef intptr_t Atomic64;
#endif
#endif
+#endif // defined(STARBOARD)
// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or
// Atomic64 routines below, depending on your architecture.
@@ -160,6 +171,11 @@
#define GOOGLE_PROTOBUF_ATOMICOPS_ERROR \
#error "Atomic operations are not supported on your platform"
+// Starboard.
+#if defined(STARBOARD)
+#include "third_party/protobuf/src/google/protobuf/stubs/atomicops_internals_starboard.h"
+#else
+
// MSVC.
#if defined(_MSC_VER)
#if defined(GOOGLE_PROTOBUF_ARCH_IA32) || defined(GOOGLE_PROTOBUF_ARCH_X64)
@@ -195,6 +211,8 @@
GOOGLE_PROTOBUF_ATOMICOPS_ERROR
#endif
+#endif // defined(STARBOARD)
+
// On some platforms we need additional declarations to make AtomicWord
// compatible with our other Atomic* types.
#if defined(GOOGLE_PROTOBUF_OS_APPLE)
diff --git a/src/third_party/protobuf/src/google/protobuf/stubs/atomicops_internals_starboard.h b/src/third_party/protobuf/src/google/protobuf/stubs/atomicops_internals_starboard.h
new file mode 100644
index 0000000..5d87cdf
--- /dev/null
+++ b/src/third_party/protobuf/src/google/protobuf/stubs/atomicops_internals_starboard.h
@@ -0,0 +1,153 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file is an internal atomic implementation, use base/atomicops.h instead.
+
+// This is the Starboard implementation, which defers all specific
+// implementation decisions for atomic operations to the Starboard port.
+
+#ifndef GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_STARBOARD_H_
+#define GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_STARBOARD_H_
+
+#include "starboard/atomic.h"
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return SbAtomicNoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ return SbAtomicNoBarrier_Exchange(ptr, new_value);
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return SbAtomicNoBarrier_Increment(ptr, increment);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return SbAtomicBarrier_Increment(ptr, increment);
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return SbAtomicAcquire_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return SbAtomicRelease_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+ SbAtomicNoBarrier_Store(ptr, value);
+}
+
+inline void MemoryBarrier() {
+ SbAtomicMemoryBarrier();
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ SbAtomicAcquire_Store(ptr, value);
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+ SbAtomicRelease_Store(ptr, value);
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+ return SbAtomicNoBarrier_Load(ptr);
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+ return SbAtomicAcquire_Load(ptr);
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ return SbAtomicRelease_Load(ptr);
+}
+
+#if SB_IS(64_BIT)
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return SbAtomicNoBarrier_CompareAndSwap64(ptr, old_value, new_value);
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+ Atomic64 new_value) {
+ return SbAtomicNoBarrier_Exchange64(ptr, new_value);
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ return SbAtomicNoBarrier_Increment64(ptr, increment);
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ return SbAtomicBarrier_Increment64(ptr, increment);
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return SbAtomicAcquire_CompareAndSwap64(ptr, old_value, new_value);
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return SbAtomicRelease_CompareAndSwap64(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+ SbAtomicNoBarrier_Store64(ptr, value);
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+ SbAtomicAcquire_Store64(ptr, value);
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+ SbAtomicRelease_Store64(ptr, value);
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+ return SbAtomicNoBarrier_Load64(ptr);
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+ return SbAtomicAcquire_Load64(ptr);
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+ return SbAtomicRelease_Load64(ptr);
+}
+#endif // SB_IS(64_BIT)
+
+} // namespace internal
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_STARBOARD_H_
diff --git a/src/third_party/protobuf/src/google/protobuf/stubs/once.cc b/src/third_party/protobuf/src/google/protobuf/stubs/once.cc
index 1e24b85..a58085b 100644
--- a/src/third_party/protobuf/src/google/protobuf/stubs/once.cc
+++ b/src/third_party/protobuf/src/google/protobuf/stubs/once.cc
@@ -39,11 +39,13 @@
#ifndef GOOGLE_PROTOBUF_NO_THREAD_SAFETY
-#ifdef _WIN32
+#if defined(STARBOARD)
+#include "starboard/thread.h"
+#elif defined(_WIN32)
#include <windows.h>
#else
#include <sched.h>
-#endif
+#endif // defined(STARBOARD)
#include <google/protobuf/stubs/atomicops.h>
@@ -53,7 +55,9 @@
namespace {
void SchedYield() {
-#ifdef _WIN32
+#if defined(STARBOARD)
+ SbThreadYield();
+#elif defined(_WIN32)
Sleep(0);
#else // POSIX
sched_yield();
diff --git a/src/third_party/protobuf/src/google/protobuf/stubs/platform_macros.h b/src/third_party/protobuf/src/google/protobuf/stubs/platform_macros.h
index 495ed19..5ebc9e1 100644
--- a/src/third_party/protobuf/src/google/protobuf/stubs/platform_macros.h
+++ b/src/third_party/protobuf/src/google/protobuf/stubs/platform_macros.h
@@ -37,6 +37,14 @@
// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
// http://www.agner.org/optimize/calling_conventions.pdf
// or with gcc, run: "echo | gcc -E -dM -"
+#if defined(STARBOARD)
+#include "starboard/configuration.h"
+#if SB_IS(32_BIT)
+#define GOOGLE_PROTOBUF_ARCH_32_BIT 1
+#elif SB_IS(64_BIT)
+#define GOOGLE_PROTOBUF_ARCH_64_BIT 1
+#endif // SB_IS(32_BIT)
+#else // defined(STARBOARD)
#if defined(_M_X64) || defined(__x86_64__)
#define GOOGLE_PROTOBUF_ARCH_X64 1
#define GOOGLE_PROTOBUF_ARCH_64_BIT 1
@@ -57,6 +65,7 @@
#else
#error Host architecture was not detected as supported by protobuf
#endif
+#endif // defined(STARBOARD)
#if defined(__APPLE__)
#define GOOGLE_PROTOBUF_OS_APPLE
diff --git a/src/third_party/sqlite/amalgamation/sqlite3.c b/src/third_party/sqlite/amalgamation/sqlite3.c
index 25aa93a..5a56cc5 100644
--- a/src/third_party/sqlite/amalgamation/sqlite3.c
+++ b/src/third_party/sqlite/amalgamation/sqlite3.c
@@ -14473,7 +14473,7 @@
#if defined(STARBOARD)
#include "starboard/memory.h"
#define malloc SbMemoryAllocate
-#define free SbMemoryFree
+#define free SbMemoryDeallocate
#define realloc SbMemoryReallocate
#endif
diff --git a/src/third_party/zlib/zutil.c b/src/third_party/zlib/zutil.c
index bf2e3cc..fd3a67b 100644
--- a/src/third_party/zlib/zutil.c
+++ b/src/third_party/zlib/zutil.c
@@ -301,7 +301,7 @@
void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
{
SB_UNREFERENCED_PARAMETER(opaque);
- SbMemoryFree(ptr);
+ SbMemoryDeallocate(ptr);
}
#endif /* STARBOARD */