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 */