Import Cobalt 10.49488
diff --git a/src/base/base_paths_starboard.cc b/src/base/base_paths_starboard.cc
index 15e7b20..6021b36 100644
--- a/src/base/base_paths_starboard.cc
+++ b/src/base/base_paths_starboard.cc
@@ -22,7 +22,7 @@
// This is where we can control the path for placement of a lot of file
// resources for cobalt.
bool PathProviderStarboard(int key, FilePath *result) {
- char path[SB_FILE_MAX_PATH];
+ char path[SB_FILE_MAX_PATH] = {0};
switch (key) {
case base::FILE_EXE: {
bool success = SbSystemGetPath(kSbSystemPathExecutableFile, path,
diff --git a/src/base/debug/stack_trace_starboard.cc b/src/base/debug/stack_trace_starboard.cc
index 163105c..2569e4d 100644
--- a/src/base/debug/stack_trace_starboard.cc
+++ b/src/base/debug/stack_trace_starboard.cc
@@ -14,6 +14,7 @@
#include "base/debug/stack_trace.h"
+#include <algorithm>
#include <ostream>
#include "base/basictypes.h"
diff --git a/src/base/file_path.cc b/src/base/file_path.cc
index acada54..4715417 100644
--- a/src/base/file_path.cc
+++ b/src/base/file_path.cc
@@ -1214,7 +1214,7 @@
// TODO(rolandsteiner) check if this is sufficient/correct.
int FilePath::CompareIgnoreCase(const StringType& string1,
const StringType& string2) {
- int comparison = strcasecmp(string1.c_str(), string2.c_str());
+ int comparison = base::strcasecmp(string1.c_str(), string2.c_str());
if (comparison < 0)
return -1;
if (comparison > 0)
diff --git a/src/base/memory/aligned_memory.cc b/src/base/memory/aligned_memory.cc
index 6b0ed98..cad5f50 100644
--- a/src/base/memory/aligned_memory.cc
+++ b/src/base/memory/aligned_memory.cc
@@ -5,6 +5,7 @@
#include "base/memory/aligned_memory.h"
#include "base/logging.h"
+#include "starboard/memory.h"
#if defined(OS_ANDROID) || defined(OS_NACL) || defined(__LB_SHELL__)
#include <malloc.h>
diff --git a/src/base/message_loop.cc b/src/base/message_loop.cc
index 83d2184..1d07390 100644
--- a/src/base/message_loop.cc
+++ b/src/base/message_loop.cc
@@ -501,6 +501,20 @@
FOR_EACH_OBSERVER(TaskObserver, task_observers_,
WillProcessTask(pending_task.time_posted));
+
+#ifdef STARBOARD_ALLOWS_MEMORY_TRACKING
+ // To assist in tracking memory for async jobs the information on where
+ // the async job came from is used.
+ const char* file_name = pending_task.posted_from.file_name();
+ const int line_num = pending_task.posted_from.line_number();
+ const char* function_name = pending_task.posted_from.function_name();
+
+ // The impl versions of these macros are not compiled out. Therefore the
+ // entire statement is wrapped up in an #ifdef.
+ TRACK_MEMORY_STATIC_NOT_CACHED_IMPL_2("MessageLoop", file_name, line_num,
+ function_name);
+#endif
+
pending_task.task.Run();
FOR_EACH_OBSERVER(TaskObserver, task_observers_,
DidProcessTask(pending_task.time_posted));
@@ -676,7 +690,7 @@
}
bool MessageLoop::DoWork() {
- TRACK_MEMORY_SCOPE("TaskProcessor");
+ TRACK_MEMORY_SCOPE("MessageLoop");
if (!nestable_tasks_allowed_) {
// Task can't be executed right now.
return false;
diff --git a/src/base/message_pump_default.cc b/src/base/message_pump_default.cc
index ba6416e..812ea08 100644
--- a/src/base/message_pump_default.cc
+++ b/src/base/message_pump_default.cc
@@ -20,7 +20,7 @@
}
void MessagePumpDefault::Run(Delegate* delegate) {
- TRACK_MEMORY_SCOPE("TaskProcessor");
+ TRACK_MEMORY_SCOPE("MessageLoop");
DCHECK(keep_running_) << "Quit must have been called outside of Run!";
for (;;) {
diff --git a/src/base/message_pump_io_starboard.cc b/src/base/message_pump_io_starboard.cc
index e08ddb8..2ef146d 100644
--- a/src/base/message_pump_io_starboard.cc
+++ b/src/base/message_pump_io_starboard.cc
@@ -165,7 +165,7 @@
// Reentrant!
void MessagePumpIOStarboard::Run(Delegate* delegate) {
- TRACK_MEMORY_SCOPE("TaskProcessor");
+ TRACK_MEMORY_SCOPE("MessageLoop");
DCHECK(keep_running_) << "Quit must have been called outside of Run!";
AutoReset<bool> auto_reset_in_run(&in_run_, true);
diff --git a/src/base/message_pump_ui_starboard.cc b/src/base/message_pump_ui_starboard.cc
index a99be50..b3c696f 100644
--- a/src/base/message_pump_ui_starboard.cc
+++ b/src/base/message_pump_ui_starboard.cc
@@ -105,7 +105,7 @@
void MessagePumpUIStarboard::ScheduleDelayedWork(
const base::TimeTicks& delayed_work_time) {
- TRACK_MEMORY_SCOPE("TaskProcessor");
+ TRACK_MEMORY_SCOPE("MessageLoop");
base::TimeDelta delay;
if (!delayed_work_time.is_null()) {
delay = delayed_work_time - base::TimeTicks::Now();
diff --git a/src/base/process_util.h b/src/base/process_util.h
index 38fbe1f..8e21ec5 100644
--- a/src/base/process_util.h
+++ b/src/base/process_util.h
@@ -237,6 +237,16 @@
// Options for launching a subprocess that are passed to LaunchProcess().
// The default constructor constructs the object with default options.
+#if defined(OS_STARBOARD)
+struct LaunchOptions {
+ LaunchOptions()
+ : wait(false),
+ fds_to_remap(NULL),
+ maximize_rlimits(NULL),
+ new_process_group(false) {
+ environ = NULL;
+ }
+#else
struct LaunchOptions {
LaunchOptions() : wait(false),
#if defined(OS_WIN)
@@ -257,6 +267,7 @@
#endif // defined(OS_MACOSX)
#endif // !defined(OS_WIN)
{}
+#endif // defined(OS_STARBOARD)
// If true, wait for the process to complete.
bool wait;
diff --git a/src/base/sys_byteorder.h b/src/base/sys_byteorder.h
index 5548347..b458974 100644
--- a/src/base/sys_byteorder.h
+++ b/src/base/sys_byteorder.h
@@ -14,7 +14,9 @@
#include "base/basictypes.h"
#include "build/build_config.h"
-#if defined(OS_WIN)
+#if defined(OS_STARBOARD)
+#include "starboard/client_porting/poem/inet_poem.h"
+#elif defined(OS_WIN)
#include <winsock2.h>
#else
#include <arpa/inet.h>
diff --git a/src/base/test/test_suite.cc b/src/base/test/test_suite.cc
index 6e0444d..40d4071 100644
--- a/src/base/test/test_suite.cc
+++ b/src/base/test/test_suite.cc
@@ -32,6 +32,10 @@
#endif // OS_IOS
#endif // OS_MACOSX
+#if defined(OS_STARBOARD)
+#include "starboard/system.h"
+#endif
+
#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
#include "base/test/test_support_android.h"
#endif
@@ -99,6 +103,11 @@
#if defined(OS_WIN)
testing::GTEST_FLAG(catch_exceptions) = false;
#endif
+
+#if defined(OS_STARBOARD)
+ testing::GTEST_FLAG(break_on_failure) = SbSystemIsDebuggerAttached();
+#endif
+
base::EnableTerminationOnHeapCorruption();
initialized_command_line_ = CommandLine::Init(argc, argv);
testing::InitGoogleTest(&argc, argv);
diff --git a/src/base/timer.cc b/src/base/timer.cc
index 7c9f26c..db670f1 100644
--- a/src/base/timer.cc
+++ b/src/base/timer.cc
@@ -32,7 +32,7 @@
}
void Run() {
- TRACK_MEMORY_SCOPE("TaskProcessor");
+ TRACK_MEMORY_SCOPE("MessageLoop");
// timer_ is NULL if we were abandoned.
if (!timer_)
return;
@@ -144,7 +144,7 @@
// PostNewScheduledTask() to ensure that the default behavior of Timer is
// exactly the same as before.
void Timer::PostNewScheduledTask(TimeDelta delay) {
- TRACK_MEMORY_SCOPE("TaskProcessor");
+ TRACK_MEMORY_SCOPE("MessageLoop");
DCHECK(scheduled_task_ == NULL);
is_running_ = true;
scheduled_task_ = new BaseTimerTaskInternal(this);
@@ -160,7 +160,7 @@
Timer::NewScheduledTaskInfo Timer::SetupNewScheduledTask(
TimeDelta expected_delay) {
- TRACK_MEMORY_SCOPE("TaskProcessor");
+ TRACK_MEMORY_SCOPE("MessageLoop");
DCHECK(scheduled_task_ == NULL);
DCHECK(is_task_run_before_scheduling_next_);
DCHECK(thread_id_);
@@ -180,7 +180,7 @@
void Timer::PostNewScheduledTask(NewScheduledTaskInfo task_info,
TimeDelta delay) {
- TRACK_MEMORY_SCOPE("TaskProcessor");
+ TRACK_MEMORY_SCOPE("MessageLoop");
// Some task runners expect a non-zero delay for PostDelayedTask.
if (delay.ToInternalValue() == 0) {
ThreadTaskRunnerHandle::Get()->PostTask(task_info.posted_from,
@@ -201,7 +201,7 @@
}
void Timer::RunScheduledTask() {
- TRACK_MEMORY_SCOPE("TaskProcessor");
+ TRACK_MEMORY_SCOPE("MessageLoop");
// Task may have been disabled.
if (!is_running_)
return;
diff --git a/src/build/common.gypi b/src/build/common.gypi
index a68007b..0164104 100644
--- a/src/build/common.gypi
+++ b/src/build/common.gypi
@@ -965,7 +965,7 @@
'sas_dll_exists': '<!(python <(DEPTH)/build/dir_exists.py <(sas_dll_path))',
'wix_exists': '<!(python <(DEPTH)/build/dir_exists.py <(wix_path))',
- 'windows_sdk_default_path': '<(DEPTH)/third_party/platformsdk_win8/files',
+ 'windows_sdk_path%': 'C:/Program Files (x86)/Windows Kits/10',
'directx_sdk_default_path': '<(DEPTH)/third_party/directxsdk/files',
# Whether we are using the rlz library or not. Platforms like Android send
@@ -973,11 +973,6 @@
'enable_rlz%': 0,
'conditions': [
- ['OS=="win" and "<!(python <(DEPTH)/build/dir_exists.py <(windows_sdk_default_path))"=="True"', {
- 'windows_sdk_path%': '<(windows_sdk_default_path)',
- }, {
- 'windows_sdk_path%': 'C:/Program Files (x86)/Windows Kits/8.0',
- }],
['OS=="win" and "<!(python <(DEPTH)/build/dir_exists.py <(directx_sdk_default_path))"=="True"', {
'directx_sdk_path%': '<(directx_sdk_default_path)',
}, {
@@ -2007,7 +2002,7 @@
],
}],
# TODO(darin): Unfortunately, some third_party code depends on base.
- [ '(OS=="win" or target_arch=="xb1") and component=="shared_library"', {
+ [ 'target_os=="win" and component=="shared_library"', {
'msvs_disabled_warnings': [
4251, # class 'std::xx' needs to have dll-interface.
],
diff --git a/src/cobalt/accessibility/internal/text_alternative_helper.cc b/src/cobalt/accessibility/internal/text_alternative_helper.cc
index c69cabd..8c803da 100644
--- a/src/cobalt/accessibility/internal/text_alternative_helper.cc
+++ b/src/cobalt/accessibility/internal/text_alternative_helper.cc
@@ -247,7 +247,8 @@
// https://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
// The only element type that supports the "alt" attribute that Cobalt
// implements is the <img> element.
- if (element->tag_name() == dom::HTMLImageElement::kTagName) {
+ if (element->AsHTMLElement() &&
+ element->AsHTMLElement()->AsHTMLImageElement()) {
base::optional<std::string> alt_attribute =
element->GetAttribute(base::Tokens::alt().c_str());
if (alt_attribute) {
diff --git a/src/cobalt/accessibility/screen_reader.cc b/src/cobalt/accessibility/screen_reader.cc
index 13a554e..54a8d8c 100644
--- a/src/cobalt/accessibility/screen_reader.cc
+++ b/src/cobalt/accessibility/screen_reader.cc
@@ -34,7 +34,7 @@
ScreenReader::ScreenReader(dom::Document* document, TTSEngine* tts_engine,
dom::MutationObserverTaskManager* task_manager)
- : document_(document), tts_engine_(tts_engine) {
+ : enabled_(true), document_(document), tts_engine_(tts_engine) {
document_->AddObserver(this);
live_region_observer_ = new dom::MutationObserver(
base::Bind(&ScreenReader::MutationObserverCallback,
@@ -47,6 +47,10 @@
document_->RemoveObserver(this);
}
+void ScreenReader::set_enabled(bool value) {
+ enabled_ = value;
+}
+
void ScreenReader::OnLoad() {
dom::MutationObserverInit init;
init.set_subtree(true);
@@ -60,7 +64,9 @@
}
void ScreenReader::OnFocusChanged() {
- // TODO: Only utter text if text-to-speech is enabled.
+ if (!enabled_) {
+ return;
+ }
scoped_refptr<dom::Element> element = document_->active_element();
if (element) {
std::string text_alternative = ComputeTextAlternative(element);
@@ -74,6 +80,9 @@
const MutationRecordSequence& sequence,
const scoped_refptr<dom::MutationObserver>& observer) {
// TODO: Only check live regions if text-to-speech is enabled.
+ if (!enabled_) {
+ return;
+ }
DCHECK_EQ(observer, live_region_observer_);
for (size_t i = 0; i < sequence.size(); ++i) {
scoped_refptr<dom::MutationRecord> record = sequence.at(i);
diff --git a/src/cobalt/accessibility/screen_reader.h b/src/cobalt/accessibility/screen_reader.h
index 7f740ad..846d50f 100644
--- a/src/cobalt/accessibility/screen_reader.h
+++ b/src/cobalt/accessibility/screen_reader.h
@@ -34,6 +34,9 @@
ScreenReader(dom::Document* document, TTSEngine* tts_engine,
dom::MutationObserverTaskManager* task_manager);
+ void set_enabled(bool enabled);
+ bool enabled() const { return enabled_; }
+
protected:
~ScreenReader();
// dom::DocumentObserver overrides.
@@ -49,6 +52,7 @@
const MutationRecordSequence& sequence,
const scoped_refptr<dom::MutationObserver>& observer);
+ bool enabled_;
dom::Document* document_;
TTSEngine* tts_engine_;
scoped_refptr<dom::MutationObserver> live_region_observer_;
diff --git a/src/cobalt/accessibility/screen_reader_tests.cc b/src/cobalt/accessibility/screen_reader_tests.cc
index 12ffb8d..64a0893 100644
--- a/src/cobalt/accessibility/screen_reader_tests.cc
+++ b/src/cobalt/accessibility/screen_reader_tests.cc
@@ -21,6 +21,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
+#include "cobalt/accessibility/screen_reader.h"
#include "cobalt/accessibility/text_alternative.h"
#include "cobalt/accessibility/tts_engine.h"
#include "cobalt/browser/web_module.h"
@@ -36,15 +37,20 @@
namespace {
struct TestInfo {
TestInfo(const std::string& html_file_name,
- const std::string& expected_result)
- : html_file_name(html_file_name), expected_result(expected_result) {}
+ const std::string& expected_result,
+ bool screen_reader_enabled)
+ : html_file_name(html_file_name),
+ expected_result(expected_result),
+ screen_reader_enabled(screen_reader_enabled) {}
std::string html_file_name;
std::string expected_result;
+ bool screen_reader_enabled;
};
// Enumerate the *.html files in the accessibility_test directory and build
// a list of input .html files and expected results.
-std::vector<TestInfo> EnumerateTests(const std::string& subdir) {
+std::vector<TestInfo> EnumerateTests(bool screen_reader_enabled,
+ const std::string& subdir) {
std::vector<TestInfo> infos;
FilePath root_directory;
PathService::Get(base::DIR_SOURCE_ROOT, &root_directory);
@@ -65,7 +71,9 @@
continue;
}
TrimWhitespaceASCII(results, TRIM_ALL, &results);
- infos.push_back(TestInfo(html_file.BaseName().value(), results));
+ infos.push_back(TestInfo(html_file.BaseName().value(),
+ results,
+ screen_reader_enabled));
}
}
return infos;
@@ -89,13 +97,29 @@
DLOG(ERROR) << error;
Quit();
}
- void Quit() { quit_event_.Signal(); }
+ void Quit() {
+ quit_event_.Signal();
+ screen_reader_.reset();
+ }
+
+ scoped_refptr<script::Wrappable> CreateWindowAttribute(
+ const scoped_refptr<dom::Window>& window,
+ dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+ screen_reader_.reset(new accessibility::ScreenReader(
+ window->document(), &tts_engine_, mutation_observer_task_manager));
+ screen_reader_->set_enabled(GetParam().screen_reader_enabled);
+ EXPECT_EQ(GetParam().screen_reader_enabled, screen_reader_->enabled());
+
+ return NULL;
+ }
+
static void OnRenderTreeProducedStub(
const browser::WebModule::LayoutResults&) {}
protected:
MockTTSEngine tts_engine_;
base::WaitableEvent quit_event_;
+ scoped_ptr<accessibility::ScreenReader> screen_reader_;
};
} // namespace
@@ -122,12 +146,13 @@
// Use test runner mode to allow the content itself to dictate when it is
// ready for layout should be performed. See cobalt/dom/test_runner.h.
- browser::WebModule::Options web_module_options(kDefaultViewportSize);
- web_module_options.tts_engine = &tts_engine_;
+ browser::WebModule::Options web_module_options;
+ web_module_options.injected_window_attributes["test"] = base::Bind(
+ &LiveRegionMutationTest::CreateWindowAttribute, base::Unretained(this));
// Set expected result from mutation.
std::string expected_speech = GetParam().expected_result;
- if (expected_speech.empty()) {
+ if (expected_speech.empty() || !GetParam().screen_reader_enabled) {
EXPECT_CALL(tts_engine_, Speak(_)).Times(0);
} else {
EXPECT_CALL(tts_engine_, Speak(expected_speech));
@@ -139,6 +164,7 @@
url, base::Bind(&LiveRegionMutationTest::OnRenderTreeProducedStub),
base::Bind(&LiveRegionMutationTest::OnError, base::Unretained(this)),
base::Bind(&LiveRegionMutationTest::Quit, base::Unretained(this)),
+ base::Closure(), /* window_minimize_callback */
NULL /* media_module */, &network_module, kDefaultViewportSize,
&resource_provider, NULL /* system_window */, kRefreshRate,
web_module_options);
@@ -149,9 +175,14 @@
INSTANTIATE_TEST_CASE_P(
TextAlternativeTest, TextAlternativeTest,
- ::testing::ValuesIn(EnumerateTests("text_alternative")));
+ ::testing::ValuesIn(EnumerateTests(true, "text_alternative")));
-INSTANTIATE_TEST_CASE_P(LiveRegionMutationTest, LiveRegionMutationTest,
- ::testing::ValuesIn(EnumerateTests("live_region")));
+INSTANTIATE_TEST_CASE_P(
+ LiveRegionMutationTestEnabled, LiveRegionMutationTest,
+ ::testing::ValuesIn(EnumerateTests(true, "live_region")));
+
+INSTANTIATE_TEST_CASE_P(
+ LiveRegionMutationTestDisabled, LiveRegionMutationTest,
+ ::testing::ValuesIn(EnumerateTests(false, "live_region")));
} // namespace accessibility
} // namespace cobalt
diff --git a/src/cobalt/audio/audio.gyp b/src/cobalt/audio/audio.gyp
index 23be191..e55e00b 100644
--- a/src/cobalt/audio/audio.gyp
+++ b/src/cobalt/audio/audio.gyp
@@ -47,9 +47,16 @@
],
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
'<(DEPTH)/cobalt/speech/speech.gyp:speech',
],
+ 'export_dependent_settings': [
+ # Additionally, ensure that the include directories for generated
+ # headers are put on the include directories for targets that depend
+ # on this one.
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+ ]
},
{
@@ -63,6 +70,10 @@
'<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
'<(DEPTH)/testing/gmock.gyp:gmock',
'<(DEPTH)/testing/gtest.gyp:gtest',
+
+ # TODO: Remove the dependency below, it works around the fact that
+ # ScriptValueFactory has non-virtual method CreatePromise().
+ '<(DEPTH)/cobalt/script/engine.gyp:engine',
],
},
diff --git a/src/cobalt/audio/audio_node.cc b/src/cobalt/audio/audio_node.cc
index 961b862..1f561ee 100644
--- a/src/cobalt/audio/audio_node.cc
+++ b/src/cobalt/audio/audio_node.cc
@@ -24,8 +24,8 @@
: audio_context_(context),
audio_lock_(context->audio_lock()),
channel_count_(2),
- channel_count_mode_(AudioNode::kMax),
- channel_interpretation_(AudioNode::kSpeakers) {}
+ channel_count_mode_(kAudioNodeChannelCountModeMax),
+ channel_interpretation_(kAudioNodeChannelInterpretationSpeakers) {}
AudioNode::~AudioNode() {
AudioLock::AutoLock lock(audio_lock());
@@ -56,14 +56,14 @@
}
void AudioNode::set_channel_count_mode(
- const ChannelCountMode& channel_count_mode) {
+ const AudioNodeChannelCountMode& channel_count_mode) {
AudioLock::AutoLock lock(audio_lock());
channel_count_mode_ = channel_count_mode;
}
void AudioNode::set_channel_interpretation(
- const ChannelInterpretation& channel_interpretation) {
+ const AudioNodeChannelInterpretation& channel_interpretation) {
AudioLock::AutoLock lock(audio_lock());
channel_interpretation_ = channel_interpretation;
diff --git a/src/cobalt/audio/audio_node.h b/src/cobalt/audio/audio_node.h
index 9b2d3ed..b956d30 100644
--- a/src/cobalt/audio/audio_node.h
+++ b/src/cobalt/audio/audio_node.h
@@ -19,6 +19,8 @@
#include <vector>
#include "cobalt/audio/audio_helpers.h"
+#include "cobalt/audio/audio_node_channel_count_mode.h"
+#include "cobalt/audio/audio_node_channel_interpretation.h"
#include "cobalt/audio/audio_node_input.h"
#include "cobalt/audio/audio_node_output.h"
#include "cobalt/dom/dom_exception.h"
@@ -55,17 +57,6 @@
#endif // defined(COBALT_MEDIA_SOURCE_2016)
public:
- enum ChannelCountMode {
- kMax,
- kClampedMax,
- kExplicit,
- };
-
- enum ChannelInterpretation {
- kSpeakers,
- kDiscrete,
- };
-
explicit AudioNode(AudioContext* context);
// Web API: AudioNode
@@ -97,19 +88,20 @@
// Determines how channels will be counted when up-mixing and down-mixing
// connections to any inputs to the node. This attributes has no effect for
// nodes with no inputs.
- const ChannelCountMode& channel_count_mode() const {
+ const AudioNodeChannelCountMode& channel_count_mode() const {
return channel_count_mode_;
}
- void set_channel_count_mode(const ChannelCountMode& channel_count_mode);
+ void set_channel_count_mode(
+ const AudioNodeChannelCountMode& channel_count_mode);
// Determines how individual channels will be treated when up-mixing and
// down-mixing connections to any inputs to the node. This attribute has no
// effect for nodes with no inputs.
- const ChannelInterpretation& channel_interpretation() const {
+ const AudioNodeChannelInterpretation& channel_interpretation() const {
return channel_interpretation_;
}
void set_channel_interpretation(
- const ChannelInterpretation& channel_interpretation);
+ const AudioNodeChannelInterpretation& channel_interpretation);
// Connects the AudioNode to another AudioNode.
void Connect(const scoped_refptr<AudioNode>& destination, uint32 output,
@@ -156,8 +148,8 @@
// Channel up-mixing and down-mixing rules for all inputs.
uint32 channel_count_;
- ChannelCountMode channel_count_mode_;
- ChannelInterpretation channel_interpretation_;
+ AudioNodeChannelCountMode channel_count_mode_;
+ AudioNodeChannelInterpretation channel_interpretation_;
DISALLOW_COPY_AND_ASSIGN(AudioNode);
};
diff --git a/src/cobalt/audio/audio_node.idl b/src/cobalt/audio/audio_node.idl
index afbb730..ce58946 100644
--- a/src/cobalt/audio/audio_node.idl
+++ b/src/cobalt/audio/audio_node.idl
@@ -14,17 +14,6 @@
// https://www.w3.org/TR/webaudio/#AudioNode-section
-enum ChannelCountMode {
- "max",
- "clamped-max",
- "explicit"
-};
-
-enum ChannelInterpretation {
- "speakers",
- "discrete"
-};
-
interface AudioNode : EventTarget {
[RaisesException] void connect(AudioNode destination,
optional unsigned long output = 0,
@@ -37,6 +26,6 @@
// Channel up-mixing and down-mixing rules for all inputs.
[RaisesException=Setter] attribute unsigned long channelCount;
- attribute ChannelCountMode channelCountMode;
- attribute ChannelInterpretation channelInterpretation;
+ attribute AudioNodeChannelCountMode channelCountMode;
+ attribute AudioNodeChannelInterpretation channelInterpretation;
};
diff --git a/src/cobalt/base/math.cc b/src/cobalt/audio/audio_node_channel_count_mode.idl
similarity index 71%
copy from src/cobalt/base/math.cc
copy to src/cobalt/audio/audio_node_channel_count_mode.idl
index d335495..bb5d8c8 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/audio/audio_node_channel_count_mode.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/webaudio/#AudioNode-section
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum AudioNodeChannelCountMode {
+ "max",
+ "clamped-max",
+ "explicit"
+};
diff --git a/src/cobalt/base/math.cc b/src/cobalt/audio/audio_node_channel_interpretation.idl
similarity index 71%
copy from src/cobalt/base/math.cc
copy to src/cobalt/audio/audio_node_channel_interpretation.idl
index d335495..52e61f4 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/audio/audio_node_channel_interpretation.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/webaudio/#AudioNode-section
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum AudioNodeChannelInterpretation {
+ "speakers",
+ "discrete"
+};
diff --git a/src/cobalt/audio/audio_node_input.cc b/src/cobalt/audio/audio_node_input.cc
index 14bfe33..2e76dcf 100644
--- a/src/cobalt/audio/audio_node_input.cc
+++ b/src/cobalt/audio/audio_node_input.cc
@@ -32,10 +32,11 @@
void MixAudioBufferBasedOnInterpretation(
const float* speaker, const float* discrete,
- const AudioNode::ChannelInterpretation& interpretation,
- ShellAudioBus* source, ShellAudioBus* output_audio_data) {
+ const AudioNodeChannelInterpretation& interpretation, ShellAudioBus* source,
+ ShellAudioBus* output_audio_data) {
const float* kMatrix =
- interpretation == AudioNode::kSpeakers ? speaker : discrete;
+ interpretation == kAudioNodeChannelInterpretationSpeakers ? speaker
+ : discrete;
size_t array_size = source->channels() * output_audio_data->channels();
std::vector<float> matrix(kMatrix, kMatrix + array_size);
output_audio_data->Mix(*source, matrix);
@@ -49,12 +50,12 @@
// "discrete".
// Up down mix equations for mono, stereo, quad, 5.1:
// https://www.w3.org/TR/webaudio/#ChannelLayouts
-void MixAudioBuffer(const AudioNode::ChannelInterpretation& interpretation,
+void MixAudioBuffer(const AudioNodeChannelInterpretation& interpretation,
ShellAudioBus* source, ShellAudioBus* output_audio_data) {
DCHECK_GT(source->channels(), 0u);
DCHECK_GT(output_audio_data->channels(), 0u);
- DCHECK(interpretation == AudioNode::kSpeakers ||
- interpretation == AudioNode::kDiscrete)
+ DCHECK(interpretation == kAudioNodeChannelInterpretationSpeakers ||
+ interpretation == kAudioNodeChannelInterpretationDiscrete)
<< interpretation;
if (output_audio_data->channels() == source->channels()) {
@@ -235,7 +236,7 @@
// TODO: Consider computing computedNumberOfChannels and do up-mix or
// down-mix base on computedNumberOfChannels. The current implementation
// is based on the fact that the channelCountMode is max.
- if (owner_node_->channel_count_mode() != AudioNode::kMax) {
+ if (owner_node_->channel_count_mode() != kAudioNodeChannelCountModeMax) {
DLOG(ERROR) << "Unsupported channel count mode: "
<< owner_node_->channel_count_mode();
return;
diff --git a/src/cobalt/audio/audio_node_input_output_test.cc b/src/cobalt/audio/audio_node_input_output_test.cc
index 50a8bac..8a69ac3 100644
--- a/src/cobalt/audio/audio_node_input_output_test.cc
+++ b/src/cobalt/audio/audio_node_input_output_test.cc
@@ -59,7 +59,7 @@
void FillAudioBusFromOneSource(
size_t num_of_src_channel, size_t num_of_frames,
scoped_array<uint8> src_data,
- const AudioNode::ChannelInterpretation& interpretation,
+ const AudioNodeChannelInterpretation& interpretation,
ShellAudioBus* audio_bus, bool* silence) {
scoped_refptr<AudioBufferSourceNode> source(new AudioBufferSourceNode(NULL));
scoped_refptr<AudioBuffer> buffer(
@@ -81,7 +81,8 @@
size_t num_of_src_channel = 2;
size_t num_of_dest_channel = 2;
size_t num_of_frames = 25;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[50];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -122,7 +123,8 @@
size_t num_of_src_channel = 2;
size_t num_of_dest_channel = 2;
size_t num_of_frames = 25;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[50];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -163,7 +165,8 @@
size_t num_of_src_channel = 1;
size_t num_of_dest_channel = 2;
size_t num_of_frames = 25;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[25];
for (size_t i = 0; i < num_of_frames; ++i) {
@@ -198,7 +201,8 @@
size_t num_of_src_channel = 1;
size_t num_of_dest_channel = 2;
size_t num_of_frames = 25;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[25];
for (size_t i = 0; i < num_of_frames; ++i) {
@@ -233,7 +237,8 @@
size_t num_of_src_channel = 4;
size_t num_of_dest_channel = 2;
size_t num_of_frames = 25;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[100];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -274,7 +279,8 @@
size_t num_of_src_channel = 4;
size_t num_of_dest_channel = 2;
size_t num_of_frames = 25;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[100];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -315,7 +321,8 @@
size_t num_of_src_channel = 6;
size_t num_of_dest_channel = 2;
size_t num_of_frames = 10;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[60];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -356,7 +363,8 @@
size_t num_of_src_channel = 6;
size_t num_of_dest_channel = 2;
size_t num_of_frames = 10;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[60];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -397,7 +405,8 @@
size_t num_of_src_channel = 2;
size_t num_of_dest_channel = 1;
size_t num_of_frames = 25;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[50];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -434,7 +443,8 @@
size_t num_of_src_channel = 2;
size_t num_of_dest_channel = 1;
size_t num_of_frames = 25;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[50];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -471,7 +481,8 @@
size_t num_of_src_channel = 4;
size_t num_of_dest_channel = 1;
size_t num_of_frames = 25;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[100];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -508,7 +519,8 @@
size_t num_of_src_channel = 4;
size_t num_of_dest_channel = 1;
size_t num_of_frames = 25;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[100];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -545,7 +557,8 @@
size_t num_of_src_channel = 6;
size_t num_of_dest_channel = 1;
size_t num_of_frames = 10;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[60];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -582,7 +595,8 @@
size_t num_of_src_channel = 6;
size_t num_of_dest_channel = 1;
size_t num_of_frames = 10;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kDiscrete;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[60];
for (size_t c = 0; c < num_of_src_channel; ++c) {
@@ -618,7 +632,8 @@
TEST(AudioNodeInputOutputTest, MultipleInputNodesLayoutTest) {
size_t num_of_src_channel = 2;
size_t num_of_dest_channel = 2;
- AudioNode::ChannelInterpretation interpretation = AudioNode::kSpeakers;
+ AudioNodeChannelInterpretation interpretation =
+ kAudioNodeChannelInterpretationSpeakers;
size_t num_of_frames_1 = 25;
float src_data_in_float_1[50];
diff --git a/src/cobalt/base/accessibility_settings_changed_event.h b/src/cobalt/base/accessibility_settings_changed_event.h
new file mode 100644
index 0000000..c0380b1
--- /dev/null
+++ b/src/cobalt/base/accessibility_settings_changed_event.h
@@ -0,0 +1,35 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BASE_ACCESSIBILITY_SETTINGS_CHANGED_EVENT_H_
+#define COBALT_BASE_ACCESSIBILITY_SETTINGS_CHANGED_EVENT_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/string_util.h"
+#include "cobalt/base/event.h"
+
+namespace base {
+
+class AccessibilitySettingsChangedEvent : public Event {
+ public:
+ AccessibilitySettingsChangedEvent() {}
+
+ BASE_EVENT_SUBCLASS(AccessibilitySettingsChangedEvent);
+};
+
+} // namespace base
+
+#endif // COBALT_BASE_ACCESSIBILITY_SETTINGS_CHANGED_EVENT_H_
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index d453855..629d727 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -21,6 +21,7 @@
'product_name': 'cobalt_base',
'type': 'static_library',
'sources': [
+ 'accessibility_changed_event.h',
'address_sanitizer.h',
'clock.h',
'cobalt_paths.h',
@@ -47,7 +48,6 @@
'localized_strings.h',
'log_message_handler.cc',
'log_message_handler.h',
- 'math.cc',
'math.h',
'message_queue.h',
'path_provider.cc',
@@ -55,6 +55,7 @@
'poller.h',
'polymorphic_downcast.h',
'polymorphic_equatable.h',
+ 'ref_counted_lock.h',
'source_location.h',
'stop_watch.cc',
'stop_watch.h',
diff --git a/src/cobalt/base/c_val.cc b/src/cobalt/base/c_val.cc
index cc41ab8..f0c9a84 100644
--- a/src/cobalt/base/c_val.cc
+++ b/src/cobalt/base/c_val.cc
@@ -22,7 +22,7 @@
StaticMemorySingletonTraits<CValManager> >::get();
}
-CValManager::CValManager() {
+CValManager::CValManager() : value_lock_refptr_(new RefCountedThreadSafeLock) {
// Allocate these dynamically since CValManager may live across DLL boundaries
// and so this allows its size to be more consistent (and avoids compiler
// warnings on some platforms).
@@ -33,6 +33,15 @@
}
CValManager::~CValManager() {
+ // Lock the value lock prior to notifying the surviving CVals that the manager
+ // has been destroyed. This ensures that they will not attempt to make calls
+ // into the manager during destruction.
+ base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+ for (NameVarMap::iterator iter = registered_vars_->begin();
+ iter != registered_vars_->end(); ++iter) {
+ iter->second->OnManagerDestroyed();
+ }
+
#if defined(ENABLE_DEBUG_C_VAL)
delete on_changed_hook_set_;
#endif // ENABLE_DEBUG_C_VAL
diff --git a/src/cobalt/base/c_val.h b/src/cobalt/base/c_val.h
index 829e3d3..48227bf 100644
--- a/src/cobalt/base/c_val.h
+++ b/src/cobalt/base/c_val.h
@@ -30,6 +30,7 @@
#include "base/optional.h"
#include "base/synchronization/lock.h"
#include "base/time.h"
+#include "cobalt/base/ref_counted_lock.h"
// The CVal system allows you to mark certain variables to be part of the
// CVal system and therefore analyzable and trackable by other systems. All
@@ -139,7 +140,6 @@
} // namespace cval
namespace CValDetail {
-
// Introduce a Traits class so that we can convert from C++ type to
// CValType.
template <typename T>
@@ -524,8 +524,10 @@
// Lock that protects against CVals being registered/deregistered.
base::Lock cvals_lock_;
- // Lock that synchronizes |value_| reads/writes.
- base::Lock values_lock_;
+ // Lock that synchronizes |value_| reads/writes. It exists as a refptr to
+ // ensure that CVals that survive longer than the CValManager can continue
+ // to use it after the CValManager has been destroyed.
+ scoped_refptr<base::RefCountedThreadSafeLock> value_lock_refptr_;
// The actual value registry, mapping CVal name to actual CVal object.
typedef base::hash_map<std::string, const CValDetail::CValBase*> NameVarMap;
@@ -559,9 +561,13 @@
virtual std::string GetValueAsPrettyString() const = 0;
private:
+ virtual void OnManagerDestroyed() const {}
+
std::string name_;
std::string description_;
CValType type_;
+
+ friend CValManager;
};
// This is a wrapper class that marks that we wish to track a value through
@@ -580,6 +586,7 @@
CommonConstructor();
}
virtual ~CValImpl() {
+ base::AutoLock auto_lock(value_lock_refptr_->GetLock());
if (registered_) {
CValManager::GetInstance()->UnregisterCVal(this);
}
@@ -588,27 +595,20 @@
operator T() const { return value(); }
const CValImpl<T>& operator=(const T& rhs) {
- bool value_changed;
- {
- base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
- value_changed = value_ != rhs;
- if (value_changed) {
- value_ = rhs;
- }
- }
-#if defined(ENABLE_DEBUG_C_VAL)
+ base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+ bool value_changed = value_ != rhs;
if (value_changed) {
+ value_ = rhs;
+#if defined(ENABLE_DEBUG_C_VAL)
OnValueChanged();
- }
#endif // ENABLE_DEBUG_C_VAL
+ }
return *this;
}
const CValImpl<T>& operator+=(const T& rhs) {
- {
- base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
- value_ += rhs;
- }
+ base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+ value_ += rhs;
#if defined(ENABLE_DEBUG_C_VAL)
OnValueChanged();
#endif // ENABLE_DEBUG_C_VAL
@@ -616,10 +616,8 @@
}
const CValImpl<T>& operator-=(const T& rhs) {
- {
- base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
- value_ -= rhs;
- }
+ base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+ value_ -= rhs;
#if defined(ENABLE_DEBUG_C_VAL)
OnValueChanged();
#endif // ENABLE_DEBUG_C_VAL
@@ -627,10 +625,8 @@
}
const CValImpl<T>& operator++() {
- {
- base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
- ++value_;
- }
+ base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+ ++value_;
#if defined(ENABLE_DEBUG_C_VAL)
OnValueChanged();
#endif // ENABLE_DEBUG_C_VAL
@@ -638,10 +634,8 @@
}
const CValImpl<T>& operator--() {
- {
- base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
- --value_;
- }
+ base::AutoLock auto_lock(value_lock_refptr_->GetLock());
+ --value_;
#if defined(ENABLE_DEBUG_C_VAL)
OnValueChanged();
#endif // ENABLE_DEBUG_C_VAL
@@ -654,19 +648,19 @@
std::string GetValueAsString() const {
// Can be called to get the value of a CVal without knowing the type first.
- base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
+ base::AutoLock auto_lock(value_lock_refptr_->GetLock());
return ValToString<T>(value_);
}
std::string GetValueAsPrettyString() const {
// Similar to GetValueAsString(), but it will also format the string to
// do things like make very large numbers more readable.
- base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
+ base::AutoLock auto_lock(value_lock_refptr_->GetLock());
return ValToPrettyString<T>(value_);
}
T value() const {
- base::AutoLock auto_lock(CValManager::GetInstance()->values_lock_);
+ base::AutoLock auto_lock(value_lock_refptr_->GetLock());
return value_;
}
@@ -678,21 +672,32 @@
void RegisterWithManager() {
if (!registered_) {
- CValManager::GetInstance()->RegisterCVal(this);
+ CValManager* manager = CValManager::GetInstance();
+ manager->RegisterCVal(this);
+ value_lock_refptr_ = manager->value_lock_refptr_;
registered_ = true;
}
}
+ void OnManagerDestroyed() const {
+ // The value lock is locked by the manager prior to it calling
+ // OnManagerDestroyed on its surviving CVals.
+ registered_ = false;
+ }
+
#if defined(ENABLE_DEBUG_C_VAL)
void OnValueChanged() {
// Push the value changed event to all listeners.
- CValManager::GetInstance()->PushValueChangedEvent(
- this, CValSpecificValue<T>(value_));
+ if (registered_) {
+ CValManager::GetInstance()->PushValueChangedEvent(
+ this, CValSpecificValue<T>(value_));
+ }
}
#endif // ENABLE_DEBUG_C_VAL
T value_;
mutable bool registered_;
+ mutable scoped_refptr<RefCountedThreadSafeLock> value_lock_refptr_;
};
#if !defined(ENABLE_DEBUG_C_VAL)
diff --git a/src/cobalt/base/math.h b/src/cobalt/base/math.h
index 7eb1f53..4723bfb 100644
--- a/src/cobalt/base/math.h
+++ b/src/cobalt/base/math.h
@@ -17,11 +17,4 @@
#include <cmath>
-#include "build/build_config.h"
-
-// The MSVC compiler doesn't have roundf().
-#if defined(COMPILER_MSVC)
-float roundf(float x);
-#endif // defined(COMPILER_MSVC)
-
#endif // COBALT_BASE_MATH_H_
diff --git a/src/cobalt/base/ref_counted_lock.h b/src/cobalt/base/ref_counted_lock.h
new file mode 100644
index 0000000..ddc77a6
--- /dev/null
+++ b/src/cobalt/base/ref_counted_lock.h
@@ -0,0 +1,48 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BASE_REF_COUNTED_LOCK_H_
+#define COBALT_BASE_REF_COUNTED_LOCK_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+
+//
+// A non-thread-safe ref-counted wrapper around a lock.
+//
+class RefCountedLock : public base::RefCounted<RefCountedLock> {
+ public:
+ base::Lock& GetLock() { return lock_; }
+
+ private:
+ base::Lock lock_;
+};
+
+//
+// A thread-safe ref-counted wrapper around a lock.
+//
+class RefCountedThreadSafeLock
+ : public base::RefCountedThreadSafe<RefCountedThreadSafeLock> {
+ public:
+ base::Lock& GetLock() { return lock_; }
+
+ private:
+ base::Lock lock_;
+};
+
+} // namespace base
+
+#endif // COBALT_BASE_REF_COUNTED_LOCK_H_
diff --git a/src/cobalt/bindings/IDLExtendedAttributes.txt b/src/cobalt/bindings/IDLExtendedAttributes.txt
index e444e91..8b1c115 100644
--- a/src/cobalt/bindings/IDLExtendedAttributes.txt
+++ b/src/cobalt/bindings/IDLExtendedAttributes.txt
@@ -127,3 +127,6 @@
NewObject
SameObject
+# Allows to specify an alternate method name for an attribute implementation.
+# Can be used to avoid using reserved keywords for method names.
+ImplementedAs=*
diff --git a/src/cobalt/bindings/bindings.gypi b/src/cobalt/bindings/bindings.gypi
index 9ba1577..29c4d39 100644
--- a/src/cobalt/bindings/bindings.gypi
+++ b/src/cobalt/bindings/bindings.gypi
@@ -20,7 +20,8 @@
# Input variables:
# source_idl_files: All IDL files for which a bindings wrapper should be
# created.
-# dictionary_idl_files: All IDL files for IDL dictionaries.
+# generated_header_idl_files: IDL files for IDL dictionaries and IDL enums. A
+# header will be generated for these that can be #included in
# dependency_idl_files: IDL files that are dependencies of other IDLs, but no
# bindings wrapper will be created. For example, partial interfaces and
# the right-hand-side of implements statements.
@@ -71,6 +72,7 @@
'bindings_include_dirs': ['<@(engine_include_dirs)'],
'bindings_templates_dir': '<(engine_templates_dir)',
'idl_compiler_script': '<(engine_idl_compiler)',
+ 'conversion_header_generator_script': '<(engine_conversion_header_generator_script)',
# Templates that are shared by the code generation for multiple engines.
'shared_template_files': [
@@ -88,11 +90,10 @@
# directory of each IDL.
'source_idl_files': [],
- # A class definition that matches the dictionary definition and is
- # suitable to be included and used in Cobalt code will be generated, as well
- # as a conversion function between an instance of this class and a JS
- # object.
- 'dictionary_idl_files': [],
+ # For each IDL file in this list, a header file that can be #included in
+ # Cobalt will be generated, as well as a .cc file that implements the
+ # conversion to/from a JS value.
+ 'generated_header_idl_files': [],
# Partial interfaces and the right-side of "implements"
# Code will not get generated for these interfaces; they are used to add
@@ -140,6 +141,7 @@
'<(DEPTH)/cobalt/bindings/expression_generator.py',
'<(DEPTH)/cobalt/bindings/contexts.py',
'<(DEPTH)/cobalt/bindings/idl_compiler_cobalt.py',
+ '<(DEPTH)/cobalt/bindings/generate_conversion_header.py',
'<(DEPTH)/cobalt/bindings/name_conversion.py',
'<(DEPTH)/cobalt/bindings/overload_context.py',
'<@(code_generator_template_files)',
@@ -180,6 +182,15 @@
' --base_directory <(DEPTH)'
' <@(source_idl_files))'],
+ # .cc files that implement conversion functions to/from a JS value for
+ # generated types (enums and dictionaries).
+ 'generated_type_conversions':
+ ['<!@pymod_do_main(cobalt.build.path_conversion -s'
+ ' --output_directory <(generated_source_output_dir)'
+ ' --output_extension cc --output_prefix <(prefix)_'
+ ' --base_directory <(DEPTH)'
+ ' <@(generated_header_idl_files))'],
+
# Generated IDL file that will define all the constructors that should be
# on the Window object
'global_constructors_generated_idl_file':
@@ -189,6 +200,11 @@
# there is a header for each IDL.
'global_constructors_generated_header_file':
'<(generated_idls_output_dir)/<(window_component)/window_constructors.h',
+
+ # Generated header file that contains forward declarations for converting
+ # to/from JS value and generated types.
+ 'generated_type_conversion_header_file':
+ '<(generated_source_output_dir)/<(prefix)_gen_type_conversion.h',
},
'targets': [
@@ -204,7 +220,8 @@
'dependencies': [
'cached_jinja_templates',
'cached_lex_yacc_tables',
- 'generated_dictionaries',
+ 'generated_types',
+ 'generated_type_conversion',
'global_constructors_idls',
'interfaces_info_overall',
'<@(bindings_dependencies)',
@@ -245,6 +262,9 @@
# not in this list is encountered, it will cause an error in the
# pipeline.
'<(extended_attributes_file)',
+
+ # Forward declarations of conversion functions for generated types.
+ '<(generated_type_conversion_header_file)',
],
'outputs': [
'<!@pymod_do_main(cobalt.build.path_conversion -s -p <(prefix)_ '
@@ -278,6 +298,7 @@
],
'sources': [
'<@(generated_sources)',
+ '<@(generated_type_conversions)',
],
'defines': [ '<@(bindings_defines)'],
}
@@ -286,7 +307,7 @@
{
# Based on the generated_bindings target above. Main difference is that
# this produces two .h files, and takes the dictionary idl files as input.
- 'target_name': 'generated_dictionaries',
+ 'target_name': 'generated_types',
'type': 'none',
'hard_dependency': 1,
'dependencies': [
@@ -296,7 +317,7 @@
'interfaces_info_overall',
],
'sources': [
- '<@(dictionary_idl_files)',
+ '<@(generated_header_idl_files)',
],
'direct_dependent_settings': {
'include_dirs': [
@@ -338,7 +359,7 @@
'<!@pymod_do_main(cobalt.build.path_conversion -s '
'-e h -d <(generated_source_output_dir) -b <(DEPTH) <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT))',
'<!@pymod_do_main(cobalt.build.path_conversion -s -p <(prefix)_ '
- '-e h -d <(generated_source_output_dir) -b <(DEPTH) <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT))'
+ '-e cc -d <(generated_source_output_dir) -b <(DEPTH) <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT))'
],
'action': [
'python',
@@ -358,13 +379,83 @@
'message': 'Generating dictionary from <(RULE_INPUT_PATH)',
}],
},
+ {
+ # Create a target that depends on this target to compile the generated
+ # source files. There should be only one such target.
+ # Based on blink/Source/bindings/core/v8/generated.gyp:bindings_core_v8_generated_individual
+ 'target_name': 'generated_type_conversion',
+ 'type': 'none',
+ # The 'binding' rule generates .h files, so mark as hard_dependency, per:
+ # https://code.google.com/p/gyp/wiki/InputFormatReference#Linking_Dependencies
+ 'hard_dependency': 1,
+ 'dependencies': [
+ 'cached_jinja_templates',
+ 'cached_lex_yacc_tables',
+ 'global_constructors_idls',
+ 'interfaces_info_overall',
+ '<@(bindings_dependencies)',
+ ],
+ 'export_dependent_settings': [
+ '<@(bindings_dependencies)',
+ ],
+ 'sources': [
+ '<@(source_idl_files)',
+ '<@(generated_header_idl_files)'
+ ],
+ 'actions': [{
+ 'action_name': 'generate_type_conversion_header',
+ 'inputs': [
+ # Script source files, etc.
+ '<@(bindings_extra_inputs)',
+
+ # This file is global, so if it changes all files are rebuilt.
+ # However, this file will only change when dependency structure
+ # changes, so shouldn't change too much.
+ # See blink/Source/bindings/core/v8/generated.gyp for more info
+ '<(interfaces_info_combined_pickle)',
+
+ # Similarly, all files are rebuilt if a partial interface or
+ # right side of 'implements' changes.
+ # See blink/Source/bindings/core/v8/generated.gyp for more info
+ '<@(dependency_idl_files)',
+
+ # Also add as a dependency the set of unsupported IDL files.
+ '<@(unsupported_interface_idl_files)',
+
+ # The generated constructors IDL is also a partial interface, and
+ # we need to rebuild if it is modified.
+ '<(global_constructors_generated_idl_file)',
+
+ # The whitelist of what extended attributes we support. If an attribute
+ # not in this list is encountered, it will cause an error in the
+ # pipeline.
+ '<(extended_attributes_file)',
+ ],
+ 'outputs': [
+ '<(generated_type_conversion_header_file)',
+ ],
+ 'action': [
+ 'python',
+ '<(conversion_header_generator_script)',
+ '--cache-dir',
+ '<(bindings_scripts_output_dir)',
+ '--output-dir',
+ '<(generated_source_output_dir)',
+ '--interfaces-info',
+ '<(interfaces_info_combined_pickle)',
+ '--component-info',
+ '<(component_info_pickle)',
+ ],
+ 'message': 'Generating generated type conversion header',
+ }],
+ },
{
# Based on blink/Source/bindings/core/generated.gyp:core_global_objects
'target_name': 'global_objects',
'variables': {
'idl_files': [
- '<@(source_idl_files)', '<@(dictionary_idl_files)'
+ '<@(source_idl_files)', '<@(generated_header_idl_files)'
],
'output_file': '<(bindings_scripts_output_dir)/GlobalObjects.pickle',
},
@@ -405,7 +496,8 @@
],
'variables': {
'static_idl_files': [
- '<@(source_idl_files)', '<@(dictionary_idl_files)',
+ '<@(source_idl_files)',
+ '<@(generated_header_idl_files)',
'<@(dependency_idl_files)',
'<@(unsupported_interface_idl_files)'],
'generated_idl_files': ['<(global_constructors_generated_idl_file)'],
diff --git a/src/cobalt/bindings/code_generator_cobalt.py b/src/cobalt/bindings/code_generator_cobalt.py
index e2e6abd..f3a7892 100644
--- a/src/cobalt/bindings/code_generator_cobalt.py
+++ b/src/cobalt/bindings/code_generator_cobalt.py
@@ -135,6 +135,7 @@
idl_type = idl_type.resolve_typedefs(info_provider.typedefs)
if isinstance(idl_type, IdlTypedef):
idl_type = idl_type.idl_type
+
if idl_type.is_interface_type:
yield get_interface_name(idl_type)
if idl_type.is_dictionary:
@@ -143,6 +144,8 @@
for interface_name in get_interface_type_names_from_idl_types(
info_provider, idl_type.member_types):
yield interface_name
+ elif idl_type.is_enum:
+ yield idl_type.name
elif isinstance(idl_type, IdlSequenceType):
for interface_name in get_interface_type_names_from_idl_types(
info_provider, [idl_type.element_type]):
@@ -193,8 +196,7 @@
self.jinja_env = initialize_jinja_env(cache_dir,
os.path.abspath(templates_dir))
self.path_builder = path_generator.PathBuilder(
- self.generated_file_prefix, self.info_provider.interfaces_info,
- cobalt_dir, output_dir)
+ self.generated_file_prefix, self.info_provider, cobalt_dir, output_dir)
@abc.abstractproperty
def generated_file_prefix(self):
@@ -220,6 +222,9 @@
return self.generate_dictionary_code(
definitions, definition_name,
definitions.dictionaries[definition_name])
+ if definition_name in definitions.enumerations:
+ return self.generate_enum_code(definitions, definition_name,
+ definitions.enumerations[definition_name])
raise ValueError('%s is not in IDL definitions' % definition_name)
def generate_interface_code(self, definitions, interface_name, interface):
@@ -245,28 +250,103 @@
def generate_dictionary_code(self, definitions, dictionary_name, dictionary):
header_template_filename = 'dictionary.h.template'
- conversion_template_filename = 'dictionary-conversion.h.template'
- template_context = self.build_dictionary_context(dictionary, definitions)
+ conversion_template_filename = 'dictionary-conversion.cc.template'
+ implementation_context = self.build_dictionary_context(
+ dictionary, definitions, False)
+ conversion_context = self.build_dictionary_context(dictionary, definitions,
+ True)
header_text = self.render_template(header_template_filename,
- template_context)
+ implementation_context)
conversion_text = self.render_template(conversion_template_filename,
- template_context)
+ conversion_context)
header_path = self.path_builder.DictionaryHeaderFullPath(dictionary_name)
- conversion_header_path = (
- self.path_builder.DictionaryConversionHeaderFullPath(dictionary_name))
- return ((header_path, header_text), (conversion_header_path,
+ conversion_impl_path = (
+ self.path_builder.DictionaryConversionImplementationPath(
+ dictionary_name))
+ return ((header_path, header_text), (conversion_impl_path,
conversion_text),)
+ def generate_enum_code(self, definitions, enumeration_name, enumeration):
+ header_template_filename = 'enumeration.h.template'
+ conversion_template_filename = 'enumeration-conversion.cc.template'
+ context_builder = ContextBuilder(self.info_provider)
+ context = context_builder.enumeration_context(enumeration)
+
+ context['components'] = self.path_builder.NamespaceComponents(
+ enumeration_name)
+ context['namespace'] = self.path_builder.Namespace(enumeration_name)
+ context['fully_qualified_name'] = self.path_builder.FullClassName(
+ enumeration_name)
+ context['enum_include'] = self.path_builder.EnumHeaderIncludePath(
+ enumeration_name)
+
+ header_text = self.render_template(header_template_filename, context)
+ conversion_text = self.render_template(conversion_template_filename,
+ context)
+
+ header_path = self.path_builder.EnumHeaderFullPath(enumeration_name)
+ conversion_impl_path = (
+ self.path_builder.EnumConversionImplementationFullPath(enumeration_name)
+ )
+ return ((header_path, header_text), (conversion_impl_path,
+ conversion_text),)
+
+ def generate_conversion_code(self):
+ enumerations = list(self.info_provider.enumerations.keys())
+ dictionaries = list(self.info_provider.interfaces_info['dictionaries'])
+ includes = [
+ self.path_builder.DictionaryHeaderIncludePath(dictionary)
+ for dictionary in dictionaries
+ ]
+ includes.extend([
+ self.path_builder.EnumHeaderIncludePath(enum) for enum in enumerations
+ ])
+
+ context = {
+ 'dictionaries': self.referenced_class_contexts(dictionaries, False),
+ 'enumerations': self.referenced_class_contexts(enumerations, False),
+ 'includes': includes,
+ }
+
+ header_template_filename = 'generated-types.h.template'
+ header_text = self.render_template(header_template_filename, context)
+
+ return self.path_builder.generated_conversion_header_path, header_text
+
+ def referenced_dictionary_context(self, dictionary_name, dictionary_info):
+ namespace = '::'.join(
+ self.path_builder.NamespaceComponents(dictionary_name))
+
+ return {
+ 'fully_qualified_name':
+ '%s::%s' % (namespace, dictionary_name),
+ 'include':
+ self.path_builder.DictionaryHeaderIncludePath(dictionary_name),
+ 'conditional':
+ dictionary_info['conditional'],
+ 'is_callback_interface':
+ False,
+ }
+
+ def referenced_enum_context(self, enum_name):
+ namespace = '::'.join(self.path_builder.NamespaceComponents(enum_name))
+ return {
+ 'fully_qualified_name': '%s::%s' % (namespace, enum_name),
+ 'include': self.path_builder.EnumHeaderIncludePath(enum_name),
+ 'conditional': None,
+ 'is_callback_interface': False,
+ }
+
def referenced_class_contexts(self,
interface_names,
- implementation_only=False):
+ include_bindings_class=True):
"""Returns a list of jinja contexts describing referenced C++ classes.
Args:
interface_names: A list of interfaces.
- implementation_only: Only include implemetation headers.
+ include_bindings_class: Include headers and classes uses only in bindings.
Returns:
list() of jinja contexts (python dicts) with information about C++ classes
related to the interfaces in |interface_names|. dict has the following
@@ -279,26 +359,20 @@
referenced_classes = []
# Iterate over it as a set to uniquify the list.
for interface_name in set(interface_names):
- interface_info = self.info_provider.interfaces_info[interface_name]
- is_dictionary = interface_info['is_dictionary']
- namespace = '::'.join(
- self.path_builder.NamespaceComponents(interface_name))
- conditional = interface_info['conditional']
- is_callback_interface = interface_name in IdlType.callback_interfaces
+ if interface_name in self.info_provider.enumerations:
+ # If this is an enumeration, get the enum context and continue.
+ referenced_classes.append(self.referenced_enum_context(interface_name))
+ continue
- # Information about the Cobalt implementation class.
- if is_dictionary:
- referenced_classes.append({
- 'fully_qualified_name':
- '%s::%s' % (namespace, interface_name),
- 'include':
- self.path_builder.BindingsHeaderIncludePath(interface_name),
- 'conditional':
- conditional,
- 'is_callback_interface':
- is_callback_interface,
- })
+ interface_info = self.info_provider.interfaces_info[interface_name]
+ if interface_info['is_dictionary']:
+ referenced_classes.append(
+ self.referenced_dictionary_context(interface_name, interface_info))
else:
+ namespace = '::'.join(
+ self.path_builder.NamespaceComponents(interface_name))
+ conditional = interface_info['conditional']
+ is_callback_interface = interface_name in IdlType.callback_interfaces
referenced_classes.append({
'fully_qualified_name':
'%s::%s' % (namespace, interface_name),
@@ -309,7 +383,7 @@
'is_callback_interface':
is_callback_interface,
})
- if not implementation_only:
+ if include_bindings_class:
referenced_classes.append({
'fully_qualified_name':
'%s::%s' % (namespace,
@@ -337,10 +411,12 @@
normalize_slashes(os.path.relpath(module_path_pyname, cobalt_dir)),
'template_path':
normalize_slashes(os.path.relpath(template.filename, cobalt_dir)),
+ 'generated_conversion_include':
+ self.path_builder.generated_conversion_include_path,
}
return context
- def build_dictionary_context(self, dictionary, definitions):
+ def build_dictionary_context(self, dictionary, definitions, for_conversion):
context_builder = ContextBuilder(self.info_provider)
context = {
'class_name':
@@ -356,7 +432,8 @@
get_interface_type_names_from_typed_objects(self.info_provider,
dictionary.members))
referenced_class_contexts = self.referenced_class_contexts(
- referenced_interface_names, True)
+ referenced_interface_names, for_conversion)
+
context['includes'] = sorted((interface['include']
for interface in referenced_class_contexts))
context['forward_declarations'] = sorted(
@@ -457,6 +534,7 @@
all_interfaces = []
for interface_name in referenced_interface_names:
if (interface_name not in IdlType.callback_interfaces and
+ interface_name not in IdlType.enums and
not interfaces_info[interface_name]['unsupported'] and
not interfaces_info[interface_name]['is_dictionary']):
all_interfaces.append({
diff --git a/src/cobalt/bindings/contexts.py b/src/cobalt/bindings/contexts.py
index eb14d72..c67a214 100644
--- a/src/cobalt/bindings/contexts.py
+++ b/src/cobalt/bindings/contexts.py
@@ -51,6 +51,8 @@
"""Map IDL literal to the corresponding cobalt value."""
if idl_literal.is_null and not idl_type.is_interface_type:
return 'base::nullopt'
+ if idl_type.is_enum:
+ return convert_to_cobalt_enumeration_value(idl_type, idl_literal.value)
return str(idl_literal)
@@ -232,6 +234,8 @@
cobalt_type = 'scoped_refptr<%s>' % get_interface_name(idl_type)
elif idl_type.is_union_type:
cobalt_type = self.idl_union_type_to_cobalt(idl_type)
+ elif idl_type.is_enum:
+ cobalt_type = idl_type.name
elif is_sequence_type(idl_type):
cobalt_type = self.idl_sequence_type_to_cobalt(idl_type)
elif idl_type.name == 'void':
@@ -257,8 +261,6 @@
idl_type = self.resolve_typedef(typed_object.idl_type)
if idl_type.is_callback_function:
cobalt_type = interface.name + '::' + get_interface_name(idl_type)
- elif idl_type.is_enum:
- cobalt_type = '%s::%s' % (interface.name, get_interface_name(idl_type))
else:
cobalt_type = self.idl_type_to_cobalt_type(idl_type)
if getattr(typed_object, 'is_variadic', False):
@@ -423,13 +425,15 @@
def attribute_context(self, interface, attribute, definitions):
"""Create template values for attribute bindings."""
+ cobalt_name = attribute.extended_attributes.get(
+ 'ImplementedAs', convert_to_cobalt_name(attribute.name))
context = {
'idl_name':
attribute.name,
'getter_function_name':
- convert_to_cobalt_name(attribute.name),
+ cobalt_name,
'setter_function_name':
- 'set_' + convert_to_cobalt_name(attribute.name),
+ 'set_' + cobalt_name,
'type':
self.typed_object_to_cobalt_type(interface, attribute),
'is_static':
@@ -478,10 +482,10 @@
def enumeration_context(self, enumeration):
"""Create template values for IDL enumeration type bindings."""
return {
- 'name':
+ 'enumeration_name':
enumeration.name,
- 'value_pairs': [(convert_to_cobalt_enumeration_value(value), value,)
- for value in enumeration.values],
+ 'value_pairs': [(convert_to_cobalt_enumeration_value(
+ enumeration.name, value), value,) for value in enumeration.values],
}
def constant_context(self, constant):
diff --git a/src/cobalt/bindings/generate_conversion_header.py b/src/cobalt/bindings/generate_conversion_header.py
new file mode 100644
index 0000000..f79e3bd
--- /dev/null
+++ b/src/cobalt/bindings/generate_conversion_header.py
@@ -0,0 +1,64 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generate a header for conversion of generated types.
+
+Generate a header with forward declarations for conversion functions to and from
+JavaScript values.
+"""
+
+from optparse import OptionParser
+import os
+import pickle
+
+from utilities import ComponentInfoProviderCobalt
+from utilities import write_file
+
+
+def create_component_info_provider(interfaces_info_path, component_info_path):
+ with open(os.path.join(interfaces_info_path)) as interface_info_file:
+ interfaces_info = pickle.load(interface_info_file)
+ with open(os.path.join(component_info_path)) as component_info_file:
+ component_info = pickle.load(component_info_file)
+ return ComponentInfoProviderCobalt(interfaces_info, component_info)
+
+
+def parse_options():
+ """Parse options needed for generating the header."""
+ parser = OptionParser()
+ parser.add_option(
+ '--cache-directory', help='cache directory, defaults to output directory')
+ parser.add_option('--component-info')
+ parser.add_option('--interfaces-info')
+ parser.add_option('--output-directory')
+ options, args = parser.parse_args()
+
+ assert not args
+ return options
+
+
+def generate_header(code_generator_class):
+ """Generate a header with forward declarations for type conversions."""
+ options = parse_options()
+
+ # Create a CodeGeneratorCobalt, which will generate the file.
+ cobalt_info_provider = create_component_info_provider(options.interfaces_info,
+ options.component_info)
+ generator = code_generator_class(
+ info_provider=cobalt_info_provider,
+ cache_dir=options.cache_directory,
+ output_dir=options.output_directory)
+
+ # Generate the file and write it out to the specified path.
+ header_path, header_text = generator.generate_conversion_code()
+ write_file(header_text, header_path)
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
new file mode 100644
index 0000000..b5178c9
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/templates/dictionary.h.template
+
+#ifndef DictionaryWithDictionaryMember_h
+#define DictionaryWithDictionaryMember_h
+
+#include <string>
+
+#include "cobalt/script/sequence.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+using cobalt::bindings::testing::TestDictionary;
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class DictionaryWithDictionaryMember {
+ public:
+ DictionaryWithDictionaryMember() {
+ has_nested_dictionary_ = false;
+ nested_dictionary_ = TestDictionary();
+ }
+
+ bool has_nested_dictionary() const {
+ return has_nested_dictionary_;
+ }
+ TestDictionary nested_dictionary() const {
+ DCHECK(has_nested_dictionary_);
+ return nested_dictionary_;
+ }
+ void set_nested_dictionary(TestDictionary value) {
+ has_nested_dictionary_ = true;
+ nested_dictionary_ = value;
+ }
+
+ private:
+ bool has_nested_dictionary_;
+ TestDictionary nested_dictionary_;
+};
+
+// This ostream override is necessary for MOCK_METHODs commonly used
+// in idl test code
+inline std::ostream& operator<<(
+ std::ostream& stream, const cobalt::bindings::testing::DictionaryWithDictionaryMember& in) {
+ UNREFERENCED_PARAMETER(in);
+ stream << "[DictionaryWithDictionaryMember]";
+ return stream;
+}
+
+} // namespace cobalt
+} // namespace bindings
+} // namespace testing
+
+#endif // DictionaryWithDictionaryMember_h
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
index 2eec092..806ac70 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
index 2db7ce9..a0d23a1 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
index c0d2e3a..717863b 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
index c10480e..9740597 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc
index 840e07d..ea95c8f 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_base_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
index 4f6d720..e47a9ed 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
index d4323fd..59c48e9 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
index c8af6c7..f52a434 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/mozjs_single_operation_interface.h"
#include "cobalt/bindings/testing/single_operation_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
index f6adfc1..90e79e7 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
index 3167249..589bf2c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
index 35fb74f..cbe05b6 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
index f33738c..754e7d5 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
index f590af2..6ba4eeb 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/mozjs_named_indexed_getter_interface.h"
#include "cobalt/bindings/testing/named_indexed_getter_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
index 60a6efb..cc0512e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/base_interface.h"
#include "cobalt/bindings/testing/mozjs_base_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
index 945e7da..d9c7928 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
@@ -24,7 +24,10 @@
#include "cobalt/script/global_environment.h"
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
-#include "cobalt/bindings/testing/mozjs_test_dictionary.h"
+#include "cobalt/bindings/testing/dictionary_with_dictionary_member.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+#include "mozjs_gen_type_conversion.h"
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
@@ -50,6 +53,7 @@
namespace {
using cobalt::bindings::testing::DictionaryInterface;
using cobalt::bindings::testing::MozjsDictionaryInterface;
+using cobalt::bindings::testing::DictionaryWithDictionaryMember;
using cobalt::bindings::testing::TestDictionary;
using cobalt::script::CallbackInterfaceTraits;
using cobalt::script::GlobalEnvironment;
@@ -338,6 +342,71 @@
return !exception_state.is_exception_set();
}
+JSBool fcn_testOperation(
+ JSContext* context, uint32_t argc, JS::Value *vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ // Compute the 'this' value.
+ JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+ // 'this' should be an object.
+ JS::RootedObject object(context);
+ if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+ NOTREACHED();
+ return false;
+ }
+ if (!JS_ValueToObject(context, this_value, object.address())) {
+ NOTREACHED();
+ return false;
+ }
+ const JSClass* proto_class =
+ MozjsDictionaryInterface::PrototypeClass(context);
+ if (proto_class == JS_GetClass(object)) {
+ // Simply returns true if the object is this class's prototype object.
+ // There is no need to return any value due to the object is not a platform
+ // object. The execution reaches here when Object.getOwnPropertyDescriptor
+ // gets called on native object prototypes.
+ return true;
+ }
+
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<DictionaryInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
+ MozjsExceptionState exception_state(context);
+ JS::RootedValue result_value(context);
+
+ WrapperPrivate* wrapper_private =
+ WrapperPrivate::GetFromObject(context, object);
+ DictionaryInterface* impl =
+ wrapper_private->wrappable<DictionaryInterface>().get();
+ const size_t kMinArguments = 1;
+ if (args.length() < kMinArguments) {
+ exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
+ return false;
+ }
+ // Non-optional arguments
+ TypeTraits<DictionaryWithDictionaryMember >::ConversionType dict;
+
+ DCHECK_LT(0, args.length());
+ JS::RootedValue non_optional_value0(
+ context, args[0]);
+ FromJSValue(context,
+ non_optional_value0,
+ kNoConversionFlags,
+ &exception_state, &dict);
+ if (exception_state.is_exception_set()) {
+ return false;
+ }
+
+ impl->TestOperation(dict);
+ result_value.set(JS::UndefinedHandleValue);
+ return !exception_state.is_exception_set();
+}
+
const JSPropertySpec prototype_properties[] = {
{ // Read/Write property
@@ -357,6 +426,13 @@
JSPROP_ENUMERATE,
NULL,
},
+ {
+ "testOperation",
+ JSOP_WRAPPER(&fcn_testOperation),
+ 1,
+ JSPROP_ENUMERATE,
+ NULL,
+ },
JS_FS_END
};
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
new file mode 100644
index 0000000..50e5fde
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs/templates/dictionary-conversion.cc.template
+
+#include "mozjs_gen_type_conversion.h"
+
+#include "cobalt/bindings/testing/dictionary_with_dictionary_member.h"
+
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+using cobalt::bindings::testing::DictionaryWithDictionaryMember;
+using cobalt::bindings::testing::TestDictionary;
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+ JSContext* context,
+ const DictionaryWithDictionaryMember& in_dictionary,
+ JS::MutableHandleValue out_value) {
+ // Create a new object that will hold the dictionary values.
+ JS::RootedObject dictionary_object(
+ context, JS_NewObject(context, NULL, NULL, NULL));
+ const int kPropertyAttributes = JSPROP_ENUMERATE;
+ if (in_dictionary.has_nested_dictionary()) {
+ JS::RootedValue member_value(context);
+ ToJSValue(context, in_dictionary.nested_dictionary(), &member_value);
+ if (!JS_DefineProperty(context, dictionary_object,
+ "nestedDictionary",
+ member_value, NULL, NULL, kPropertyAttributes)) {
+ // Some internal error occurred.
+ NOTREACHED();
+ return;
+ }
+ }
+ out_value.set(OBJECT_TO_JSVAL(dictionary_object));
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+ int conversion_flags, ExceptionState* exception_state,
+ DictionaryWithDictionaryMember* out_dictionary) {
+ DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+ // https://heycam.github.io/webidl/#es-dictionary
+
+ if (value.isUndefined() || value.isNull()) {
+ // The default constructor will assign appropriate values to dictionary
+ // members with default values and leave the others unset.
+ *out_dictionary = DictionaryWithDictionaryMember();
+ return;
+ }
+ if (!value.isObject()) {
+ // 1. If Type(V) is not Undefined, Null or Object, then throw a TypeError.
+ exception_state->SetSimpleException(kNotObjectType);
+ return;
+ }
+ JS::RootedObject dictionary_object(context, JSVAL_TO_OBJECT(value));
+ JS::RootedValue nested_dictionary(context);
+ if (!JS_GetProperty(context, dictionary_object,
+ "nestedDictionary",
+ nested_dictionary.address())) {
+ exception_state->SetSimpleException(kSimpleError);
+ return;
+ }
+ if (!nested_dictionary.isUndefined()) {
+ TestDictionary converted_value;
+ FromJSValue(context,
+ nested_dictionary,
+ kNoConversionFlags,
+ exception_state,
+ &converted_value);
+ if (context->isExceptionPending()) {
+ return;
+ }
+ out_dictionary->set_nested_dictionary(converted_value);
+ }
+}
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
+
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
index 203d729..5b3a925 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
index 7134234..68fa836 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
index 93a42bc..e66d97c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
@@ -24,6 +24,9 @@
#include "cobalt/script/global_environment.h"
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "cobalt/bindings/testing/test_enum.h"
+
+#include "mozjs_gen_type_conversion.h"
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
@@ -49,6 +52,7 @@
namespace {
using cobalt::bindings::testing::EnumerationInterface;
using cobalt::bindings::testing::MozjsEnumerationInterface;
+using cobalt::bindings::testing::TestEnum;
using cobalt::script::CallbackInterfaceTraits;
using cobalt::script::GlobalEnvironment;
using cobalt::script::OpaqueHandle;
@@ -79,15 +83,6 @@
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
using cobalt::script::mozjs::kNoConversionFlags;
-// Declare and define these in the same namespace that the other overloads
-// were brought into with the using declaration.
-void ToJSValue(
- JSContext* context,
- EnumerationInterface::TestEnum in_enum,
- JS::MutableHandleValue out_value);
-void FromJSValue(JSContext* context, JS::HandleValue value,
- int conversion_flags, ExceptionState* exception_state,
- EnumerationInterface::TestEnum* out_enum);
} // namespace
namespace cobalt {
@@ -270,7 +265,7 @@
WrapperPrivate::GetFromObject(context, object);
EnumerationInterface* impl =
wrapper_private->wrappable<EnumerationInterface>().get();
- TypeTraits<EnumerationInterface::TestEnum >::ConversionType value;
+ TypeTraits<TestEnum >::ConversionType value;
FromJSValue(context, vp, kNoConversionFlags, &exception_state,
&value);
if (exception_state.is_exception_set()) {
@@ -282,6 +277,69 @@
return !exception_state.is_exception_set();
}
+JSBool fcn_optionalEnumWithDefault(
+ JSContext* context, uint32_t argc, JS::Value *vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ // Compute the 'this' value.
+ JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+ // 'this' should be an object.
+ JS::RootedObject object(context);
+ if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+ NOTREACHED();
+ return false;
+ }
+ if (!JS_ValueToObject(context, this_value, object.address())) {
+ NOTREACHED();
+ return false;
+ }
+ const JSClass* proto_class =
+ MozjsEnumerationInterface::PrototypeClass(context);
+ if (proto_class == JS_GetClass(object)) {
+ // Simply returns true if the object is this class's prototype object.
+ // There is no need to return any value due to the object is not a platform
+ // object. The execution reaches here when Object.getOwnPropertyDescriptor
+ // gets called on native object prototypes.
+ return true;
+ }
+
+ 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);
+
+ WrapperPrivate* wrapper_private =
+ WrapperPrivate::GetFromObject(context, object);
+ EnumerationInterface* impl =
+ wrapper_private->wrappable<EnumerationInterface>().get();
+ // Optional arguments with default values
+ TypeTraits<TestEnum >::ConversionType value =
+ kTestEnumBeta;
+ size_t num_set_arguments = 1;
+ if (args.length() > 0) {
+ JS::RootedValue optional_value0(
+ context, args[0]);
+ FromJSValue(context,
+ optional_value0,
+ kNoConversionFlags,
+ &exception_state,
+ &value);
+ if (exception_state.is_exception_set()) {
+ return false;
+ }
+ }
+
+ impl->OptionalEnumWithDefault(value);
+ result_value.set(JS::UndefinedHandleValue);
+ return !exception_state.is_exception_set();
+}
+
const JSPropertySpec prototype_properties[] = {
{ // Read/Write property
@@ -294,6 +352,13 @@
};
const JSFunctionSpec prototype_functions[] = {
+ {
+ "optionalEnumWithDefault",
+ JSOP_WRAPPER(&fcn_optionalEnumWithDefault),
+ 0,
+ JSPROP_ENUMERATE,
+ NULL,
+ },
JS_FS_END
};
@@ -487,58 +552,3 @@
} // namespace testing
} // namespace bindings
} // namespace cobalt
-
-namespace {
-
-inline void ToJSValue(
- JSContext* context,
- EnumerationInterface::TestEnum in_enum,
- JS::MutableHandleValue out_value) {
-
- switch (in_enum) {
- case EnumerationInterface::kAlpha:
- ToJSValue(context, std::string("alpha"), out_value);
- return;
- case EnumerationInterface::kBeta:
- ToJSValue(context, std::string("beta"), out_value);
- return;
- case EnumerationInterface::kGamma:
- ToJSValue(context, std::string("gamma"), out_value);
- return;
- default:
- NOTREACHED();
- out_value.set(JS::UndefinedValue());
- }
-}
-
-
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
- int conversion_flags, ExceptionState* exception_state,
- EnumerationInterface::TestEnum* out_enum) {
- DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
- // JSValue -> IDL enum algorithm described here:
- // http://heycam.github.io/webidl/#es-enumeration
- // 1. Let S be the result of calling ToString(V).
- JS::RootedString rooted_string(context, JS_ValueToString(context, value));
-
- JSBool match = JS_FALSE;
-// 3. Return the enumeration value of type E that is equal to S.
-if (JS_StringEqualsAscii(
- context, rooted_string, "alpha", &match)
- && match) {
- *out_enum = EnumerationInterface::kAlpha;
- } else if (JS_StringEqualsAscii(
- context, rooted_string, "beta", &match)
- && match) {
- *out_enum = EnumerationInterface::kBeta;
- } else if (JS_StringEqualsAscii(
- context, rooted_string, "gamma", &match)
- && match) {
- *out_enum = EnumerationInterface::kGamma;
- } else {
- // 2. If S is not one of E's enumeration values, then throw a TypeError.
- exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
- return;
- }
-}
-} // namespace
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
index 4132357..9b4ff43 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
index 186f21e..9ec5d55 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
index c609226..36681ea 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
@@ -189,6 +191,88 @@
return interface_data;
}
+JSBool get_default(
+ JSContext* context, JS::HandleObject object, JS::HandleId id,
+ JS::MutableHandleValue vp) {
+ const JSClass* proto_class =
+ MozjsExtendedIDLAttributesInterface::PrototypeClass(context);
+ if (proto_class == JS_GetClass(object)) {
+ // Simply returns true if the object is this class's prototype object.
+ // There is no need to return any value due to the object is not a platform
+ // object. The execution reaches here when Object.getOwnPropertyDescriptor
+ // gets called on native object prototypes.
+ return true;
+ }
+
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ExtendedIDLAttributesInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
+ MozjsExceptionState exception_state(context);
+ JS::RootedValue result_value(context);
+
+ WrapperPrivate* wrapper_private =
+ WrapperPrivate::GetFromObject(context, object);
+ ExtendedIDLAttributesInterface* impl =
+ wrapper_private->wrappable<ExtendedIDLAttributesInterface>().get();
+
+ if (!exception_state.is_exception_set()) {
+ ToJSValue(context,
+ impl->attribute_default(),
+ &result_value);
+ }
+ if (!exception_state.is_exception_set()) {
+ vp.set(result_value);
+ }
+ return !exception_state.is_exception_set();
+}
+
+JSBool set_default(
+ JSContext* context, JS::HandleObject object, JS::HandleId id,
+ JSBool strict, JS::MutableHandleValue vp) {
+ const JSClass* proto_class =
+ MozjsExtendedIDLAttributesInterface::PrototypeClass(context);
+ if (proto_class == JS_GetClass(object)) {
+ // Simply returns true if the object is this class's prototype object.
+ // There is no need to return any value due to the object is not a platform
+ // object. The execution reaches here when Object.getOwnPropertyDescriptor
+ // gets called on native object prototypes.
+ return true;
+ }
+
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ExtendedIDLAttributesInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
+ MozjsExceptionState exception_state(context);
+ JS::RootedValue result_value(context);
+
+ WrapperPrivate* wrapper_private =
+ WrapperPrivate::GetFromObject(context, object);
+ ExtendedIDLAttributesInterface* impl =
+ wrapper_private->wrappable<ExtendedIDLAttributesInterface>().get();
+ TypeTraits<bool >::ConversionType value;
+ FromJSValue(context, vp, kNoConversionFlags, &exception_state,
+ &value);
+ if (exception_state.is_exception_set()) {
+ return false;
+ }
+
+ impl->set_attribute_default(value);
+ result_value.set(JS::UndefinedHandleValue);
+ return !exception_state.is_exception_set();
+}
+
JSBool fcn_callWithSettings(
JSContext* context, uint32_t argc, JS::Value *vp) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@@ -305,6 +389,12 @@
const JSPropertySpec prototype_properties[] = {
+ { // Read/Write property
+ "default", 0,
+ JSPROP_SHARED | JSPROP_ENUMERATE,
+ JSOP_WRAPPER(&get_default),
+ JSOP_WRAPPER(&set_default),
+ },
JS_PS_END
};
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
index 4bf4b5c..e496560 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/garbage_collection_test_interface.h"
#include "cobalt/bindings/testing/mozjs_garbage_collection_test_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
index 3698732..0ef8902 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
index 5ec4a72..5e4454a 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
index 50b9278..d9a2ece 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
index 7d61bf7..cfe8949 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
index 2c6fe76..2f323de 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
index 3aac1fe..052f2186 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
index e2e0a85..12df01c 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
index b7ba06f..7b5593e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
index 6b1689e..6ba2f03 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/mozjs_put_forwards_interface.h"
#include "cobalt/bindings/testing/put_forwards_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
index 7c5ff9a..a3ca932 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
index bc382f7..23a7a5d 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
index 3796736..b52697e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
index 00b8359..728b36e 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
index adc219c..8e2d83d 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
@@ -31,6 +31,8 @@
#include "cobalt/bindings/testing/mozjs_base_interface.h"
#include "cobalt/bindings/testing/mozjs_derived_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
index d2c44c0..e30f3c5 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
index e37f1f9..61d37cb 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
index 20c4381..0e4090d 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
index 1efad36..69655e2 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
index 9a0f19f..f4ede71 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
@@ -22,6 +22,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "cobalt/script/logging_exception_state.h"
#include "cobalt/script/mozjs/conversion_helpers.h"
#include "cobalt/script/mozjs/mozjs_callback_interface.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
index e20ac1c..47a2a55 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
index c475463..345a16f 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
index 8f8c7ba..4afb9bc 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
index 0901b3e..bba0eb7 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc
index 4d48366..50d9053 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_target_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
similarity index 93%
rename from src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.h
rename to src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
index fd4f532..50bd3c2 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.h
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
@@ -17,14 +17,20 @@
// clang-format off
// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
-// Auto-generated from template: bindings/mozjs/templates/dictionary-conversion.h.template
+// Auto-generated from template: bindings/mozjs/templates/dictionary-conversion.cc.template
-#ifndef TestDictionary_conversion_h
-#define TestDictionary_conversion_h
+#include "mozjs_gen_type_conversion.h"
#include "cobalt/bindings/testing/test_dictionary.h"
#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+#include "cobalt/bindings/testing/arbitrary_interface.h"
+#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+
+using cobalt::bindings::testing::TestDictionary;
+using cobalt::bindings::testing::ArbitraryInterface;
+using cobalt::bindings::testing::MozjsArbitraryInterface;
namespace cobalt {
namespace script {
@@ -32,28 +38,6 @@
void ToJSValue(
JSContext* context,
- const cobalt::bindings::testing::TestDictionary& in_dictionary,
- JS::MutableHandleValue out_value);
-
-void FromJSValue(JSContext* context, JS::HandleValue value,
- int conversion_flags,
- cobalt::script::ExceptionState* exception_state,
- cobalt::bindings::testing::TestDictionary* out_dictionary);
-} // namespace mozjs
-} // namespace script
-} // namespace cobalt
-
-#include "cobalt/script/mozjs/conversion_helpers.h"
-
-using cobalt::bindings::testing::TestDictionary;
-using cobalt::bindings::testing::ArbitraryInterface;
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-
-inline void ToJSValue(
- JSContext* context,
const TestDictionary& in_dictionary,
JS::MutableHandleValue out_value) {
// Create a new object that will hold the dictionary values.
@@ -151,7 +135,7 @@
out_value.set(OBJECT_TO_JSVAL(dictionary_object));
}
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
+void FromJSValue(JSContext* context, JS::HandleValue value,
int conversion_flags, ExceptionState* exception_state,
TestDictionary* out_dictionary) {
DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
@@ -327,4 +311,3 @@
} // namespace script
} // namespace cobalt
-#endif // TestDictionary_h
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_enum.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_enum.cc
new file mode 100644
index 0000000..445ddbc
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_test_enum.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs/templates/enumeration-conversion.cc.template
+
+#include "cobalt/bindings/testing/test_enum.h"
+
+#include "mozjs_gen_type_conversion.h"
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+
+using cobalt::bindings::testing::TestEnum;
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+ JSContext* context,
+ TestEnum in_enum,
+ JS::MutableHandleValue out_value) {
+ switch (in_enum) {
+ case cobalt::bindings::testing::kTestEnumAlpha:
+ ToJSValue(context, std::string("alpha"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumBeta:
+ ToJSValue(context, std::string("beta"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumGamma:
+ ToJSValue(context, std::string("gamma"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumEnumWithDashes:
+ ToJSValue(context, std::string("enum-with-dashes"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumEnumWithSpaces:
+ ToJSValue(context, std::string("enum with spaces"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumTerribleEnum:
+ ToJSValue(context, std::string("terrible----enum"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumThisIsATerribleEnum:
+ ToJSValue(context, std::string("this is a terrible @#$%#$% enum"), out_value);
+ return;
+ default:
+ NOTREACHED();
+ out_value.set(JS::UndefinedValue());
+ }
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+ int conversion_flags, ExceptionState* exception_state,
+ TestEnum* out_enum) {
+ DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+ // JSValue -> IDL enum algorithm described here:
+ // http://heycam.github.io/webidl/#es-enumeration
+ // 1. Let S be the result of calling ToString(V).
+ JS::RootedString rooted_string(context, JS_ValueToString(context, value));
+
+ JSBool match = JS_FALSE;
+// 3. Return the enumeration value of type E that is equal to S.
+if (JS_StringEqualsAscii(
+ context, rooted_string, "alpha", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumAlpha;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "beta", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumBeta;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "gamma", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumGamma;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "enum-with-dashes", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumEnumWithDashes;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "enum with spaces", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumEnumWithSpaces;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "terrible----enum", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumTerribleEnum;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "this is a terrible @#$%#$% enum", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumThisIsATerribleEnum;
+ } else {
+ // 2. If S is not one of E's enumeration values, then throw a TypeError.
+ exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
+ return;
+ }
+}
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
index 8f4cb24..2eb8e32 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
@@ -29,6 +29,8 @@
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_base_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
index 07d98af..883ae30 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/mozjs_window.cc
@@ -123,6 +123,8 @@
#include "cobalt/bindings/testing/union_types_interface.h"
#include "cobalt/bindings/testing/window.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h
index 4dc5055..4c36160 100644
--- a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_dictionary.h
@@ -27,6 +27,8 @@
#include "cobalt/script/sequence.h"
#include "cobalt/bindings/testing/arbitrary_interface.h"
+using cobalt::bindings::testing::ArbitraryInterface;
+
namespace cobalt {
namespace bindings {
namespace testing {
diff --git a/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_enum.h b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_enum.h
new file mode 100644
index 0000000..5e4bb69
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs/testing/cobalt/bindings/testing/test_enum.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/templates/enumeration.h.template
+
+#ifndef TestEnum_h
+#define TestEnum_h
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+enum TestEnum {
+ kTestEnumAlpha,
+ kTestEnumBeta,
+ kTestEnumGamma,
+ kTestEnumEnumWithDashes,
+ kTestEnumEnumWithSpaces,
+ kTestEnumTerribleEnum,
+ kTestEnumThisIsATerribleEnum,
+};
+
+} // namespace cobalt
+} // namespace bindings
+} // namespace testing
+
+#endif // TestEnum_h
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
new file mode 100644
index 0000000..b5178c9
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/templates/dictionary.h.template
+
+#ifndef DictionaryWithDictionaryMember_h
+#define DictionaryWithDictionaryMember_h
+
+#include <string>
+
+#include "cobalt/script/sequence.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+using cobalt::bindings::testing::TestDictionary;
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+class DictionaryWithDictionaryMember {
+ public:
+ DictionaryWithDictionaryMember() {
+ has_nested_dictionary_ = false;
+ nested_dictionary_ = TestDictionary();
+ }
+
+ bool has_nested_dictionary() const {
+ return has_nested_dictionary_;
+ }
+ TestDictionary nested_dictionary() const {
+ DCHECK(has_nested_dictionary_);
+ return nested_dictionary_;
+ }
+ void set_nested_dictionary(TestDictionary value) {
+ has_nested_dictionary_ = true;
+ nested_dictionary_ = value;
+ }
+
+ private:
+ bool has_nested_dictionary_;
+ TestDictionary nested_dictionary_;
+};
+
+// This ostream override is necessary for MOCK_METHODs commonly used
+// in idl test code
+inline std::ostream& operator<<(
+ std::ostream& stream, const cobalt::bindings::testing::DictionaryWithDictionaryMember& in) {
+ UNREFERENCED_PARAMETER(in);
+ stream << "[DictionaryWithDictionaryMember]";
+ return stream;
+}
+
+} // namespace cobalt
+} // namespace bindings
+} // namespace testing
+
+#endif // DictionaryWithDictionaryMember_h
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
index 46880fb..b06defe 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
index f83e1d7..680c83a 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
index 654870d..52b9f5d 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_anonymous_named_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
index fdfc37e..2051e93 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_arbitrary_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_base_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_base_interface.cc
index 8f3c8f9..278db4a 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_base_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_base_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
index 4b00338..60e62a8 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_boolean_type_test_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
index 9dc43d9..287ca7e 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_function_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
index 560292c..c8d4f23 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/mozjs_single_operation_interface.h"
#include "cobalt/bindings/testing/single_operation_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
index e741632..8aa44f7 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -73,6 +77,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constants_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
index 3a47240..9fc7f7f 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constants_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
index a08bfff..12948a7 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
index 5efabd2..d7e4244 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
index 8ff047c..d346835 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/mozjs_named_indexed_getter_interface.h"
#include "cobalt/bindings/testing/named_indexed_getter_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
index 6503829..8971ca7 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/base_interface.h"
#include "cobalt/bindings/testing/mozjs_base_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
index 653b32b..d2278fb 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
@@ -24,7 +24,10 @@
#include "cobalt/script/global_environment.h"
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
-#include "cobalt/bindings/testing/mozjs_test_dictionary.h"
+#include "cobalt/bindings/testing/dictionary_with_dictionary_member.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+#include "mozjs_gen_type_conversion.h"
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
@@ -36,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -48,6 +53,7 @@
namespace {
using cobalt::bindings::testing::DictionaryInterface;
using cobalt::bindings::testing::MozjsDictionaryInterface;
+using cobalt::bindings::testing::DictionaryWithDictionaryMember;
using cobalt::bindings::testing::TestDictionary;
using cobalt::script::CallbackInterfaceTraits;
using cobalt::script::GlobalEnvironment;
@@ -73,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
@@ -329,6 +336,71 @@
return !exception_state.is_exception_set();
}
+bool fcn_testOperation(
+ JSContext* context, uint32_t argc, JS::Value *vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ // Compute the 'this' value.
+ JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+ // 'this' should be an object.
+ JS::RootedObject object(context);
+ if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+ NOTREACHED();
+ return false;
+ }
+ if (!JS_ValueToObject(context, this_value, &object)) {
+ NOTREACHED();
+ return false;
+ }
+ const JSClass* proto_class =
+ MozjsDictionaryInterface::PrototypeClass(context);
+ if (proto_class == JS_GetClass(object)) {
+ // Simply returns true if the object is this class's prototype object.
+ // There is no need to return any value due to the object is not a platform
+ // object. The execution reaches here when Object.getOwnPropertyDescriptor
+ // gets called on native object prototypes.
+ return true;
+ }
+
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<DictionaryInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
+ MozjsExceptionState exception_state(context);
+ JS::RootedValue result_value(context);
+
+ WrapperPrivate* wrapper_private =
+ WrapperPrivate::GetFromObject(context, object);
+ DictionaryInterface* impl =
+ wrapper_private->wrappable<DictionaryInterface>().get();
+ const size_t kMinArguments = 1;
+ if (args.length() < kMinArguments) {
+ exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
+ return false;
+ }
+ // Non-optional arguments
+ TypeTraits<DictionaryWithDictionaryMember >::ConversionType dict;
+
+ DCHECK_LT(0, args.length());
+ JS::RootedValue non_optional_value0(
+ context, args[0]);
+ FromJSValue(context,
+ non_optional_value0,
+ kNoConversionFlags,
+ &exception_state, &dict);
+ if (exception_state.is_exception_set()) {
+ return false;
+ }
+
+ impl->TestOperation(dict);
+ result_value.set(JS::UndefinedHandleValue);
+ return !exception_state.is_exception_set();
+}
+
const JSPropertySpec prototype_properties[] = {
@@ -345,6 +417,9 @@
JS_FNSPEC(
"dictionaryOperation", fcn_dictionaryOperation, NULL,
1, JSPROP_ENUMERATE, NULL),
+ JS_FNSPEC(
+ "testOperation", fcn_testOperation, NULL,
+ 1, JSPROP_ENUMERATE, NULL),
JS_FS_END
};
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
new file mode 100644
index 0000000..5d193d4
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs45/templates/dictionary-conversion.cc.template
+
+#include "mozjs_gen_type_conversion.h"
+
+#include "cobalt/bindings/testing/dictionary_with_dictionary_member.h"
+
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "cobalt/bindings/testing/test_dictionary.h"
+
+using cobalt::bindings::testing::DictionaryWithDictionaryMember;
+using cobalt::bindings::testing::TestDictionary;
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+ JSContext* context,
+ const DictionaryWithDictionaryMember& in_dictionary,
+ JS::MutableHandleValue out_value) {
+ // Create a new object that will hold the dictionary values.
+ JS::RootedObject dictionary_object(
+ context, JS_NewObject(context, nullptr));
+ const int kPropertyAttributes = JSPROP_ENUMERATE;
+ if (in_dictionary.has_nested_dictionary()) {
+ JS::RootedValue member_value(context);
+ ToJSValue(context, in_dictionary.nested_dictionary(), &member_value);
+ if (!JS_DefineProperty(context, dictionary_object,
+ "nestedDictionary",
+ member_value, kPropertyAttributes, nullptr, nullptr)) {
+ // Some internal error occurred.
+ NOTREACHED();
+ return;
+ }
+ }
+ out_value.setObject(*dictionary_object);
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+ int conversion_flags, ExceptionState* exception_state,
+ DictionaryWithDictionaryMember* out_dictionary) {
+ DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+ // https://heycam.github.io/webidl/#es-dictionary
+
+ if (value.isUndefined() || value.isNull()) {
+ // The default constructor will assign appropriate values to dictionary
+ // members with default values and leave the others unset.
+ *out_dictionary = DictionaryWithDictionaryMember();
+ return;
+ }
+ if (!value.isObject()) {
+ // 1. If Type(V) is not Undefined, Null or Object, then throw a TypeError.
+ exception_state->SetSimpleException(kNotObjectType);
+ return;
+ }
+ JS::RootedObject dictionary_object(context, &value.toObject());
+ JS::RootedValue nested_dictionary(context);
+ if (!JS_GetProperty(context, dictionary_object,
+ "nestedDictionary",
+ &nested_dictionary)) {
+ exception_state->SetSimpleException(kSimpleError);
+ return;
+ }
+ if (!nested_dictionary.isUndefined()) {
+ TestDictionary converted_value;
+ FromJSValue(context,
+ nested_dictionary,
+ kNoConversionFlags,
+ exception_state,
+ &converted_value);
+ if (context->isExceptionPending()) {
+ return;
+ }
+ out_dictionary->set_nested_dictionary(converted_value);
+ }
+}
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
+
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
index eaf3ec7..59f0f29 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -73,6 +77,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
index 22e6f43..dcece70 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
index af0579c..e5b771e 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
@@ -24,6 +24,9 @@
#include "cobalt/script/global_environment.h"
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "cobalt/bindings/testing/test_enum.h"
+
+#include "mozjs_gen_type_conversion.h"
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
@@ -35,6 +38,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -47,6 +52,7 @@
namespace {
using cobalt::bindings::testing::EnumerationInterface;
using cobalt::bindings::testing::MozjsEnumerationInterface;
+using cobalt::bindings::testing::TestEnum;
using cobalt::script::CallbackInterfaceTraits;
using cobalt::script::GlobalEnvironment;
using cobalt::script::OpaqueHandle;
@@ -71,20 +77,12 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
using cobalt::script::mozjs::kNoConversionFlags;
-// Declare and define these in the same namespace that the other overloads
-// were brought into with the using declaration.
-void ToJSValue(
- JSContext* context,
- EnumerationInterface::TestEnum in_enum,
- JS::MutableHandleValue out_value);
-void FromJSValue(JSContext* context, JS::HandleValue value,
- int conversion_flags, ExceptionState* exception_state,
- EnumerationInterface::TestEnum* out_enum);
} // namespace
namespace cobalt {
@@ -256,7 +254,7 @@
WrapperPrivate::GetFromObject(context, object);
EnumerationInterface* impl =
wrapper_private->wrappable<EnumerationInterface>().get();
- TypeTraits<EnumerationInterface::TestEnum >::ConversionType value;
+ TypeTraits<TestEnum >::ConversionType value;
if (args.length() != 1) {
NOTREACHED();
return false;
@@ -272,6 +270,69 @@
return !exception_state.is_exception_set();
}
+bool fcn_optionalEnumWithDefault(
+ JSContext* context, uint32_t argc, JS::Value *vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ // Compute the 'this' value.
+ JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
+ // 'this' should be an object.
+ JS::RootedObject object(context);
+ if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
+ NOTREACHED();
+ return false;
+ }
+ if (!JS_ValueToObject(context, this_value, &object)) {
+ NOTREACHED();
+ return false;
+ }
+ const JSClass* proto_class =
+ MozjsEnumerationInterface::PrototypeClass(context);
+ if (proto_class == JS_GetClass(object)) {
+ // Simply returns true if the object is this class's prototype object.
+ // There is no need to return any value due to the object is not a platform
+ // object. The execution reaches here when Object.getOwnPropertyDescriptor
+ // gets called on native object prototypes.
+ return true;
+ }
+
+ 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);
+
+ WrapperPrivate* wrapper_private =
+ WrapperPrivate::GetFromObject(context, object);
+ EnumerationInterface* impl =
+ wrapper_private->wrappable<EnumerationInterface>().get();
+ // Optional arguments with default values
+ TypeTraits<TestEnum >::ConversionType value =
+ kTestEnumBeta;
+ size_t num_set_arguments = 1;
+ if (args.length() > 0) {
+ JS::RootedValue optional_value0(
+ context, args[0]);
+ FromJSValue(context,
+ optional_value0,
+ kNoConversionFlags,
+ &exception_state,
+ &value);
+ if (exception_state.is_exception_set()) {
+ return false;
+ }
+ }
+
+ impl->OptionalEnumWithDefault(value);
+ result_value.set(JS::UndefinedHandleValue);
+ return !exception_state.is_exception_set();
+}
+
const JSPropertySpec prototype_properties[] = {
@@ -285,6 +346,9 @@
};
const JSFunctionSpec prototype_functions[] = {
+ JS_FNSPEC(
+ "optionalEnumWithDefault", fcn_optionalEnumWithDefault, NULL,
+ 0, JSPROP_ENUMERATE, NULL),
JS_FS_END
};
@@ -479,57 +543,3 @@
} // namespace testing
} // namespace bindings
} // namespace cobalt
-
-namespace {
-
-inline void ToJSValue(
- JSContext* context,
- EnumerationInterface::TestEnum in_enum,
- JS::MutableHandleValue out_value) {
-
- switch (in_enum) {
- case EnumerationInterface::kAlpha:
- ToJSValue(context, std::string("alpha"), out_value);
- return;
- case EnumerationInterface::kBeta:
- ToJSValue(context, std::string("beta"), out_value);
- return;
- case EnumerationInterface::kGamma:
- ToJSValue(context, std::string("gamma"), out_value);
- return;
- default:
- NOTREACHED();
- out_value.set(JS::UndefinedValue());
- }
-}
-
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
- int conversion_flags, ExceptionState* exception_state,
- EnumerationInterface::TestEnum* out_enum) {
- DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
- // JSValue -> IDL enum algorithm described here:
- // http://heycam.github.io/webidl/#es-enumeration
- // 1. Let S be the result of calling ToString(V).
- JS::RootedString rooted_string(context, JS::ToString(context, value));
-
- bool match = false;
-// 3. Return the enumeration value of type E that is equal to S.
-if (JS_StringEqualsAscii(
- context, rooted_string, "alpha", &match)
- && match) {
- *out_enum = EnumerationInterface::kAlpha;
- } else if (JS_StringEqualsAscii(
- context, rooted_string, "beta", &match)
- && match) {
- *out_enum = EnumerationInterface::kBeta;
- } else if (JS_StringEqualsAscii(
- context, rooted_string, "gamma", &match)
- && match) {
- *out_enum = EnumerationInterface::kGamma;
- } else {
- // 2. If S is not one of E's enumeration values, then throw a TypeError.
- exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
- return;
- }
-}
-} // namespace
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
index 9d9e036..b24c533 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -72,6 +76,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
index a960a35..cd6f023 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
index a7b6263..82699e1 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
@@ -172,6 +177,96 @@
NULL,
};
+bool get_default(
+ JSContext* context, unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedObject object(context, &args.thisv().toObject());
+ const JSClass* proto_class =
+ MozjsExtendedIDLAttributesInterface::PrototypeClass(context);
+ if (proto_class == JS_GetClass(object)) {
+ // Simply returns true if the object is this class's prototype object.
+ // There is no need to return any value due to the object is not a platform
+ // object. The execution reaches here when Object.getOwnPropertyDescriptor
+ // gets called on native object prototypes.
+ return true;
+ }
+
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ExtendedIDLAttributesInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
+ MozjsExceptionState exception_state(context);
+ JS::RootedValue result_value(context);
+
+ WrapperPrivate* wrapper_private =
+ WrapperPrivate::GetFromObject(context, object);
+ ExtendedIDLAttributesInterface* impl =
+ wrapper_private->wrappable<ExtendedIDLAttributesInterface>().get();
+
+ if (!exception_state.is_exception_set()) {
+ ToJSValue(context,
+ impl->attribute_default(),
+ &result_value);
+ }
+ if (!exception_state.is_exception_set()) {
+ args.rval().set(result_value);
+ }
+ return !exception_state.is_exception_set();
+}
+
+bool set_default(
+ JSContext* context, unsigned argc, JS::Value* vp) {
+
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedObject object(context, &args.thisv().toObject());
+
+ const JSClass* proto_class =
+ MozjsExtendedIDLAttributesInterface::PrototypeClass(context);
+ if (proto_class == JS_GetClass(object)) {
+ // Simply returns true if the object is this class's prototype object.
+ // There is no need to return any value due to the object is not a platform
+ // object. The execution reaches here when Object.getOwnPropertyDescriptor
+ // gets called on native object prototypes.
+ return true;
+ }
+
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
+ if (!wrapper_factory->DoesObjectImplementInterface(
+ object, base::GetTypeId<ExtendedIDLAttributesInterface>())) {
+ MozjsExceptionState exception(context);
+ exception.SetSimpleException(script::kDoesNotImplementInterface);
+ return false;
+ }
+ MozjsExceptionState exception_state(context);
+ JS::RootedValue result_value(context);
+
+ WrapperPrivate* wrapper_private =
+ WrapperPrivate::GetFromObject(context, object);
+ ExtendedIDLAttributesInterface* impl =
+ wrapper_private->wrappable<ExtendedIDLAttributesInterface>().get();
+ TypeTraits<bool >::ConversionType value;
+ if (args.length() != 1) {
+ NOTREACHED();
+ return false;
+ }
+ FromJSValue(context, args[0], kNoConversionFlags, &exception_state,
+ &value);
+ if (exception_state.is_exception_set()) {
+ return false;
+ }
+
+ impl->set_attribute_default(value);
+ result_value.set(JS::UndefinedHandleValue);
+ return !exception_state.is_exception_set();
+}
+
bool fcn_callWithSettings(
JSContext* context, uint32_t argc, JS::Value *vp) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@@ -289,6 +384,12 @@
const JSPropertySpec prototype_properties[] = {
+ { // Read/Write property
+ "default",
+ JSPROP_SHARED | JSPROP_ENUMERATE,
+ { { &get_default, NULL } },
+ { { &set_default, NULL } },
+ },
JS_PS_END
};
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
index 39c62da..0c62a2d 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/garbage_collection_test_interface.h"
#include "cobalt/bindings/testing/mozjs_garbage_collection_test_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
index a91cfc5..f2001fc 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_get_opaque_root_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
index b7d243f..cd39673 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
index 63cde6c..77f6e6f 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
index 7f7880a..85578ca 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
index f1057fa..bf9a3d0 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
index 68e85ca..a719c77 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
index e15cfad..0367daa 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
index a4ac41e..5dea1d5 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
index d07df50..108fc17 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/mozjs_put_forwards_interface.h"
#include "cobalt/bindings/testing/put_forwards_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
index b38b987..35d8aae 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
index 03e32c0..d7bae5a 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
index 5f832ef..7b18b10 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
index 9809983..48dd07b 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
index fe85cd3..11db14d 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
@@ -31,6 +31,8 @@
#include "cobalt/bindings/testing/mozjs_base_interface.h"
#include "cobalt/bindings/testing/mozjs_derived_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -41,6 +43,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -83,6 +87,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
index ddab4a5..78ee810 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
index b918e34..bc07a75 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
index ac1e14e..b139037 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
index ebd378a..4473e30 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
index 017c4fe..3b0c942 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_single_operation_interface.cc
@@ -22,6 +22,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "cobalt/script/logging_exception_state.h"
#include "cobalt/script/mozjs-45/conversion_helpers.h"
#include "cobalt/script/mozjs-45/mozjs_callback_interface.h"
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
index 5717417..d03a770 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
@@ -27,6 +27,8 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -37,6 +39,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -75,6 +79,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
index f82e3b1..0699d67 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
index b2f806d..bd3097d 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
index 7c67a24..da67d25 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc
index ff6a0b7..f0da3bc 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc
@@ -25,6 +25,8 @@
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -35,6 +37,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -71,6 +75,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
similarity index 84%
rename from src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.h
rename to src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
index 197f039..98fc778 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.h
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
@@ -17,14 +17,20 @@
// clang-format off
// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
-// Auto-generated from template: bindings/mozjs45/templates/dictionary-conversion.h.template
+// Auto-generated from template: bindings/mozjs45/templates/dictionary-conversion.cc.template
-#ifndef TestDictionary_conversion_h
-#define TestDictionary_conversion_h
+#include "mozjs_gen_type_conversion.h"
#include "cobalt/bindings/testing/test_dictionary.h"
#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+#include "cobalt/bindings/testing/arbitrary_interface.h"
+#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
+
+using cobalt::bindings::testing::TestDictionary;
+using cobalt::bindings::testing::ArbitraryInterface;
+using cobalt::bindings::testing::MozjsArbitraryInterface;
namespace cobalt {
namespace script {
@@ -32,29 +38,6 @@
void ToJSValue(
JSContext* context,
- const cobalt::bindings::testing::TestDictionary& in_dictionary,
- JS::MutableHandleValue out_value);
-
-void FromJSValue(JSContext* context, JS::HandleValue value,
- int conversion_flags,
- cobalt::script::ExceptionState* exception_state,
- cobalt::bindings::testing::TestDictionary* out_dictionary);
-} // namespace mozjs
-} // namespace script
-} // namespace cobalt
-
-#include "cobalt/script/mozjs-45/conversion_helpers.h"
-#include "third_party/mozjs-45/js/src/jscntxt.h"
-
-using cobalt::bindings::testing::TestDictionary;
-using cobalt::bindings::testing::ArbitraryInterface;
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-
-inline void ToJSValue(
- JSContext* context,
const TestDictionary& in_dictionary,
JS::MutableHandleValue out_value) {
// Create a new object that will hold the dictionary values.
@@ -152,7 +135,7 @@
out_value.setObject(*dictionary_object);
}
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
+void FromJSValue(JSContext* context, JS::HandleValue value,
int conversion_flags, ExceptionState* exception_state,
TestDictionary* out_dictionary) {
DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
@@ -179,7 +162,11 @@
}
if (!boolean_member.isUndefined()) {
bool converted_value;
- FromJSValue(context, boolean_member, 0, exception_state, &converted_value);
+ FromJSValue(context,
+ boolean_member,
+ kNoConversionFlags,
+ exception_state,
+ &converted_value);
if (context->isExceptionPending()) {
return;
}
@@ -194,7 +181,11 @@
}
if (!short_clamp_member.isUndefined()) {
int16_t converted_value;
- FromJSValue(context, short_clamp_member, 0, exception_state, &converted_value);
+ FromJSValue(context,
+ short_clamp_member,
+ (kConversionFlagClamped),
+ exception_state,
+ &converted_value);
if (context->isExceptionPending()) {
return;
}
@@ -209,7 +200,11 @@
}
if (!long_member.isUndefined()) {
int32_t converted_value;
- FromJSValue(context, long_member, 0, exception_state, &converted_value);
+ FromJSValue(context,
+ long_member,
+ kNoConversionFlags,
+ exception_state,
+ &converted_value);
if (context->isExceptionPending()) {
return;
}
@@ -224,7 +219,11 @@
}
if (!double_member.isUndefined()) {
double converted_value;
- FromJSValue(context, double_member, 0, exception_state, &converted_value);
+ FromJSValue(context,
+ double_member,
+ (kConversionFlagRestricted),
+ exception_state,
+ &converted_value);
if (context->isExceptionPending()) {
return;
}
@@ -239,7 +238,11 @@
}
if (!string_member.isUndefined()) {
std::string converted_value;
- FromJSValue(context, string_member, 0, exception_state, &converted_value);
+ FromJSValue(context,
+ string_member,
+ kNoConversionFlags,
+ exception_state,
+ &converted_value);
if (context->isExceptionPending()) {
return;
}
@@ -254,7 +257,11 @@
}
if (!interface_member.isUndefined()) {
scoped_refptr<ArbitraryInterface> converted_value;
- FromJSValue(context, interface_member, 0, exception_state, &converted_value);
+ FromJSValue(context,
+ interface_member,
+ kNoConversionFlags,
+ exception_state,
+ &converted_value);
if (context->isExceptionPending()) {
return;
}
@@ -269,7 +276,11 @@
}
if (!member_with_default.isUndefined()) {
int32_t converted_value;
- FromJSValue(context, member_with_default, 0, exception_state, &converted_value);
+ FromJSValue(context,
+ member_with_default,
+ kNoConversionFlags,
+ exception_state,
+ &converted_value);
if (context->isExceptionPending()) {
return;
}
@@ -284,7 +295,11 @@
}
if (!non_default_member.isUndefined()) {
int32_t converted_value;
- FromJSValue(context, non_default_member, 0, exception_state, &converted_value);
+ FromJSValue(context,
+ non_default_member,
+ kNoConversionFlags,
+ exception_state,
+ &converted_value);
if (context->isExceptionPending()) {
return;
}
@@ -292,8 +307,7 @@
}
}
-}
-}
-}
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
-#endif // TestDictionary_h
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_enum.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_enum.cc
new file mode 100644
index 0000000..7aa9e08
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_enum.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/mozjs45/templates/enumeration-conversion.cc.template
+
+#include "cobalt/bindings/testing/test_enum.h"
+
+#include "mozjs_gen_type_conversion.h"
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+using cobalt::bindings::testing::TestEnum;
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+ JSContext* context,
+ TestEnum in_enum,
+ JS::MutableHandleValue out_value) {
+ switch (in_enum) {
+ case cobalt::bindings::testing::kTestEnumAlpha:
+ ToJSValue(context, std::string("alpha"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumBeta:
+ ToJSValue(context, std::string("beta"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumGamma:
+ ToJSValue(context, std::string("gamma"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumEnumWithDashes:
+ ToJSValue(context, std::string("enum-with-dashes"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumEnumWithSpaces:
+ ToJSValue(context, std::string("enum with spaces"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumTerribleEnum:
+ ToJSValue(context, std::string("terrible----enum"), out_value);
+ return;
+ case cobalt::bindings::testing::kTestEnumThisIsATerribleEnum:
+ ToJSValue(context, std::string("this is a terrible @#$%#$% enum"), out_value);
+ return;
+ default:
+ NOTREACHED();
+ out_value.set(JS::UndefinedValue());
+ }
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+ int conversion_flags, ExceptionState* exception_state,
+ TestEnum* out_enum) {
+ DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+ // JSValue -> IDL enum algorithm described here:
+ // http://heycam.github.io/webidl/#es-enumeration
+ // 1. Let S be the result of calling ToString(V).
+ JS::RootedString rooted_string(context, JS::ToString(context, value));
+
+ bool match = false;
+// 3. Return the enumeration value of type E that is equal to S.
+if (JS_StringEqualsAscii(
+ context, rooted_string, "alpha", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumAlpha;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "beta", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumBeta;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "gamma", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumGamma;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "enum-with-dashes", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumEnumWithDashes;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "enum with spaces", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumEnumWithSpaces;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "terrible----enum", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumTerribleEnum;
+ } else if (JS_StringEqualsAscii(
+ context, rooted_string, "this is a terrible @#$%#$% enum", &match)
+ && match) {
+ *out_enum = cobalt::bindings::testing::kTestEnumThisIsATerribleEnum;
+ } else {
+ // 2. If S is not one of E's enumeration values, then throw a TypeError.
+ exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
+ return;
+ }
+}
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
index 1934f7f..9d1fe7b 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
@@ -29,6 +29,8 @@
#include "cobalt/bindings/testing/mozjs_arbitrary_interface.h"
#include "cobalt/bindings/testing/mozjs_base_interface.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -39,6 +41,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -79,6 +83,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
index a16151c..c26f450 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
@@ -123,6 +123,8 @@
#include "cobalt/bindings/testing/union_types_interface.h"
#include "cobalt/bindings/testing/window.h"
+#include "mozjs_gen_type_conversion.h"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -133,6 +135,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -275,6 +279,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
@@ -1055,6 +1060,7 @@
MozjsWindow::CreateProxy(
context, global_interface);
mozjs_global_environment->SetEnvironmentSettings(environment_settings);
+ mozjs_global_environment->EvaluateAutomatics();
WrapperFactory* wrapper_factory =
mozjs_global_environment->wrapper_factory();
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h
index 4dc5055..4c36160 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h
@@ -27,6 +27,8 @@
#include "cobalt/script/sequence.h"
#include "cobalt/bindings/testing/arbitrary_interface.h"
+using cobalt::bindings::testing::ArbitraryInterface;
+
namespace cobalt {
namespace bindings {
namespace testing {
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_enum.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_enum.h
new file mode 100644
index 0000000..5e4bb69
--- /dev/null
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_enum.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
+// Auto-generated from template: bindings/templates/enumeration.h.template
+
+#ifndef TestEnum_h
+#define TestEnum_h
+
+namespace cobalt {
+namespace bindings {
+namespace testing {
+
+enum TestEnum {
+ kTestEnumAlpha,
+ kTestEnumBeta,
+ kTestEnumGamma,
+ kTestEnumEnumWithDashes,
+ kTestEnumEnumWithSpaces,
+ kTestEnumTerribleEnum,
+ kTestEnumThisIsATerribleEnum,
+};
+
+} // namespace cobalt
+} // namespace bindings
+} // namespace testing
+
+#endif // TestEnum_h
diff --git a/src/cobalt/bindings/mozjs/generate_conversion_header_mozjs.py b/src/cobalt/bindings/mozjs/generate_conversion_header_mozjs.py
new file mode 100644
index 0000000..b43e30c
--- /dev/null
+++ b/src/cobalt/bindings/mozjs/generate_conversion_header_mozjs.py
@@ -0,0 +1,24 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generate a conversion header for SpiderMonkey."""
+
+import sys
+
+import bootstrap_path # pylint: disable=g-bad-import-order,unused-import
+
+from cobalt.bindings.generate_conversion_header import generate_header
+from cobalt.bindings.mozjs.code_generator_mozjs import CodeGeneratorMozjs
+
+if __name__ == '__main__':
+ sys.exit(generate_header(CodeGeneratorMozjs))
diff --git a/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template b/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template
index 0d2272c..f3031f9 100644
--- a/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template
+++ b/src/cobalt/bindings/mozjs/templates/callback-interface.cc.template
@@ -17,6 +17,8 @@
{% block includes %}
{{ super() }}
+#include "{{generated_conversion_include}}"
+
#include "cobalt/script/logging_exception_state.h"
#include "cobalt/script/mozjs/conversion_helpers.h"
#include "cobalt/script/mozjs/mozjs_callback_interface.h"
diff --git a/src/cobalt/bindings/mozjs/templates/dictionary-conversion.h.template b/src/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template
similarity index 85%
rename from src/cobalt/bindings/mozjs/templates/dictionary-conversion.h.template
rename to src/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template
index 9872892..cc878eb 100644
--- a/src/cobalt/bindings/mozjs/templates/dictionary-conversion.h.template
+++ b/src/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template
@@ -34,37 +34,18 @@
// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
// Auto-generated from template: {{template_path}}
-#ifndef {{class_name}}_conversion_h
-#define {{class_name}}_conversion_h
-
{% if conditional %}
#if defined({{conditional}})
{% endif %}
{% block includes %}
+#include "{{generated_conversion_include}}"
+
#include "{{header_file}}"
#include "cobalt/script/exception_state.h"
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-
-void ToJSValue(
- JSContext* context,
- const {{components|join('::')}}::{{class_name}}& in_dictionary,
- JS::MutableHandleValue out_value);
-
-void FromJSValue(JSContext* context, JS::HandleValue value,
- int conversion_flags,
- cobalt::script::ExceptionState* exception_state,
- {{components|join('::')}}::{{class_name}}* out_dictionary);
-} // namespace mozjs
-} // namespace script
-} // namespace cobalt
-
-#include "cobalt/script/mozjs/conversion_helpers.h"
-{% for include in header_includes %}
+#include "third_party/mozjs/js/src/jsapi.h"
+{% for include in includes %}
#include "{{include}}"
{% endfor %}
{% endblock includes %}
@@ -84,7 +65,7 @@
namespace script {
namespace mozjs {
-inline void ToJSValue(
+void ToJSValue(
JSContext* context,
const {{class_name}}& in_dictionary,
JS::MutableHandleValue out_value) {
@@ -108,7 +89,7 @@
out_value.set(OBJECT_TO_JSVAL(dictionary_object));
}
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
+void FromJSValue(JSContext* context, JS::HandleValue value,
int conversion_flags, ExceptionState* exception_state,
{{class_name}}* out_dictionary) {
DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
@@ -155,6 +136,4 @@
{% if conditional %}
#endif // defined({{conditional}})
-
{% endif %}
-#endif // {{class_name}}_h
diff --git a/src/cobalt/bindings/mozjs/templates/enumeration-conversion.cc.template b/src/cobalt/bindings/mozjs/templates/enumeration-conversion.cc.template
new file mode 100644
index 0000000..3e4df11
--- /dev/null
+++ b/src/cobalt/bindings/mozjs/templates/enumeration-conversion.cc.template
@@ -0,0 +1,90 @@
+{#
+ # Copyright 2017 Google Inc. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #}
+/*
+ * Copyright {{today.year}} Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
+// Auto-generated from template: {{template_path}}
+
+#include "{{enum_include}}"
+
+#include "{{generated_conversion_include}}"
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+
+using {{fully_qualified_name}};
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+ JSContext* context,
+ {{enumeration_name}} in_enum,
+ JS::MutableHandleValue out_value) {
+ switch (in_enum) {
+{% for value, idl_value in value_pairs %}
+ case {{namespace}}::{{value}}:
+ ToJSValue(context, std::string("{{idl_value}}"), out_value);
+ return;
+{% endfor %}
+ default:
+ NOTREACHED();
+ out_value.set(JS::UndefinedValue());
+ }
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+ int conversion_flags, ExceptionState* exception_state,
+ {{enumeration_name}}* out_enum) {
+ DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+ // JSValue -> IDL enum algorithm described here:
+ // http://heycam.github.io/webidl/#es-enumeration
+ // 1. Let S be the result of calling ToString(V).
+ JS::RootedString rooted_string(context, JS_ValueToString(context, value));
+
+ JSBool match = JS_FALSE;
+// 3. Return the enumeration value of type E that is equal to S.
+{% for value, idl_value in value_pairs %}
+ {{-" else " if not loop.first}}if (JS_StringEqualsAscii(
+ context, rooted_string, "{{idl_value}}", &match)
+ && match) {
+ *out_enum = {{namespace}}::{{value}};
+ }{% endfor %} else {
+ // 2. If S is not one of E's enumeration values, then throw a TypeError.
+ exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
+ return;
+ }
+}
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
diff --git a/src/cobalt/bindings/mozjs/templates/generated-types.h.template b/src/cobalt/bindings/mozjs/templates/generated-types.h.template
new file mode 100644
index 0000000..d34d409
--- /dev/null
+++ b/src/cobalt/bindings/mozjs/templates/generated-types.h.template
@@ -0,0 +1,92 @@
+{#
+ # Copyright 2017 Google Inc. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #}
+/*
+ * Copyright {{today.year}} Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
+// Auto-generated from template: {{template_path}}
+
+#ifndef GENERATED_TYPE_CONVERSION_H_
+#define GENERATED_TYPE_CONVERSION_H_
+
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs/js/src/jsapi.h"
+
+// #includes for generated types.
+{% for path in includes %}
+#include "{{path}}"
+{% endfor %}
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+{% for dictionary in dictionaries %}
+// {{dictionary.fully_qualified_name}} -> JSValue
+void ToJSValue(
+ JSContext* context,
+ const {{dictionary.fully_qualified_name}}& in_value,
+ JS::MutableHandleValue out_value);
+
+// JSValue -> {{dictionary.fully_qualified_name}}
+void FromJSValue(
+ JSContext* context,
+ JS::HandleValue value,
+ int conversion_flags,
+ ExceptionState* exception_state,
+ {{dictionary.fully_qualified_name}}* out_value);
+
+{% endfor %}
+{% for enum in enumerations %}
+// {{enum.fully_qualified_name}} -> JSValue
+void ToJSValue(
+ JSContext* context,
+ {{enum.fully_qualified_name}} in_value,
+ JS::MutableHandleValue out_value);
+
+// JSValue -> {{enum.fully_qualified_name}}
+void FromJSValue(
+ JSContext* context,
+ JS::HandleValue value,
+ int conversion_flags,
+ ExceptionState* exception_state,
+ {{enum.fully_qualified_name}}* out_value);
+
+{% endfor %}
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
+
+// #include this here so the conversion functions for generated types are
+// visible refer to template types, such as sequence<T>.
+#include "cobalt/script/mozjs/conversion_helpers.h"
+
+#endif // GENERATED_TYPE_CONVERSION_H_
diff --git a/src/cobalt/bindings/mozjs/templates/interface.cc.template b/src/cobalt/bindings/mozjs/templates/interface.cc.template
index 92af4f7..12ad4c0 100644
--- a/src/cobalt/bindings/mozjs/templates/interface.cc.template
+++ b/src/cobalt/bindings/mozjs/templates/interface.cc.template
@@ -26,6 +26,8 @@
{% extends "interface-base.cc.template" %}
{% block includes %}
{{ super() }}
+#include "{{generated_conversion_include}}"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs/callback_function_conversion.h"
@@ -75,21 +77,6 @@
using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString;
using cobalt::script::mozjs::kNoConversionFlags;
{% endblock using_directives %}
-{% block enumeration_declarations %}
-{% if enumerations|length %}
-// Declare and define these in the same namespace that the other overloads
-// were brought into with the using declaration.
-{% for enumeration in enumerations %}
-void ToJSValue(
- JSContext* context,
- {{impl_class}}::{{enumeration.name}} in_enum,
- JS::MutableHandleValue out_value);
-void FromJSValue(JSContext* context, JS::HandleValue value,
- int conversion_flags, ExceptionState* exception_state,
- {{impl_class}}::{{enumeration.name}}* out_enum);
-{% endfor %}
-{% endif %}
-{% endblock enumeration_declarations %}
{% block top_level_unnamed_namespace %}
{% if is_global_interface %}
JSObject* DummyFunctor(
@@ -975,49 +962,3 @@
{% endfor %}
{% endblock create_global_object_impl %}
-
-{% block enumeration_definitions %}
-{% for enumeration in enumerations %}
-
-inline void ToJSValue(
- JSContext* context,
- {{impl_class}}::{{enumeration.name}} in_enum,
- JS::MutableHandleValue out_value) {
-
- switch (in_enum) {
-{% for value, idl_value in enumeration.value_pairs %}
- case {{impl_class}}::{{value}}:
- ToJSValue(context, std::string("{{idl_value}}"), out_value);
- return;
-{% endfor %}
- default:
- NOTREACHED();
- out_value.set(JS::UndefinedValue());
- }
-}
-
-
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
- int conversion_flags, ExceptionState* exception_state,
- {{impl_class}}::{{enumeration.name}}* out_enum) {
- DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
- // JSValue -> IDL enum algorithm described here:
- // http://heycam.github.io/webidl/#es-enumeration
- // 1. Let S be the result of calling ToString(V).
- JS::RootedString rooted_string(context, JS_ValueToString(context, value));
-
- JSBool match = JS_FALSE;
-// 3. Return the enumeration value of type E that is equal to S.
-{% for value, idl_value in enumeration.value_pairs %}
- {{-" else " if not loop.first}}if (JS_StringEqualsAscii(
- context, rooted_string, "{{idl_value}}", &match)
- && match) {
- *out_enum = {{impl_class}}::{{value}};
- }{% endfor %} else {
- // 2. If S is not one of E's enumeration values, then throw a TypeError.
- exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
- return;
- }
-}
-{% endfor %}
-{% endblock enumeration_definitions %}
diff --git a/src/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py b/src/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py
new file mode 100644
index 0000000..d149eee
--- /dev/null
+++ b/src/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py
@@ -0,0 +1,24 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generate a conversion header for SpiderMonkey."""
+
+import sys
+
+import bootstrap_path # pylint: disable=g-bad-import-order,unused-import
+
+from cobalt.bindings.generate_conversion_header import generate_header
+from cobalt.bindings.mozjs45.code_generator_mozjs45 import CodeGeneratorMozjs45
+
+if __name__ == '__main__':
+ sys.exit(generate_header(CodeGeneratorMozjs45))
diff --git a/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template b/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template
index 3087785..04f6ccd 100644
--- a/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template
+++ b/src/cobalt/bindings/mozjs45/templates/callback-interface.cc.template
@@ -17,6 +17,8 @@
{% block includes %}
{{ super() }}
+#include "{{generated_conversion_include}}"
+
#include "cobalt/script/logging_exception_state.h"
#include "cobalt/script/mozjs-45/conversion_helpers.h"
#include "cobalt/script/mozjs-45/mozjs_callback_interface.h"
diff --git a/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.h.template b/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
similarity index 84%
rename from src/cobalt/bindings/mozjs45/templates/dictionary-conversion.h.template
rename to src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
index 5b8b907..f276cbc 100644
--- a/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.h.template
+++ b/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
@@ -34,38 +34,18 @@
// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
// Auto-generated from template: {{template_path}}
-#ifndef {{class_name}}_conversion_h
-#define {{class_name}}_conversion_h
-
{% if conditional %}
#if defined({{conditional}})
{% endif %}
{% block includes %}
+#include "{{generated_conversion_include}}"
+
#include "{{header_file}}"
#include "cobalt/script/exception_state.h"
-
-namespace cobalt {
-namespace script {
-namespace mozjs {
-
-void ToJSValue(
- JSContext* context,
- const {{components|join('::')}}::{{class_name}}& in_dictionary,
- JS::MutableHandleValue out_value);
-
-void FromJSValue(JSContext* context, JS::HandleValue value,
- int conversion_flags,
- cobalt::script::ExceptionState* exception_state,
- {{components|join('::')}}::{{class_name}}* out_dictionary);
-} // namespace mozjs
-} // namespace script
-} // namespace cobalt
-
-#include "cobalt/script/mozjs-45/conversion_helpers.h"
-#include "third_party/mozjs-45/js/src/jscntxt.h"
-{% for include in header_includes %}
+#include "third_party/mozjs-45/js/src/jsapi.h"
+{% for include in includes %}
#include "{{include}}"
{% endfor %}
{% endblock includes %}
@@ -85,7 +65,7 @@
namespace script {
namespace mozjs {
-inline void ToJSValue(
+void ToJSValue(
JSContext* context,
const {{class_name}}& in_dictionary,
JS::MutableHandleValue out_value) {
@@ -109,7 +89,7 @@
out_value.setObject(*dictionary_object);
}
-inline void FromJSValue(JSContext* context, JS::HandleValue value,
+void FromJSValue(JSContext* context, JS::HandleValue value,
int conversion_flags, ExceptionState* exception_state,
{{class_name}}* out_dictionary) {
DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
@@ -137,7 +117,11 @@
}
if (!{{member.name}}.isUndefined()) {
{{member.type}} converted_value;
- FromJSValue(context, {{member.name}}, 0, exception_state, &converted_value);
+ FromJSValue(context,
+ {{member.name}},
+ {{member.conversion_flags}},
+ exception_state,
+ &converted_value);
if (context->isExceptionPending()) {
return;
}
@@ -146,12 +130,10 @@
{% endfor %}
}
-}
-}
-}
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
{% if conditional %}
#endif // defined({{conditional}})
-
{% endif %}
-#endif // {{class_name}}_h
diff --git a/src/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template b/src/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template
new file mode 100644
index 0000000..0c708c5
--- /dev/null
+++ b/src/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template
@@ -0,0 +1,90 @@
+{#
+ # Copyright 2017 Google Inc. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #}
+/*
+ * Copyright {{today.year}} Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
+// Auto-generated from template: {{template_path}}
+
+#include "{{enum_include}}"
+
+#include "{{generated_conversion_include}}"
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+using {{fully_qualified_name}};
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+void ToJSValue(
+ JSContext* context,
+ {{enumeration_name}} in_enum,
+ JS::MutableHandleValue out_value) {
+ switch (in_enum) {
+{% for value, idl_value in value_pairs %}
+ case {{namespace}}::{{value}}:
+ ToJSValue(context, std::string("{{idl_value}}"), out_value);
+ return;
+{% endfor %}
+ default:
+ NOTREACHED();
+ out_value.set(JS::UndefinedValue());
+ }
+}
+
+void FromJSValue(JSContext* context, JS::HandleValue value,
+ int conversion_flags, ExceptionState* exception_state,
+ {{enumeration_name}}* out_enum) {
+ DCHECK_EQ(0, conversion_flags) << "Unexpected conversion flags.";
+ // JSValue -> IDL enum algorithm described here:
+ // http://heycam.github.io/webidl/#es-enumeration
+ // 1. Let S be the result of calling ToString(V).
+ JS::RootedString rooted_string(context, JS::ToString(context, value));
+
+ bool match = false;
+// 3. Return the enumeration value of type E that is equal to S.
+{% for value, idl_value in value_pairs %}
+ {{-" else " if not loop.first}}if (JS_StringEqualsAscii(
+ context, rooted_string, "{{idl_value}}", &match)
+ && match) {
+ *out_enum = {{namespace}}::{{value}};
+ }{% endfor %} else {
+ // 2. If S is not one of E's enumeration values, then throw a TypeError.
+ exception_state->SetSimpleException(cobalt::script::kConvertToEnumFailed);
+ return;
+ }
+}
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
diff --git a/src/cobalt/bindings/mozjs45/templates/generated-types.h.template b/src/cobalt/bindings/mozjs45/templates/generated-types.h.template
new file mode 100644
index 0000000..96d2650
--- /dev/null
+++ b/src/cobalt/bindings/mozjs45/templates/generated-types.h.template
@@ -0,0 +1,92 @@
+{#
+ # Copyright 2017 Google Inc. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #}
+/*
+ * Copyright {{today.year}} Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
+// Auto-generated from template: {{template_path}}
+
+#ifndef GENERATED_TYPE_CONVERSION_H_
+#define GENERATED_TYPE_CONVERSION_H_
+
+#include "cobalt/script/exception_state.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+// #includes for generated types.
+{% for path in includes %}
+#include "{{path}}"
+{% endfor %}
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+{% for dictionary in dictionaries %}
+// {{dictionary.fully_qualified_name}} -> JSValue
+void ToJSValue(
+ JSContext* context,
+ const {{dictionary.fully_qualified_name}}& in_value,
+ JS::MutableHandleValue out_value);
+
+// JSValue -> {{dictionary.fully_qualified_name}}
+void FromJSValue(
+ JSContext* context,
+ JS::HandleValue value,
+ int conversion_flags,
+ ExceptionState* exception_state,
+ {{dictionary.fully_qualified_name}}* out_value);
+
+{% endfor %}
+{% for enum in enumerations %}
+// {{enum.fully_qualified_name}} -> JSValue
+void ToJSValue(
+ JSContext* context,
+ {{enum.fully_qualified_name}} in_value,
+ JS::MutableHandleValue out_value);
+
+// JSValue -> {{enum.fully_qualified_name}}
+void FromJSValue(
+ JSContext* context,
+ JS::HandleValue value,
+ int conversion_flags,
+ ExceptionState* exception_state,
+ {{enum.fully_qualified_name}}* out_value);
+
+{% endfor %}
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
+
+// #include this here so the conversion functions for generated types are
+// visible refer to template types, such as sequence<T>.
+#include "cobalt/script/mozjs-45/conversion_helpers.h"
+
+#endif // GENERATED_TYPE_CONVERSION_H_
diff --git a/src/cobalt/bindings/mozjs45/templates/interface.cc.template b/src/cobalt/bindings/mozjs45/templates/interface.cc.template
index 4e2839b..6296ec9 100644
--- a/src/cobalt/bindings/mozjs45/templates/interface.cc.template
+++ b/src/cobalt/bindings/mozjs45/templates/interface.cc.template
@@ -26,6 +26,8 @@
{% extends "interface-base.cc.template" %}
{% block includes %}
{{ super() }}
+#include "{{generated_conversion_include}}"
+
#include "base/lazy_instance.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/mozjs-45/callback_function_conversion.h"
@@ -36,6 +38,8 @@
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/wrapper_factory.h"
@@ -66,6 +70,7 @@
using cobalt::script::mozjs::TypeTraits;
using cobalt::script::mozjs::WrapperFactory;
using cobalt::script::mozjs::WrapperPrivate;
+using cobalt::script::mozjs::kConversionFlagClamped;
using cobalt::script::mozjs::kConversionFlagNullable;
using cobalt::script::mozjs::kConversionFlagRestricted;
using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString;
@@ -949,6 +954,7 @@
{{binding_class}}::CreateProxy(
context, global_interface);
mozjs_global_environment->SetEnvironmentSettings(environment_settings);
+ mozjs_global_environment->EvaluateAutomatics();
WrapperFactory* wrapper_factory =
mozjs_global_environment->wrapper_factory();
diff --git a/src/cobalt/bindings/name_conversion.py b/src/cobalt/bindings/name_conversion.py
index 2a0c885..917b760 100644
--- a/src/cobalt/bindings/name_conversion.py
+++ b/src/cobalt/bindings/name_conversion.py
@@ -55,6 +55,7 @@
def convert_to_cobalt_name(class_name):
+ """Convert an IDL attribute name to Cobalt naming convention."""
replacement = []
for token in special_token_re.split(class_name):
@@ -78,10 +79,10 @@
for token in constant_name.split('_')))
-def convert_to_cobalt_enumeration_value(enum_value):
- return 'k' + ''.join(
+def convert_to_cobalt_enumeration_value(enum_type, enum_value):
+ return 'k%s%s' % (enum_type, ''.join(
(token.capitalize()
- for token in enumeration_value_word_delimeter_re.split(enum_value)))
+ for token in enumeration_value_word_delimeter_re.split(enum_value))))
def get_interface_name(idl_type):
diff --git a/src/cobalt/bindings/path_generator.py b/src/cobalt/bindings/path_generator.py
index 6f3a089..46206cb 100644
--- a/src/cobalt/bindings/path_generator.py
+++ b/src/cobalt/bindings/path_generator.py
@@ -28,32 +28,53 @@
class PathBuilder(object):
"""Provides helper functions for getting paths related to an interface."""
- def __init__(self, engine_prefix, interfaces_info, interfaces_root,
+ def __init__(self, engine_prefix, info_provider, interfaces_root,
generated_root_directory):
self.interfaces_root = _NormalizeSlashes(interfaces_root)
self.generated_root = _NormalizeSlashes(generated_root_directory)
self.engine_prefix = engine_prefix
- self.interfaces_info = interfaces_info
+ self.info_provider = info_provider
+ self.interfaces_info = info_provider.interfaces_info
+
+ @property
+ def generated_conversion_header_path(self):
+ return os.path.join(self.generated_root,
+ '%s_gen_type_conversion.h' % self.engine_prefix)
+
+ @property
+ def generated_conversion_include_path(self):
+ return os.path.relpath(self.generated_conversion_header_path,
+ self.generated_root)
def NamespaceComponents(self, interface_name):
"""Get the interface's namespace as a list of namespace components."""
# Get the IDL filename relative to the cobalt directory, and split the
# directory to get the list of namespace components.
- interface_info = self.interfaces_info[interface_name]
- idl_path = interface_info['full_path']
+ if interface_name in self.interfaces_info:
+ interface_info = self.interfaces_info[interface_name]
+ idl_path = interface_info['full_path']
+ elif interface_name in self.info_provider.enumerations:
+ enum_info = self.info_provider.enumerations[interface_name]
+ idl_path = enum_info['full_path']
+ else:
+ raise KeyError('Unknown interface name %s', interface_name)
idl_path = os.path.relpath(idl_path, self.interfaces_root)
components = os.path.dirname(idl_path).split(os.sep)
return [os.path.basename(self.interfaces_root)] + components
+ def Namespace(self, interface_name):
+ """Get the interface's namespace."""
+ return '::'.join(self.NamespaceComponents(interface_name))
+
def BindingsClass(self, interface_name):
"""Get the name of the generated bindings class."""
return self.engine_prefix.capitalize() + interface_name
def FullBindingsClassName(self, interface_name):
"""Get the fully qualified name of the generated bindings class."""
- components = self.NamespaceComponents(interface_name)
- return '::'.join(components + [self.BindingsClass(interface_name)])
+ return '%s::%s' % (self.Namespace(interface_name),
+ self.BindingsClass(interface_name))
def FullClassName(self, interface_name):
"""Get the fully qualified name of the implementation class."""
@@ -109,12 +130,7 @@
output_extension='h',
base_directory=os.path.dirname(self.interfaces_root))
- def DictionaryConversionHeaderIncludePath(self, dictionary_name):
- """Get the #include path to the dictionary's conversion header."""
- path = self.BindingsHeaderFullPath(dictionary_name)
- return os.path.relpath(path, self.generated_root)
-
- def DictionaryConversionHeaderFullPath(self, dictionary_name):
+ def DictionaryConversionImplementationPath(self, dictionary_name):
"""Get the full path to the dictionary's conversion header."""
interface_info = self.interfaces_info[dictionary_name]
return ConvertPath(
@@ -122,5 +138,31 @@
forward_slashes=True,
output_directory=self.generated_root,
output_prefix='%s_' % self.engine_prefix,
+ output_extension='cc',
+ base_directory=os.path.dirname(self.interfaces_root))
+
+ def EnumHeaderIncludePath(self, enum_name):
+ """Get the #include path to the dictionary's header."""
+ path = self.EnumHeaderFullPath(enum_name)
+ return os.path.relpath(path, self.generated_root)
+
+ def EnumHeaderFullPath(self, enum_name):
+ """Get the full path to the dictionary's generated implementation header."""
+ interface_info = self.info_provider.enumerations[enum_name]
+ return ConvertPath(
+ interface_info['full_path'],
+ forward_slashes=True,
+ output_directory=self.generated_root,
output_extension='h',
base_directory=os.path.dirname(self.interfaces_root))
+
+ def EnumConversionImplementationFullPath(self, enum_name):
+ """Get the full path to the dictionary's conversion header."""
+ interface_info = self.info_provider.enumerations[enum_name]
+ return ConvertPath(
+ interface_info['full_path'],
+ forward_slashes=True,
+ output_directory=self.generated_root,
+ output_prefix='%s_' % self.engine_prefix,
+ output_extension='cc',
+ base_directory=os.path.dirname(self.interfaces_root))
diff --git a/src/cobalt/bindings/templates/dictionary.h.template b/src/cobalt/bindings/templates/dictionary.h.template
index 01eca9e..2999e8b 100644
--- a/src/cobalt/bindings/templates/dictionary.h.template
+++ b/src/cobalt/bindings/templates/dictionary.h.template
@@ -48,6 +48,16 @@
#include "{{include}}"
{% endfor %}
+{% for used_class in forward_declarations %}
+{% if used_class.conditional %}
+#if defined({{used_class.conditional}})
+{% endif %}
+using {{used_class.fully_qualified_name}};
+{% if used_class.conditional %}
+#endif // defined({{used_class.conditional}})
+{% endif %}
+{% endfor %}
+
{% for component in components %}
namespace {{component}} {
{% endfor %}
diff --git a/src/cobalt/bindings/templates/enumeration.h.template b/src/cobalt/bindings/templates/enumeration.h.template
new file mode 100644
index 0000000..d161069
--- /dev/null
+++ b/src/cobalt/bindings/templates/enumeration.h.template
@@ -0,0 +1,54 @@
+{#
+ # Copyright 2017 Google Inc. All Rights Reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+ # you may not use this file except in compliance with the License.
+ # You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing, software
+ # distributed under the License is distributed on an "AS IS" BASIS,
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ # See the License for the specific language governing permissions and
+ # limitations under the License.
+ #}
+/*
+ * Copyright {{today.year}} Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// clang-format off
+
+// This file has been auto-generated by {{code_generator}}. DO NOT MODIFY!
+// Auto-generated from template: {{template_path}}
+
+#ifndef {{enumeration_name}}_h
+#define {{enumeration_name}}_h
+
+{% for component in components %}
+namespace {{component}} {
+{% endfor %}
+
+enum {{enumeration_name}} {
+{% for value, idl_value in value_pairs %}
+ {{value}},
+{% endfor %}
+};
+
+{% for component in components %}
+} // namespace {{component}}
+{% endfor %}
+
+#endif // {{enumeration_name}}_h
diff --git a/src/cobalt/bindings/templates/interface-base.cc.template b/src/cobalt/bindings/templates/interface-base.cc.template
index dfe5675..cf3ccdf 100644
--- a/src/cobalt/bindings/templates/interface-base.cc.template
+++ b/src/cobalt/bindings/templates/interface-base.cc.template
@@ -70,10 +70,6 @@
using cobalt::script::ValueHandle;
using cobalt::script::Wrappable;
{% endblock using_directives %}
-{% if enumerations|length %}
-{% block enumeration_declarations %}
-{% endblock enumeration_declarations %}
-{% endif %}
{% block top_level_unnamed_namespace %}
{% endblock top_level_unnamed_namespace %}
} // namespace
@@ -104,13 +100,6 @@
} // namespace script
} // namespace cobalt
{% endif %}
-{% if enumerations|length %}
-
-namespace {
-{% block enumeration_definitions %}
-{% endblock enumeration_definitions %}
-} // namespace
-{% endif %}
{% if conditional %}
#endif // defined({{conditional}})
{% endif %}
diff --git a/src/cobalt/bindings/testing/bindings_test_base.h b/src/cobalt/bindings/testing/bindings_test_base.h
index f6e42a2..768dfb3 100644
--- a/src/cobalt/bindings/testing/bindings_test_base.h
+++ b/src/cobalt/bindings/testing/bindings_test_base.h
@@ -47,8 +47,7 @@
BindingsTestBase()
: environment_settings_(new script::EnvironmentSettings),
engine_(script::JavaScriptEngine::CreateEngine()),
- global_environment_(engine_->CreateGlobalEnvironment(
- script::JavaScriptEngine::Options())),
+ global_environment_(engine_->CreateGlobalEnvironment()),
window_(new Window()) {
global_environment_->CreateGlobalObject(window_,
environment_settings_.get());
@@ -57,8 +56,7 @@
explicit BindingsTestBase(const scoped_refptr<Window> window)
: environment_settings_(new script::EnvironmentSettings),
engine_(script::JavaScriptEngine::CreateEngine()),
- global_environment_(engine_->CreateGlobalEnvironment(
- script::JavaScriptEngine::Options())),
+ global_environment_(engine_->CreateGlobalEnvironment()),
window_(window) {
global_environment_->CreateGlobalObject(window_,
environment_settings_.get());
diff --git a/src/cobalt/bindings/testing/dictionary_interface.h b/src/cobalt/bindings/testing/dictionary_interface.h
index 4ec4793..4d8debf 100644
--- a/src/cobalt/bindings/testing/dictionary_interface.h
+++ b/src/cobalt/bindings/testing/dictionary_interface.h
@@ -19,6 +19,7 @@
#include <string>
+#include "cobalt/bindings/testing/dictionary_with_dictionary_member.h"
#include "cobalt/bindings/testing/test_dictionary.h"
#include "cobalt/script/sequence.h"
#include "cobalt/script/wrappable.h"
@@ -37,6 +38,8 @@
MOCK_METHOD1(set_dictionary_sequence,
void(script::Sequence<TestDictionary> test_dictionary));
+ void TestOperation(DictionaryWithDictionaryMember /* dict */) {}
+
DEFINE_WRAPPABLE_TYPE(DictionaryInterface);
};
diff --git a/src/cobalt/bindings/testing/dictionary_interface.idl b/src/cobalt/bindings/testing/dictionary_interface.idl
index ddb725c..80f4f21 100644
--- a/src/cobalt/bindings/testing/dictionary_interface.idl
+++ b/src/cobalt/bindings/testing/dictionary_interface.idl
@@ -17,4 +17,7 @@
interface DictionaryInterface {
void dictionaryOperation(TestDictionary dictionary);
attribute sequence<TestDictionary> dictionarySequence;
+
+ // Dictionary with dictionary member.
+ void testOperation(DictionaryWithDictionaryMember dict);
};
diff --git a/src/cobalt/bindings/testing/dictionary_with_dictionary_member.idl b/src/cobalt/bindings/testing/dictionary_with_dictionary_member.idl
new file mode 100644
index 0000000..60f967a
--- /dev/null
+++ b/src/cobalt/bindings/testing/dictionary_with_dictionary_member.idl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+dictionary DictionaryWithDictionaryMember {
+ TestDictionary nestedDictionary;
+};
diff --git a/src/cobalt/bindings/testing/enumeration_bindings_test.cc b/src/cobalt/bindings/testing/enumeration_bindings_test.cc
index 9ed5c79..c991c20 100644
--- a/src/cobalt/bindings/testing/enumeration_bindings_test.cc
+++ b/src/cobalt/bindings/testing/enumeration_bindings_test.cc
@@ -14,6 +14,7 @@
#include "cobalt/bindings/testing/bindings_test_base.h"
#include "cobalt/bindings/testing/enumeration_interface.h"
+#include "cobalt/bindings/testing/test_enum.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,13 +32,13 @@
TEST_F(EnumerationBindingsTest, SetEnumeration) {
InSequence dummy;
- EXPECT_CALL(test_mock(), set_enum_property(EnumerationInterface::kAlpha));
+ EXPECT_CALL(test_mock(), set_enum_property(kTestEnumAlpha));
EXPECT_TRUE(EvaluateScript("test.enumProperty = \"alpha\";", NULL));
- EXPECT_CALL(test_mock(), set_enum_property(EnumerationInterface::kBeta));
+ EXPECT_CALL(test_mock(), set_enum_property(kTestEnumBeta));
EXPECT_TRUE(EvaluateScript("test.enumProperty = \"beta\";", NULL));
- EXPECT_CALL(test_mock(), set_enum_property(EnumerationInterface::kGamma));
+ EXPECT_CALL(test_mock(), set_enum_property(kTestEnumGamma));
EXPECT_TRUE(EvaluateScript("test.enumProperty = \"gamma\";", NULL));
}
@@ -45,18 +46,15 @@
InSequence dummy;
std::string result;
- EXPECT_CALL(test_mock(), enum_property())
- .WillOnce(Return(EnumerationInterface::kAlpha));
+ EXPECT_CALL(test_mock(), enum_property()).WillOnce(Return(kTestEnumAlpha));
EXPECT_TRUE(EvaluateScript("test.enumProperty == \"alpha\";", &result));
EXPECT_STREQ("true", result.c_str());
- EXPECT_CALL(test_mock(), enum_property())
- .WillOnce(Return(EnumerationInterface::kBeta));
+ EXPECT_CALL(test_mock(), enum_property()).WillOnce(Return(kTestEnumBeta));
EXPECT_TRUE(EvaluateScript("test.enumProperty == \"beta\";", &result));
EXPECT_STREQ("true", result.c_str());
- EXPECT_CALL(test_mock(), enum_property())
- .WillOnce(Return(EnumerationInterface::kGamma));
+ EXPECT_CALL(test_mock(), enum_property()).WillOnce(Return(kTestEnumGamma));
EXPECT_TRUE(EvaluateScript("test.enumProperty == \"gamma\";", &result));
EXPECT_STREQ("true", result.c_str());
}
diff --git a/src/cobalt/bindings/testing/enumeration_interface.h b/src/cobalt/bindings/testing/enumeration_interface.h
index e6c0d16..58938ff 100644
--- a/src/cobalt/bindings/testing/enumeration_interface.h
+++ b/src/cobalt/bindings/testing/enumeration_interface.h
@@ -15,6 +15,7 @@
#ifndef COBALT_BINDINGS_TESTING_ENUMERATION_INTERFACE_H_
#define COBALT_BINDINGS_TESTING_ENUMERATION_INTERFACE_H_
+#include "cobalt/bindings/testing/test_enum.h"
#include "cobalt/script/wrappable.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -24,10 +25,11 @@
class EnumerationInterface : public script::Wrappable {
public:
- enum TestEnum { kAlpha, kBeta, kGamma };
MOCK_METHOD0(enum_property, TestEnum());
MOCK_METHOD1(set_enum_property, void(TestEnum));
+ void OptionalEnumWithDefault(TestEnum /* value */) {}
+
DEFINE_WRAPPABLE_TYPE(EnumerationInterface);
};
diff --git a/src/cobalt/bindings/testing/enumeration_interface.idl b/src/cobalt/bindings/testing/enumeration_interface.idl
index b56de55..3116abb 100644
--- a/src/cobalt/bindings/testing/enumeration_interface.idl
+++ b/src/cobalt/bindings/testing/enumeration_interface.idl
@@ -12,13 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-enum TestEnum {
- "alpha",
- "beta",
- "gamma"
-};
-
[Constructor]
interface EnumerationInterface {
attribute TestEnum enumProperty;
+
+ void optionalEnumWithDefault(optional TestEnum value = "beta");
};
diff --git a/src/cobalt/bindings/testing/exceptions_bindings_test.cc b/src/cobalt/bindings/testing/exceptions_bindings_test.cc
index 505ed9a..43c221a 100644
--- a/src/cobalt/bindings/testing/exceptions_bindings_test.cc
+++ b/src/cobalt/bindings/testing/exceptions_bindings_test.cc
@@ -59,16 +59,6 @@
scoped_refptr<script::ScriptException> exception_object_;
};
-std::string GetExceptionMessageString(script::MessageType message_type,
- int dummy, ...) {
- va_list arguments;
- va_start(arguments, dummy);
- std::string error_string = base::StringPrintV(
- GetExceptionMessageFormat(message_type), arguments);
- va_end(arguments);
- return error_string;
-}
-
} // namespace
TEST_F(ExceptionsBindingsTest, ThrowExceptionFromConstructor) {
@@ -147,16 +137,6 @@
EXPECT_STREQ("the message", result.c_str());
}
-TEST_F(ExceptionsBindingsTest, GetExceptionMessageStringTest) {
- std::string error_message =
- GetExceptionMessageString(script::kWrongByteLengthMultiple, 0, 8);
- EXPECT_STREQ("Byte length should be a multiple of 8.", error_message.c_str());
- error_message =
- GetExceptionMessageString(script::kWrongByteOffsetMultiple, 0, 16);
- EXPECT_STREQ("Byte offset should be a multiple of 16.",
- error_message.c_str());
-}
-
} // namespace testing
} // namespace bindings
} // namespace cobalt
diff --git a/src/cobalt/bindings/testing/extended_attributes_test.cc b/src/cobalt/bindings/testing/extended_attributes_test.cc
index 09ca65d..88d8b78 100644
--- a/src/cobalt/bindings/testing/extended_attributes_test.cc
+++ b/src/cobalt/bindings/testing/extended_attributes_test.cc
@@ -43,6 +43,35 @@
NULL));
}
+TEST_F(ExtendedAttributesTest, ImplementedAsAttributeGetter) {
+ EXPECT_CALL(test_mock(), attribute_default()).WillOnce(Return(true));
+ EXPECT_TRUE(EvaluateScript("var result = test.default;", NULL));
+
+ std::string result;
+ EXPECT_TRUE(EvaluateScript("typeof result;", &result));
+ EXPECT_STREQ("boolean", result.c_str());
+
+ EXPECT_TRUE(EvaluateScript("result;", &result));
+ EXPECT_STREQ("true", result.c_str());
+
+ EXPECT_CALL(test_mock(), attribute_default()).WillOnce(Return(false));
+ EXPECT_TRUE(EvaluateScript("var result = test.default;", NULL));
+
+ EXPECT_TRUE(EvaluateScript("typeof result;", &result));
+ EXPECT_STREQ("boolean", result.c_str());
+
+ EXPECT_TRUE(EvaluateScript("result;", &result));
+ EXPECT_STREQ("false", result.c_str());
+}
+
+TEST_F(ExtendedAttributesTest, ImplementedAsAttributeSetter) {
+ EXPECT_CALL(test_mock(), set_attribute_default(true));
+ EXPECT_TRUE(EvaluateScript(StringPrintf("test.default = true;"), NULL));
+
+ EXPECT_CALL(test_mock(), set_attribute_default(false));
+ EXPECT_TRUE(EvaluateScript(StringPrintf("test.default = false;"), NULL));
+}
+
} // namespace testing
} // namespace bindings
} // namespace cobalt
diff --git a/src/cobalt/bindings/testing/extended_idl_attributes_interface.h b/src/cobalt/bindings/testing/extended_idl_attributes_interface.h
index 1b8c901..897d80d 100644
--- a/src/cobalt/bindings/testing/extended_idl_attributes_interface.h
+++ b/src/cobalt/bindings/testing/extended_idl_attributes_interface.h
@@ -27,6 +27,8 @@
public:
MOCK_METHOD1(CallWithSettings, void(script::EnvironmentSettings*));
MOCK_METHOD1(ClampArgument, void(uint16_t));
+ MOCK_METHOD0(attribute_default, bool());
+ MOCK_METHOD1(set_attribute_default, void(bool));
DEFINE_WRAPPABLE_TYPE(ExtendedIDLAttributesInterface);
};
diff --git a/src/cobalt/bindings/testing/extended_idl_attributes_interface.idl b/src/cobalt/bindings/testing/extended_idl_attributes_interface.idl
index 9535a26..fe4b8d2 100644
--- a/src/cobalt/bindings/testing/extended_idl_attributes_interface.idl
+++ b/src/cobalt/bindings/testing/extended_idl_attributes_interface.idl
@@ -15,4 +15,5 @@
interface ExtendedIDLAttributesInterface {
[CallWith=EnvironmentSettings] void callWithSettings();
void clampArgument([Clamp] unsigned short arg);
+ [ImplementedAs=attribute_default] attribute boolean default;
};
diff --git a/src/cobalt/base/math.cc b/src/cobalt/bindings/testing/test_enum.idl
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/cobalt/bindings/testing/test_enum.idl
index d335495..d674515 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/bindings/testing/test_enum.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
-
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum TestEnum {
+ "alpha",
+ "beta",
+ "gamma",
+ "enum-with-dashes",
+ "enum with spaces",
+ "terrible----enum",
+ "this is a terrible @#$%#$% enum"
+};
diff --git a/src/cobalt/bindings/testing/testing.gyp b/src/cobalt/bindings/testing/testing.gyp
index d005089..c32311a 100644
--- a/src/cobalt/bindings/testing/testing.gyp
+++ b/src/cobalt/bindings/testing/testing.gyp
@@ -75,8 +75,10 @@
'window.idl',
],
- 'dictionary_idl_files': [
+ 'generated_header_idl_files': [
+ 'dictionary_with_dictionary_member.idl',
'test_dictionary.idl',
+ 'test_enum.idl'
],
# Partial interfaces and the right-side of "implements"
@@ -127,13 +129,13 @@
],
'defines': [ '<@(bindings_defines)'],
'dependencies': [
- 'generated_dictionaries',
+ 'generated_types',
'<(DEPTH)/cobalt/base/base.gyp:base',
'<(DEPTH)/testing/gmock.gyp:gmock',
'<(DEPTH)/testing/gtest.gyp:gtest',
],
'export_dependent_settings': [
- 'generated_dictionaries',
+ 'generated_types',
],
},
{
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 1aa2b3b..77ecccd 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -14,6 +14,7 @@
#include "cobalt/browser/application.h"
+#include <algorithm>
#include <string>
#include <vector>
@@ -32,8 +33,8 @@
#include "cobalt/base/language.h"
#include "cobalt/base/localized_strings.h"
#include "cobalt/base/user_log.h"
-#include "cobalt/browser/memory_settings/memory_settings.h"
-#include "cobalt/browser/memory_tracker/memory_tracker_tool.h"
+#include "cobalt/browser/memory_settings/auto_mem.h"
+#include "cobalt/browser/memory_tracker/tool.h"
#include "cobalt/browser/switches.h"
#include "cobalt/loader/image/image_decoder.h"
#include "cobalt/math/size.h"
@@ -51,6 +52,7 @@
#include "lbshell/src/lb_memory_pages.h"
#endif // defined(__LB_SHELL__)
#if defined(OS_STARBOARD)
+#include "nb/lexical_cast.h"
#include "starboard/configuration.h"
#include "starboard/log.h"
#endif // defined(OS_STARBOARD)
@@ -195,6 +197,30 @@
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
+// Represents a parsed int.
+struct ParsedIntValue {
+ public:
+ ParsedIntValue() : value_(0), error_(false) {}
+ ParsedIntValue(const ParsedIntValue& other)
+ : value_(other.value_), error_(other.error_) {}
+ int value_;
+ bool error_; // true if there was a parse error.
+};
+// Parses a string like "1234x5678" to vector of parsed int values.
+std::vector<ParsedIntValue> ParseDimensions(const std::string& value_str) {
+ std::vector<ParsedIntValue> output;
+
+ std::vector<std::string> lengths;
+ base::SplitString(value_str, 'x', &lengths);
+
+ for (size_t i = 0; i < lengths.size(); ++i) {
+ ParsedIntValue parsed_value;
+ parsed_value.error_ = !base::StringToInt(lengths[i], &parsed_value.value_);
+ output.push_back(parsed_value);
+ }
+ return output;
+}
+
std::string GetMinLogLevelString() {
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
CommandLine* command_line = CommandLine::ForCurrentProcess();
@@ -241,19 +267,33 @@
&options->surface_cache_size_in_bytes);
SetIntegerIfSwitchIsSet(browser::switches::kScratchSurfaceCacheSizeInBytes,
&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) {
- SetIntegerIfSwitchIsSet(browser::switches::kImageCacheSizeInBytes,
- &options->image_cache_capacity);
SetIntegerIfSwitchIsSet(browser::switches::kRemoteTypefaceCacheSizeInBytes,
&options->remote_typeface_cache_capacity);
}
+template <typename T>
+base::optional<T> ParseSetting(const CommandLine* command_line,
+ const char* switch_name) {
+ base::optional<T> output;
+ if (!command_line->HasSwitch(switch_name)) {
+ return output;
+ }
+ std::string switch_value = command_line->GetSwitchValueNative(switch_name);
+
+ bool parse_ok = false;
+ T value = nb::lexical_cast<T>(switch_value.c_str(), &parse_ok);
+
+ if (parse_ok) {
+ output = static_cast<T>(value);
+ } else {
+ LOG(ERROR) << "Invalid value for command line setting: " << switch_name;
+ }
+ return output;
+}
+
// Restrict navigation to a couple of whitelisted URLs by default.
const char kYouTubeTvLocationPolicy[] =
"h5vcc-location-src "
@@ -311,6 +351,29 @@
int Application::network_connect_count_ = 0;
int Application::network_disconnect_count_ = 0;
+void ApplyAutoMemSettings(const memory_settings::AutoMem& auto_mem,
+ BrowserModule::Options* options) {
+ std::stringstream ss;
+ ss << "\n\n" << auto_mem.ToPrettyPrintString() << "\n\n";
+ SB_LOG(INFO) << ss.str();
+
+ options->web_module_options.image_cache_capacity =
+ static_cast<int>(auto_mem.image_cache_size_in_bytes()->value());
+
+ options->renderer_module_options.skia_cache_size_in_bytes =
+ static_cast<int>(auto_mem.skia_cache_size_in_bytes()->value());
+
+ options->renderer_module_options.skia_texture_atlas_dimensions =
+ auto_mem.skia_atlas_texture_dimensions()->value();
+
+ options->web_module_options.javascript_options.gc_threshold_bytes =
+ static_cast<size_t>(auto_mem.javascript_gc_threshold_in_bytes()->value());
+
+ options->renderer_module_options.software_surface_cache_size_in_bytes =
+ static_cast<int>(
+ auto_mem.software_surface_cache_size_in_bytes()->value());
+}
+
Application::Application(const base::Closure& quit_closure)
: message_loop_(MessageLoop::current()),
quit_closure_(quit_closure),
@@ -357,8 +420,7 @@
CommandLine* command_line = CommandLine::ForCurrentProcess();
math::Size window_size = InitSystemWindow(command_line);
- WebModule::Options web_options(window_size);
-
+ WebModule::Options web_options;
// Create the main components of our browser.
BrowserModule::Options options(web_options);
options.web_module_options.name = "MainWebModule";
@@ -373,6 +435,10 @@
options.web_module_options.javascript_options.disable_jit = true;
}
+ memory_settings::AutoMem auto_mem(window_size, *command_line,
+ memory_settings::GetDefaultBuildSettings());
+ ApplyAutoMemSettings(auto_mem, &options);
+
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
if (command_line->HasSwitch(browser::switches::kNullSavegame)) {
options.storage_manager_options.savegame_options.factory =
@@ -691,23 +757,31 @@
if (command_line->HasSwitch(browser::switches::kViewport)) {
const std::string switchValue =
command_line->GetSwitchValueASCII(browser::switches::kViewport);
- std::vector<std::string> lengths;
- base::SplitString(switchValue, 'x', &lengths);
- if (lengths.size() >= 1) {
- int width = -1;
- if (base::StringToInt(lengths[0], &width) && width >= 1) {
- int height = -1;
- if (lengths.size() < 2) {
+
+ std::vector<ParsedIntValue> parsed_ints = ParseDimensions(switchValue);
+
+ if (parsed_ints.size() >= 1) {
+ const ParsedIntValue parsed_width = parsed_ints[0];
+ if (!parsed_width.error_) {
+ const ParsedIntValue* parsed_height_ptr = NULL;
+ if (parsed_ints.size() >= 2) {
+ parsed_height_ptr = &parsed_ints[1];
+ }
+
+ if (!parsed_height_ptr) {
// Allow shorthand specification of the viewport by only giving the
// width. This calculates the height at 4:3 aspect ratio for smaller
// viewport widths, and 16:9 for viewports 1280 pixels wide or larger.
- if (width >= 1280) {
- viewport_size.emplace(width, 9 * width / 16);
+ if (parsed_width.value_ >= 1280) {
+ viewport_size.emplace(parsed_width.value_,
+ 9 * parsed_width.value_ / 16);
} else {
- viewport_size.emplace(width, 3 * width / 4);
+ viewport_size.emplace(parsed_width.value_,
+ 3 * parsed_width.value_ / 4);
}
- } else if (base::StringToInt(lengths[1], &height) && height >= 1) {
- viewport_size.emplace(width, height);
+ } else if (!parsed_height_ptr->error_) {
+ viewport_size.emplace(parsed_width.value_,
+ parsed_height_ptr->value_);
} else {
DLOG(ERROR) << "Invalid value specified for viewport height: "
<< switchValue << ". Using default viewport size.";
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index 2c10118..6c1ac47 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -22,7 +22,7 @@
#include "cobalt/account/account_manager.h"
#include "cobalt/base/event_dispatcher.h"
#include "cobalt/browser/browser_module.h"
-#include "cobalt/browser/memory_tracker/memory_tracker_tool.h"
+#include "cobalt/browser/memory_tracker/tool.h"
#include "cobalt/system_window/system_window.h"
#if defined(ENABLE_WEBDRIVER)
@@ -172,7 +172,7 @@
base::Timer stats_update_timer_;
base::Timer lite_stats_update_timer_;
- scoped_ptr<memory_tracker::MemoryTrackerTool> memory_tracker_tool_;
+ scoped_ptr<memory_tracker::Tool> memory_tracker_tool_;
};
// Factory method for creating an application. It should be implemented
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index a3d141a..db75521 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -29,14 +29,34 @@
'debug_console.h',
'h5vcc_url_handler.cc',
'h5vcc_url_handler.h',
+ 'memory_settings/auto_mem.cc',
+ 'memory_settings/auto_mem.h',
+ 'memory_settings/build_settings.cc',
+ 'memory_settings/build_settings.h',
+ 'memory_settings/calculations.cc',
+ 'memory_settings/calculations.h',
'memory_settings/memory_settings.cc',
'memory_settings/memory_settings.h',
- 'memory_tracker/buffered_file_writer.cc',
- 'memory_tracker/buffered_file_writer.h',
- 'memory_tracker/memory_tracker_tool.cc',
- 'memory_tracker/memory_tracker_tool.h',
- 'memory_tracker/memory_tracker_tool_impl.cc',
- 'memory_tracker/memory_tracker_tool_impl.h',
+ 'memory_settings/pretty_print.cc',
+ 'memory_settings/pretty_print.h',
+ 'memory_tracker/tool.cc',
+ 'memory_tracker/tool.h',
+ 'memory_tracker/tool/buffered_file_writer.cc',
+ 'memory_tracker/tool/buffered_file_writer.h',
+ 'memory_tracker/tool/compressed_time_series_tool.cc',
+ 'memory_tracker/tool/compressed_time_series_tool.h',
+ 'memory_tracker/tool/leak_finder_tool.cc',
+ 'memory_tracker/tool/leak_finder_tool.h',
+ 'memory_tracker/tool/log_writer_tool.cc',
+ 'memory_tracker/tool/log_writer_tool.h',
+ 'memory_tracker/tool/params.cc',
+ 'memory_tracker/tool/params.h',
+ 'memory_tracker/tool/tool_impl.cc',
+ 'memory_tracker/tool/tool_impl.h',
+ 'memory_tracker/tool/tool_thread.cc',
+ 'memory_tracker/tool/tool_thread.h',
+ 'memory_tracker/tool/util.cc',
+ 'memory_tracker/tool/util.h',
'render_tree_combiner.cc',
'render_tree_combiner.h',
'resource_provider_array_buffer_allocator.cc',
@@ -57,9 +77,14 @@
'web_module_stat_tracker.h',
],
'defines': [
+ 'COBALT_SKIA_CACHE_SIZE_IN_BYTES=<(skia_cache_size_in_bytes)',
+ 'COBALT_SKIA_GLYPH_ATLAS_WIDTH=<(skia_glyph_atlas_width)',
+ 'COBALT_SKIA_GLYPH_ATLAS_HEIGHT=<(skia_glyph_atlas_height)',
'COBALT_IMAGE_CACHE_SIZE_IN_BYTES=<(image_cache_size_in_bytes)',
- 'COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES=<(remote_typeface_cache_size_in_bytes)',
+ 'COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES=<(remote_font_cache_size_in_bytes)',
'COBALT_IMAGE_CACHE_CAPACITY_MULTIPLIER_WHEN_PLAYING_VIDEO=<(image_cache_capacity_multiplier_when_playing_video)',
+ 'COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES=<(software_surface_cache_size_in_bytes)',
+ 'COBALT_JS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES=<(mozjs_garbage_collection_threshold_in_bytes)',
],
'dependencies': [
'<@(cobalt_platform_dependencies)',
@@ -171,11 +196,12 @@
'type': '<(gtest_target_type)',
'sources': [
'storage_upgrade_handler_test.cc',
+ 'memory_settings/auto_mem_test.cc',
+ 'memory_settings/calculations_test.cc',
+ 'memory_settings/pretty_print_test.cc',
'memory_settings/memory_settings_test.cc',
- 'memory_tracker/memory_tracker_tool_test.cc',
- ],
- 'defines': [
- 'COBALT_IMAGE_CACHE_SIZE_IN_BYTES=<(image_cache_size_in_bytes)',
+ 'memory_tracker/tool/tool_impl_test.cc',
+ 'memory_tracker/tool/util_test.cc',
],
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index 7572715..aa35c9a 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -117,7 +117,7 @@
#'../dom/media_key_message_event_init.idl',
'../dom/media_key_needed_event.idl',
#'../dom/media_key_session.idl',
- '../dom/media_key_status_map.idl',
+ #'../dom/media_key_status_map.idl',
#'../dom/media_key_system_access.idl',
#'../dom/media_key_system_configuration.idl',
#'../dom/media_key_system_media_capability.idl',
@@ -211,6 +211,7 @@
'../webdriver/script_executor_params.idl',
'../webdriver/script_executor_result.idl',
+ '../websocket/close_event.idl',
'../websocket/web_socket.idl',
'../xhr/xml_http_request.idl',
@@ -218,10 +219,30 @@
'../xhr/xml_http_request_upload.idl',
],
- 'dictionary_idl_files': [
+
+ # IDL files that will end up generating a .h that will be #included in
+ # Cobalt directly. IDL files for dictionaries and enums.
+ 'generated_header_idl_files': [
+ '../audio/audio_node_channel_count_mode.idl',
+ '../audio/audio_node_channel_interpretation.idl',
+ '../dom/blob_property_bag.idl',
+ '../dom/dom_parser_supported_type.idl',
+ '../dom/media_key_status.idl',
+ '../dom/media_keys_requirement.idl',
+ '../dom/media_source_end_of_stream_error.idl',
+ '../dom/media_source_ready_state.idl',
'../dom/mutation_observer_init.idl',
+ '../dom/source_buffer_append_mode.idl',
+ '../dom/track_default_type.idl',
'../media_session/media_image.idl',
'../media_session/media_metadata_init.idl',
+ '../media_session/media_session_action.idl',
+ '../media_session/media_session_playback_state.idl',
+ '../speech/speech_synthesis_error_code.idl',
+ '../speech/speech_recognition_error_code.idl',
+ '../websocket/close_event_init.idl',
+ '../web_animations/animation_fill_mode.idl',
+ '../web_animations/animation_playback_direction.idl',
],
# Partial interfaces and the right-side of "implements". Also includes
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 32c9309..fb03d85 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -35,6 +35,8 @@
#include "cobalt/browser/switches.h"
#include "cobalt/dom/csp_delegate_factory.h"
#include "cobalt/dom/keycode.h"
+#include "cobalt/dom/mutation_observer_task_manager.h"
+#include "cobalt/dom/window.h"
#include "cobalt/h5vcc/h5vcc.h"
#include "cobalt/input/input_device_manager_fuzzer.h"
#include "nb/memory_scope.h"
@@ -189,8 +191,11 @@
#endif
scoped_refptr<script::Wrappable> CreateH5VCC(
- const h5vcc::H5vcc::Settings& settings) {
- return scoped_refptr<script::Wrappable>(new h5vcc::H5vcc(settings));
+ const h5vcc::H5vcc::Settings& settings,
+ const scoped_refptr<dom::Window>& window,
+ dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+ return scoped_refptr<script::Wrappable>(
+ new h5vcc::H5vcc(settings, window, mutation_observer_task_manager));
}
} // namespace
@@ -358,7 +363,8 @@
DCHECK(web_module_);
web_module_->ExecuteJavascript(
"location.reload();",
- base::SourceLocation("[object BrowserModule]", 1, 1));
+ base::SourceLocation("[object BrowserModule]", 1, 1),
+ NULL /* output: succeeded */);
}
#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
@@ -433,6 +439,7 @@
base::Unretained(this)),
base::Bind(&BrowserModule::OnError, base::Unretained(this)),
base::Bind(&BrowserModule::OnWindowClose, base::Unretained(this)),
+ base::Bind(&BrowserModule::OnWindowMinimize, base::Unretained(this)),
media_module_.get(), &network_module_, viewport_size,
renderer_module_.pipeline()->GetResourceProvider(), system_window_,
kLayoutMaxRefreshFrequencyInHz, options));
@@ -525,7 +532,21 @@
#if defined(OS_STARBOARD)
SbSystemRequestStop(0);
#else
- LOG(WARNING) << "window.close is not supported on this platform.";
+ LOG(WARNING) << "window.close() is not supported on this platform.";
+#endif
+}
+
+void BrowserModule::OnWindowMinimize() {
+#if defined(ENABLE_DEBUG_CONSOLE)
+ if (input_device_manager_fuzzer_) {
+ return;
+ }
+#endif
+
+#if defined(OS_STARBOARD) && SB_API_VERSION >= 4
+ SbSystemRequestSuspend();
+#else
+ LOG(WARNING) << "window.minimize() is not supported on this platform.";
#endif
}
@@ -857,7 +878,13 @@
renderer_module_.Resume();
- media_module_->Resume();
+ // Note that at this point, it is probable that this resource provider is
+ // different than the one that was managed in the associated call to
+ // Suspend().
+ render_tree::ResourceProvider* resource_provider =
+ renderer_module_.pipeline()->GetResourceProvider();
+
+ media_module_->Resume(resource_provider);
#if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
// Resume() is not supported on platforms that allocates ArrayBuffer on GPU
@@ -865,12 +892,6 @@
NOTREACHED();
#endif // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
- // Note that at this point, it is probable that this resource provider is
- // different than the one that was managed in the associated call to
- // Suspend().
- render_tree::ResourceProvider* resource_provider =
- renderer_module_.pipeline()->GetResourceProvider();
-
#if defined(ENABLE_DEBUG_CONSOLE)
if (debug_console_) {
debug_console_->Resume(resource_provider);
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 7acc494..fcb24ad 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -184,9 +184,12 @@
// Destroys the splash screen, if currently displayed.
void DestroySplashScreen();
- // Called when web module has received window.close.
+ // Called when web module has received window.close().
void OnWindowClose();
+ // Called when web module has received window.minimize().
+ void OnWindowMinimize();
+
#if defined(ENABLE_DEBUG_CONSOLE)
// Toggles the input fuzzer on/off. Ignores the parameter.
void OnFuzzerToggle(const std::string&);
diff --git a/src/cobalt/browser/cobalt.gyp b/src/cobalt/browser/cobalt.gyp
index f5bcfe9..6950cdd 100644
--- a/src/cobalt/browser/cobalt.gyp
+++ b/src/cobalt/browser/cobalt.gyp
@@ -20,10 +20,12 @@
{
'target_name': 'cobalt',
'type': '<(final_executable_type)',
- 'sources': [
- 'main.cc',
- ],
'conditions': [
+ ['cobalt_enable_lib == 1', {
+ 'sources': ['lib/main.cc',],
+ }, {
+ 'sources': ['main.cc',],
+ }],
['OS=="lb_shell"', {
'dependencies': [
'<(DEPTH)/cobalt/browser/<(actual_target_arch)/platform_browser.gyp:platform_browser',
diff --git a/src/cobalt/browser/debug_console.cc b/src/cobalt/browser/debug_console.cc
index acb019d..fea12f6 100644
--- a/src/cobalt/browser/debug_console.cc
+++ b/src/cobalt/browser/debug_console.cc
@@ -15,7 +15,6 @@
#if defined(ENABLE_DEBUG_CONSOLE)
#include "cobalt/browser/debug_console.h"
-
#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
@@ -24,6 +23,8 @@
#include "cobalt/base/source_location.h"
#include "cobalt/browser/switches.h"
#include "cobalt/dom/csp_delegate_factory.h"
+#include "cobalt/dom/mutation_observer_task_manager.h"
+#include "cobalt/dom/window.h"
namespace cobalt {
namespace browser {
@@ -147,7 +148,11 @@
// A function to create a DebugHub object, to be injected into WebModule.
scoped_refptr<script::Wrappable> CreateDebugHub(
const debug::DebugHub::GetHudModeCallback& get_hud_mode_function,
- const debug::Debugger::GetDebugServerCallback& get_debug_server_callback) {
+ const debug::Debugger::GetDebugServerCallback& get_debug_server_callback,
+ const scoped_refptr<dom::Window>& window,
+ dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+ UNREFERENCED_PARAMETER(window);
+ UNREFERENCED_PARAMETER(mutation_observer_task_manager);
return new debug::DebugHub(get_hud_mode_function, get_debug_server_callback);
}
@@ -163,9 +168,11 @@
const script::JavaScriptEngine::Options& js_options) {
mode_ = GetInitialMode();
- WebModule::Options web_module_options(window_dimensions);
+ WebModule::Options web_module_options;
web_module_options.javascript_options = js_options;
web_module_options.name = "DebugConsoleWebModule";
+ // The debug console does not load any image assets.
+ web_module_options.image_cache_capacity = 0;
// Disable CSP for the Debugger's WebModule. This will also allow eval() in
// javascript.
web_module_options.csp_enforcement_mode = dom::kCspEnforcementDisable;
@@ -183,6 +190,7 @@
GURL(kInitialDebugConsoleUrl), render_tree_produced_callback,
base::Bind(&DebugConsole::OnError, base::Unretained(this)),
base::Closure(), /* window_close_callback */
+ base::Closure(), /* window_minimize_callback */
media_module, network_module, window_dimensions, resource_provider,
media_module->system_window(), layout_refresh_rate, web_module_options));
}
diff --git a/src/cobalt/browser/lib/README.md b/src/cobalt/browser/lib/README.md
new file mode 100644
index 0000000..55af499
--- /dev/null
+++ b/src/cobalt/browser/lib/README.md
@@ -0,0 +1,9 @@
+This directory provides an API for clients that build Cobalt into a lib using
+the 'cobalt_enable_lib' flag.
+
+It is highly experimental at this point and is expected to change significantly
+across releases.
+
+As a general convention, functions whose symbols are exported are defined in an
+'exported' subdirectory and functions that are required to be implemented by
+clients are defined in a 'imported' subdirectory.
diff --git a/src/cobalt/browser/lib/imported/main.h b/src/cobalt/browser/lib/imported/main.h
new file mode 100644
index 0000000..c3abcb8
--- /dev/null
+++ b/src/cobalt/browser/lib/imported/main.h
@@ -0,0 +1,45 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// All imported functions defined below MUST be implemented by client
+// applications.
+
+#ifndef COBALT_BROWSER_LIB_IMPORTED_MAIN_H_
+#define COBALT_BROWSER_LIB_IMPORTED_MAIN_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Structure representing an input event and its data. This provides a subset
+// of SbEvent so that clients don't have to copy the Starboard headers into
+// their codebase.
+typedef struct CbLibKeyInputEvent {
+ unsigned char keycode;
+ bool pressed;
+} CbLibKeyInputEvent;
+
+// Invoked after Cobalt has been initialized.
+void CbLibOnCobaltInitialized();
+
+// Invoked when Cobalt is receiving an event from Starboard.
+// Returns true if the event should pass through to Cobalt; returns false if
+// the event was consumed.
+bool CbLibHandleEvent(const CbLibKeyInputEvent& event);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // COBALT_BROWSER_LIB_IMPORTED_MAIN_H_
diff --git a/src/cobalt/browser/lib/main.cc b/src/cobalt/browser/lib/main.cc
new file mode 100644
index 0000000..51cd1e4
--- /dev/null
+++ b/src/cobalt/browser/lib/main.cc
@@ -0,0 +1,76 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "cobalt/base/wrap_main.h"
+#include "cobalt/browser/application.h"
+#include "cobalt/browser/lib/imported/main.h"
+#include "cobalt/browser/starboard/event_handler.h"
+#include "starboard/event.h"
+#include "starboard/input.h"
+
+namespace {
+
+bool SbEventToCbLibKeyInputEvent(
+ const SbEvent* starboard_event,
+ CbLibKeyInputEvent* out_key_input_event) {
+ if (starboard_event == NULL ||
+ starboard_event->type != SbEventType::kSbEventTypeInput) {
+ return false;
+ }
+ const SbInputData* input_data =
+ static_cast<SbInputData*>(starboard_event->data);
+ if (input_data->device_type !=
+ SbInputDeviceType::kSbInputDeviceTypeKeyboard) {
+ return false;
+ }
+ out_key_input_event->pressed =
+ input_data->type == SbInputEventType::kSbInputEventTypePress;
+ out_key_input_event->keycode =
+ static_cast<unsigned char>(input_data->character);
+ return true;
+}
+
+cobalt::browser::Application* g_application = NULL;
+
+void StartApplication(int /*argc*/, char** /*argv*/, const char* /*link*/,
+ const base::Closure& quit_closure) {
+ DCHECK(!g_application);
+ LOG(INFO) << "Starting application!";
+ g_application = cobalt::browser::CreateApplication(quit_closure).release();
+ DCHECK(g_application);
+ CbLibOnCobaltInitialized();
+}
+
+void StopApplication() {
+ DCHECK(g_application);
+ LOG(INFO) << "Stopping application!";
+ delete g_application;
+ g_application = NULL;
+}
+
+void HandleEvent(const SbEvent* starboard_event) {
+ CbLibKeyInputEvent key;
+ if (!SbEventToCbLibKeyInputEvent(starboard_event, &key) ||
+ CbLibHandleEvent(key)) {
+ cobalt::browser::EventHandler::HandleEvent(starboard_event);
+ }
+}
+
+} // namespace
+
+COBALT_WRAP_EVENT_MAIN(StartApplication,
+ HandleEvent,
+ StopApplication);
diff --git a/src/cobalt/browser/memory_settings/auto_mem.cc b/src/cobalt/browser/memory_settings/auto_mem.cc
new file mode 100644
index 0000000..1c28846
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/auto_mem.cc
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/auto_mem.h"
+
+#include <string>
+#include <vector>
+
+#include "base/optional.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/stringprintf.h"
+
+#include "cobalt/browser/memory_settings/build_settings.h"
+#include "cobalt/browser/memory_settings/calculations.h"
+#include "cobalt/browser/memory_settings/pretty_print.h"
+#include "cobalt/browser/switches.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+namespace {
+
+// Creates the specified memory setting type and binds it to (1) command line
+// or else (2) build setting or else (3) an auto_set value.
+template <typename MemorySettingType, typename ValueType>
+scoped_ptr<MemorySettingType> CreateMemorySetting(
+ const char* setting_name, const CommandLine& cmd_line,
+ const base::optional<ValueType>& build_setting,
+ const ValueType& autoset_value) {
+ scoped_ptr<MemorySettingType> output(new MemorySettingType(setting_name));
+
+ // The value is set according to importance:
+ // 1) Command line switches are the most important, so set those if they
+ // exist.
+ if (cmd_line.HasSwitch(setting_name)) {
+ std::string value = cmd_line.GetSwitchValueNative(setting_name);
+ if (output->TryParseValue(MemorySetting::kCmdLine, value)) {
+ return output.Pass();
+ }
+ }
+
+ // 2) Is there a build setting? Then set to build_setting.
+ if (build_setting) {
+ output->set_value(MemorySetting::kBuildSetting, *build_setting);
+ } else {
+ // 3) Otherwise bind to the autoset_value.
+ output->set_value(MemorySetting::kAutoSet, autoset_value);
+ }
+ return output.Pass();
+}
+
+void EnsureValuePositive(IntSetting* setting) {
+ if (setting->value() < 0) {
+ setting->set_value(setting->source_type(), 0);
+ }
+}
+
+void EnsureValuePositive(DimensionSetting* setting) {
+ const math::Size value = setting->value();
+ if (value.width() < 0 || value.height() < 0) {
+ setting->set_value(setting->source_type(), math::Size(0, 0));
+ }
+}
+
+} // namespace
+
+AutoMem::AutoMem(const math::Size& ui_resolution,
+ const CommandLine& command_line,
+ const BuildSettings& build_settings) {
+ // Set the ImageCache
+ image_cache_size_in_bytes_ = CreateMemorySetting<IntSetting, int64_t>(
+ switches::kImageCacheSizeInBytes, command_line,
+ build_settings.cobalt_image_cache_size_in_bytes,
+ CalculateImageCacheSize(ui_resolution));
+ EnsureValuePositive(image_cache_size_in_bytes_.get());
+
+ // Set javascript gc threshold
+ javascript_gc_threshold_in_bytes_ = CreateMemorySetting<IntSetting, int64_t>(
+ switches::kJavaScriptGcThresholdInBytes, command_line,
+ build_settings.javascript_garbage_collection_threshold_in_bytes,
+ kDefaultJsGarbageCollectionThresholdSize);
+ EnsureValuePositive(javascript_gc_threshold_in_bytes_.get());
+
+ // Set skia_atlas_texture_dimensions
+ skia_atlas_texture_dimensions_ =
+ CreateMemorySetting<DimensionSetting, math::Size>(
+ switches::kSkiaTextureAtlasDimensions, command_line,
+ build_settings.skia_texture_atlas_dimensions,
+ CalculateSkiaGlyphAtlasTextureSize(ui_resolution));
+ // Not available for non-blitter platforms.
+ if (build_settings.has_blitter) {
+ skia_atlas_texture_dimensions_->set_value(MemorySetting::kNotApplicable,
+ math::Size(0, 0));
+ }
+ EnsureValuePositive(skia_atlas_texture_dimensions_.get());
+
+ // Set skia_cache_size_in_bytes
+ skia_cache_size_in_bytes_ = CreateMemorySetting<IntSetting, int64_t>(
+ switches::kSkiaCacheSizeInBytes, command_line,
+ build_settings.skia_cache_size_in_bytes,
+ CalculateSkiaCacheSize(ui_resolution));
+ // Not available for blitter platforms.
+ if (build_settings.has_blitter) {
+ skia_cache_size_in_bytes_->set_value(MemorySetting::kNotApplicable, 0);
+ }
+ EnsureValuePositive(skia_cache_size_in_bytes_.get());
+
+ // Set software_surface_cache_size_in_bytes
+ software_surface_cache_size_in_bytes_ =
+ CreateMemorySetting<IntSetting, int64_t>(
+ switches::kSoftwareSurfaceCacheSizeInBytes, command_line,
+ build_settings.software_surface_cache_size_in_bytes,
+ CalculateSoftwareSurfaceCacheSizeInBytes(ui_resolution));
+ // Blitter only feature.
+ if (!build_settings.has_blitter) {
+ software_surface_cache_size_in_bytes_->set_value(
+ MemorySetting::kNotApplicable, 0);
+ }
+ EnsureValuePositive(software_surface_cache_size_in_bytes_.get());
+}
+
+AutoMem::~AutoMem() {}
+
+const IntSetting* AutoMem::image_cache_size_in_bytes() const {
+ return image_cache_size_in_bytes_.get();
+}
+
+const IntSetting* AutoMem::javascript_gc_threshold_in_bytes() const {
+ return javascript_gc_threshold_in_bytes_.get();
+}
+
+const DimensionSetting* AutoMem::skia_atlas_texture_dimensions() const {
+ return skia_atlas_texture_dimensions_.get();
+}
+
+const IntSetting* AutoMem::skia_cache_size_in_bytes() const {
+ return skia_cache_size_in_bytes_.get();
+}
+
+const IntSetting* AutoMem::software_surface_cache_size_in_bytes() const {
+ return software_surface_cache_size_in_bytes_.get();
+}
+
+std::vector<const MemorySetting*> AutoMem::AllMemorySetttings() const {
+ std::vector<const MemorySetting*> all_settings;
+ // Keep these in alphabetical order.
+ all_settings.push_back(image_cache_size_in_bytes_.get());
+ all_settings.push_back(javascript_gc_threshold_in_bytes_.get());
+ all_settings.push_back(skia_atlas_texture_dimensions_.get());
+ all_settings.push_back(skia_cache_size_in_bytes_.get());
+ all_settings.push_back(software_surface_cache_size_in_bytes_.get());
+ return all_settings;
+}
+
+std::string AutoMem::ToPrettyPrintString() const {
+ std::vector<const MemorySetting*> all_settings = AllMemorySetttings();
+ return GeneratePrettyPrintTable(all_settings);
+}
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_settings/auto_mem.h b/src/cobalt/browser/memory_settings/auto_mem.h
new file mode 100644
index 0000000..7d4bd2d
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/auto_mem.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_BROWSER_MEMORY_SETTINGS_AUTO_MEM_H_
+#define COBALT_BROWSER_MEMORY_SETTINGS_AUTO_MEM_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/browser/memory_settings/build_settings.h"
+#include "cobalt/browser/memory_settings/memory_settings.h"
+#include "cobalt/math/size.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+// AutoMem is contains system memory settings. The command line is required to
+// instantiate this object and after construction, all memory settings are
+// calculated.
+class AutoMem {
+ public:
+ explicit AutoMem(const math::Size& ui_resolution,
+ const CommandLine& command_line,
+ const BuildSettings& build_settings);
+ ~AutoMem();
+
+ const IntSetting* image_cache_size_in_bytes() const;
+ const IntSetting* javascript_gc_threshold_in_bytes() const;
+ const DimensionSetting* skia_atlas_texture_dimensions() const;
+ const IntSetting* skia_cache_size_in_bytes() const;
+ const IntSetting* software_surface_cache_size_in_bytes() const;
+
+ std::vector<const MemorySetting*> AllMemorySetttings() const;
+
+ // Generates a string table of the memory settings. This is used during
+ // startup to display memory configuration information.
+ std::string ToPrettyPrintString() const;
+
+ private:
+ scoped_ptr<IntSetting> image_cache_size_in_bytes_;
+ scoped_ptr<IntSetting> javascript_gc_threshold_in_bytes_;
+ scoped_ptr<DimensionSetting> skia_atlas_texture_dimensions_;
+ scoped_ptr<IntSetting> skia_cache_size_in_bytes_;
+ scoped_ptr<IntSetting> software_surface_cache_size_in_bytes_;
+};
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_SETTINGS_AUTO_MEM_H_
diff --git a/src/cobalt/browser/memory_settings/auto_mem_test.cc b/src/cobalt/browser/memory_settings/auto_mem_test.cc
new file mode 100644
index 0000000..4f6d6d7
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/auto_mem_test.cc
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/auto_mem.h"
+
+#include <string>
+#include <vector>
+
+#include "base/optional.h"
+#include "cobalt/browser/memory_settings/build_settings.h"
+#include "cobalt/browser/memory_settings/calculations.h"
+#include "cobalt/browser/switches.h"
+#include "cobalt/math/size.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+const math::Size kResolution1080p(1920, 1080);
+
+#define EXPECT_MEMORY_SETTING(SETTING, SOURCE, VALUE) \
+ EXPECT_EQ(VALUE, SETTING->value()) << " failure for " << SETTING->name(); \
+ EXPECT_EQ(SOURCE, SETTING->source_type()) << " failure for " \
+ << SETTING->name();
+
+// Tests the expectation that the command-line overrides will be applied.
+// Settings which are enabled/disabled when blitter is enabled/disabled are
+// also tested.
+TEST(AutoMem, CommandLineOverrides) {
+ // Load up command line settings of command lines.
+ CommandLine command_line(CommandLine::NO_PROGRAM);
+ command_line.AppendSwitchASCII(switches::kImageCacheSizeInBytes, "1234");
+ command_line.AppendSwitchASCII(switches::kJavaScriptGcThresholdInBytes,
+ "2345");
+ command_line.AppendSwitchASCII(switches::kSkiaCacheSizeInBytes, "3456");
+ command_line.AppendSwitchASCII(switches::kSkiaTextureAtlasDimensions,
+ "1234x5678");
+ command_line.AppendSwitchASCII(switches::kSoftwareSurfaceCacheSizeInBytes,
+ "4567");
+
+ for (int i = 0; i <= 1; ++i) {
+ BuildSettings builtin_settings = GetDefaultBuildSettings();
+ builtin_settings.has_blitter = (i == 0);
+
+ AutoMem auto_mem(kResolution1080p, command_line, builtin_settings);
+
+ // image_cache_size_in_bytes and javascript_gc_threshold_in_bytes settings
+ // ignore the blitter type.
+ EXPECT_MEMORY_SETTING(auto_mem.image_cache_size_in_bytes(),
+ MemorySetting::kCmdLine, 1234);
+
+ EXPECT_MEMORY_SETTING(auto_mem.javascript_gc_threshold_in_bytes(),
+ MemorySetting::kCmdLine, 2345);
+
+ // Certain features are only available for the blitter, and some features
+ // are disabled, vice versa.
+ if (builtin_settings.has_blitter) {
+ // When blitter is active then skia_atlas_texture_dimensions are
+ // not applicable because this is an OpenGl egl feature.
+ EXPECT_MEMORY_SETTING(auto_mem.skia_atlas_texture_dimensions(),
+ MemorySetting::kNotApplicable, math::Size(0, 0));
+
+ // Skia cache is also an egl-only feature, which is not applicable
+ // for blitter.
+ EXPECT_MEMORY_SETTING(auto_mem.skia_cache_size_in_bytes(),
+ MemorySetting::kNotApplicable, 0);
+
+ EXPECT_MEMORY_SETTING(auto_mem.software_surface_cache_size_in_bytes(),
+ MemorySetting::kCmdLine, 4567);
+ } else {
+ // Skia atlas is an egl-only feature and therefore enabled.
+ EXPECT_MEMORY_SETTING(auto_mem.skia_atlas_texture_dimensions(),
+ MemorySetting::kCmdLine, math::Size(1234, 5678));
+
+ // Skia cache is an egl-only feature therefore it is enabled for egl.
+ EXPECT_MEMORY_SETTING(auto_mem.skia_cache_size_in_bytes(),
+ MemorySetting::kCmdLine, 3456);
+
+ // Software surface cache is a blitter-only feature, therefore disabled
+ // in egl.
+ EXPECT_MEMORY_SETTING(auto_mem.software_surface_cache_size_in_bytes(),
+ MemorySetting::kNotApplicable, 0);
+ }
+ }
+}
+
+// Tests that skia atlas texture will be bind to the built in value, iff it has
+// been set.
+TEST(AutoMem, SkiaAtlasTextureAtlasSize) {
+ CommandLine empty_command_line(CommandLine::NO_PROGRAM);
+ BuildSettings builtin_settings;
+ BuildSettings builtin_settings_with_default;
+
+ builtin_settings_with_default.skia_texture_atlas_dimensions =
+ math::Size(1234, 5678);
+
+ AutoMem auto_mem(kResolution1080p, empty_command_line, builtin_settings);
+ AutoMem auto_mem_with_default(kResolution1080p, empty_command_line,
+ builtin_settings_with_default);
+
+ // Expect that when the skia_atlas_texture_dimensions is specified in the
+ // build settings that it will bind to the auto-set value (computed from
+ // CalculateSkiaGlyphAtlasTextureSize(...)).
+ EXPECT_MEMORY_SETTING(auto_mem.skia_atlas_texture_dimensions(),
+ MemorySetting::kAutoSet,
+ CalculateSkiaGlyphAtlasTextureSize(kResolution1080p));
+
+ // Expect that when the skia_atlas_texture_dimensions is specified in the
+ // build settings that it will bind to the final value.
+ EXPECT_MEMORY_SETTING(auto_mem_with_default.skia_atlas_texture_dimensions(),
+ MemorySetting::kBuildSetting, math::Size(1234, 5678));
+}
+
+// Tests that software surface cache will be bind to the built in value, iff
+// it has been set.
+TEST(AutoMem, SoftwareSurfaceCacheSizeInBytes) {
+ CommandLine empty_command_line(CommandLine::NO_PROGRAM);
+ BuildSettings builtin_settings;
+ BuildSettings builtin_settings_with_default;
+ // Enable the setting by enabling the blitter.
+ builtin_settings.has_blitter = true;
+ builtin_settings_with_default.has_blitter = true;
+ builtin_settings_with_default.software_surface_cache_size_in_bytes = 1234;
+
+ AutoMem auto_mem(kResolution1080p, empty_command_line, builtin_settings);
+ AutoMem auto_mem_with_surface_cache(kResolution1080p, empty_command_line,
+ builtin_settings_with_default);
+
+ // Expect that when the software_surface_cache_size_in_bytes is specified in
+ // the/ build settings that it will bind to the auto-set value (computed from
+ // CalculateSoftwareSurfaceCacheSizeInBytes(...)).
+ EXPECT_MEMORY_SETTING(
+ auto_mem.software_surface_cache_size_in_bytes(), MemorySetting::kAutoSet,
+ CalculateSoftwareSurfaceCacheSizeInBytes(kResolution1080p));
+
+ EXPECT_MEMORY_SETTING(
+ auto_mem_with_surface_cache.software_surface_cache_size_in_bytes(),
+ MemorySetting::kBuildSetting, 1234);
+}
+
+// Tests that skia cache will be bind to the built in value, iff
+// it has been set.
+TEST(AutoMem, SkiaCacheSizeInBytes) {
+ CommandLine empty_command_line(CommandLine::NO_PROGRAM);
+ BuildSettings builtin_settings;
+ BuildSettings builtin_settings_with_default;
+ builtin_settings_with_default.skia_cache_size_in_bytes = 1234;
+
+ AutoMem auto_mem(kResolution1080p, empty_command_line, builtin_settings);
+ AutoMem auto_mem_with_skia_cache(kResolution1080p, empty_command_line,
+ builtin_settings_with_default);
+
+ EXPECT_MEMORY_SETTING(auto_mem.skia_cache_size_in_bytes(),
+ MemorySetting::kAutoSet,
+ CalculateSkiaCacheSize(kResolution1080p));
+
+ EXPECT_MEMORY_SETTING(auto_mem_with_skia_cache.skia_cache_size_in_bytes(),
+ MemorySetting::kBuildSetting, 1234);
+}
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
+
+#undef EXPECT_MEMORY_SETTING
diff --git a/src/cobalt/browser/memory_settings/build_settings.cc b/src/cobalt/browser/memory_settings/build_settings.cc
new file mode 100644
index 0000000..65c646e
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/build_settings.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/build_settings.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+namespace {
+bool HasBlitter() {
+#if SB_HAS(BLITTER)
+ const bool has_blitter = true;
+#else
+ const bool has_blitter = false;
+#endif
+ return has_blitter;
+}
+
+base::optional<int64_t> MakeValidIfGreaterThanOrEqualToZero(int64_t value) {
+ base::optional<int64_t> output;
+ if (value >= 0) {
+ output = value;
+ }
+ return output;
+}
+
+base::optional<math::Size> MakeDimensionsIfPositive(int width, int height) {
+ base::optional<math::Size> output;
+ if ((width > 0) && (height > 0)) {
+ output = math::Size(width, height);
+ }
+ return output;
+}
+} // namespace
+
+BuildSettings GetDefaultBuildSettings() {
+ BuildSettings settings;
+ settings.has_blitter = HasBlitter();
+ settings.cobalt_image_cache_size_in_bytes =
+ MakeValidIfGreaterThanOrEqualToZero(COBALT_IMAGE_CACHE_SIZE_IN_BYTES);
+ settings.javascript_garbage_collection_threshold_in_bytes =
+ MakeValidIfGreaterThanOrEqualToZero(
+ COBALT_JS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES);
+ settings.skia_cache_size_in_bytes =
+ MakeValidIfGreaterThanOrEqualToZero(COBALT_SKIA_CACHE_SIZE_IN_BYTES);
+ settings.skia_texture_atlas_dimensions = MakeDimensionsIfPositive(
+ COBALT_SKIA_GLYPH_ATLAS_WIDTH, COBALT_SKIA_GLYPH_ATLAS_HEIGHT);
+ settings.software_surface_cache_size_in_bytes =
+ MakeValidIfGreaterThanOrEqualToZero(
+ COBALT_SOFTWARE_SURFACE_CACHE_SIZE_IN_BYTES);
+ return settings;
+}
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_settings/build_settings.h b/src/cobalt/browser/memory_settings/build_settings.h
new file mode 100644
index 0000000..c57daea
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/build_settings.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_BROWSER_MEMORY_SETTINGS_BUILD_SETTINGS_H_
+#define COBALT_BROWSER_MEMORY_SETTINGS_BUILD_SETTINGS_H_
+
+#include "base/optional.h"
+#include "cobalt/math/size.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+struct BuildSettings {
+ BuildSettings() : has_blitter(false) {}
+ bool has_blitter;
+ base::optional<int64_t> cobalt_image_cache_size_in_bytes;
+ base::optional<int64_t> javascript_garbage_collection_threshold_in_bytes;
+ base::optional<int64_t> skia_cache_size_in_bytes;
+ base::optional<math::Size> skia_texture_atlas_dimensions;
+ base::optional<int64_t> software_surface_cache_size_in_bytes;
+};
+
+BuildSettings GetDefaultBuildSettings();
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_SETTINGS_BUILD_SETTINGS_H_
diff --git a/src/cobalt/browser/memory_settings/calculations.cc b/src/cobalt/browser/memory_settings/calculations.cc
new file mode 100644
index 0000000..6111d95
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/calculations.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/calculations.h"
+
+#include <algorithm>
+
+#include "cobalt/math/size.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+namespace {
+
+template <typename T>
+T ClampValue(const T& input, const T& minimum, const T& maximum) {
+ return std::max<T>(minimum, std::min<T>(maximum, input));
+}
+
+double DisplayScaleTo1080p(const math::Size& dimensions) {
+ static const double kNumReferencePixels = 1920. * 1080.;
+ const double num_pixels = static_cast<double>(dimensions.width()) *
+ static_cast<double>(dimensions.height());
+ return num_pixels / kNumReferencePixels;
+}
+
+// LinearRemap is a type of linear interpolation which maps a value from
+// one number line to a corresponding value on another number line.
+// Example:
+// LinearRemap linear_remap(0, 1, 5, 10);
+// EXPECT_EQ(5.0f, linear_remap.Map(0));
+// EXPECT_EQ(7.5f, linear_remap.Map(.5));
+// EXPECT_EQ(10.f, linear_remap.Map(1));
+class LinearRemap {
+ public:
+ LinearRemap(double amin, double amax, double bmin, double bmax)
+ : amin_(amin), amax_(amax), bmin_(bmin), bmax_(bmax) {}
+
+ // Maps the value from the number line [amin,amax] to [bmin,bmax]. For
+ // example:
+ // Map(amin) -> bmin.
+ // Map(amax) -> bmax.
+ // Map((amax+amin)/2) -> (bmax+bmin)/2.
+ double Map(double value) const {
+ value -= amin_;
+ value /= (amax_ - amin_);
+ value *= (bmax_ - bmin_);
+ value += bmin_;
+ return value;
+ }
+
+ private:
+ const double amin_, amax_, bmin_, bmax_;
+};
+
+math::Size ExpandTextureSizeToContain(const int64_t num_pixels) {
+ // Iterate through to find size to contain number of pixels.
+ math::Size texture_size(1, 1);
+ bool toggle = false;
+
+ // Expand the texture by powers of two until the specific number of pixels
+ // is contained.
+ while (texture_size.GetArea() < num_pixels) {
+ if (!toggle) {
+ texture_size.set_width(texture_size.width() * 2);
+ } else {
+ texture_size.set_height(texture_size.height() * 2);
+ }
+ toggle = !toggle;
+ }
+ return texture_size;
+}
+
+} // namespace
+
+int64_t CalculateImageCacheSize(const math::Size& dimensions) {
+ const double display_scale = DisplayScaleTo1080p(dimensions);
+ static const int64_t kReferenceSize1080p = 32 * 1024 * 1024;
+ double output_bytes = kReferenceSize1080p * display_scale;
+
+ return ClampValue<int64_t>(static_cast<int64_t>(output_bytes),
+ kMinImageCacheSize, kMaxImageCacheSize);
+}
+
+math::Size CalculateSkiaGlyphAtlasTextureSize(const math::Size& ui_resolution) {
+ // LinearRemap defines a mapping function which will map the number
+ // of ui_resolution pixels to the number of texture atlas pixels such that:
+ // 1080p (1920x1080) => maps to => 2048x2048 texture atlas pixels
+ // 4k (3840x2160) => maps to => 8192x4096 texture atlas pixels
+ LinearRemap remap(1920 * 1080, 3840 * 2160, 2048 * 2048, 4096 * 8192);
+
+ // Apply mapping.
+ const int num_ui_pixels = ui_resolution.GetArea();
+ const int64_t num_atlas_pixels =
+ static_cast<int64_t>(remap.Map(num_ui_pixels));
+
+ // Texture atlas sizes are generated in powers of two. This function will
+ // produce such a texture.
+ math::Size atlas_texture = ExpandTextureSizeToContain(num_atlas_pixels);
+
+ // Clamp the atlas texture to be within a minimum range.
+ math::Size clamped_atlas_texture(
+ std::max<int>(kMinSkiaTextureAtlasWidth, atlas_texture.width()),
+ std::max<int>(kMinSkiaTextureAtlasWidth, atlas_texture.height()));
+ return clamped_atlas_texture;
+}
+
+int64_t CalculateSoftwareSurfaceCacheSizeInBytes(
+ const math::Size& ui_resolution) {
+ // LinearRemap defines a mapping function which will map the number
+ // of ui_resolution pixels to the number of surface texture cache such:
+ // 720p (1280x720) => maps to => 4MB &
+ // 1080p (1920x1200) => maps to => 9MB
+ LinearRemap remap(1280 * 720, 1920 * 1080, 4 * 1024 * 1024, 9 * 1024 * 1024);
+
+ int64_t surface_cache_size_in_bytes =
+ static_cast<int64_t>(remap.Map(ui_resolution.GetArea()));
+
+ return surface_cache_size_in_bytes;
+}
+
+int64_t CalculateSkiaCacheSize(const math::Size& ui_resolution) {
+ // This is normalized return 4MB @ 1080p and scales accordingly.
+ LinearRemap remap(0, 1920 * 1080, 0, 4 * 1024 * 1024);
+ int64_t output = static_cast<int64_t>(remap.Map(ui_resolution.GetArea()));
+ return std::max<int64_t>(output, kMinSkiaCacheSize);
+}
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_settings/calculations.h b/src/cobalt/browser/memory_settings/calculations.h
new file mode 100644
index 0000000..c90426a
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/calculations.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_BROWSER_MEMORY_SETTINGS_CALCULATIONS_H_
+#define COBALT_BROWSER_MEMORY_SETTINGS_CALCULATIONS_H_
+
+#include "base/optional.h"
+#include "cobalt/math/size.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+////////////////////////// Calculate Functions ////////////////////////////////
+// These functions are exposed here for testing purposes and should not be used
+// directly.
+// Calculates the ImageCacheSize in bytes.
+// The return ranges from [kMinImageCacheSize, kMaxImageCacheSize].
+int64_t CalculateImageCacheSize(const math::Size& dimensions);
+
+// Calculates the SkiaAtlasTextureSize.
+// When the ui resolution is 1920x1080, then the returned atlas texture size
+// will be 8192x4096. The texture will scale up and down, by powers of two,
+// in relation to the input ui_resolution. The returned value will be clamped
+// such that
+// kMinSkiaTextureAtlasWidth <= output.width() <= kMaxSkiaTextureAtlasWidth
+// and
+// kMinSkiaTextureAtlasHeight <= output.height() <= kMaxSkiaTextureAtlasHeight
+// will be true.
+math::Size CalculateSkiaGlyphAtlasTextureSize(const math::Size& ui_resolution);
+
+// Calculates the SoftwareSurfaceCacheSize given the ui_resolution.
+int64_t CalculateSoftwareSurfaceCacheSizeInBytes(
+ const math::Size& ui_resolution);
+
+// Calculates the SkiaCachSize from the ui_resolution. This is normalized
+// to be 4MB @ 1080p and scales accordingly.
+int64_t CalculateSkiaCacheSize(const math::Size& ui_resolution);
+
+// These internal values are exposed for testing.
+enum MemorySizes {
+ kMinImageCacheSize = 20 * 1024 * 1024, // 20mb.
+ kMaxImageCacheSize = 64 * 1024 * 1024, // 64mb
+
+ kMinSkiaTextureAtlasWidth = 2048,
+ kMinSkiaTextureAtlasHeight = 2048,
+
+ kDefaultJsGarbageCollectionThresholdSize = 1 * 1024 * 1024, // 1mb
+
+ kMinSkiaCacheSize = 4 * 1024 * 1024, // 4mb.
+};
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_SETTINGS_CALCULATIONS_H_
diff --git a/src/cobalt/browser/memory_settings/calculations_test.cc b/src/cobalt/browser/memory_settings/calculations_test.cc
new file mode 100644
index 0000000..935dc41
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/calculations_test.cc
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/calculations.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "cobalt/browser/switches.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/system.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+///////////////////////////////////
+//
+// WIDTH | HEIGHT
+// -------------------------
+// 8K: 7,680 x 4,320
+// Cinema 4K: 4,096 x 2,160
+// 4k/UHD: 3,840 x 2,160
+// 2K: 2,560 x 1,440
+// WUXGA: 1,920 x 1,200
+// 1080p: 1,920 x 1,080
+// 720p: 1,280 x 720
+// 480p: 640 x 480
+enum StandardDisplaySettings {
+ k8k,
+ kCinema4k,
+ kUHD4k,
+ k2k,
+ kWUXGA,
+ k1080p,
+ k720p,
+ k480p
+};
+
+math::Size GetDimensions(StandardDisplaySettings enum_value) {
+ switch (enum_value) {
+ case k8k: {
+ return math::Size(7680, 4320);
+ }
+ case kCinema4k: {
+ return math::Size(4096, 2160);
+ }
+ case kUHD4k: {
+ return math::Size(3840, 2160);
+ }
+ case k2k: {
+ return math::Size(2560, 1440);
+ }
+ case kWUXGA: {
+ return math::Size(1920, 1200);
+ }
+ case k1080p: {
+ return math::Size(1920, 1080);
+ }
+ case k720p: {
+ return math::Size(1280, 720);
+ }
+ case k480p: {
+ return math::Size(640, 480);
+ }
+ }
+
+ EXPECT_TRUE(false) << "Should not be reached. Unknown enum_value: "
+ << enum_value;
+ return GetDimensions(k1080p);
+}
+
+// Tests the expectation that CalculateImageCacheSize() is a pure function
+// (side effect free) and will produce the expected results.
+TEST(MemoryCalculations, CalculateImageCacheSize) {
+ EXPECT_EQ(kMinImageCacheSize, CalculateImageCacheSize(GetDimensions(k720p)));
+ EXPECT_EQ(32 * 1024 * 1024, // 32MB.
+ CalculateImageCacheSize(GetDimensions(k1080p)));
+ EXPECT_EQ(kMaxImageCacheSize, CalculateImageCacheSize(GetDimensions(kUHD4k)));
+
+ // Expect that the floor is hit for smaller values.
+ EXPECT_EQ(kMinImageCacheSize, CalculateImageCacheSize(GetDimensions(k480p)));
+
+ // Expect that the ceiling is hit for larger values.
+ EXPECT_EQ(kMaxImageCacheSize,
+ CalculateImageCacheSize(GetDimensions(kCinema4k)));
+ EXPECT_EQ(kMaxImageCacheSize, CalculateImageCacheSize(GetDimensions(k8k)));
+}
+
+// Tests the expectation that CalculateSkiaAtlasTextureSize() is a pure
+// function (side effect free) and will produce expected results.
+TEST(MemoryCalculations, CalculateSkiaGlyphAtlasTextureSize) {
+ math::Size ui_dimensions;
+ math::Size atlas_texture_size;
+
+ // Test that the small resolution of 480p produces a texture atlas
+ // that is minimal.
+ ui_dimensions = GetDimensions(k480p);
+ atlas_texture_size = CalculateSkiaGlyphAtlasTextureSize(ui_dimensions);
+ EXPECT_EQ(kMinSkiaTextureAtlasWidth, atlas_texture_size.width());
+ EXPECT_EQ(kMinSkiaTextureAtlasHeight, atlas_texture_size.height());
+
+ // Test that expected resolution of 1080p produces a 2048x2048
+ // atlas texture.
+ ui_dimensions = GetDimensions(k1080p);
+ atlas_texture_size = CalculateSkiaGlyphAtlasTextureSize(ui_dimensions);
+ EXPECT_EQ(2048, atlas_texture_size.width());
+ EXPECT_EQ(2048, atlas_texture_size.height());
+
+ // Test that expected resolution of 4k produces a 8192x4096
+ // atlas texture.
+ ui_dimensions = GetDimensions(kUHD4k);
+ atlas_texture_size = CalculateSkiaGlyphAtlasTextureSize(ui_dimensions);
+ EXPECT_EQ(8192, atlas_texture_size.width());
+ EXPECT_EQ(4096, atlas_texture_size.height());
+
+ // Test that ultra-high 8k resolution produces expected results.
+ ui_dimensions = GetDimensions(k8k);
+ atlas_texture_size = CalculateSkiaGlyphAtlasTextureSize(ui_dimensions);
+ EXPECT_EQ(16384, atlas_texture_size.width());
+ EXPECT_EQ(16384, atlas_texture_size.height());
+}
+
+TEST(MemoryCalculations, CalculateSoftwareSurfaceCacheSizeInBytes) {
+ math::Size ui_resolution = GetDimensions(k720p);
+ // Expect that a 720p resolution produces a 4mb SoftwareSurfaceCache.
+ EXPECT_EQ(4 * 1024 * 1024,
+ CalculateSoftwareSurfaceCacheSizeInBytes(ui_resolution));
+
+ ui_resolution = GetDimensions(k1080p);
+ // Expect that a 1080p resolution produces a 9mb SoftwareSurfaceCache.
+ EXPECT_EQ(9 * 1024 * 1024,
+ CalculateSoftwareSurfaceCacheSizeInBytes(ui_resolution));
+}
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_settings/memory_settings.cc b/src/cobalt/browser/memory_settings/memory_settings.cc
index 91333cc..f6097c4 100644
--- a/src/cobalt/browser/memory_settings/memory_settings.cc
+++ b/src/cobalt/browser/memory_settings/memory_settings.cc
@@ -17,43 +17,95 @@
#include "cobalt/browser/memory_settings/memory_settings.h"
#include <algorithm>
+#include <string>
+#include <vector>
#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/stringprintf.h"
+#include "cobalt/browser/switches.h"
+#include "nb/lexical_cast.h"
namespace cobalt {
namespace browser {
namespace memory_settings {
namespace {
-template <typename T>
-T ClampValue(const T& input, const T& minimum, const T& maximum) {
- return std::max<T>(minimum, std::min<T>(maximum, input));
+struct ParsedIntValue {
+ public:
+ ParsedIntValue() : value_(0), error_(false) {}
+ ParsedIntValue(const ParsedIntValue& other)
+ : value_(other.value_), error_(other.error_) {}
+ int value_;
+ bool error_; // true if there was a parse error.
+};
+// Parses a string like "1234x5678" to vector of parsed int values.
+std::vector<ParsedIntValue> ParseDimensions(const std::string& value_str) {
+ std::vector<ParsedIntValue> output;
+
+ std::vector<std::string> lengths;
+ base::SplitString(value_str, 'x', &lengths);
+
+ for (size_t i = 0; i < lengths.size(); ++i) {
+ ParsedIntValue parsed_value;
+ parsed_value.error_ = !base::StringToInt(lengths[i], &parsed_value.value_);
+ output.push_back(parsed_value);
+ }
+ return output;
}
-double DisplayScaleTo1080p(const math::Size& dimensions) {
- static const double kNumReferencePixels = 1920. * 1080.;
- const double num_pixels = static_cast<double>(dimensions.width()) *
- static_cast<double>(dimensions.height());
- return num_pixels / kNumReferencePixels;
-}
} // namespace
-size_t GetImageCacheSize(const math::Size& dimensions) {
- if (COBALT_IMAGE_CACHE_SIZE_IN_BYTES >= 0) {
- return COBALT_IMAGE_CACHE_SIZE_IN_BYTES;
- }
- size_t return_val = CalculateImageCacheSize(dimensions);
- return static_cast<int>(return_val);
+MemorySetting::MemorySetting(ClassType type, const std::string& name)
+ : class_type_(type), name_(name), source_type_(kUnset) {}
+
+IntSetting::IntSetting(const std::string& name)
+ : MemorySetting(kInt, name), value_() {}
+
+std::string IntSetting::ValueToString() const {
+ std::stringstream ss;
+ ss << value_;
+ return ss.str();
}
-size_t CalculateImageCacheSize(const math::Size& dimensions) {
- const double display_scale = DisplayScaleTo1080p(dimensions);
- static const size_t kReferenceSize1080p = 32 * 1024 * 1024;
- double output_bytes = kReferenceSize1080p * display_scale;
+bool IntSetting::TryParseValue(SourceType source_type,
+ const std::string& string_value) {
+ bool parse_ok = false;
+ int64_t int_value = nb::lexical_cast<int64_t>(string_value, &parse_ok);
- return ClampValue<size_t>(static_cast<size_t>(output_bytes),
- kMinImageCacheSize, kMaxImageCacheSize);
+ if (parse_ok) {
+ set_value(source_type, int_value);
+ return true;
+ } else {
+ LOG(ERROR) << "Invalid value for command line setting: " << string_value;
+ return false;
+ }
+}
+
+DimensionSetting::DimensionSetting(const std::string& name)
+ : MemorySetting(kDimensions, name) {}
+
+std::string DimensionSetting::ValueToString() const {
+ std::stringstream ss;
+ ss << value_.width() << "x" << value_.height();
+ return ss.str();
+}
+
+bool DimensionSetting::TryParseValue(SourceType source_type,
+ const std::string& string_value) {
+ std::vector<ParsedIntValue> int_values = ParseDimensions(string_value);
+ const bool parse_ok = (int_values.size() == 2) && (!int_values[0].error_) &&
+ (!int_values[1].error_);
+ if (parse_ok) {
+ math::Size rectangle(int_values[0].value_, int_values[1].value_);
+ set_value(source_type, rectangle);
+ return true;
+ } else {
+ LOG(ERROR) << "Invalid value for parse value setting: " << string_value;
+ return false;
+ }
}
} // namespace memory_settings
diff --git a/src/cobalt/browser/memory_settings/memory_settings.h b/src/cobalt/browser/memory_settings/memory_settings.h
index b111574..1d13aa7 100644
--- a/src/cobalt/browser/memory_settings/memory_settings.h
+++ b/src/cobalt/browser/memory_settings/memory_settings.h
@@ -17,27 +17,91 @@
#ifndef COBALT_BROWSER_MEMORY_SETTINGS_MEMORY_SETTINGS_H_
#define COBALT_BROWSER_MEMORY_SETTINGS_MEMORY_SETTINGS_H_
+#include <map>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/optional.h"
#include "cobalt/math/size.h"
+#include "starboard/configuration.h"
#include "starboard/types.h"
namespace cobalt {
namespace browser {
namespace memory_settings {
-// Gets the ImageCacheSize in bytes from the given dimensions. If
-// COBALT_IMAGE_CACHE_SIZE_IN_BYTES is defined, then this is the value
-// that is returned, otherwise the value is generated via a call to
-// CalculateImageCacheSize().
-size_t GetImageCacheSize(const math::Size& dimensions);
+// An abstract MemorySetting. Subclasses will implement how the class parses
+// string values.
+class MemorySetting {
+ public:
+ virtual ~MemorySetting() {}
+ // SourceType defines the location where the setting was set from.
+ // kNotApplicable means the setting is not supported on the current system
+ // configuration.
+ enum SourceType { kUnset, kCmdLine, kBuildSetting, kAutoSet, kNotApplicable };
+ enum ClassType { kInt, kDimensions };
-// Calculates the ImageCacheSize in bytes.
-// The return ranges from [kMinImageCacheSize, kMaxImageCacheSize].
-size_t CalculateImageCacheSize(const math::Size& dimensions);
+ virtual std::string ValueToString() const = 0;
+ // Returns true if the TryParseValue() succeeded when converting the string
+ // into the internal value. If false, then the object should not be changed.
+ virtual bool TryParseValue(SourceType source_type,
+ const std::string& string_value) = 0;
-/////////////////////////////// Implementation ///////////////////////////////
-enum MemorySizes {
- kMinImageCacheSize = 20 * 1024 * 1024, // 20mb.
- kMaxImageCacheSize = 64 * 1024 * 1024 // 64mb
+ const std::string& name() const { return name_; }
+ SourceType source_type() const { return source_type_; }
+ ClassType class_type() const { return class_type_; }
+
+ protected:
+ MemorySetting(ClassType type, const std::string& name);
+
+ const ClassType class_type_;
+ const std::string name_;
+ SourceType source_type_;
+
+ private:
+ // Default constructor for MemorySetting is forbidden. Do not use it.
+ MemorySetting();
+ SB_DISALLOW_COPY_AND_ASSIGN(MemorySetting);
+};
+
+// A memory setting for integer values.
+class IntSetting : public MemorySetting {
+ public:
+ explicit IntSetting(const std::string& name);
+
+ std::string ValueToString() const OVERRIDE;
+ int64_t value() const { return value_; }
+ void set_value(SourceType source_type, int64_t val) {
+ source_type_ = source_type;
+ value_ = val;
+ }
+ bool TryParseValue(SourceType source_type,
+ const std::string& string_value) OVERRIDE;
+
+ private:
+ int64_t value_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(IntSetting);
+};
+
+// A memory setting for dimensions type values like "2048x4096".
+class DimensionSetting : public MemorySetting {
+ public:
+ explicit DimensionSetting(const std::string& name);
+
+ std::string ValueToString() const OVERRIDE;
+ math::Size value() const { return value_; }
+ void set_value(SourceType source_type, const math::Size& val) {
+ source_type_ = source_type;
+ value_ = val;
+ }
+ bool TryParseValue(SourceType source_type,
+ const std::string& string_value) OVERRIDE;
+
+ private:
+ math::Size value_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(DimensionSetting);
};
} // namespace memory_settings
diff --git a/src/cobalt/browser/memory_settings/memory_settings_test.cc b/src/cobalt/browser/memory_settings/memory_settings_test.cc
index 5fa60b2..565d1c1 100644
--- a/src/cobalt/browser/memory_settings/memory_settings_test.cc
+++ b/src/cobalt/browser/memory_settings/memory_settings_test.cc
@@ -21,108 +21,29 @@
#include <string>
#include <vector>
-#include "base/logging.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/system.h"
-
+#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace browser {
namespace memory_settings {
-///////////////////////////////////
-//
-// WIDTH | HEIGHT
-// -------------------------
-// 8K: 7,680 x 4,320
-// Cinema 4K: 4,096 x 2,160
-// 4k/UHD: 3,840 x 2,160
-// 2K: 2,048 x 1,080
-// WUXGA: 1,920 x 1,200
-// 1080p: 1,920 x 1,080
-// 720p: 1,280 x 720
-// 480p: 640 x 480
-enum StandardDisplaySettings {
- k8k,
- kCinema4k,
- kUHD4k,
- k2k,
- kWUXGA,
- k1080p,
- k720p,
- k480p
-};
-
-math::Size GetDimensions(StandardDisplaySettings enum_value) {
- switch (enum_value) {
- case k8k: {
- return math::Size(7680, 4320);
- }
- case kCinema4k: {
- return math::Size(4096, 2160);
- }
- case kUHD4k: {
- return math::Size(3840, 2160);
- }
- case k2k: {
- return math::Size(2048, 1080);
- }
- case kWUXGA: {
- return math::Size(1920, 1200);
- }
- case k1080p: {
- return math::Size(1920, 1080);
- }
- case k720p: {
- return math::Size(1280, 720);
- }
- case k480p: {
- return math::Size(640, 480);
- }
- }
-
- EXPECT_TRUE(false)
- << "Should not be reached. Unknown enum_value: " << enum_value;
- return GetDimensions(k1080p);
+TEST(IntSetting, ParseFromString) {
+ scoped_ptr<IntSetting> int_setting(new IntSetting("dummy"));
+ EXPECT_EQ(std::string("dummy"), int_setting->name());
+ ASSERT_TRUE(int_setting->TryParseValue(MemorySetting::kCmdLine, "123"));
+ EXPECT_EQ(123, int_setting->value());
+ EXPECT_EQ(MemorySetting::kCmdLine, int_setting->source_type());
+ EXPECT_EQ(std::string("123"), int_setting->ValueToString());
}
-TEST(MemorySettings, CalculateImageCacheSize) {
- EXPECT_EQ(kMinImageCacheSize, CalculateImageCacheSize(GetDimensions(k720p)));
- EXPECT_EQ(32 * 1024 * 1024, // 32MB.
- CalculateImageCacheSize(GetDimensions(k1080p)));
- EXPECT_EQ(kMaxImageCacheSize, CalculateImageCacheSize(GetDimensions(kUHD4k)));
-
- // Expect that the floor is hit for smaller values.
- EXPECT_EQ(kMinImageCacheSize, CalculateImageCacheSize(GetDimensions(k480p)));
-
- // Expect that the ceiling is hit for larger values.
- EXPECT_EQ(kMaxImageCacheSize,
- CalculateImageCacheSize(GetDimensions(kCinema4k)));
- EXPECT_EQ(kMaxImageCacheSize, CalculateImageCacheSize(GetDimensions(k8k)));
-}
-
-TEST(MemorySettings, GetImageCacheSize) {
- if (COBALT_IMAGE_CACHE_SIZE_IN_BYTES >= 0) {
- // Expect that regardless of the display size, the value returned will
- // be equal to COBALT_IMAGE_CACHE_SIZE_IN_BYTES
- EXPECT_EQ(COBALT_IMAGE_CACHE_SIZE_IN_BYTES,
- GetImageCacheSize(GetDimensions(k720p)));
- EXPECT_EQ(COBALT_IMAGE_CACHE_SIZE_IN_BYTES,
- GetImageCacheSize(GetDimensions(k1080p)));
- EXPECT_EQ(COBALT_IMAGE_CACHE_SIZE_IN_BYTES,
- GetImageCacheSize(GetDimensions(kUHD4k)));
- } else {
- // ... otherwise expect that the GetImageCacheSize() is equal to
- // CalculateImageCacheSize().
- EXPECT_EQ(CalculateImageCacheSize(GetDimensions(k720p)),
- GetImageCacheSize(GetDimensions(k720p)));
- EXPECT_EQ(CalculateImageCacheSize(GetDimensions(k1080p)),
- GetImageCacheSize(GetDimensions(k1080p)));
- EXPECT_EQ(CalculateImageCacheSize(GetDimensions(kUHD4k)),
- GetImageCacheSize(GetDimensions(kUHD4k)));
- }
+TEST(DimensionSetting, ParseFromString) {
+ scoped_ptr<DimensionSetting> rect_setting(new DimensionSetting("dummy"));
+ ASSERT_TRUE(
+ rect_setting->TryParseValue(MemorySetting::kCmdLine, "1234x5678"));
+ EXPECT_EQ(math::Size(1234, 5678), rect_setting->value());
+ EXPECT_EQ(MemorySetting::kCmdLine, rect_setting->source_type());
+ EXPECT_EQ(std::string("1234x5678"), rect_setting->ValueToString());
}
} // namespace memory_settings
diff --git a/src/cobalt/browser/memory_settings/pretty_print.cc b/src/cobalt/browser/memory_settings/pretty_print.cc
new file mode 100644
index 0000000..b153b5f
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/pretty_print.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/pretty_print.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "cobalt/browser/memory_settings/memory_settings.h"
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+namespace {
+
+class TablePrinter {
+ public:
+ struct Row {
+ std::string name;
+ std::string value;
+ std::string source;
+ };
+
+ static std::string ToString(const std::vector<Row>& rows) {
+ // First column is the name, the second is the value, then source.
+ size_t name_col_width = 0;
+ size_t value_col_width = 0;
+ size_t source_col_width = 0;
+
+ for (size_t i = 0; i < rows.size(); ++i) {
+ const Row& row = rows[i];
+ name_col_width = std::max<size_t>(name_col_width, row.name.size());
+ value_col_width = std::max<size_t>(value_col_width, row.value.size());
+ source_col_width = std::max<size_t>(source_col_width, row.source.size());
+ }
+
+ TablePrinter printer(name_col_width, value_col_width, source_col_width);
+
+ std::stringstream output_ss;
+ std::string row_delimiter = printer.MakeDelimiterRow();
+
+ output_ss << printer.MakeHeaderRow() << "\n";
+ output_ss << row_delimiter << "\n";
+
+ for (size_t i = 0; i < rows.size(); ++i) {
+ const Row& row = rows[i];
+ std::string row_str =
+ printer.MakeDataRow(row.name, row.value, row.source);
+ output_ss << row_str << "\n";
+ output_ss << row_delimiter << "\n";
+ }
+
+ return output_ss.str();
+ }
+
+ private:
+ TablePrinter(size_t name_col_width, size_t value_col_width,
+ size_t source_col_width)
+ : name_col_width_(name_col_width),
+ value_col_width_(value_col_width),
+ source_col_width_(source_col_width) {
+ Init();
+ }
+
+ void Init() {
+ std::stringstream fmt_row_ss;
+ // Example: "| %-30s | %9s | %9s |\n"
+ fmt_row_ss << "| %-" << name_col_width_ << "s | %" << value_col_width_
+ << "s | %" << source_col_width_ << "s |";
+ fmt_data_row_ = fmt_row_ss.str();
+ }
+
+ std::string MakeHeaderRow() const {
+ std::string name_str = "NAME";
+ std::string value_str = "VALUE";
+ std::string source_str = "SOURCE";
+
+ std::stringstream ss;
+ ss << name_str;
+ for (size_t i = 0; i < name_col_width_ + 3 - name_str.size(); ++i) {
+ ss << " ";
+ }
+
+ ss << value_str;
+ for (size_t i = 0; i < value_col_width_ + 3 - value_str.size(); ++i) {
+ ss << " ";
+ }
+
+ ss << source_str;
+ for (size_t i = 0; i < source_col_width_ + 4 - source_str.size(); ++i) {
+ ss << " ";
+ }
+ return ss.str();
+ }
+
+ std::string MakeDataRow(const std::string& name, const std::string& value,
+ const std::string& source) const {
+ std::string row_str = base::StringPrintf(
+ fmt_data_row_.c_str(), name.c_str(), value.c_str(), source.c_str());
+ return row_str;
+ }
+
+ std::string MakeDelimiterRow() const {
+ std::stringstream ss;
+ ss << "+";
+ for (size_t i = 0; i < name_col_width_ + 2; ++i) {
+ ss << "-";
+ }
+ ss << "+";
+ for (size_t i = 0; i < value_col_width_ + 2; ++i) {
+ ss << "-";
+ }
+ ss << "+";
+ for (size_t i = 0; i < source_col_width_ + 2; ++i) {
+ ss << "-";
+ }
+ ss << "+";
+ return ss.str();
+ }
+
+ const size_t name_col_width_;
+ const size_t value_col_width_;
+ const size_t source_col_width_;
+ std::string fmt_data_row_;
+};
+
+std::string StringifySourceType(MemorySetting::SourceType source_type) {
+ switch (source_type) {
+ case MemorySetting::kUnset: {
+ return "Unset";
+ }
+ case MemorySetting::kCmdLine: {
+ return "CmdLine";
+ }
+ case MemorySetting::kBuildSetting: {
+ return "BuildSetting";
+ }
+ case MemorySetting::kAutoSet: {
+ return "AutoSet";
+ }
+ case MemorySetting::kNotApplicable: {
+ return "N/A";
+ }
+ }
+
+ SB_NOTIMPLEMENTED() << "Unimplemented string for type " << source_type;
+ return "UNKNOWN";
+}
+
+} // namespace
+
+std::string GeneratePrettyPrintTable(
+ const std::vector<const MemorySetting*>& settings) {
+ std::vector<TablePrinter::Row> rows;
+
+ for (size_t i = 0; i < settings.size(); ++i) {
+ const MemorySetting* setting = settings[i];
+
+ TablePrinter::Row row;
+ row.name = setting->name();
+ row.value = setting->ValueToString();
+ row.source = StringifySourceType(setting->source_type());
+
+ rows.push_back(row);
+ }
+
+ std::string table_string = TablePrinter::ToString(rows);
+ return table_string;
+}
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_settings/pretty_print.h b/src/cobalt/browser/memory_settings/pretty_print.h
new file mode 100644
index 0000000..dd533c0
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/pretty_print.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_BROWSER_MEMORY_SETTINGS_PRETTY_PRINT_H_
+#define COBALT_BROWSER_MEMORY_SETTINGS_PRETTY_PRINT_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/stringprintf.h"
+#include "cobalt/browser/memory_settings/memory_settings.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+// Generates a table, ie:
+// "NAME VALUE SOURCE \n"
+// "+--------------------------------------+-----------+--------------+\n"
+// "| image_cache_size_in_bytes | 1234 | CmdLine |\n"
+// "+--------------------------------------+-----------+--------------+\n"
+// "| javascript_gc_threshold_in_bytes | 1112 | AutoSet |\n"
+// "+--------------------------------------+-----------+--------------+\n"
+// "| skia_atlas_texture_dimensions | 1234x4567 | CmdLine |\n"
+// "+--------------------------------------+-----------+--------------+\n"
+// "| skia_cache_size_in_bytes | 0 | N/A |\n"
+// "+--------------------------------------+-----------+--------------+\n"
+// "| software_surface_cache_size_in_bytes | 8910 | BuildSetting |\n"
+// "+--------------------------------------+-----------+--------------+\n";
+std::string GeneratePrettyPrintTable(
+ const std::vector<const MemorySetting*>& memory_settings);
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_SETTINGS_PRETTY_PRINT_H_
diff --git a/src/cobalt/browser/memory_settings/pretty_print_test.cc b/src/cobalt/browser/memory_settings/pretty_print_test.cc
new file mode 100644
index 0000000..079a099
--- /dev/null
+++ b/src/cobalt/browser/memory_settings/pretty_print_test.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/browser/memory_settings/pretty_print.h"
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/browser/memory_settings/memory_settings.h"
+#include "cobalt/browser/switches.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/system.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_settings {
+
+scoped_ptr<MemorySetting> CreateMemorySetting(
+ MemorySetting::ClassType class_type, MemorySetting::SourceType source_type,
+ const std::string& name, const std::string& value) {
+ scoped_ptr<MemorySetting> setting;
+
+ switch (class_type) {
+ case MemorySetting::kInt: {
+ setting.reset(new IntSetting(name));
+ break;
+ }
+ case MemorySetting::kDimensions: {
+ setting.reset(new DimensionSetting(name));
+ break;
+ }
+ default: {
+ EXPECT_TRUE(false) << "Unexpected type " << class_type;
+ setting.reset(new IntSetting(name));
+ break;
+ }
+ }
+ EXPECT_TRUE(setting->TryParseValue(source_type, value));
+ return setting.Pass();
+}
+
+TEST(MemorySettingsPrettyPrint, ToString) {
+ std::vector<const MemorySetting*> memory_settings_ptr;
+ scoped_ptr<MemorySetting> image_cache_setting =
+ CreateMemorySetting(MemorySetting::kInt, MemorySetting::kCmdLine,
+ switches::kImageCacheSizeInBytes, "1234");
+ memory_settings_ptr.push_back(image_cache_setting.get());
+
+ scoped_ptr<MemorySetting> js_gc_setting =
+ CreateMemorySetting(MemorySetting::kInt, MemorySetting::kAutoSet,
+ switches::kJavaScriptGcThresholdInBytes, "1112");
+ memory_settings_ptr.push_back(js_gc_setting.get());
+
+ scoped_ptr<MemorySetting> skia_texture_setting =
+ CreateMemorySetting(MemorySetting::kDimensions, MemorySetting::kCmdLine,
+ switches::kSkiaTextureAtlasDimensions, "1234x4567");
+ memory_settings_ptr.push_back(skia_texture_setting.get());
+
+ scoped_ptr<MemorySetting> skia_cache_setting =
+ CreateMemorySetting(MemorySetting::kInt, MemorySetting::kNotApplicable,
+ switches::kSkiaCacheSizeInBytes, "0");
+ memory_settings_ptr.push_back(skia_cache_setting.get());
+
+ scoped_ptr<MemorySetting> software_surface_setting =
+ CreateMemorySetting(MemorySetting::kInt, MemorySetting::kBuildSetting,
+ switches::kSoftwareSurfaceCacheSizeInBytes, "8910");
+ memory_settings_ptr.push_back(software_surface_setting.get());
+
+ std::string actual_string = GeneratePrettyPrintTable(memory_settings_ptr);
+
+ std::string expected_string =
+ "NAME VALUE SOURCE \n"
+ "+--------------------------------------+-----------+--------------+\n"
+ "| image_cache_size_in_bytes | 1234 | CmdLine |\n"
+ "+--------------------------------------+-----------+--------------+\n"
+ "| javascript_gc_threshold_in_bytes | 1112 | AutoSet |\n"
+ "+--------------------------------------+-----------+--------------+\n"
+ "| skia_atlas_texture_dimensions | 1234x4567 | CmdLine |\n"
+ "+--------------------------------------+-----------+--------------+\n"
+ "| skia_cache_size_in_bytes | 0 | N/A |\n"
+ "+--------------------------------------+-----------+--------------+\n"
+ "| software_surface_cache_size_in_bytes | 8910 | BuildSetting |\n"
+ "+--------------------------------------+-----------+--------------+\n";
+
+ const bool strings_matched = (expected_string == actual_string);
+ EXPECT_TRUE(strings_matched) << "Strings Mismatched:\n\n"
+ << "Actual:\n" << actual_string << "\n"
+ << "Expected:\n" << expected_string << "\n";
+}
+
+} // namespace memory_settings
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc b/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc
deleted file mode 100644
index b30c612..0000000
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc
+++ /dev/null
@@ -1,1743 +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/browser/memory_tracker/memory_tracker_tool_impl.h"
-
-#include <algorithm>
-#include <cstring>
-#include <iomanip>
-#include <iterator>
-#include <set>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/time.h"
-#include "cobalt/browser/memory_tracker/buffered_file_writer.h"
-#include "cobalt/script/mozjs/util/stack_trace_helpers.h"
-#include "nb/analytics/memory_tracker.h"
-#include "nb/analytics/memory_tracker_helpers.h"
-#include "nb/concurrent_map.h"
-#include "nb/memory_scope.h"
-#include "starboard/common/semaphore.h"
-#include "starboard/configuration.h"
-#include "starboard/file.h"
-#include "starboard/string.h"
-#include "starboard/system.h"
-
-namespace cobalt {
-namespace browser {
-namespace memory_tracker {
-
-using nb::analytics::AllocationGroup;
-using nb::analytics::AllocationRecord;
-using nb::analytics::AllocationVisitor;
-using nb::analytics::GetProcessMemoryStats;
-using nb::analytics::MemoryStats;
-using nb::analytics::MemoryTracker;
-
-namespace {
-const char kQuote[] = "\"";
-const char kDelimiter[] = ",";
-const char kNewLine[] = "\n";
-
-// This is a simple algorithm to remove the "needle" from the haystack. Note
-// that this function is simple and not well optimized.
-std::string RemoveString(const std::string& haystack, const char* needle) {
- const size_t kNotFound = std::string::npos;
-
- // Base case. No modification needed.
- size_t pos = haystack.find(needle);
- if (pos == kNotFound) {
- return haystack;
- }
- const size_t n = strlen(needle);
- std::string output;
- output.reserve(haystack.size());
-
- // Copy string, omitting the portion containing the "needle".
- std::copy(haystack.begin(), haystack.begin() + pos,
- std::back_inserter(output));
- std::copy(haystack.begin() + pos + n, haystack.end(),
- std::back_inserter(output));
-
- // Recursively remove same needle in haystack.
- return RemoveString(output, needle);
-}
-
-// Not optimized but works ok for a tool that dumps out in user time.
-std::string SanitizeCSVKey(std::string key) {
- key = RemoveString(key, kQuote);
- key = RemoveString(key, kDelimiter);
- key = RemoveString(key, kNewLine);
- return key;
-}
-
-// Converts "2345.54" => "2,345.54".
-std::string InsertCommasIntoNumberString(const std::string& input) {
- typedef std::vector<char> CharVector;
- typedef CharVector::iterator CharIt;
-
- CharVector chars(input.begin(), input.end());
- std::reverse(chars.begin(), chars.end());
-
- CharIt curr_it = chars.begin();
- CharIt mid = std::find(chars.begin(), chars.end(), '.');
- if (mid == chars.end()) {
- mid = curr_it;
- }
-
- CharVector out(curr_it, mid);
-
- int counter = 0;
- for (CharIt it = mid; it != chars.end(); ++it) {
- if (counter != 0 && (counter % 3 == 0)) {
- out.push_back(',');
- }
- if (*it != '.') {
- counter++;
- }
- out.push_back(*it);
- }
-
- std::reverse(out.begin(), out.end());
- std::stringstream ss;
- for (size_t i = 0; i < out.size(); ++i) {
- ss << out[i];
- }
- return ss.str();
-}
-
-template <typename T>
-std::string NumberFormatWithCommas(T val) {
- // Convert value to string.
- std::stringstream ss;
- ss << val;
- std::string s = InsertCommasIntoNumberString(ss.str());
- return s;
-}
-
-// Removes odd elements and resizes vector.
-template <typename VectorType>
-void RemoveOddElements(VectorType* v) {
- typedef typename VectorType::iterator iterator;
-
- iterator read_it = v->end();
- iterator write_it = v->end();
- for (size_t i = 0; i*2 < v->size(); ++i) {
- write_it = v->begin() + i;
- read_it = v->begin() + (i*2);
- *write_it = *read_it;
- }
- if (write_it != v->end()) {
- write_it++;
- }
- v->erase(write_it, v->end());
-}
-
-// NoMemoryTracking will disable memory tracking while in the current scope of
-// execution. When the object is destroyed it will reset the previous state
-// of allocation tracking.
-// Example:
-// void Foo() {
-// NoMemoryTracking no_memory_tracking_in_scope;
-// int* ptr = new int(); // ptr is not tracked.
-// delete ptr;
-// return; // Previous memory tracking state is restored.
-// }
-class NoMemoryTracking {
- public:
- explicit NoMemoryTracking(nb::analytics::MemoryTracker* owner);
- ~NoMemoryTracking();
-
- private:
- bool prev_val_;
- nb::analytics::MemoryTracker* owner_;
-};
-
-// Simple timer class that fires once after dt time has elapsed.
-class Timer {
- public:
- explicit Timer(base::TimeDelta dt)
- : start_time_(base::TimeTicks::Now()), time_before_expiration_(dt) {}
-
- void Restart() { start_time_ = base::TimeTicks::Now(); }
-
- bool UpdateAndIsExpired() {
- base::TimeTicks now_time = base::TimeTicks::Now();
- base::TimeDelta dt = now_time - start_time_;
- if (dt > time_before_expiration_) {
- start_time_ = now_time;
- return true;
- } else {
- return false;
- }
- }
-
- private:
- base::TimeTicks start_time_;
- base::TimeDelta time_before_expiration_;
-};
-
-// Returns true if linear fit was calculated. False otherwise. Reasons for
-// returning false include passing in an empty range, such that
-// begin_it == end_it, or all x-values being the same.
-//
-// Algorithm adapted from:
-// https://en.wikipedia.org/wiki/Simple_linear_regression#Fitting_the_regression_line
-// Example:
-// std::vector<std::pair<int, int> > data;
-// for (int i = 0; i < 10; ++i) {
-// data.push_back(std::pair<int, int>(i+1, 2*i));
-// }
-// double slope = 0;
-// double y_intercept = 0;
-// GetLinearFit(data.begin(), data.end(), &slope, &y_intercept);
-// std::cout << "slope: " << slope << "\n";
-// std::cout << "y_intercept: " << y_intercept<< "\n";
-template <typename PairIterator>
-bool GetLinearFit(PairIterator begin_it, PairIterator end_it, double* out_slope,
- double* out_yintercept) {
- if (begin_it == end_it) {
- return false;
- }
-
- size_t n = 0;
- double x_avg = 0;
- double y_avg = 0;
-
- for (PairIterator it = begin_it; it != end_it; ++it) {
- x_avg += it->first;
- y_avg += it->second;
- n++;
- }
-
- x_avg /= n;
- y_avg /= n;
-
- double numerator = 0;
- double denominator = 0;
-
- for (PairIterator it = begin_it; it != end_it; ++it) {
- double x_variance = it->first - x_avg;
- double y_variance = it->second - y_avg;
- numerator += (x_variance * y_variance);
- denominator += (x_variance * x_variance);
- }
-
- if (denominator == 0.0) {
- return false;
- }
-
- double slope = numerator / denominator;
- double yintercept = y_avg - slope * x_avg;
-
- *out_slope = slope;
- *out_yintercept = yintercept;
- return true;
-}
-
-// Returns a substring with the directory path removed from the filename.
-// Example:
-// F::BaseNameFast("directory/filename.cc") => "filename.cc"
-// F::BaseNameFast("directory\filename.cc") => "filename.cc"
-//
-// Note that base::FilePath::BaseName() isn't used because of performance
-// reasons.
-const char* BaseNameFast(const char* file_name) {
- const char* end_pos = file_name + strlen(file_name);
- const char* last_forward_slash = SbStringFindLastCharacter(file_name, '/');
- if (last_forward_slash) {
- if (end_pos != last_forward_slash) {
- ++last_forward_slash;
- }
- return last_forward_slash;
- }
-
- const char* last_backward_slash = SbStringFindLastCharacter(file_name, '\\');
- if (last_backward_slash) {
- if (end_pos != last_backward_slash) {
- ++last_backward_slash;
- }
- return last_backward_slash;
- }
- return file_name;
-}
-
-} // namespace
-
-class Params {
- public:
- Params(nb::analytics::MemoryTracker* memory_tracker, AbstractLogger* logger,
- base::Time start_time)
- : memory_tracker_(memory_tracker),
- finished_(false),
- logger_(logger),
- timer_(start_time) {}
- bool finished() const { return finished_; }
- bool wait_for_finish_signal(SbTime wait_us) {
- return finished_semaphore_.TakeWait(wait_us);
- }
- void set_finished(bool val) {
- finished_ = val;
- finished_semaphore_.Put();
- }
-
- nb::analytics::MemoryTracker* memory_tracker() const {
- return memory_tracker_;
- }
- AbstractLogger* logger() { return logger_.get(); }
- base::TimeDelta time_since_start() const {
- return base::Time::NowFromSystemTime() - timer_;
- }
- std::string TimeInMinutesString() const {
- base::TimeDelta delta_t = time_since_start();
- int64 seconds = delta_t.InSeconds();
- float time_mins = static_cast<float>(seconds) / 60.f;
- std::stringstream ss;
-
- ss << time_mins;
- return ss.str();
- }
-
- private:
- nb::analytics::MemoryTracker* memory_tracker_;
- bool finished_;
- scoped_ptr<AbstractLogger> logger_;
- base::Time timer_;
- starboard::Semaphore finished_semaphore_;
-};
-
-MemoryTrackerToolThread::MemoryTrackerToolThread(
- nb::analytics::MemoryTracker* memory_tracker,
- AbstractMemoryTrackerTool* tool, AbstractLogger* logger)
- : Super(tool->tool_name()),
- params_(
- new Params(memory_tracker, logger, base::Time::NowFromSystemTime())),
- tool_(tool) {
- Start();
-}
-
-MemoryTrackerToolThread::~MemoryTrackerToolThread() {
- Join();
- tool_.reset();
- params_.reset();
-}
-
-void MemoryTrackerToolThread::Join() {
- params_->set_finished(true);
- Super::Join();
-}
-
-void MemoryTrackerToolThread::Run() {
- NoMemoryTracking no_mem_tracking_in_this_scope(params_->memory_tracker());
- // This tool will run until the finished_ if flipped to false.
- tool_->Run(params_.get());
-}
-
-NoMemoryTracking::NoMemoryTracking(nb::analytics::MemoryTracker* owner)
- : prev_val_(false), owner_(owner) {
- if (owner_) {
- prev_val_ = owner_->IsMemoryTrackingEnabled();
- owner_->SetMemoryTrackingEnabled(false);
- }
-}
-
-NoMemoryTracking::~NoMemoryTracking() {
- if (owner_) {
- owner_->SetMemoryTrackingEnabled(prev_val_);
- }
-}
-
-void MemoryTrackerPrint::Run(Params* params) {
- const std::string kSeperator
- = "--------------------------------------------------";
-
- while (!params->finished()) {
- std::vector<const AllocationGroup*> vector_output;
- params->memory_tracker()->GetAllocationGroups(&vector_output);
-
- typedef std::map<std::string, const AllocationGroup*> Map;
- typedef Map::const_iterator MapIt;
-
- Map output;
- for (size_t i = 0; i < vector_output.size(); ++i) {
- const AllocationGroup* group = vector_output[i];
- output[group->name()] = group;
- }
-
- int32 num_allocs = 0;
- int64 total_bytes = 0;
-
- struct F {
- static void PrintRow(std::stringstream* ss, const std::string& v1,
- const std::string& v2, const std::string& v3) {
- ss->width(25);
- *ss << std::left << v1;
- ss->width(13);
- *ss << std::right << v2 << " ";
- ss->width(10);
- *ss << std::right << v3 << "\n";
- }
- };
-
- if (params->memory_tracker()->IsMemoryTrackingEnabled()) {
- // If this isn't true then it would cause an infinite loop. The
- // following will likely crash.
- SB_DCHECK(false) << "Unexpected, memory tracking should be disabled.";
- }
-
- std::stringstream ss;
-
- ss << kNewLine;
- ss << "TimeNow " << params->TimeInMinutesString()
- << " (minutes):" << kNewLine << kNewLine;
-
- ss << kSeperator << kNewLine;
- MemoryStats memstats = GetProcessMemoryStats();
-
- F::PrintRow(&ss, "MALLOC STAT", "IN USE BYTES", "");
- ss << kSeperator << kNewLine;
- F::PrintRow(&ss,
- "Total CPU Reserved",
- NumberFormatWithCommas(memstats.total_cpu_memory),
- "");
-
- F::PrintRow(&ss,
- "Total CPU Used",
- NumberFormatWithCommas(memstats.used_cpu_memory),
- "");
-
- F::PrintRow(&ss,
- "Total GPU Reserved",
- NumberFormatWithCommas(memstats.total_gpu_memory),
- "");
-
- F::PrintRow(&ss,
- "Total GPU Used",
- NumberFormatWithCommas(memstats.used_gpu_memory),
- "");
-
- ss << kSeperator << kNewLine << kNewLine;
-
- ss << kSeperator << kNewLine;
- F::PrintRow(&ss, "MEMORY REGION", "IN USE BYTES", "NUM ALLOCS");
- ss << kSeperator << kNewLine;
-
- for (MapIt it = output.begin(); it != output.end(); ++it) {
- const AllocationGroup* group = it->second;
- if (!group) {
- continue;
- }
-
- int32 num_group_allocs = -1;
- int64 total_group_bytes = -1;
-
- group->GetAggregateStats(&num_group_allocs, &total_group_bytes);
- SB_DCHECK(-1 != num_group_allocs);
- SB_DCHECK(-1 != total_group_bytes);
- num_allocs += num_group_allocs;
- total_bytes += total_group_bytes;
-
- F::PrintRow(&ss, it->first, NumberFormatWithCommas(total_group_bytes),
- NumberFormatWithCommas(num_group_allocs));
- }
-
- ss << kNewLine;
-
- F::PrintRow(&ss,
- "Total (in groups above)",
- NumberFormatWithCommas(total_bytes),
- NumberFormatWithCommas(num_allocs));
-
- ss << kSeperator << kNewLine;
- ss << kNewLine << kNewLine;
-
- params->logger()->Output(ss.str().c_str());
- // Output once every 5 seconds.
- base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
- }
-}
-
-MemoryTrackerPrintCSV::MemoryTrackerPrintCSV(int sampling_interval_ms,
- int sampling_time_ms)
- : sample_interval_ms_(sampling_interval_ms),
- sampling_time_ms_(sampling_time_ms) {}
-
-std::string MemoryTrackerPrintCSV::ToCsvString(
- const MapAllocationSamples& samples_in) {
- typedef MapAllocationSamples Map;
- typedef Map::const_iterator MapIt;
-
- size_t largest_sample_size = 0;
- size_t smallest_sample_size = INT_MAX;
-
- // Sanitize samples_in and store as samples.
- MapAllocationSamples samples;
- for (MapIt it = samples_in.begin(); it != samples_in.end(); ++it) {
- std::string name = it->first;
- const AllocationSamples& value = it->second;
-
- if (value.allocated_bytes_.size() != value.number_allocations_.size()) {
- SB_NOTREACHED() << "Error at " << __FILE__ << ":" << __LINE__;
- return "ERROR";
- }
-
- const size_t n = value.allocated_bytes_.size();
- if (n > largest_sample_size) {
- largest_sample_size = n;
- }
- if (n < smallest_sample_size) {
- smallest_sample_size = n;
- }
-
- const bool duplicate_found = (samples.end() != samples.find(name));
- if (duplicate_found) {
- SB_NOTREACHED() << "Error, duplicate found for entry: " << name
- << kNewLine;
- }
- // Store value as a sanitized sample.
- samples[name] = value;
- }
-
- SB_DCHECK(largest_sample_size == smallest_sample_size);
-
- std::stringstream ss;
-
- // Begin output to CSV.
- // Sometimes we need to skip the CPU memory entry.
- const MapIt total_cpu_memory_it = samples.find(UntrackedMemoryKey());
-
- // Preamble
- ss << kNewLine << "//////////////////////////////////////////////";
- ss << kNewLine << "// CSV of bytes / allocation" << kNewLine;
- // HEADER.
- ss << "Name" << kDelimiter << kQuote << "Bytes/Alloc" << kQuote << kNewLine;
- // DATA.
- for (MapIt it = samples.begin(); it != samples.end(); ++it) {
- if (total_cpu_memory_it == it) {
- continue;
- }
-
- const AllocationSamples& samples = it->second;
- if (samples.allocated_bytes_.empty() ||
- samples.number_allocations_.empty()) {
- SB_NOTREACHED() << "Should not be here";
- return "ERROR";
- }
- const int64 n_allocs = samples.number_allocations_.back();
- const int64 n_bytes = samples.allocated_bytes_.back();
- int64 bytes_per_alloc = 0;
- if (n_allocs > 0) {
- bytes_per_alloc = n_bytes / n_allocs;
- }
- const std::string& name = it->first;
- ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter
- << bytes_per_alloc << kNewLine;
- }
- ss << kNewLine;
-
- // Preamble
- ss << kNewLine << "//////////////////////////////////////////////" << kNewLine
- << "// CSV of bytes allocated per region (MB's)." << kNewLine
- << "// Units are in Megabytes. This is designed" << kNewLine
- << "// to be used in a stacked graph." << kNewLine;
-
- // HEADER.
- for (MapIt it = samples.begin(); it != samples.end(); ++it) {
- if (total_cpu_memory_it == it) {
- continue;
- }
- // Strip out any characters that could make parsing the csv difficult.
- const std::string name = SanitizeCSVKey(it->first);
- ss << kQuote << name << kQuote << kDelimiter;
- }
- // Save the total for last.
- if (total_cpu_memory_it != samples.end()) {
- const std::string& name = SanitizeCSVKey(total_cpu_memory_it->first);
- ss << kQuote << name << kQuote << kDelimiter;
- }
- ss << kNewLine;
-
- // Print out the values of each of the samples.
- for (size_t i = 0; i < smallest_sample_size; ++i) {
- for (MapIt it = samples.begin(); it != samples.end(); ++it) {
- if (total_cpu_memory_it == it) {
- continue;
- }
- const int64 alloc_bytes = it->second.allocated_bytes_[i];
- // Convert to float megabytes with decimals of precision.
- double n = alloc_bytes / (1000 * 10);
- n = n / (100.);
- ss << n << kDelimiter;
- }
- if (total_cpu_memory_it != samples.end()) {
- const int64 alloc_bytes = total_cpu_memory_it->second.allocated_bytes_[i];
- // Convert to float megabytes with decimals of precision.
- double n = alloc_bytes / (1000 * 10);
- n = n / (100.);
- ss << n << kDelimiter;
- }
- ss << kNewLine;
- }
-
- ss << kNewLine;
- // Preamble
- ss << kNewLine << "//////////////////////////////////////////////";
- ss << kNewLine << "// CSV of number of allocations per region." << kNewLine;
-
- // HEADER
- for (MapIt it = samples.begin(); it != samples.end(); ++it) {
- if (total_cpu_memory_it == it) {
- continue;
- }
- const std::string& name = SanitizeCSVKey(it->first);
- ss << kQuote << name << kQuote << kDelimiter;
- }
- ss << kNewLine;
- for (size_t i = 0; i < smallest_sample_size; ++i) {
- for (MapIt it = samples.begin(); it != samples.end(); ++it) {
- if (total_cpu_memory_it == it) {
- continue;
- }
- const int64 n_allocs = it->second.number_allocations_[i];
- ss << n_allocs << kDelimiter;
- }
- ss << kNewLine;
- }
- std::string output = ss.str();
- return output;
-}
-
-const char* MemoryTrackerPrintCSV::UntrackedMemoryKey() {
- return "Untracked Memory";
-}
-
-void MemoryTrackerPrintCSV::Run(Params* params) {
- params->logger()->Output("\nMemoryTrackerPrintCSVThread is sampling...\n");
- int sample_count = 0;
- MapAllocationSamples map_samples;
-
- while (!TimeExpiredYet(*params) && !params->finished()) {
- // Sample total memory used by the system.
- MemoryStats mem_stats = GetProcessMemoryStats();
- int64 untracked_used_memory =
- mem_stats.used_cpu_memory + mem_stats.used_gpu_memory;
-
- std::vector<const AllocationGroup*> vector_output;
- params->memory_tracker()->GetAllocationGroups(&vector_output);
-
- // Sample all known memory scopes.
- for (size_t i = 0; i < vector_output.size(); ++i) {
- const AllocationGroup* group = vector_output[i];
- const std::string& name = group->name();
-
- const bool first_creation =
- map_samples.find(group->name()) == map_samples.end();
-
- AllocationSamples* new_entry = &(map_samples[name]);
-
- // Didn't see it before so create new entry.
- if (first_creation) {
- // Make up for lost samples...
- new_entry->allocated_bytes_.resize(sample_count, 0);
- new_entry->number_allocations_.resize(sample_count, 0);
- }
-
- int32 num_allocs = -1;
- int64 allocation_bytes = -1;
- group->GetAggregateStats(&num_allocs, &allocation_bytes);
-
- new_entry->allocated_bytes_.push_back(allocation_bytes);
- new_entry->number_allocations_.push_back(num_allocs);
-
- untracked_used_memory -= allocation_bytes;
- }
-
- // Now push in remaining total.
- AllocationSamples* process_sample = &(map_samples[UntrackedMemoryKey()]);
- if (untracked_used_memory < 0) {
- // On some platforms, total GPU memory may not be correctly reported.
- // However the allocations from the GPU memory may be reported. In this
- // case untracked_used_memory will go negative. To protect the memory
- // reporting the untracked_used_memory is set to 0 so that it doesn't
- // cause an error in reporting.
- untracked_used_memory = 0;
- }
- process_sample->allocated_bytes_.push_back(untracked_used_memory);
- process_sample->number_allocations_.push_back(-1);
-
- ++sample_count;
- base::PlatformThread::Sleep(
- base::TimeDelta::FromMilliseconds(sample_interval_ms_));
- }
-
- std::stringstream ss;
- ss.precision(2);
- ss << "Time now: " << params->TimeInMinutesString() << ",\n";
- ss << ToCsvString(map_samples);
- params->logger()->Output(ss.str().c_str());
- params->logger()->Flush();
- // Prevents the "thread exited code 0" from being interleaved into the
- // output. This happens if flush is not implemented correctly in the system.
- base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
-}
-
-bool MemoryTrackerPrintCSV::TimeExpiredYet(const Params& params) {
- base::TimeDelta dt = params.time_since_start();
- int64 dt_ms = dt.InMilliseconds();
- const bool expired_time = dt_ms > sampling_time_ms_;
- return expired_time;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-MemoryTrackerCompressedTimeSeries::MemoryTrackerCompressedTimeSeries()
- : number_samples_(400) {}
-
-void MemoryTrackerCompressedTimeSeries::Run(Params* params) {
- TimeSeries timeseries;
- Timer timer_status_message(base::TimeDelta::FromSeconds(1));
-
- // Outputs CSV once every minute.
- Timer timer_output_csv(base::TimeDelta::FromMinutes(1));
-
- // Initial sample time is every 50 milliseconds.
- base::TimeDelta current_sample_interval =
- base::TimeDelta::FromMilliseconds(50);
-
- while (!params->wait_for_finish_signal(current_sample_interval.ToSbTime())) {
- AcquireSample(params->memory_tracker(), ×eries,
- params->time_since_start());
-
- if (IsFull(timeseries, number_samples_)) {
- Compress(×eries); // Remove every other element.
- current_sample_interval *= 2; // Double the sample time.
- timer_status_message.Restart(); // Skip status message.
- }
-
- if (timer_output_csv.UpdateAndIsExpired()) {
- const std::string str = ToCsvString(timeseries);
- params->logger()->Output(str.c_str());
- timer_status_message.Restart(); // Skip status message.
- }
-
- // Print status message.
- if (timer_status_message.UpdateAndIsExpired()) {
- std::stringstream ss;
- ss << tool_name() << " is running..." << kNewLine;
- params->logger()->Output(ss.str().c_str());
- }
- }
-}
-
-std::string MemoryTrackerCompressedTimeSeries::ToCsvString(
- const TimeSeries& timeseries) {
- size_t largest_sample_size = 0;
- size_t smallest_sample_size = INT_MAX;
-
- typedef MapAllocationSamples::const_iterator MapIt;
-
- // Sanitize samples_in and store as samples.
- const MapAllocationSamples& samples_in = timeseries.samples_;
- MapAllocationSamples samples;
- for (MapIt it = samples_in.begin(); it != samples_in.end(); ++it) {
- std::string name = it->first;
- const AllocationSamples& value = it->second;
-
- if (value.allocated_bytes_.size() != value.number_allocations_.size()) {
- SB_NOTREACHED() << "Error at " << __FILE__ << ":" << __LINE__;
- return "ERROR";
- }
-
- const size_t n = value.allocated_bytes_.size();
- if (n > largest_sample_size) {
- largest_sample_size = n;
- }
- if (n < smallest_sample_size) {
- smallest_sample_size = n;
- }
-
- const bool duplicate_found = (samples.end() != samples.find(name));
- if (duplicate_found) {
- SB_NOTREACHED() << "Error, duplicate found for entry: " << name
- << kNewLine;
- }
- // Store value as a sanitized sample.
- samples[name] = value;
- }
-
- SB_DCHECK(largest_sample_size == smallest_sample_size);
-
- std::stringstream ss;
-
- // Begin output to CSV.
-
- // Preamble
- ss << kNewLine
- << "//////////////////////////////////////////////" << kNewLine
- << "// CSV of BYTES allocated per region (MB's)." << kNewLine
- << "// Units are in Megabytes. This is designed" << kNewLine
- << "// to be used in a stacked graph." << kNewLine;
-
- // HEADER.
- ss << kQuote << "Time(mins)" << kQuote << kDelimiter;
- for (MapIt it = samples.begin(); it != samples.end(); ++it) {
- const std::string& name = it->first;
- ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
- }
- ss << kNewLine;
-
- // Print out the values of each of the samples.
- for (size_t i = 0; i < smallest_sample_size; ++i) {
- // Output time first so that it can be used as an x-axis.
- const double time_mins =
- timeseries.time_stamps_[i].InMilliseconds() / (1000. * 60.);
- ss << time_mins << ",";
- for (MapIt it = samples.begin(); it != samples.end(); ++it) {
- const int64 alloc_bytes = it->second.allocated_bytes_[i];
- // Convert to float megabytes with decimals of precision.
- double n = alloc_bytes / (1000 * 10);
- n = n / (100.);
- ss << n << kDelimiter;
- }
- ss << kNewLine;
- }
- ss << "// END CSV of BYTES allocated per region." << kNewLine;
- ss << "//////////////////////////////////////////////";
-
- ss << kNewLine;
- // Preamble
- ss << kNewLine << "//////////////////////////////////////////////";
- ss << kNewLine << "// CSV of COUNT of allocations per region." << kNewLine;
-
- // HEADER
- ss << kQuote << "Time(mins)" << kQuote << kDelimiter;
- for (MapIt it = samples.begin(); it != samples.end(); ++it) {
- const std::string& name = it->first;
- ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
- }
- ss << kNewLine;
- for (size_t i = 0; i < smallest_sample_size; ++i) {
- // Output time first so that it can be used as an x-axis.
- const double time_mins =
- timeseries.time_stamps_[i].InMilliseconds() / (1000. * 60.);
- ss << time_mins << ",";
- for (MapIt it = samples.begin(); it != samples.end(); ++it) {
- const int64 n_allocs = it->second.number_allocations_[i];
- ss << n_allocs << kDelimiter;
- }
- ss << kNewLine;
- }
- ss << "// END CSV of COUNT of allocations per region." << kNewLine;
- ss << "//////////////////////////////////////////////";
- ss << kNewLine << kNewLine;
-
- std::string output = ss.str();
- return output;
-}
-
-void MemoryTrackerCompressedTimeSeries::AcquireSample(
- MemoryTracker* memory_tracker, TimeSeries* timeseries,
- const base::TimeDelta& time_now) {
- const size_t sample_count = timeseries->time_stamps_.size();
- timeseries->time_stamps_.push_back(time_now);
-
- MapAllocationSamples& map_samples = timeseries->samples_;
-
- std::vector<const AllocationGroup*> vector_output;
- memory_tracker->GetAllocationGroups(&vector_output);
-
- // Sample all known memory scopes.
- for (size_t i = 0; i < vector_output.size(); ++i) {
- const AllocationGroup* group = vector_output[i];
- const std::string& name = group->name();
-
- const bool first_creation =
- map_samples.find(group->name()) == map_samples.end();
-
- AllocationSamples& new_entry = map_samples[name];
-
- // Didn't see it before so create new entry.
- if (first_creation) {
- // Make up for lost samples...
- new_entry.allocated_bytes_.resize(sample_count, 0);
- new_entry.number_allocations_.resize(sample_count, 0);
- }
-
- int32 num_allocs = -1;
- int64 allocation_bytes = -1;
- group->GetAggregateStats(&num_allocs, &allocation_bytes);
-
- new_entry.allocated_bytes_.push_back(allocation_bytes);
- new_entry.number_allocations_.push_back(num_allocs);
- }
-
- // Sample the in use memory bytes reported by malloc.
- MemoryStats memory_stats = GetProcessMemoryStats();
- AllocationSamples& process_mem_in_use = map_samples["ProcessMemoryInUse"];
- process_mem_in_use.allocated_bytes_.push_back(memory_stats.used_cpu_memory);
- process_mem_in_use.number_allocations_.push_back(0);
-
- // Sample the reserved memory bytes reported by malloc.
- AllocationSamples& process_mem_reserved
- = map_samples["ProcessMemoryReserved"];
- process_mem_reserved.allocated_bytes_
- .push_back(memory_stats.used_cpu_memory);
- process_mem_reserved.number_allocations_.push_back(0);
-}
-
-bool MemoryTrackerCompressedTimeSeries::IsFull(const TimeSeries& timeseries,
- size_t samples_limit) {
- return timeseries.time_stamps_.size() >= samples_limit;
-}
-
-void MemoryTrackerCompressedTimeSeries::Compress(TimeSeries* timeseries) {
- typedef MapAllocationSamples::iterator MapIt;
- MapAllocationSamples& samples = timeseries->samples_;
- RemoveOddElements(&(timeseries->time_stamps_));
- for (MapIt it = samples.begin(); it != samples.end(); ++it) {
- AllocationSamples& data = it->second;
- RemoveOddElements(&data.allocated_bytes_);
- RemoveOddElements(&data.number_allocations_);
- }
-}
-
-MemorySizeBinner::MemorySizeBinner(const std::string& memory_scope_name)
- : memory_scope_name_(memory_scope_name) {}
-
-const AllocationGroup* FindAllocationGroup(const std::string& name,
- MemoryTracker* memory_tracker) {
- std::vector<const AllocationGroup*> groups;
- memory_tracker->GetAllocationGroups(&groups);
- // Find by exact string match.
- for (size_t i = 0; i < groups.size(); ++i) {
- const AllocationGroup* group = groups[i];
- if (group->name().compare(name) == 0) {
- return group;
- }
- }
- return NULL;
-}
-
-void MemorySizeBinner::Run(Params* params) {
- const AllocationGroup* target_group = NULL;
-
- while (!params->finished()) {
- if (target_group == NULL && !memory_scope_name_.empty()) {
- target_group =
- FindAllocationGroup(memory_scope_name_, params->memory_tracker());
- }
-
- std::stringstream ss;
- ss.precision(2);
- if (target_group || memory_scope_name_.empty()) {
- AllocationSizeBinner visitor_binner = AllocationSizeBinner(target_group);
- params->memory_tracker()->Accept(&visitor_binner);
-
- size_t min_size = 0;
- size_t max_size = 0;
-
- visitor_binner.GetLargestSizeRange(&min_size, &max_size);
-
- FindTopSizes top_size_visitor =
- FindTopSizes(min_size, max_size, target_group);
- params->memory_tracker()->Accept(&top_size_visitor);
-
- ss << kNewLine;
- ss << "TimeNow " << params->TimeInMinutesString() << " (minutes):";
- ss << kNewLine;
- if (!memory_scope_name_.empty()) {
- ss << "Tracking Memory Scope \"" << memory_scope_name_ << "\", ";
- } else {
- ss << "Tracking whole program, ";
- }
- ss << "first row is allocation size range, second row is number of "
- << kNewLine << "allocations in that range." << kNewLine;
- ss << visitor_binner.ToCSVString();
- ss << kNewLine;
- ss << "Largest allocation range: \"" << min_size << "..." << max_size
- << "\"" << kNewLine;
- ss << "Printing out top allocations from this range: " << kNewLine;
- ss << top_size_visitor.ToString(5) << kNewLine;
- } else {
- ss << "No allocations for \"" << memory_scope_name_ << "\".";
- }
-
- params->logger()->Output(ss.str().c_str());
- params->logger()->Flush();
-
- // Sleep until the next sample.
- base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
- }
-}
-
-size_t AllocationSizeBinner::GetBucketIndexForAllocationSize(size_t size) {
- for (int i = 0; i < 32; ++i) {
- size_t val = 0x1 << i;
- if (val > size) {
- return i;
- }
- }
- SB_NOTREACHED();
- return 32;
-}
-
-void AllocationSizeBinner::GetSizeRange(size_t size, size_t* min_value,
- size_t* max_value) {
- size_t idx = GetBucketIndexForAllocationSize(size);
- IndexToSizeRange(idx, min_value, max_value);
-}
-
-void AllocationSizeBinner::IndexToSizeRange(size_t idx, size_t* min_value,
- size_t* max_value) {
- if (idx == 0) {
- *min_value = 0;
- *max_value = 0;
- return;
- }
- *min_value = 0x1 << (idx - 1);
- *max_value = (*min_value << 1) - 1;
- return;
-}
-
-size_t AllocationSizeBinner::GetIndexRepresentingMostMemoryConsumption() const {
- int64 largest_allocation_total = 0;
- size_t largest_allocation_total_idx = 0;
-
- for (size_t i = 0; i < allocation_histogram_.size(); ++i) {
- size_t alloc_size = 0x1 << i;
- size_t count = allocation_histogram_[i];
- int64 allocation_total =
- static_cast<int64>(alloc_size) * static_cast<int64>(count);
-
- if (largest_allocation_total < allocation_total) {
- largest_allocation_total = allocation_total;
- largest_allocation_total_idx = i;
- }
- }
- return largest_allocation_total_idx;
-}
-
-void AllocationSizeBinner::GetLargestSizeRange(size_t* min_value,
- size_t* max_value) const {
- size_t index = GetIndexRepresentingMostMemoryConsumption();
- IndexToSizeRange(index, min_value, max_value);
-}
-
-AllocationSizeBinner::AllocationSizeBinner(const AllocationGroup* group_filter)
- : group_filter_(group_filter) {
- allocation_histogram_.resize(33);
-}
-
-bool AllocationSizeBinner::PassesFilter(
- const AllocationRecord& alloc_record) const {
- if (group_filter_ == NULL) {
- return true;
- }
-
- return alloc_record.allocation_group == group_filter_;
-}
-
-bool AllocationSizeBinner::Visit(const void* /*memory*/,
- const AllocationRecord& alloc_record) {
- if (PassesFilter(alloc_record)) {
- const size_t idx = GetBucketIndexForAllocationSize(alloc_record.size);
- allocation_histogram_[idx]++;
- }
- return true;
-}
-
-std::string AllocationSizeBinner::ToCSVString() const {
- size_t first_idx = 0;
- size_t end_idx = allocation_histogram_.size();
-
- // Determine the start index by skipping all consecutive head entries
- // that are 0.
- while (first_idx < allocation_histogram_.size()) {
- const size_t num_allocs = allocation_histogram_[first_idx];
- if (num_allocs > 0) {
- break;
- }
- first_idx++;
- }
-
- // Determine the end index by skipping all consecutive tail entries
- // that are 0.
- while (end_idx > 0) {
- if (end_idx < allocation_histogram_.size()) {
- const size_t num_allocs = allocation_histogram_[end_idx];
- if (num_allocs > 0) {
- ++end_idx;
- break;
- }
- }
- end_idx--;
- }
-
- std::stringstream ss;
- for (size_t i = first_idx; i < end_idx; ++i) {
- size_t min = 0;
- size_t max = 0;
- IndexToSizeRange(i, &min, &max);
- std::stringstream name_ss;
- name_ss << kQuote << min << "..." << max << kQuote;
- ss << name_ss.str() << kDelimiter;
- }
- ss << kNewLine;
-
- for (size_t i = first_idx; i < end_idx; ++i) {
- const size_t num_allocs = allocation_histogram_[i];
- ss << num_allocs << kDelimiter;
- }
- ss << kNewLine;
- return ss.str();
-}
-
-FindTopSizes::FindTopSizes(size_t minimum_size, size_t maximum_size,
- const AllocationGroup* group)
- : minimum_size_(minimum_size),
- maximum_size_(maximum_size),
- group_filter_(group) {}
-
-bool FindTopSizes::Visit(const void* /*memory*/,
- const AllocationRecord& alloc_record) {
- if (PassesFilter(alloc_record)) {
- size_counter_[alloc_record.size]++;
- }
- return true;
-}
-
-std::string FindTopSizes::ToString(size_t max_elements_to_print) const {
- std::vector<GroupAllocation> group_allocs = GetTopAllocations();
- const size_t n = std::min(max_elements_to_print, group_allocs.size());
-
- if (!group_allocs.empty()) {
- std::stringstream ss;
-
- for (size_t i = 0; i < n; ++i) {
- GroupAllocation g = group_allocs[i];
- size_t total_size = g.allocation_count * g.allocation_size;
- ss << " " << total_size
- << " bytes allocated with object size: " << g.allocation_size
- << " bytes in " << g.allocation_count << " instances " << kNewLine;
- }
- return ss.str();
- } else {
- return std::string();
- }
-}
-
-std::vector<FindTopSizes::GroupAllocation> FindTopSizes::GetTopAllocations()
- const {
- std::vector<GroupAllocation> group_allocs;
- // Push objects to a vector.
- for (SizeCounterMap::const_iterator it = size_counter_.begin();
- it != size_counter_.end(); ++it) {
- GroupAllocation alloc = {it->first, it->second};
- group_allocs.push_back(alloc);
- }
-
- std::sort(group_allocs.begin(), group_allocs.end(),
- GroupAllocation::LessAllocationSize);
- // Biggest first.
- std::reverse(group_allocs.begin(), group_allocs.end());
- return group_allocs;
-}
-
-bool FindTopSizes::PassesFilter(const AllocationRecord& alloc_record) const {
- if (alloc_record.size < minimum_size_) return false;
- if (alloc_record.size > maximum_size_) return false;
- if (!group_filter_) return true; // No group filter when null.
- return group_filter_ == alloc_record.allocation_group;
-}
-
-MemoryTrackerLogWriter::MemoryTrackerLogWriter() : start_time_(NowTime()) {
- buffered_file_writer_.reset(new BufferedFileWriter(MemoryLogPath()));
- InitAndRegisterMemoryReporter();
-}
-
-MemoryTrackerLogWriter::~MemoryTrackerLogWriter() {
- // No locks are used for the thread reporter, so when it's set to null
- // we allow one second for any suspended threads to run through and finish
- // their reporting.
- SbMemorySetReporter(NULL);
- SbThreadSleep(kSbTimeSecond);
- buffered_file_writer_.reset(NULL);
-}
-
-std::string MemoryTrackerLogWriter::tool_name() const {
- return "MemoryTrackerLogWriter";
-}
-
-void MemoryTrackerLogWriter::Run(Params* params) {
- // Run function does almost nothing.
- params->logger()->Output("MemoryTrackerLogWriter running...");
-}
-
-void MemoryTrackerLogWriter::OnMemoryAllocation(const void* memory_block,
- size_t size) {
- void* addresses[kMaxStackSize] = {};
- // Though the SbSystemGetStack API documentation does not specify any possible
- // negative return values, we take no chance.
- const size_t count = std::max(SbSystemGetStack(addresses, kMaxStackSize), 0);
-
- const size_t n = 256;
- char buff[n] = {0};
- size_t buff_pos = 0;
-
- int time_since_start_ms = GetTimeSinceStartMs();
- // Writes "+ <ALLOCATION ADDRESS> <size> <time>"
- int bytes_written =
- SbStringFormatF(buff, sizeof(buff), "+ %" PRIXPTR " %x %d",
- reinterpret_cast<uintptr_t>(memory_block),
- static_cast<unsigned int>(size), time_since_start_ms);
-
- buff_pos += bytes_written;
- const size_t end_index = std::min(count, kStartIndex + kNumAddressPrints);
-
- // For each of the stack addresses that we care about, concat them to the
- // buffer. This was originally written to do multiple stack addresses but
- // this tends to overflow on some lower platforms so it's possible that
- // this loop only iterates once.
- for (size_t i = kStartIndex; i < end_index; ++i) {
- void* p = addresses[i];
- int bytes_written = SbStringFormatF(buff + buff_pos,
- sizeof(buff) - buff_pos,
- " %" PRIXPTR "",
- reinterpret_cast<uintptr_t>(p));
- DCHECK_GE(bytes_written, 0);
-
- if (bytes_written < 0) {
- DCHECK(false) << "Error occurred while writing string.";
- continue;
- }
-
- buff_pos += static_cast<size_t>(bytes_written);
- }
- // Adds a "\n" at the end.
- SbStringConcat(buff + buff_pos, "\n", static_cast<int>(n - buff_pos));
- buffered_file_writer_->Append(buff, strlen(buff));
-}
-
-void MemoryTrackerLogWriter::OnMemoryDeallocation(const void* memory_block) {
- const size_t n = 256;
- char buff[n] = {0};
- // Writes "- <ADDRESS OF ALLOCATION> \n"
- SbStringFormatF(buff, sizeof(buff), "- %" PRIXPTR "\n",
- reinterpret_cast<uintptr_t>(memory_block));
- buffered_file_writer_->Append(buff, strlen(buff));
-}
-
-void MemoryTrackerLogWriter::OnAlloc(void* context, const void* memory,
- size_t size) {
- MemoryTrackerLogWriter* self = static_cast<MemoryTrackerLogWriter*>(context);
- self->OnMemoryAllocation(memory, size);
-}
-
-void MemoryTrackerLogWriter::OnDealloc(void* context, const void* memory) {
- MemoryTrackerLogWriter* self = static_cast<MemoryTrackerLogWriter*>(context);
- self->OnMemoryDeallocation(memory);
-}
-
-void MemoryTrackerLogWriter::OnMapMemory(void* context, const void* memory,
- size_t size) {
- MemoryTrackerLogWriter* self = static_cast<MemoryTrackerLogWriter*>(context);
- self->OnMemoryAllocation(memory, size);
-}
-
-void MemoryTrackerLogWriter::OnUnMapMemory(void* context, const void* memory,
- size_t size) {
- SB_UNREFERENCED_PARAMETER(size);
- MemoryTrackerLogWriter* self = static_cast<MemoryTrackerLogWriter*>(context);
- self->OnMemoryDeallocation(memory);
-}
-
-std::string MemoryTrackerLogWriter::MemoryLogPath() {
- char file_name_buff[2048] = {};
- SbSystemGetPath(kSbSystemPathDebugOutputDirectory, file_name_buff,
- arraysize(file_name_buff));
- std::string path(file_name_buff);
- if (!path.empty()) { // Protect against a dangling "/" at end.
- const int back_idx_signed = static_cast<int>(path.length()) - 1;
- if (back_idx_signed >= 0) {
- const size_t idx = back_idx_signed;
- if (path[idx] == '/') {
- path.erase(idx);
- }
- }
- }
- path.append("/memory_log.txt");
- return path;
-}
-
-base::TimeTicks MemoryTrackerLogWriter::NowTime() {
- // NowFromSystemTime() is slower but more accurate. However it might
- // be useful to use the faster but less accurate version if there is
- // a speedup.
- return base::TimeTicks::Now();
-}
-
-int MemoryTrackerLogWriter::GetTimeSinceStartMs() const {
- base::TimeDelta dt = NowTime() - start_time_;
- return static_cast<int>(dt.InMilliseconds());
-}
-
-void MemoryTrackerLogWriter::InitAndRegisterMemoryReporter() {
- DCHECK(!memory_reporter_.get()) << "Memory Reporter already registered.";
-
- SbMemoryReporter mem_reporter = {OnAlloc, OnDealloc, OnMapMemory,
- OnUnMapMemory, this};
- memory_reporter_.reset(new SbMemoryReporter(mem_reporter));
- SbMemorySetReporter(memory_reporter_.get());
-}
-
-MemoryTrackerLeakFinder::MemoryTrackerLeakFinder(StackTraceMode mode)
- : string_pool_(128),
- frame_map_(128),
- callframe_map_(128),
- stack_trace_mode_(mode) {
- default_callframe_str_ = &string_pool_.Intern("<Unknown>");
-}
-
-MemoryTrackerLeakFinder::~MemoryTrackerLeakFinder() {
- frame_map_.Clear();
- callframe_map_.Clear();
-}
-
-bool MemoryTrackerLeakFinder::IsJavascriptScope(
- const nb::analytics::CallStack& callstack) {
- // March through all MemoryScopes in the callstack and check if any of them
- // contains a javascript scope. If it does return true.
- for (nb::analytics::CallStack::const_iterator it = callstack.begin();
- it != callstack.end(); ++it) {
- const NbMemoryScopeInfo* memory_scope = *it;
- const bool is_javascript_scope =
- SbStringFindString(memory_scope->memory_scope_name_, "Javascript");
- if (is_javascript_scope) {
- return true;
- }
- }
- return false;
-}
-
-void MemoryTrackerLeakFinder::OnMemoryAllocation(
- const void* memory_block, const nb::analytics::AllocationRecord& record,
- const nb::analytics::CallStack& callstack) {
- // When in javascript mode, filter only allocations with "Javascript" in
- // the memory scope name.
- if (stack_trace_mode_ == kJavascript) {
- if (!IsJavascriptScope(callstack)) {
- return;
- }
- }
-
- // symbol_str can be used as a unique key. The same value of callstack will
- // always produce the same string pointer.
- const std::string* symbol_str = GetOrCreateSymbol(callstack);
- // Track the allocation.
- if (!callframe_map_.SetIfMissing(memory_block, symbol_str)) {
- CallFrameMap::EntryHandle entry_handle;
- callframe_map_.Get(memory_block, &entry_handle);
-
- const void* ptr = entry_handle.Valid() ? entry_handle.Key() : NULL;
- entry_handle.ReleaseLockAndInvalidate();
-
- DCHECK(false) << "Unexpected memory block at " << memory_block
- << " already existed as: " << ptr;
- }
-
- AllocationFrameMap::EntryHandle entry_handle;
- frame_map_.GetOrCreate(symbol_str, &entry_handle);
-
- // While this entry_handle is in use, no other threads
- // can modify this entry.
- AllocRec& alloc_rec = entry_handle.ValueMutable();
- alloc_rec.total_bytes += record.size;
- alloc_rec.num_allocs++;
-}
-
-void MemoryTrackerLeakFinder::OnMemoryDeallocation(
- const void* memory_block, const nb::analytics::AllocationRecord& record,
- const nb::analytics::CallStack& callstack) {
- UNREFERENCED_PARAMETER(callstack);
-
- const std::string* symbol_str = NULL;
-
- {
- CallFrameMap::EntryHandle entry_handle;
- if (!callframe_map_.Get(memory_block, &entry_handle)) {
- // This happens if the allocation happened before this tool attached
- // to the memory tracker or if the memory allocation was filtered and
- // therefore isn't being tracked.
- return;
- }
- symbol_str = entry_handle.Value();
- callframe_map_.Remove(&entry_handle);
- }
-
- // This case can happen if the memory tracker attaches after the allocation.
- if (!symbol_str) {
- return;
- }
-
- AllocationFrameMap::EntryHandle entry_handle;
- frame_map_.GetOrCreate(symbol_str, &entry_handle);
-
- // While entry_handle is in use, no other element can modify
- // this element.
- AllocRec& alloc_rec = entry_handle.ValueMutable();
- alloc_rec.total_bytes -= record.size;
- alloc_rec.num_allocs--;
-}
-
-std::string MemoryTrackerLeakFinder::tool_name() const {
- return "MemoryTrackerLeakFinder";
-}
-
-void MemoryTrackerLeakFinder::Run(Params* params) {
- // Run function does almost nothing.
- params->logger()->Output("MemoryTrackerLeakFinder running...");
-
- static const int kMaxSamples = 400;
-
- // This value will decay whenever the buffer fills up and is compressed via
- // sample elimination.
- SbTime sample_time = 50; // 50 microseconds.
-
- std::vector<base::TimeDelta> time_values;
- std::map<const std::string*, std::vector<AllocRec> > map_allocations;
-
- SbTime start_time = SbTimeGetMonotonicNow();
- Timer output_trigger(base::TimeDelta::FromMinutes(1));
-
- const double recording_delay_mins = 5.0;
-
- // Controls how often an update status message is sent to output.
- Timer output_status_timer(base::TimeDelta::FromSeconds(1));
-
- while (true) {
- if (params->wait_for_finish_signal(sample_time)) {
- break; // Finish received, break.
- }
- SbTime now_time = SbTimeGetMonotonicNow();
-
- const base::TimeDelta time_since_start =
- base::Time::FromSbTime(now_time) - base::Time::FromSbTime(start_time);
-
- // DELAY RECORDING AS STARTUP MEMORY IS INITIALIZED
- // Cleaner graphs if we wait until after startup.
- //
- const double times_since_start_mins = time_since_start.InSecondsF() / 60.0;
- if (times_since_start_mins < recording_delay_mins) {
- if (output_status_timer.UpdateAndIsExpired()) {
- double remaining_time_mins =
- (recording_delay_mins - times_since_start_mins);
- std::stringstream ss;
- ss << "MemoryTrackerLeakFinder starting in " << remaining_time_mins
- << " minutes.\n";
- params->logger()->Output(ss.str().c_str());
- }
- continue;
- }
-
- time_values.push_back(time_since_start);
-
- // To improve performance, make a copy of the values to a vector on
- // the stack.
- std::vector<std::pair<const std::string*, AllocRec> > samples;
- SampleSnapshot(&samples);
-
- // Take a snapshot.
- for (size_t i = 0; i < samples.size(); ++i) {
- std::pair<const std::string*, AllocRec> sample = samples[i];
- std::vector<AllocRec>& sample_history = map_allocations[sample.first];
-
- sample_history.resize(time_values.size());
- sample_history.back() = sample.second;
- }
-
- if (output_trigger.UpdateAndIsExpired() && time_values.size() > 10) {
- // Timer expired so dump the current information output.
-
- std::vector<AllocationProfile> alloc_profiles;
- GenerateTopLeakingAllocationProfiles(time_values, map_allocations,
- &alloc_profiles);
-
- if (alloc_profiles.empty()) {
- params->logger()->Output(
- "MemoryTrackerLeakFinder: alloc_profiles was "
- "empty and nothing is written.");
- } else {
- if (alloc_profiles.size() > 20) {
- alloc_profiles.resize(20);
- }
-
- std::string csv_str = GenerateCSV(time_values, alloc_profiles);
- params->logger()->Output(csv_str.c_str());
- }
- }
-
- // Compress the buffer, and modify sample_time so that it's twice as slow.
- if (time_values.size() >= kMaxSamples) {
- sample_time *= 2; // Double the time that it takes to trigger a sample.
- // Remove every other element in time_values.
- RemoveOddElements(&time_values);
-
- // Remove every other element in the samples.
- for (size_t i = 0; i < samples.size(); ++i) {
- std::pair<const std::string*, AllocRec> sample = samples[i];
- std::vector<AllocRec>& sample_history = map_allocations[sample.first];
- RemoveOddElements(&sample_history);
- }
- }
- }
-}
-
-const std::string* MemoryTrackerLeakFinder::GetOrCreateSymbol(
- const nb::analytics::CallStack& callstack) {
- const std::string* symbol_str = NULL;
-
- // In javascript mode we try and get the javascript symbol. Otherwise
- // fallback to C++ symbol.
- if (stack_trace_mode_ == kJavascript) {
- symbol_str = TryGetJavascriptSymbol();
- if (symbol_str) {
- return symbol_str;
- }
- }
-
- symbol_str = GetOrCreateCplusPlusSymbol(callstack);
- return symbol_str;
-}
-
-const std::string* MemoryTrackerLeakFinder::GetOrCreateCplusPlusSymbol(
- const nb::analytics::CallStack& callstack) {
- if (callstack.empty()) {
- return default_callframe_str_;
- } else {
- const NbMemoryScopeInfo* memory_scope = callstack.back();
-
- const bool skip =
- SbStringFindString(memory_scope->function_name_, "js_malloc") ||
- SbStringFindString(memory_scope->function_name_, "js_realloc") ||
- SbStringFindString(memory_scope->function_name_, "new_");
-
- // Skip up one callstack because we don't want to track calls to
- // allocation functions.
- if (skip && callstack.size() > 1) {
- memory_scope = callstack[callstack.size() - 2];
- }
-
- const char* file_name = BaseNameFast(memory_scope->file_name_);
-
- // Generates a symbol.
- // Example:
- // "Javascript:Interpreter.cpp(415):RunScript()"
- char symbol_buff[128];
- SbStringFormatF(symbol_buff, sizeof(symbol_buff), "%s:%s(%d)::%s()",
- memory_scope->memory_scope_name_, file_name,
- memory_scope->line_number_, memory_scope->function_name_);
-
- // Get's a unique pointer to a string containing the symbol. If the symbol
- // was previously generated then the previous symbol is returned.
- return &string_pool_.Intern(symbol_buff);
- }
-}
-
-const std::string* MemoryTrackerLeakFinder::TryGetJavascriptSymbol() {
- script::mozjs::util::StackTraceGenerator* js_stack_gen =
- script::mozjs::util::GetThreadLocalStackTraceGenerator();
- if (!js_stack_gen || !js_stack_gen->Valid()) return NULL;
-
- // Only get one symbol.
- char buffer[256];
- if (!js_stack_gen->GenerateStackTraceString(1, buffer, sizeof(buffer))) {
- return NULL;
- }
- const char* file_name = BaseNameFast(buffer);
- return &string_pool_.Intern(file_name);
-}
-
-void MemoryTrackerLeakFinder::SampleSnapshot(
- std::vector<std::pair<const std::string*, AllocRec> >* destination) {
- destination->erase(destination->begin(), destination->end());
-
- const size_t sample_size = frame_map_.GetSize();
-
- // Do this locally.
- destination->reserve(sample_size + 10);
- frame_map_.CopyToStdVector(destination);
-}
-
-std::string MemoryTrackerLeakFinder::GenerateCSV(
- const std::vector<base::TimeDelta>& time_values,
- const std::vector<AllocationProfile>& data) {
- std::stringstream ss;
- ss << std::fixed; // Turn off scientific notation for CSV values.
- ss << std::setprecision(3);
- ss << kNewLine << kNewLine;
-
- // HEADER
- ss << "// Allocation in megabytes." << kNewLine;
- ss << kQuote << "Time(min)" << kQuote << kDelimiter;
- for (size_t i = 0; i < data.size(); ++i) {
- const AllocationProfile& alloc_profile = data[i];
- const std::string& name = *alloc_profile.name_;
- ss << kQuote << name << kQuote << kDelimiter;
- }
- ss << kNewLine;
-
- // BODY
- for (size_t i = 0; i < time_values.size(); ++i) {
- for (size_t j = 0; j < data.size(); ++j) {
- if (j == 0) {
- double mins = time_values[i].InSecondsF() / 60.f;
- if (mins < .001) {
- mins = 0;
- }
- ss << mins << kDelimiter;
- }
-
- const AllocationProfile& alloc_profile = data[j];
- const std::vector<AllocRec>& alloc_history =
- *alloc_profile.alloc_history_;
-
- double megabytes = static_cast<double>(alloc_history[i].total_bytes) /
- static_cast<double>(1024 * 1024);
-
- DCHECK_EQ(alloc_history.size(), time_values.size());
- ss << megabytes << kDelimiter;
- }
- ss << kNewLine;
- }
- ss << kNewLine << kNewLine;
- ss << "// Object counts." << kNewLine;
- ss << kQuote << "Time(min)" << kQuote << kDelimiter;
- for (size_t i = 0; i < data.size(); ++i) {
- const AllocationProfile& alloc_profile = data[i];
- const std::string& name = *alloc_profile.name_;
- ss << kQuote << name << kQuote << kDelimiter;
- }
- ss << kNewLine;
- for (size_t i = 0; i < time_values.size(); ++i) {
- for (size_t j = 0; j < data.size(); ++j) {
- if (j == 0) {
- double mins = time_values[i].InSecondsF() / 60.f;
- if (mins < .001) {
- mins = 0;
- }
- ss << mins << kDelimiter;
- }
- const AllocationProfile& alloc_profile = data[j];
- const std::vector<AllocRec>& alloc_history =
- *alloc_profile.alloc_history_;
- DCHECK_EQ(alloc_history.size(), time_values.size());
- ss << alloc_history[i].num_allocs << kDelimiter;
- }
- ss << kNewLine;
- }
-
- ss << kNewLine << kNewLine;
- return ss.str();
-}
-
-void MemoryTrackerLeakFinder::GenerateTopLeakingAllocationProfiles(
- const std::vector<base::TimeDelta>& time_values, const MapSamples& samples,
- std::vector<AllocationProfile>* destination) {
- // GENERATE LINEAR REGRESSION LINE
- // first value is time in microseconds.
- // second value is total_bytes.
- std::vector<std::pair<int64_t, int64_t> > sample_data;
-
- typedef std::map<const std::string*, SlopeYIntercept> LinearRegressionMap;
- LinearRegressionMap linear_regression_map;
-
- std::vector<AllocationProfile> allocation_profiles;
-
- for (MapSamples::const_iterator it = samples.begin(); it != samples.end();
- ++it) {
- const std::string* allocation_name = it->first;
- const std::vector<AllocRec>& allocation_samples = it->second;
-
- if (allocation_samples.empty()) {
- continue;
- }
-
- // Filter out allocations records that have low number of allocations.
- if (allocation_samples.back().num_allocs < 10) {
- continue;
- }
-
- // Filter out allocations that are insignificant.
- int64_t largest_allocation_sample = 0;
- const int64_t kMinAllocationSize = 1024 * 64;
- for (size_t i = 0; i < allocation_samples.size(); ++i) {
- int64_t bytes = allocation_samples[i].total_bytes;
- largest_allocation_sample = std::max(largest_allocation_sample, bytes);
- }
- if (largest_allocation_sample < kMinAllocationSize) {
- continue;
- }
-
- // Filter out allocations where there is no growth between first quartile
- // and final output.
- const AllocRec& first_quartile_sample =
- allocation_samples[allocation_samples.size() / 4];
- const AllocRec& final_sample = allocation_samples.back();
-
- const double increase_ratio =
- static_cast<double>(final_sample.total_bytes) /
- static_cast<double>(first_quartile_sample.total_bytes);
-
- // 5% threshold of increase to be qualified as a lead.
- static const double kMinIncreaseThreshold = .05;
-
- // If the increase between first quartile and final sample less than 5%
- // then skip.
- if (increase_ratio < kMinIncreaseThreshold) {
- continue;
- }
-
- sample_data.clear(); // Recycle.
- for (size_t i = 0; i < time_values.size(); ++i) {
- std::pair<int64_t, int64_t> datum(time_values[i].InMicroseconds(),
- allocation_samples[i].total_bytes);
- sample_data.push_back(datum);
- }
-
- double slope = 0;
- double y_intercept = 0;
- bool valid = GetLinearFit(sample_data.begin(), sample_data.end(), &slope,
- &y_intercept);
- DCHECK(valid);
- linear_regression_map[allocation_name] =
- SlopeYIntercept(slope, y_intercept);
- AllocationProfile alloc_profile(allocation_name, &allocation_samples, slope,
- y_intercept);
- alloc_profile.leak_potential_ =
- allocation_samples.back().total_bytes * slope;
- allocation_profiles.push_back(alloc_profile);
- }
-
- std::sort(allocation_profiles.begin(), allocation_profiles.end(),
- AllocationProfile::CompareLeakPotential);
- // Biggest one first.
- std::reverse(allocation_profiles.begin(), allocation_profiles.end());
- *destination = allocation_profiles;
-}
-
-} // namespace memory_tracker
-} // namespace browser
-} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.h b/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.h
deleted file mode 100644
index 706dc8a..0000000
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.h
+++ /dev/null
@@ -1,468 +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_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_IMPL_H_
-#define COBALT_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_IMPL_H_
-
-#include <deque>
-#include <map>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/debug/stack_trace.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/simple_thread.h"
-#include "base/time.h"
-#include "cobalt/browser/memory_tracker/buffered_file_writer.h"
-#include "nb/analytics/memory_tracker.h"
-#include "nb/analytics/memory_tracker_helpers.h"
-#include "nb/concurrent_map.h"
-#include "nb/string_interner.h"
-#include "nb/thread_local_object.h"
-#include "starboard/memory_reporter.h"
-
-namespace starboard {
-class ScopedFile;
-} // namespace starboard
-
-namespace nb {
-namespace analytics {
-class AllocationGroup;
-class AllocationRecord;
-class MemoryTracker;
-} // namespace analytics
-} // namespace nb
-
-namespace cobalt {
-namespace browser {
-namespace memory_tracker {
-
-// Interface for logging. This allows dependency inject to override in the case
-// of future tests.
-class AbstractLogger {
- public:
- virtual ~AbstractLogger() {}
- virtual void Output(const char* str) = 0;
- virtual void Flush() = 0;
-};
-
-// Params holds important data needed by the MemoryTracker tools during
-// their run cycle.
-class Params;
-
-//
-class AbstractMemoryTrackerTool {
- public:
- virtual ~AbstractMemoryTrackerTool() {}
- virtual std::string tool_name() const = 0;
- virtual void Run(Params* params) = 0;
-};
-
-class MemoryTrackerToolThread : public base::SimpleThread {
- public:
- typedef base::SimpleThread Super;
- MemoryTrackerToolThread(nb::analytics::MemoryTracker* memory_tracker,
- AbstractMemoryTrackerTool* tool,
- AbstractLogger* logger);
- virtual ~MemoryTrackerToolThread();
-
- virtual void Join() OVERRIDE;
- virtual void Run() OVERRIDE;
-
- private:
- scoped_ptr<Params> params_;
- scoped_ptr<AbstractMemoryTrackerTool> tool_;
-};
-
-// Start() is called when this object is created, and Cancel() & Join() are
-// called during destruction.
-class MemoryTrackerPrint : public AbstractMemoryTrackerTool {
- public:
- MemoryTrackerPrint() {}
-
- // Overridden so that the thread can exit gracefully.
- virtual void Run(Params* params) OVERRIDE;
- virtual std::string tool_name() const OVERRIDE {
- return "MemoryTrackerPrintThread";
- }
-};
-
-// Generates CSV values of the engine.
-// There are three sections of data including:
-// 1. average bytes / alloc
-// 2. # Bytes allocated per memory scope.
-// 3. # Allocations per memory scope.
-// This data can be pasted directly into a Google spreadsheet and visualized.
-// Note that this thread will implicitly call Start() is called during
-// construction and Cancel() & Join() during destruction.
-class MemoryTrackerPrintCSV : public AbstractMemoryTrackerTool {
- public:
- // This tool will only produce on CSV dump of the engine. This is useful
- // for profiling startup memory consumption.
- MemoryTrackerPrintCSV(int sampling_interval_ms, int sampling_time_ms);
-
- // Overridden so that the thread can exit gracefully.
- virtual void Run(Params* params) OVERRIDE;
- virtual std::string tool_name() const OVERRIDE {
- return "MemoryTrackerPrintCSV";
- }
-
- private:
- struct AllocationSamples {
- std::vector<int32_t> number_allocations_;
- std::vector<int64_t> allocated_bytes_;
- };
- typedef std::map<std::string, AllocationSamples> MapAllocationSamples;
- static std::string ToCsvString(const MapAllocationSamples& samples);
- static const char* UntrackedMemoryKey();
- bool TimeExpiredYet(const Params& params);
-
- const int sample_interval_ms_;
- const int sampling_time_ms_;
-};
-
-struct AllocationSamples {
- std::vector<int32_t> number_allocations_;
- std::vector<int64_t> allocated_bytes_;
-};
-typedef std::map<std::string, AllocationSamples> MapAllocationSamples;
-typedef std::vector<base::TimeDelta> TimeStamps;
-
-struct TimeSeries {
- MapAllocationSamples samples_;
- TimeStamps time_stamps_;
-};
-
-// Generates CSV values of the engine memory. This includes memory
-// consumption in bytes and also the number of allocations.
-// Allocations are segmented by category such as "Javascript" and "Network and
-// are periodically dumped to the Params::abstract_logger_.
-//
-// Compression:
-// During early run, the sampling rate is extremely high, however the sampling
-// rate will degrade by half each time the sampling buffer fills up.
-//
-// Additionally, every time the sampling buffer fills up, space is freed by
-// evicting every other element.
-//
-// Example output:
-// //////////////////////////////////////////////
-// // CSV of BYTES allocated per region (MB's).
-// // Units are in Megabytes. This is designed
-// // to be used in a stacked graph.
-// "Time(mins)","CSS","DOM","Javascript","Media","MessageLoop"....
-// 0,0,0,0,0,0...
-// 1022.54,3.3,7.19,49.93,50.33....
-// ...
-// // END CSV of BYTES allocated per region.
-// //////////////////////////////////////////////
-//
-// //////////////////////////////////////////////
-// // CSV of COUNT of allocations per region.
-// "Time(mins)","CSS","DOM","Javascript","Media","MessageLoop"....
-// 0,0,0,0,0,0...
-// 1022.54,57458,70011,423217,27,54495,43460...
-// ...
-// // END CSV of COUNT of allocations per region.
-// //////////////////////////////////////////////
-class MemoryTrackerCompressedTimeSeries : public AbstractMemoryTrackerTool {
- public:
- MemoryTrackerCompressedTimeSeries();
- virtual void Run(Params* params) OVERRIDE;
- virtual std::string tool_name() const OVERRIDE {
- return "MemoryTrackerCompressedTimeSeries";
- }
-
- private:
- static std::string ToCsvString(const TimeSeries& histogram);
- static void AcquireSample(nb::analytics::MemoryTracker* memory_tracker,
- TimeSeries* histogram, const base::TimeDelta& dt);
- static bool IsFull(const TimeSeries& histogram, size_t num_samples);
- static void Compress(TimeSeries* histogram);
-
- const int number_samples_;
-};
-
-// This tool inspects a memory scope and reports on the memory usage.
-// The output will be a CSV file printed to stdout representing
-// the number of memory allocations for objects. The objects are binned
-// according to the size of the memory allocation. Objects within the same
-// power of two are binned together. For example 1024 will be binned with 1025.
-class MemorySizeBinner : public AbstractMemoryTrackerTool {
- public:
- // memory_scope_name represents the memory scope that is to be investigated.
- explicit MemorySizeBinner(const std::string& memory_scope_name);
-
- virtual void Run(Params* params) OVERRIDE;
- virtual std::string tool_name() const OVERRIDE {
- return "MemoryTrackerCompressedTimeSeries";
- }
-
- private:
- std::string memory_scope_name_;
-};
-
-// Bins the size according from all the encountered allocations.
-// If AllocationGroup* is non-null, then this is used as a filter such that
-// ONLY allocations belonging to that AllocationGroup are considered.
-class AllocationSizeBinner : public nb::analytics::AllocationVisitor {
- public:
- typedef std::vector<int> AllocationHistogram;
- // Maps the input size to a bin number. This function performs a 2^n
- // mapping. Here is a table of some size mappings:
- // Example:
- // GetIndex(0) == 0;
- // GetIndex(1) == 1;
- // GetIndex(2) == 2;
- // GetIndex(3) == 2;
- // GetIndex(4) == 3;
- // ...
- // GetIndex(15) == 4;
- // GetIndex(16) == 5;
- static size_t GetBucketIndexForAllocationSize(size_t size);
- static void GetSizeRange(size_t size, size_t* min_value, size_t* max_value);
- static void IndexToSizeRange(size_t size, size_t* min_value,
- size_t* max_value);
-
- explicit AllocationSizeBinner(
- const nb::analytics::AllocationGroup* group_filter);
- virtual bool Visit(
- const void* memory,
- const nb::analytics::AllocationRecord& alloc_record) OVERRIDE;
-
- size_t GetIndexRepresentingMostMemoryConsumption() const;
- void GetLargestSizeRange(size_t* min_value, size_t* max_value) const;
- bool PassesFilter(const nb::analytics::AllocationRecord& alloc_record) const;
- // Outputs CSV formatted string of the data values.
- // The header containing the binning range is printed first, then the
- // the number of allocations in that bin.
- // Example:
- // "16...32","32...64","64...128","128...256",...
- // 831,3726,3432,10285,...
- //
- // In this example there are 831 allocations of size 16-32 bytes.
- std::string ToCSVString() const;
- const AllocationHistogram& allocation_histogram() const {
- return allocation_histogram_;
- }
-
- private:
- AllocationHistogram allocation_histogram_;
- // Only these allocations are tracked.
- const nb::analytics::AllocationGroup* group_filter_;
-};
-
-// Finds the top allocations by size.
-class FindTopSizes : public nb::analytics::AllocationVisitor {
- public:
- FindTopSizes(size_t minimum_size, size_t maximum_size,
- const nb::analytics::AllocationGroup* group);
-
- virtual bool Visit(
- const void* memory,
- const nb::analytics::AllocationRecord& alloc_record) OVERRIDE;
-
- struct GroupAllocation {
- size_t allocation_size;
- size_t allocation_count;
-
- static bool LessAllocationSize(GroupAllocation a, GroupAllocation b) {
- size_t total_size_a = a.allocation_size * a.allocation_count;
- size_t total_size_b = b.allocation_size * b.allocation_count;
- return total_size_a < total_size_b;
- }
- };
-
- std::string ToString(size_t max_elements_to_print) const;
- std::vector<GroupAllocation> GetTopAllocations() const;
-
- private:
- typedef std::map<size_t, size_t> SizeCounterMap;
-
- bool PassesFilter(const nb::analytics::AllocationRecord& alloc_record) const;
- size_t minimum_size_;
- size_t maximum_size_;
- const nb::analytics::AllocationGroup* group_filter_;
- SizeCounterMap size_counter_;
-};
-
-// Outputs memory_log.txt to the output log location. This log contains
-// allocations with a stack trace (non-symbolized) and deallocations without
-// a stack.
-class MemoryTrackerLogWriter : public AbstractMemoryTrackerTool {
- public:
- MemoryTrackerLogWriter();
- virtual ~MemoryTrackerLogWriter();
-
- // Interface AbstrctMemoryTrackerTool
- virtual std::string tool_name() const OVERRIDE;
- virtual void Run(Params* params) OVERRIDE;
-
- void OnMemoryAllocation(const void* memory_block, size_t size);
- void OnMemoryDeallocation(const void* memory_block);
-
- private:
- // Callbacks for MemoryReporter
- static void OnAlloc(void* context, const void* memory, size_t size);
- static void OnDealloc(void* context, const void* memory);
- static void OnMapMemory(void* context, const void* memory, size_t size);
- static void OnUnMapMemory(void* context, const void* memory, size_t size);
- static std::string MemoryLogPath();
-
- static base::TimeTicks NowTime();
- static const size_t kStartIndex = 5;
- static const size_t kNumAddressPrints = 1;
- static const size_t kMaxStackSize = 10;
-
- int GetTimeSinceStartMs() const;
- void InitAndRegisterMemoryReporter();
-
- base::TimeTicks start_time_;
- scoped_ptr<SbMemoryReporter> memory_reporter_;
- scoped_ptr<BufferedFileWriter> buffered_file_writer_;
-};
-
-// Records allocations and outputs leaks as a CSV. Each column will
-// have an associated symbol.
-class MemoryTrackerLeakFinder
- : public AbstractMemoryTrackerTool,
- public nb::analytics::MemoryTrackerDebugCallback {
- public:
- enum StackTraceMode {
- // Always get the C++ version of the stack trace.
- kCPlusPlus,
- // Whenever possible, get the javascript stack trace,
- // else fallback to CPlusPlus.
- kJavascript,
- };
-
- explicit MemoryTrackerLeakFinder(StackTraceMode pref);
- virtual ~MemoryTrackerLeakFinder();
-
- // OnMemoryAllocation() and OnMemoryDeallocation() are part of
- // class MemoryTrackerDebugCallback.
- void OnMemoryAllocation(const void* memory_block,
- const nb::analytics::AllocationRecord& record,
- const nb::analytics::CallStack& callstack) OVERRIDE;
-
- void OnMemoryDeallocation(const void* memory_block,
- const nb::analytics::AllocationRecord& record,
- const nb::analytics::CallStack& callstack) OVERRIDE;
-
- // Interface AbstractMemoryTrackerTool
- virtual std::string tool_name() const OVERRIDE;
- virtual void Run(Params* params) OVERRIDE;
-
- const std::string* GetOrCreateSymbol(
- const nb::analytics::CallStack& callstack);
-
- const std::string* TryGetJavascriptSymbol();
- const std::string* GetOrCreateCplusPlusSymbol(
- const nb::analytics::CallStack& callstack);
-
- private:
- struct AllocRec;
- // A map from callsite -> allocation size.
- // typedef std::map<const std::string*, AllocRec> AllocationFrameMap;
- typedef nb::ConcurrentMap<const std::string*, AllocRec,
- nb::PODHasher<const std::string*> >
- AllocationFrameMap;
-
- // A map of memory -> callsite.
- // This keeps track of what callsites belong to which allocations.
- // typedef std::map<const void*, const std::string*> CallFrameMap;
- //
- typedef nb::ConcurrentMap<const void*, const std::string*,
- nb::PODHasher<const std::string*> > CallFrameMap;
-
- // A map from callsite -> allocation data.
- typedef std::map<const std::string*, std::vector<AllocRec> > MapSamples;
- // A value type for holding a slope and Y-intercept.
- typedef std::pair<double, double> SlopeYIntercept;
- // An AllocationRecord.
- struct AllocRec {
- AllocRec() : total_bytes(0), num_allocs(0) {}
- AllocRec(const AllocRec& other)
- : total_bytes(other.total_bytes), num_allocs(other.num_allocs) {}
- AllocRec& operator=(const AllocRec& other) {
- total_bytes = other.total_bytes;
- num_allocs = other.num_allocs;
- return *this;
- }
- int64_t total_bytes;
- int32_t num_allocs;
- };
-
- // This data structure is used to for sorting leaks. The important variable
- // is |leak_potential|, which represents how likely this AllocationProfile
- // is a leak.
- struct AllocationProfile {
- AllocationProfile()
- : name_(NULL),
- alloc_history_(NULL),
- slope_(0),
- y_intercept_(0),
- leak_potential_(0) {}
-
- AllocationProfile(const std::string* name,
- const std::vector<AllocRec>* alloc_history, double slope,
- double y_intercept)
- : name_(name),
- alloc_history_(alloc_history),
- slope_(slope),
- y_intercept_(y_intercept),
- leak_potential_(0) {}
- const std::string* name_;
- const std::vector<AllocRec>* alloc_history_;
- // Linear regression data.
- double slope_;
- double y_intercept_;
-
- // This this value is set externally. Higher values indicate higher chance
- // that this is a leak.
- double leak_potential_;
-
- static bool CompareLeakPotential(const AllocationProfile& a,
- const AllocationProfile& b) {
- return a.leak_potential_ < b.leak_potential_;
- }
- };
-
- static std::string GenerateCSV(
- const std::vector<base::TimeDelta>& time_values,
- const std::vector<AllocationProfile>& data);
-
- void SampleSnapshot(
- std::vector<std::pair<const std::string*, AllocRec> >* destination);
-
- static void GenerateTopLeakingAllocationProfiles(
- const std::vector<base::TimeDelta>& time_values,
- const MapSamples& samples, std::vector<AllocationProfile>* destination);
-
- static bool IsJavascriptScope(const nb::analytics::CallStack& callstack);
-
- const std::string* default_callframe_str_;
- nb::ConcurrentStringInterner string_pool_;
- AllocationFrameMap frame_map_;
- CallFrameMap callframe_map_;
- StackTraceMode stack_trace_mode_;
-};
-} // namespace memory_tracker
-} // namespace browser
-} // namespace cobalt
-
-#endif // COBALT_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_IMPL_H_
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool.cc b/src/cobalt/browser/memory_tracker/tool.cc
similarity index 87%
rename from src/cobalt/browser/memory_tracker/memory_tracker_tool.cc
rename to src/cobalt/browser/memory_tracker/tool.cc
index f21b544..72cb675 100644
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool.cc
+++ b/src/cobalt/browser/memory_tracker/tool.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/browser/memory_tracker/memory_tracker_tool.h"
+#include "cobalt/browser/memory_tracker/tool.h"
#include <cstdlib>
#include <map>
@@ -20,7 +20,11 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
-#include "cobalt/browser/memory_tracker/memory_tracker_tool_impl.h"
+#include "cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h"
+#include "cobalt/browser/memory_tracker/tool/leak_finder_tool.h"
+#include "cobalt/browser/memory_tracker/tool/log_writer_tool.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "cobalt/browser/memory_tracker/tool/tool_thread.h"
#include "nb/analytics/memory_tracker_helpers.h"
#include "nb/lexical_cast.h"
#include "starboard/log.h"
@@ -31,14 +35,14 @@
#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING)
// Null implementation for memory tracker tool.
-class MemoryTrackerToolNull : public MemoryTrackerTool {
+class NullTool : public Tool {
public:
- virtual ~MemoryTrackerToolNull() {}
+ virtual ~NullTool() {}
};
// Instantiates the memory tracker tool from the command argument.
-scoped_ptr<MemoryTrackerTool> CreateMemoryTrackerTool(const std::string&) {
- return scoped_ptr<MemoryTrackerTool>(new MemoryTrackerToolNull);
+scoped_ptr<Tool> CreateMemoryTrackerTool(const std::string&) {
+ return scoped_ptr<Tool>(new NullTool);
}
#else
@@ -98,7 +102,7 @@
const size_t last_closing_paren_idx = command_arg.find_last_of(')');
if ((first_open_paren_idx == std::string::npos) ||
- (last_closing_paren_idx == std::string::npos)) {
+ (last_closing_paren_idx == std::string::npos)) {
return "";
}
@@ -106,14 +110,14 @@
return "";
}
- const size_t begin_idx = first_open_paren_idx+1;
- const size_t length = (last_closing_paren_idx-begin_idx);
+ const size_t begin_idx = first_open_paren_idx + 1;
+ const size_t length = (last_closing_paren_idx - begin_idx);
std::string arg_str = command_arg.substr(begin_idx, length);
return arg_str;
}
} // namespace.
-class MemoryTrackerThreadImpl : public MemoryTrackerTool {
+class MemoryTrackerThreadImpl : public Tool {
public:
explicit MemoryTrackerThreadImpl(base::SimpleThread* ptr)
: thread_ptr_(ptr) {}
@@ -121,8 +125,7 @@
scoped_ptr<base::SimpleThread> thread_ptr_;
};
-scoped_ptr<MemoryTrackerTool> CreateMemoryTrackerTool(
- const std::string& command_arg) {
+scoped_ptr<Tool> CreateMemoryTrackerTool(const std::string& command_arg) {
// The command line switch for memory_tracker was used. Look into the args
// and determine the mode that the memory_tracker should be used for.
@@ -240,7 +243,7 @@
// Tools are expected to instantiate the MemoryTracker if they need it.
MemoryTracker* memory_tracker = NULL;
- scoped_ptr<AbstractMemoryTrackerTool> tool_ptr;
+ scoped_ptr<AbstractTool> tool_ptr;
const SwitchVal& value = found_it->second;
switch (value.enum_value) {
@@ -290,22 +293,21 @@
// Time until output is triggered.
int sampling_time_ms = F::ToMilliseconds(num_mins);
// Create a thread that will gather memory metrics for startup.
- tool_ptr.reset(
- new MemoryTrackerPrintCSV(sampling_interval_ms, sampling_time_ms));
+ tool_ptr.reset(new PrintCSVTool(sampling_interval_ms, sampling_time_ms));
break;
}
case kContinuousPrinter: {
memory_tracker = MemoryTracker::Get();
memory_tracker->InstallGlobalTrackingHooks();
// Create a thread that will continuously report memory use.
- tool_ptr.reset(new MemoryTrackerPrint);
+ tool_ptr.reset(new PrintTool);
break;
}
case kCompressedTimeseries: {
memory_tracker = MemoryTracker::Get();
memory_tracker->InstallGlobalTrackingHooks();
// Create a thread that will continuously report memory use.
- tool_ptr.reset(new MemoryTrackerCompressedTimeSeries);
+ tool_ptr.reset(new CompressedTimeSeriesTool);
break;
}
case kBinnerAnalytics: {
@@ -317,14 +319,13 @@
break;
}
case kAllocationLogger: {
- scoped_ptr<MemoryTrackerLogWriter> disk_writer_tool(
- new MemoryTrackerLogWriter());
+ scoped_ptr<LogWriterTool> disk_writer_tool(new LogWriterTool());
tool_ptr.reset(disk_writer_tool.release());
break;
}
case kLeakTracer: {
- scoped_ptr<MemoryTrackerLeakFinder> leak_finder(
- new MemoryTrackerLeakFinder(MemoryTrackerLeakFinder::kCPlusPlus));
+ scoped_ptr<LeakFinderTool> leak_finder(
+ new LeakFinderTool(LeakFinderTool::kCPlusPlus));
memory_tracker = MemoryTracker::Get();
memory_tracker->InstallGlobalTrackingHooks();
@@ -333,8 +334,8 @@
break;
}
case kJavascriptLeakTracer: {
- scoped_ptr<MemoryTrackerLeakFinder> leak_finder(
- new MemoryTrackerLeakFinder(MemoryTrackerLeakFinder::kJavascript));
+ scoped_ptr<LeakFinderTool> leak_finder(
+ new LeakFinderTool(LeakFinderTool::kJavascript));
memory_tracker = MemoryTracker::Get();
memory_tracker->InstallGlobalTrackingHooks();
@@ -350,11 +351,11 @@
if (tool_ptr.get()) {
base::SimpleThread* thread =
- new MemoryTrackerToolThread(memory_tracker, // May be NULL.
- tool_ptr.release(), new SbLogger);
- return scoped_ptr<MemoryTrackerTool>(new MemoryTrackerThreadImpl(thread));
+ new ToolThread(memory_tracker, // May be NULL.
+ tool_ptr.release(), new SbLogger);
+ return scoped_ptr<Tool>(new MemoryTrackerThreadImpl(thread));
} else {
- return scoped_ptr<MemoryTrackerTool>();
+ return scoped_ptr<Tool>();
}
}
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool.h b/src/cobalt/browser/memory_tracker/tool.h
similarity index 72%
rename from src/cobalt/browser/memory_tracker/memory_tracker_tool.h
rename to src/cobalt/browser/memory_tracker/tool.h
index 8d17d9f..cd8eeaf 100644
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool.h
+++ b/src/cobalt/browser/memory_tracker/tool.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_H_
-#define COBALT_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_H_
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_H_
#include <string>
@@ -23,17 +23,17 @@
namespace browser {
namespace memory_tracker {
-class MemoryTrackerTool {
+// Self running tool. It only has a destructor defined.
+class Tool {
public:
- virtual ~MemoryTrackerTool() {}
+ virtual ~Tool() {}
};
// Instantiates the memory tracker tool from the command argument.
-scoped_ptr<MemoryTrackerTool> CreateMemoryTrackerTool(
- const std::string& command_arg);
+scoped_ptr<Tool> CreateMemoryTrackerTool(const std::string& command_arg);
} // namespace memory_tracker
} // namespace browser
} // namespace cobalt
-#endif // COBALT_BROWSER_MEMORY_TRACKER_MEMORY_TRACKER_TOOL_H_
+#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/buffered_file_writer.cc b/src/cobalt/browser/memory_tracker/tool/buffered_file_writer.cc
similarity index 90%
rename from src/cobalt/browser/memory_tracker/buffered_file_writer.cc
rename to src/cobalt/browser/memory_tracker/tool/buffered_file_writer.cc
index 3a8555a..284119b 100644
--- a/src/cobalt/browser/memory_tracker/buffered_file_writer.cc
+++ b/src/cobalt/browser/memory_tracker/tool/buffered_file_writer.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/browser/memory_tracker/buffered_file_writer.h"
+#include "cobalt/browser/memory_tracker/tool/buffered_file_writer.h"
#include "base/basictypes.h"
#include "base/logging.h"
@@ -67,13 +67,11 @@
return;
}
- flush_thread_ = SbThreadCreate(0, // default stack size.
- kSbThreadPriorityHigh,
- kSbThreadNoAffinity,
- true, // true - joinable.
- "AllocationLoggerWriter",
- ThreadEntryFunc,
- static_cast<void*>(this));
+ flush_thread_ = SbThreadCreate(0, // default stack size.
+ kSbThreadPriorityHigh, kSbThreadNoAffinity,
+ true, // true - joinable.
+ "AllocationLoggerWriter", ThreadEntryFunc,
+ static_cast<void*>(this));
}
void BufferedFileWriter::Append(const char* data, size_t num_bytes) {
diff --git a/src/cobalt/browser/memory_tracker/buffered_file_writer.h b/src/cobalt/browser/memory_tracker/tool/buffered_file_writer.h
similarity index 92%
rename from src/cobalt/browser/memory_tracker/buffered_file_writer.h
rename to src/cobalt/browser/memory_tracker/tool/buffered_file_writer.h
index 63c25e3..be3b4bb 100644
--- a/src/cobalt/browser/memory_tracker/buffered_file_writer.h
+++ b/src/cobalt/browser/memory_tracker/tool/buffered_file_writer.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_BROWSER_MEMORY_TRACKER_BUFFERED_FILE_WRITER_H_
-#define COBALT_BROWSER_MEMORY_TRACKER_BUFFERED_FILE_WRITER_H_
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_BUFFERED_FILE_WRITER_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_BUFFERED_FILE_WRITER_H_
#include <string>
@@ -80,4 +80,4 @@
} // namespace browser
} // namespace cobalt
-#endif // COBALT_BROWSER_MEMORY_TRACKER_BUFFERED_FILE_WRITER_H_
+#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_BUFFERED_FILE_WRITER_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.cc b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.cc
new file mode 100644
index 0000000..2942749
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.cc
@@ -0,0 +1,247 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h"
+
+#include <iomanip>
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "cobalt/browser/memory_tracker/tool/util.h"
+#include "nb/analytics/memory_tracker.h"
+#include "starboard/log.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+///////////////////////////////////////////////////////////////////////////////
+CompressedTimeSeriesTool::CompressedTimeSeriesTool() : number_samples_(400) {}
+
+void CompressedTimeSeriesTool::Run(Params* params) {
+ TimeSeries timeseries;
+ Timer timer_status_message(base::TimeDelta::FromSeconds(1));
+
+ // Outputs CSV once every minute.
+ Timer timer_output_csv(base::TimeDelta::FromMinutes(1));
+
+ // Initial sample time is every 50 milliseconds.
+ base::TimeDelta current_sample_interval =
+ base::TimeDelta::FromMilliseconds(50);
+
+ while (!params->wait_for_finish_signal(current_sample_interval.ToSbTime())) {
+ AcquireSample(params->memory_tracker(), ×eries,
+ params->time_since_start());
+
+ if (IsFull(timeseries, number_samples_)) {
+ Compress(×eries); // Remove every other element.
+ current_sample_interval *= 2; // Double the sample time.
+ timer_status_message.Restart(); // Skip status message.
+ }
+
+ if (timer_output_csv.UpdateAndIsExpired()) {
+ const std::string str = ToCsvString(timeseries);
+ params->logger()->Output(str.c_str());
+ timer_status_message.Restart(); // Skip status message.
+ }
+
+ // Print status message.
+ if (timer_status_message.UpdateAndIsExpired()) {
+ std::stringstream ss;
+ ss << tool_name() << " is running..." << kNewLine;
+ params->logger()->Output(ss.str().c_str());
+ }
+ }
+}
+
+std::string CompressedTimeSeriesTool::ToCsvString(
+ const TimeSeries& timeseries) {
+ size_t largest_sample_size = 0;
+ size_t smallest_sample_size = INT_MAX;
+
+ typedef MapAllocationSamples::const_iterator MapIt;
+
+ // Sanitize samples_in and store as samples.
+ const MapAllocationSamples& samples_in = timeseries.samples_;
+ MapAllocationSamples samples;
+ for (MapIt it = samples_in.begin(); it != samples_in.end(); ++it) {
+ std::string name = it->first;
+ const AllocationSamples& value = it->second;
+
+ if (value.allocated_bytes_.size() != value.number_allocations_.size()) {
+ SB_NOTREACHED() << "Error at " << __FILE__ << ":" << __LINE__;
+ return "ERROR";
+ }
+
+ const size_t n = value.allocated_bytes_.size();
+ if (n > largest_sample_size) {
+ largest_sample_size = n;
+ }
+ if (n < smallest_sample_size) {
+ smallest_sample_size = n;
+ }
+
+ const bool duplicate_found = (samples.end() != samples.find(name));
+ if (duplicate_found) {
+ SB_NOTREACHED() << "Error, duplicate found for entry: " << name
+ << kNewLine;
+ }
+ // Store value as a sanitized sample.
+ samples[name] = value;
+ }
+
+ SB_DCHECK(largest_sample_size == smallest_sample_size);
+
+ std::stringstream ss;
+
+ // Begin output to CSV.
+
+ // Preamble
+ ss << kNewLine << "//////////////////////////////////////////////" << kNewLine
+ << "// CSV of BYTES allocated per region (MB's)." << kNewLine
+ << "// Units are in Megabytes. This is designed" << kNewLine
+ << "// to be used in a stacked graph." << kNewLine;
+
+ // HEADER.
+ ss << kQuote << "Time(mins)" << kQuote << kDelimiter;
+ for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+ const std::string& name = it->first;
+ ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
+ }
+ ss << kNewLine;
+
+ // Print out the values of each of the samples.
+ for (size_t i = 0; i < smallest_sample_size; ++i) {
+ // Output time first so that it can be used as an x-axis.
+ const double time_mins =
+ timeseries.time_stamps_[i].InMilliseconds() / (1000. * 60.);
+ ss << time_mins << ",";
+ for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+ const int64 alloc_bytes = it->second.allocated_bytes_[i];
+ // Convert to float megabytes with decimals of precision.
+ double n = alloc_bytes / (1000 * 10);
+ n = n / (100.);
+ ss << n << kDelimiter;
+ }
+ ss << kNewLine;
+ }
+ ss << "// END CSV of BYTES allocated per region." << kNewLine;
+ ss << "//////////////////////////////////////////////";
+
+ ss << kNewLine;
+ // Preamble
+ ss << kNewLine << "//////////////////////////////////////////////";
+ ss << kNewLine << "// CSV of COUNT of allocations per region." << kNewLine;
+
+ // HEADER
+ ss << kQuote << "Time(mins)" << kQuote << kDelimiter;
+ for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+ const std::string& name = it->first;
+ ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
+ }
+ ss << kNewLine;
+ for (size_t i = 0; i < smallest_sample_size; ++i) {
+ // Output time first so that it can be used as an x-axis.
+ const double time_mins =
+ timeseries.time_stamps_[i].InMilliseconds() / (1000. * 60.);
+ ss << time_mins << ",";
+ for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+ const int64 n_allocs = it->second.number_allocations_[i];
+ ss << n_allocs << kDelimiter;
+ }
+ ss << kNewLine;
+ }
+ ss << "// END CSV of COUNT of allocations per region." << kNewLine;
+ ss << "//////////////////////////////////////////////";
+ ss << kNewLine << kNewLine;
+
+ std::string output = ss.str();
+ return output;
+}
+
+void CompressedTimeSeriesTool::AcquireSample(
+ nb::analytics::MemoryTracker* memory_tracker, TimeSeries* timeseries,
+ const base::TimeDelta& time_now) {
+ const size_t sample_count = timeseries->time_stamps_.size();
+ timeseries->time_stamps_.push_back(time_now);
+
+ MapAllocationSamples& map_samples = timeseries->samples_;
+
+ std::vector<const nb::analytics::AllocationGroup*> vector_output;
+ memory_tracker->GetAllocationGroups(&vector_output);
+
+ // Sample all known memory scopes.
+ for (size_t i = 0; i < vector_output.size(); ++i) {
+ const nb::analytics::AllocationGroup* group = vector_output[i];
+ const std::string& name = group->name();
+
+ const bool first_creation =
+ map_samples.find(group->name()) == map_samples.end();
+
+ AllocationSamples& new_entry = map_samples[name];
+
+ // Didn't see it before so create new entry.
+ if (first_creation) {
+ // Make up for lost samples...
+ new_entry.allocated_bytes_.resize(sample_count, 0);
+ new_entry.number_allocations_.resize(sample_count, 0);
+ }
+
+ int32 num_allocs = -1;
+ int64 allocation_bytes = -1;
+ group->GetAggregateStats(&num_allocs, &allocation_bytes);
+
+ new_entry.allocated_bytes_.push_back(allocation_bytes);
+ new_entry.number_allocations_.push_back(num_allocs);
+ }
+
+ // Sample the in use memory bytes reported by malloc.
+ nb::analytics::MemoryStats memory_stats =
+ nb::analytics::GetProcessMemoryStats();
+ AllocationSamples& process_mem_in_use = map_samples["ProcessMemoryInUse"];
+ process_mem_in_use.allocated_bytes_.push_back(memory_stats.used_cpu_memory);
+ process_mem_in_use.number_allocations_.push_back(0);
+
+ // Sample the reserved memory bytes reported by malloc.
+ AllocationSamples& process_mem_reserved =
+ map_samples["ProcessMemoryReserved"];
+ process_mem_reserved.allocated_bytes_.push_back(memory_stats.used_cpu_memory);
+ process_mem_reserved.number_allocations_.push_back(0);
+}
+
+bool CompressedTimeSeriesTool::IsFull(const TimeSeries& timeseries,
+ size_t samples_limit) {
+ return timeseries.time_stamps_.size() >= samples_limit;
+}
+
+void CompressedTimeSeriesTool::Compress(TimeSeries* timeseries) {
+ typedef MapAllocationSamples::iterator MapIt;
+ MapAllocationSamples& samples = timeseries->samples_;
+ RemoveOddElements(&(timeseries->time_stamps_));
+ for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+ AllocationSamples& data = it->second;
+ RemoveOddElements(&data.allocated_bytes_);
+ RemoveOddElements(&data.number_allocations_);
+ }
+}
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h
new file mode 100644
index 0000000..caa5a6b
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h
@@ -0,0 +1,81 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_COMPRESSED_TIME_SERIES_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_COMPRESSED_TIME_SERIES_TOOL_H_
+
+#include <string>
+
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "nb/analytics/memory_tracker.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Generates CSV values of the engine memory. This includes memory
+// consumption in bytes and also the number of allocations.
+// Allocations are segmented by category such as "Javascript" and "Network and
+// are periodically dumped to the Params::abstract_logger_.
+//
+// Compression:
+// During early run, the sampling rate is extremely high, however the sampling
+// rate will degrade by half each time the sampling buffer fills up.
+//
+// Additionally, every time the sampling buffer fills up, space is freed by
+// evicting every other element.
+//
+// Example output:
+// //////////////////////////////////////////////
+// // CSV of BYTES allocated per region (MB's).
+// // Units are in Megabytes. This is designed
+// // to be used in a stacked graph.
+// "Time(mins)","CSS","DOM","Javascript","Media","MessageLoop"....
+// 0,0,0,0,0,0...
+// 1022.54,3.3,7.19,49.93,50.33....
+// ...
+// // END CSV of BYTES allocated per region.
+// //////////////////////////////////////////////
+//
+// //////////////////////////////////////////////
+// // CSV of COUNT of allocations per region.
+// "Time(mins)","CSS","DOM","Javascript","Media","MessageLoop"....
+// 0,0,0,0,0,0...
+// 1022.54,57458,70011,423217,27,54495,43460...
+// ...
+// // END CSV of COUNT of allocations per region.
+// //////////////////////////////////////////////
+class CompressedTimeSeriesTool : public AbstractTool {
+ public:
+ CompressedTimeSeriesTool();
+ virtual void Run(Params* params) OVERRIDE;
+ virtual std::string tool_name() const OVERRIDE {
+ return "MemoryTrackerCompressedTimeSeries";
+ }
+
+ private:
+ static std::string ToCsvString(const TimeSeries& histogram);
+ static void AcquireSample(nb::analytics::MemoryTracker* memory_tracker,
+ TimeSeries* histogram, const base::TimeDelta& dt);
+ static bool IsFull(const TimeSeries& histogram, size_t num_samples);
+ static void Compress(TimeSeries* histogram);
+
+ const int number_samples_;
+};
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_COMPRESSED_TIME_SERIES_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.cc b/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.cc
new file mode 100644
index 0000000..95313d2
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.cc
@@ -0,0 +1,461 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/leak_finder_tool.h"
+
+#include <algorithm>
+#include <iomanip>
+#include <map>
+#include <utility>
+
+#include "base/timer.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "cobalt/browser/memory_tracker/tool/util.h"
+#include "nb/memory_scope.h"
+#include "starboard/string.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+LeakFinderTool::LeakFinderTool(StackTraceMode mode)
+ : string_pool_(128),
+ frame_map_(128),
+ callframe_map_(128),
+ stack_trace_mode_(mode) {
+ default_callframe_str_ = &string_pool_.Intern("<Unknown>");
+}
+
+LeakFinderTool::~LeakFinderTool() {
+ frame_map_.Clear();
+ callframe_map_.Clear();
+}
+
+bool LeakFinderTool::IsJavascriptScope(
+ const nb::analytics::CallStack& callstack) {
+ // March through all MemoryScopes in the callstack and check if any of them
+ // contains a javascript scope. If it does return true.
+ for (nb::analytics::CallStack::const_iterator it = callstack.begin();
+ it != callstack.end(); ++it) {
+ const NbMemoryScopeInfo* memory_scope = *it;
+ const bool is_javascript_scope =
+ SbStringFindString(memory_scope->memory_scope_name_, "Javascript");
+ if (is_javascript_scope) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void LeakFinderTool::OnMemoryAllocation(
+ const void* memory_block, const nb::analytics::AllocationRecord& record,
+ const nb::analytics::CallStack& callstack) {
+ // When in javascript mode, filter only allocations with "Javascript" in
+ // the memory scope name.
+ if (stack_trace_mode_ == kJavascript) {
+ if (!IsJavascriptScope(callstack)) {
+ return;
+ }
+ }
+
+ // symbol_str can be used as a unique key. The same value of callstack will
+ // always produce the same string pointer.
+ const std::string* symbol_str = GetOrCreateSymbol(callstack);
+ // Track the allocation.
+ if (!callframe_map_.SetIfMissing(memory_block, symbol_str)) {
+ CallFrameMap::EntryHandle entry_handle;
+ callframe_map_.Get(memory_block, &entry_handle);
+
+ const void* ptr = entry_handle.Valid() ? entry_handle.Key() : NULL;
+ entry_handle.ReleaseLockAndInvalidate();
+
+ DCHECK(false) << "Unexpected memory block at " << memory_block
+ << " already existed as: " << ptr;
+ }
+
+ AllocationFrameMap::EntryHandle entry_handle;
+ frame_map_.GetOrCreate(symbol_str, &entry_handle);
+
+ // While this entry_handle is in use, no other threads
+ // can modify this entry.
+ AllocRec& alloc_rec = entry_handle.ValueMutable();
+ alloc_rec.total_bytes += record.size;
+ alloc_rec.num_allocs++;
+}
+
+void LeakFinderTool::OnMemoryDeallocation(
+ const void* memory_block, const nb::analytics::AllocationRecord& record,
+ const nb::analytics::CallStack& callstack) {
+ UNREFERENCED_PARAMETER(callstack);
+
+ const std::string* symbol_str = NULL;
+
+ {
+ CallFrameMap::EntryHandle entry_handle;
+ if (!callframe_map_.Get(memory_block, &entry_handle)) {
+ // This happens if the allocation happened before this tool attached
+ // to the memory tracker or if the memory allocation was filtered and
+ // therefore isn't being tracked.
+ return;
+ }
+ symbol_str = entry_handle.Value();
+ callframe_map_.Remove(&entry_handle);
+ }
+
+ // This case can happen if the memory tracker attaches after the allocation.
+ if (!symbol_str) {
+ return;
+ }
+
+ AllocationFrameMap::EntryHandle entry_handle;
+ frame_map_.GetOrCreate(symbol_str, &entry_handle);
+
+ // While entry_handle is in use, no other element can modify
+ // this element.
+ AllocRec& alloc_rec = entry_handle.ValueMutable();
+ alloc_rec.total_bytes -= record.size;
+ alloc_rec.num_allocs--;
+}
+
+std::string LeakFinderTool::tool_name() const {
+ return "MemoryTrackerLeakFinder";
+}
+
+void LeakFinderTool::Run(Params* params) {
+ // Run function does almost nothing.
+ params->logger()->Output("MemoryTrackerLeakFinder running...");
+
+ static const size_t kMaxSamples = 400;
+
+ // This value will decay whenever the buffer fills up and is compressed via
+ // sample elimination.
+ SbTime sample_time = 50; // 50 microseconds.
+
+ std::vector<base::TimeDelta> time_values;
+ std::map<const std::string*, std::vector<AllocRec> > map_allocations;
+
+ SbTime start_time = SbTimeGetMonotonicNow();
+ Timer output_trigger(base::TimeDelta::FromMinutes(1));
+
+ const double recording_delay_mins = 5.0;
+
+ // Controls how often an update status message is sent to output.
+ Timer output_status_timer(base::TimeDelta::FromSeconds(1));
+
+ while (true) {
+ if (params->wait_for_finish_signal(sample_time)) {
+ break; // Finish received, break.
+ }
+ SbTime now_time = SbTimeGetMonotonicNow();
+
+ const base::TimeDelta time_since_start =
+ base::Time::FromSbTime(now_time) - base::Time::FromSbTime(start_time);
+
+ // DELAY RECORDING AS STARTUP MEMORY IS INITIALIZED
+ // Cleaner graphs if we wait until after startup.
+ //
+ const double times_since_start_mins = time_since_start.InSecondsF() / 60.0;
+ if (times_since_start_mins < recording_delay_mins) {
+ if (output_status_timer.UpdateAndIsExpired()) {
+ double remaining_time_mins =
+ (recording_delay_mins - times_since_start_mins);
+ std::stringstream ss;
+ ss << "MemoryTrackerLeakFinder starting in " << remaining_time_mins
+ << " minutes.\n";
+ params->logger()->Output(ss.str().c_str());
+ }
+ continue;
+ }
+
+ time_values.push_back(time_since_start);
+
+ // To improve performance, make a copy of the values to a vector on
+ // the stack.
+ std::vector<std::pair<const std::string*, AllocRec> > samples;
+ SampleSnapshot(&samples);
+
+ // Take a snapshot.
+ for (size_t i = 0; i < samples.size(); ++i) {
+ std::pair<const std::string*, AllocRec> sample = samples[i];
+ std::vector<AllocRec>& sample_history = map_allocations[sample.first];
+
+ sample_history.resize(time_values.size());
+ sample_history.back() = sample.second;
+ }
+
+ if (output_trigger.UpdateAndIsExpired() && time_values.size() > 10) {
+ // Timer expired so dump the current information output.
+
+ std::vector<AllocationProfile> alloc_profiles;
+ GenerateTopLeakingAllocationProfiles(time_values, map_allocations,
+ &alloc_profiles);
+
+ if (alloc_profiles.empty()) {
+ params->logger()->Output(
+ "MemoryTrackerLeakFinder: alloc_profiles was "
+ "empty and nothing is written.");
+ } else {
+ if (alloc_profiles.size() > 20) {
+ alloc_profiles.resize(20);
+ }
+
+ std::string csv_str = GenerateCSV(time_values, alloc_profiles);
+ params->logger()->Output(csv_str.c_str());
+ }
+ }
+
+ // Compress the buffer, and modify sample_time so that it's twice as slow.
+ if (time_values.size() >= kMaxSamples) {
+ sample_time *= 2; // Double the time that it takes to trigger a sample.
+ // Remove every other element in time_values.
+ RemoveOddElements(&time_values);
+
+ // Remove every other element in the samples.
+ for (size_t i = 0; i < samples.size(); ++i) {
+ std::pair<const std::string*, AllocRec> sample = samples[i];
+ std::vector<AllocRec>& sample_history = map_allocations[sample.first];
+ RemoveOddElements(&sample_history);
+ }
+ }
+ }
+}
+
+const std::string* LeakFinderTool::GetOrCreateSymbol(
+ const nb::analytics::CallStack& callstack) {
+ const std::string* symbol_str = NULL;
+
+ // In javascript mode we try and get the javascript symbol. Otherwise
+ // fallback to C++ symbol.
+ if (stack_trace_mode_ == kJavascript) {
+ symbol_str = TryGetJavascriptSymbol();
+ if (symbol_str) {
+ return symbol_str;
+ }
+ }
+
+ symbol_str = GetOrCreateCplusPlusSymbol(callstack);
+ return symbol_str;
+}
+
+const std::string* LeakFinderTool::GetOrCreateCplusPlusSymbol(
+ const nb::analytics::CallStack& callstack) {
+ if (callstack.empty()) {
+ return default_callframe_str_;
+ } else {
+ const NbMemoryScopeInfo* memory_scope = callstack.back();
+
+ const bool skip =
+ SbStringFindString(memory_scope->function_name_, "js_malloc") ||
+ SbStringFindString(memory_scope->function_name_, "js_realloc") ||
+ SbStringFindString(memory_scope->function_name_, "new_");
+
+ // Skip up one callstack because we don't want to track calls to
+ // allocation functions.
+ if (skip && callstack.size() > 1) {
+ memory_scope = callstack[callstack.size() - 2];
+ }
+
+ const char* file_name = BaseNameFast(memory_scope->file_name_);
+
+ // Generates a symbol.
+ // Example:
+ // "Javascript:Interpreter.cpp(415):RunScript()"
+ char symbol_buff[128];
+ SbStringFormatF(symbol_buff, sizeof(symbol_buff), "%s:%s(%d)::%s()",
+ memory_scope->memory_scope_name_, file_name,
+ memory_scope->line_number_, memory_scope->function_name_);
+
+ // Get's a unique pointer to a string containing the symbol. If the symbol
+ // was previously generated then the previous symbol is returned.
+ return &string_pool_.Intern(symbol_buff);
+ }
+}
+
+const std::string* LeakFinderTool::TryGetJavascriptSymbol() {
+ // TODO: Actually get and use a stack trace here.
+ return NULL;
+}
+
+void LeakFinderTool::SampleSnapshot(
+ std::vector<std::pair<const std::string*, AllocRec> >* destination) {
+ destination->erase(destination->begin(), destination->end());
+
+ const size_t sample_size = frame_map_.GetSize();
+
+ // Do this locally.
+ destination->reserve(sample_size + 10);
+ frame_map_.CopyToStdVector(destination);
+}
+
+std::string LeakFinderTool::GenerateCSV(
+ const std::vector<base::TimeDelta>& time_values,
+ const std::vector<AllocationProfile>& data) {
+ std::stringstream ss;
+ ss << std::fixed; // Turn off scientific notation for CSV values.
+ ss << std::setprecision(3);
+ ss << kNewLine << kNewLine;
+
+ // HEADER
+ ss << "// Allocation in megabytes." << kNewLine;
+ ss << kQuote << "Time(min)" << kQuote << kDelimiter;
+ for (size_t i = 0; i < data.size(); ++i) {
+ const AllocationProfile& alloc_profile = data[i];
+ const std::string& name = *alloc_profile.name_;
+ ss << kQuote << name << kQuote << kDelimiter;
+ }
+ ss << kNewLine;
+
+ // BODY
+ for (size_t i = 0; i < time_values.size(); ++i) {
+ for (size_t j = 0; j < data.size(); ++j) {
+ if (j == 0) {
+ double mins = time_values[i].InSecondsF() / 60.f;
+ if (mins < .001) {
+ mins = 0;
+ }
+ ss << mins << kDelimiter;
+ }
+
+ const AllocationProfile& alloc_profile = data[j];
+ const std::vector<AllocRec>& alloc_history =
+ *alloc_profile.alloc_history_;
+
+ double megabytes = static_cast<double>(alloc_history[i].total_bytes) /
+ static_cast<double>(1024 * 1024);
+
+ DCHECK_EQ(alloc_history.size(), time_values.size());
+ ss << megabytes << kDelimiter;
+ }
+ ss << kNewLine;
+ }
+ ss << kNewLine << kNewLine;
+ ss << "// Object counts." << kNewLine;
+ ss << kQuote << "Time(min)" << kQuote << kDelimiter;
+ for (size_t i = 0; i < data.size(); ++i) {
+ const AllocationProfile& alloc_profile = data[i];
+ const std::string& name = *alloc_profile.name_;
+ ss << kQuote << name << kQuote << kDelimiter;
+ }
+ ss << kNewLine;
+ for (size_t i = 0; i < time_values.size(); ++i) {
+ for (size_t j = 0; j < data.size(); ++j) {
+ if (j == 0) {
+ double mins = time_values[i].InSecondsF() / 60.f;
+ if (mins < .001) {
+ mins = 0;
+ }
+ ss << mins << kDelimiter;
+ }
+ const AllocationProfile& alloc_profile = data[j];
+ const std::vector<AllocRec>& alloc_history =
+ *alloc_profile.alloc_history_;
+ DCHECK_EQ(alloc_history.size(), time_values.size());
+ ss << alloc_history[i].num_allocs << kDelimiter;
+ }
+ ss << kNewLine;
+ }
+
+ ss << kNewLine << kNewLine;
+ return ss.str();
+}
+
+void LeakFinderTool::GenerateTopLeakingAllocationProfiles(
+ const std::vector<base::TimeDelta>& time_values, const MapSamples& samples,
+ std::vector<AllocationProfile>* destination) {
+ // GENERATE LINEAR REGRESSION LINE
+ // first value is time in microseconds.
+ // second value is total_bytes.
+ std::vector<std::pair<int64_t, int64_t> > sample_data;
+
+ typedef std::map<const std::string*, SlopeYIntercept> LinearRegressionMap;
+ LinearRegressionMap linear_regression_map;
+
+ std::vector<AllocationProfile> allocation_profiles;
+
+ for (MapSamples::const_iterator it = samples.begin(); it != samples.end();
+ ++it) {
+ const std::string* allocation_name = it->first;
+ const std::vector<AllocRec>& allocation_samples = it->second;
+
+ if (allocation_samples.empty()) {
+ continue;
+ }
+
+ // Filter out allocations records that have low number of allocations.
+ if (allocation_samples.back().num_allocs < 10) {
+ continue;
+ }
+
+ // Filter out allocations that are insignificant.
+ int64_t largest_allocation_sample = 0;
+ const int64_t kMinAllocationSize = 1024 * 64;
+ for (size_t i = 0; i < allocation_samples.size(); ++i) {
+ int64_t bytes = allocation_samples[i].total_bytes;
+ largest_allocation_sample = std::max(largest_allocation_sample, bytes);
+ }
+ if (largest_allocation_sample < kMinAllocationSize) {
+ continue;
+ }
+
+ // Filter out allocations where there is no growth between first quartile
+ // and final output.
+ const AllocRec& first_quartile_sample =
+ allocation_samples[allocation_samples.size() / 4];
+ const AllocRec& final_sample = allocation_samples.back();
+
+ const double increase_ratio =
+ static_cast<double>(final_sample.total_bytes) /
+ static_cast<double>(first_quartile_sample.total_bytes);
+
+ // 5% threshold of increase to be qualified as a lead.
+ static const double kMinIncreaseThreshold = .05;
+
+ // If the increase between first quartile and final sample less than 5%
+ // then skip.
+ if (increase_ratio < kMinIncreaseThreshold) {
+ continue;
+ }
+
+ sample_data.clear(); // Recycle.
+ for (size_t i = 0; i < time_values.size(); ++i) {
+ std::pair<int64_t, int64_t> datum(time_values[i].InMicroseconds(),
+ allocation_samples[i].total_bytes);
+ sample_data.push_back(datum);
+ }
+
+ double slope = 0;
+ double y_intercept = 0;
+ bool valid = GetLinearFit(sample_data.begin(), sample_data.end(), &slope,
+ &y_intercept);
+ DCHECK(valid);
+ linear_regression_map[allocation_name] =
+ SlopeYIntercept(slope, y_intercept);
+ AllocationProfile alloc_profile(allocation_name, &allocation_samples, slope,
+ y_intercept);
+ alloc_profile.leak_potential_ =
+ allocation_samples.back().total_bytes * slope;
+ allocation_profiles.push_back(alloc_profile);
+ }
+
+ std::sort(allocation_profiles.begin(), allocation_profiles.end(),
+ AllocationProfile::CompareLeakPotential);
+ // Biggest one first.
+ std::reverse(allocation_profiles.begin(), allocation_profiles.end());
+ *destination = allocation_profiles;
+}
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.h b/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.h
new file mode 100644
index 0000000..170ddfd
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/leak_finder_tool.h
@@ -0,0 +1,162 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_LEAK_FINDER_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_LEAK_FINDER_TOOL_H_
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "nb/analytics/memory_tracker.h"
+#include "nb/concurrent_map.h"
+#include "nb/hash.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Records allocations and outputs leaks as a CSV. Each column will
+// have an associated symbol.
+class LeakFinderTool : public AbstractTool,
+ public nb::analytics::MemoryTrackerDebugCallback {
+ public:
+ enum StackTraceMode {
+ // Always get the C++ version of the stack trace.
+ kCPlusPlus,
+ // Whenever possible, get the javascript stack trace,
+ // else fallback to CPlusPlus.
+ kJavascript,
+ };
+
+ explicit LeakFinderTool(StackTraceMode pref);
+ virtual ~LeakFinderTool();
+
+ // OnMemoryAllocation() and OnMemoryDeallocation() are part of
+ // class MemoryTrackerDebugCallback.
+ void OnMemoryAllocation(const void* memory_block,
+ const nb::analytics::AllocationRecord& record,
+ const nb::analytics::CallStack& callstack) OVERRIDE;
+
+ void OnMemoryDeallocation(const void* memory_block,
+ const nb::analytics::AllocationRecord& record,
+ const nb::analytics::CallStack& callstack) OVERRIDE;
+
+ // Interface AbstractMemoryTrackerTool
+ virtual std::string tool_name() const OVERRIDE;
+ virtual void Run(Params* params) OVERRIDE;
+
+ const std::string* GetOrCreateSymbol(
+ const nb::analytics::CallStack& callstack);
+
+ const std::string* TryGetJavascriptSymbol();
+ const std::string* GetOrCreateCplusPlusSymbol(
+ const nb::analytics::CallStack& callstack);
+
+ private:
+ struct AllocRec;
+ // A map from callsite -> allocation size.
+ // typedef std::map<const std::string*, AllocRec> AllocationFrameMap;
+ typedef nb::ConcurrentMap<const std::string*, AllocRec,
+ nb::PODHasher<const std::string*> >
+ AllocationFrameMap;
+
+ // A map of memory -> callsite.
+ // This keeps track of what callsites belong to which allocations.
+ // typedef std::map<const void*, const std::string*> CallFrameMap;
+ //
+ typedef nb::ConcurrentMap<const void*, const std::string*,
+ nb::PODHasher<const std::string*> > CallFrameMap;
+
+ // A map from callsite -> allocation data.
+ typedef std::map<const std::string*, std::vector<AllocRec> > MapSamples;
+ // A value type for holding a slope and Y-intercept.
+ typedef std::pair<double, double> SlopeYIntercept;
+ // An AllocationRecord.
+ struct AllocRec {
+ AllocRec() : total_bytes(0), num_allocs(0) {}
+ AllocRec(const AllocRec& other)
+ : total_bytes(other.total_bytes), num_allocs(other.num_allocs) {}
+ AllocRec& operator=(const AllocRec& other) {
+ total_bytes = other.total_bytes;
+ num_allocs = other.num_allocs;
+ return *this;
+ }
+ int64_t total_bytes;
+ int32_t num_allocs;
+ };
+
+ // This data structure is used to for sorting leaks. The important variable
+ // is |leak_potential|, which represents how likely this AllocationProfile
+ // is a leak.
+ struct AllocationProfile {
+ AllocationProfile()
+ : name_(NULL),
+ alloc_history_(NULL),
+ slope_(0),
+ y_intercept_(0),
+ leak_potential_(0) {}
+
+ AllocationProfile(const std::string* name,
+ const std::vector<AllocRec>* alloc_history, double slope,
+ double y_intercept)
+ : name_(name),
+ alloc_history_(alloc_history),
+ slope_(slope),
+ y_intercept_(y_intercept),
+ leak_potential_(0) {}
+ const std::string* name_;
+ const std::vector<AllocRec>* alloc_history_;
+ // Linear regression data.
+ double slope_;
+ double y_intercept_;
+
+ // This this value is set externally. Higher values indicate higher chance
+ // that this is a leak.
+ double leak_potential_;
+
+ static bool CompareLeakPotential(const AllocationProfile& a,
+ const AllocationProfile& b) {
+ return a.leak_potential_ < b.leak_potential_;
+ }
+ };
+
+ static std::string GenerateCSV(
+ const std::vector<base::TimeDelta>& time_values,
+ const std::vector<AllocationProfile>& data);
+
+ void SampleSnapshot(
+ std::vector<std::pair<const std::string*, AllocRec> >* destination);
+
+ static void GenerateTopLeakingAllocationProfiles(
+ const std::vector<base::TimeDelta>& time_values,
+ const MapSamples& samples, std::vector<AllocationProfile>* destination);
+
+ static bool IsJavascriptScope(const nb::analytics::CallStack& callstack);
+
+ const std::string* default_callframe_str_;
+ nb::ConcurrentStringInterner string_pool_;
+ AllocationFrameMap frame_map_;
+ CallFrameMap callframe_map_;
+ StackTraceMode stack_trace_mode_;
+};
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_LEAK_FINDER_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/log_writer_tool.cc b/src/cobalt/browser/memory_tracker/tool/log_writer_tool.cc
new file mode 100644
index 0000000..65ad229
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/log_writer_tool.cc
@@ -0,0 +1,168 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/log_writer_tool.h"
+
+#include <algorithm>
+
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/buffered_file_writer.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "starboard/string.h"
+#include "starboard/types.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+LogWriterTool::LogWriterTool() : start_time_(NowTime()) {
+ buffered_file_writer_.reset(new BufferedFileWriter(MemoryLogPath()));
+ InitAndRegisterMemoryReporter();
+}
+
+LogWriterTool::~LogWriterTool() {
+ // No locks are used for the thread reporter, so when it's set to null
+ // we allow one second for any suspended threads to run through and finish
+ // their reporting.
+ SbMemorySetReporter(NULL);
+ SbThreadSleep(kSbTimeSecond);
+ buffered_file_writer_.reset(NULL);
+}
+
+std::string LogWriterTool::tool_name() const {
+ return "MemoryTrackerLogWriter";
+}
+
+void LogWriterTool::Run(Params* params) {
+ // Run function does almost nothing.
+ params->logger()->Output("MemoryTrackerLogWriter running...");
+}
+
+void LogWriterTool::OnMemoryAllocation(const void* memory_block, size_t size) {
+ void* addresses[kMaxStackSize] = {};
+ // Though the SbSystemGetStack API documentation does not specify any possible
+ // negative return values, we take no chance.
+ const size_t count = std::max(SbSystemGetStack(addresses, kMaxStackSize), 0);
+
+ const size_t n = 256;
+ char buff[n] = {0};
+ size_t buff_pos = 0;
+
+ int time_since_start_ms = GetTimeSinceStartMs();
+ // Writes "+ <ALLOCATION ADDRESS> <size> <time>"
+ int bytes_written =
+ SbStringFormatF(buff, sizeof(buff), "+ %" PRIXPTR " %x %d",
+ reinterpret_cast<uintptr_t>(memory_block),
+ static_cast<unsigned int>(size), time_since_start_ms);
+
+ buff_pos += bytes_written;
+ const size_t end_index = std::min(count, kStartIndex + kNumAddressPrints);
+
+ // For each of the stack addresses that we care about, concat them to the
+ // buffer. This was originally written to do multiple stack addresses but
+ // this tends to overflow on some lower platforms so it's possible that
+ // this loop only iterates once.
+ for (size_t i = kStartIndex; i < end_index; ++i) {
+ void* p = addresses[i];
+ int bytes_written =
+ SbStringFormatF(buff + buff_pos, sizeof(buff) - buff_pos,
+ " %" PRIXPTR "", reinterpret_cast<uintptr_t>(p));
+ DCHECK_GE(bytes_written, 0);
+
+ if (bytes_written < 0) {
+ DCHECK(false) << "Error occurred while writing string.";
+ continue;
+ }
+
+ buff_pos += static_cast<size_t>(bytes_written);
+ }
+ // Adds a "\n" at the end.
+ SbStringConcat(buff + buff_pos, "\n", static_cast<int>(n - buff_pos));
+ buffered_file_writer_->Append(buff, strlen(buff));
+}
+
+void LogWriterTool::OnMemoryDeallocation(const void* memory_block) {
+ const size_t n = 256;
+ char buff[n] = {0};
+ // Writes "- <ADDRESS OF ALLOCATION> \n"
+ SbStringFormatF(buff, sizeof(buff), "- %" PRIXPTR "\n",
+ reinterpret_cast<uintptr_t>(memory_block));
+ buffered_file_writer_->Append(buff, strlen(buff));
+}
+
+void LogWriterTool::OnAlloc(void* context, const void* memory, size_t size) {
+ LogWriterTool* self = static_cast<LogWriterTool*>(context);
+ self->OnMemoryAllocation(memory, size);
+}
+
+void LogWriterTool::OnDealloc(void* context, const void* memory) {
+ LogWriterTool* self = static_cast<LogWriterTool*>(context);
+ self->OnMemoryDeallocation(memory);
+}
+
+void LogWriterTool::OnMapMemory(void* context, const void* memory,
+ size_t size) {
+ LogWriterTool* self = static_cast<LogWriterTool*>(context);
+ self->OnMemoryAllocation(memory, size);
+}
+
+void LogWriterTool::OnUnMapMemory(void* context, const void* memory,
+ size_t size) {
+ SB_UNREFERENCED_PARAMETER(size);
+ LogWriterTool* self = static_cast<LogWriterTool*>(context);
+ self->OnMemoryDeallocation(memory);
+}
+
+std::string LogWriterTool::MemoryLogPath() {
+ char file_name_buff[2048] = {};
+ SbSystemGetPath(kSbSystemPathDebugOutputDirectory, file_name_buff,
+ arraysize(file_name_buff));
+ std::string path(file_name_buff);
+ if (!path.empty()) { // Protect against a dangling "/" at end.
+ const int back_idx_signed = static_cast<int>(path.length()) - 1;
+ if (back_idx_signed >= 0) {
+ const size_t idx = back_idx_signed;
+ if (path[idx] == '/') {
+ path.erase(idx);
+ }
+ }
+ }
+ path.append("/memory_log.txt");
+ return path;
+}
+
+base::TimeTicks LogWriterTool::NowTime() {
+ // NowFromSystemTime() is slower but more accurate. However it might
+ // be useful to use the faster but less accurate version if there is
+ // a speedup.
+ return base::TimeTicks::Now();
+}
+
+int LogWriterTool::GetTimeSinceStartMs() const {
+ base::TimeDelta dt = NowTime() - start_time_;
+ return static_cast<int>(dt.InMilliseconds());
+}
+
+void LogWriterTool::InitAndRegisterMemoryReporter() {
+ DCHECK(!memory_reporter_.get()) << "Memory Reporter already registered.";
+
+ SbMemoryReporter mem_reporter = {OnAlloc, OnDealloc, OnMapMemory,
+ OnUnMapMemory, this};
+ memory_reporter_.reset(new SbMemoryReporter(mem_reporter));
+ SbMemorySetReporter(memory_reporter_.get());
+}
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/log_writer_tool.h b/src/cobalt/browser/memory_tracker/tool/log_writer_tool.h
new file mode 100644
index 0000000..c22da65
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/log_writer_tool.h
@@ -0,0 +1,72 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_LOG_WRITER_TOOL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_LOG_WRITER_TOOL_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "starboard/memory_reporter.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+class BufferedFileWriter;
+
+// Outputs memory_log.txt to the output log location. This log contains
+// allocations with a stack trace (non-symbolized) and deallocations without
+// a stack.
+class LogWriterTool : public AbstractTool {
+ public:
+ LogWriterTool();
+ virtual ~LogWriterTool();
+
+ // Interface AbstrctMemoryTrackerTool
+ virtual std::string tool_name() const OVERRIDE;
+ virtual void Run(Params* params) OVERRIDE;
+
+ void OnMemoryAllocation(const void* memory_block, size_t size);
+ void OnMemoryDeallocation(const void* memory_block);
+
+ private:
+ // Callbacks for MemoryReporter
+ static void OnAlloc(void* context, const void* memory, size_t size);
+ static void OnDealloc(void* context, const void* memory);
+ static void OnMapMemory(void* context, const void* memory, size_t size);
+ static void OnUnMapMemory(void* context, const void* memory, size_t size);
+ static std::string MemoryLogPath();
+
+ static base::TimeTicks NowTime();
+ static const size_t kStartIndex = 5;
+ static const size_t kNumAddressPrints = 1;
+ static const size_t kMaxStackSize = 10;
+
+ int GetTimeSinceStartMs() const;
+ void InitAndRegisterMemoryReporter();
+
+ base::TimeTicks start_time_;
+ scoped_ptr<SbMemoryReporter> memory_reporter_;
+ scoped_ptr<BufferedFileWriter> buffered_file_writer_;
+};
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_LOG_WRITER_TOOL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/params.cc b/src/cobalt/browser/memory_tracker/tool/params.cc
new file mode 100644
index 0000000..42170fb
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/params.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/params.h"
+
+#include <sstream>
+
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+Params::Params(nb::analytics::MemoryTracker* memory_tracker,
+ AbstractLogger* logger, base::Time start_time)
+ : memory_tracker_(memory_tracker),
+ finished_(false),
+ logger_(logger),
+ timer_(start_time) {}
+
+bool Params::finished() const { return finished_; }
+
+bool Params::wait_for_finish_signal(SbTime wait_us) {
+ return finished_semaphore_.TakeWait(wait_us);
+}
+
+void Params::set_finished(bool val) {
+ finished_ = val;
+ finished_semaphore_.Put();
+}
+
+nb::analytics::MemoryTracker* Params::memory_tracker() const {
+ return memory_tracker_;
+}
+
+cobalt::browser::memory_tracker::AbstractLogger* Params::logger() {
+ return logger_.get();
+}
+
+base::TimeDelta Params::time_since_start() const {
+ return base::Time::NowFromSystemTime() - timer_;
+}
+
+std::string Params::TimeInMinutesString() const {
+ base::TimeDelta delta_t = time_since_start();
+ int64 seconds = delta_t.InSeconds();
+ float time_mins = static_cast<float>(seconds) / 60.f;
+ std::stringstream ss;
+
+ ss << time_mins;
+ return ss.str();
+}
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/params.h b/src/cobalt/browser/memory_tracker/tool/params.h
new file mode 100644
index 0000000..516d7ee
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/params.h
@@ -0,0 +1,56 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_PARAMS_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_PARAMS_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/time.h"
+#include "nb/analytics/memory_tracker.h"
+#include "starboard/common/semaphore.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+class AbstractLogger;
+
+class Params {
+ public:
+ Params(nb::analytics::MemoryTracker* memory_tracker, AbstractLogger* logger,
+ base::Time start_time);
+ bool finished() const;
+ bool wait_for_finish_signal(SbTime wait_us);
+ void set_finished(bool val);
+
+ nb::analytics::MemoryTracker* memory_tracker() const;
+ AbstractLogger* logger();
+ base::TimeDelta time_since_start() const;
+ std::string TimeInMinutesString() const;
+
+ private:
+ nb::analytics::MemoryTracker* memory_tracker_;
+ bool finished_;
+ scoped_ptr<AbstractLogger> logger_;
+ base::Time timer_;
+ starboard::Semaphore finished_semaphore_;
+};
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_PARAMS_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_impl.cc b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
new file mode 100644
index 0000000..3f32416
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
@@ -0,0 +1,678 @@
+// 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/browser/memory_tracker/tool/tool_impl.h"
+
+#include <algorithm>
+#include <cstring>
+#include <iomanip>
+#include <iterator>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/time.h"
+#include "cobalt/base/c_val_collection_entry_stats.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_thread.h"
+#include "cobalt/browser/memory_tracker/tool/util.h"
+#include "cobalt/script/mozjs/util/stack_trace_helpers.h"
+#include "nb/analytics/memory_tracker.h"
+#include "nb/analytics/memory_tracker_helpers.h"
+#include "nb/concurrent_map.h"
+#include "nb/memory_scope.h"
+#include "starboard/common/semaphore.h"
+#include "starboard/configuration.h"
+#include "starboard/file.h"
+#include "starboard/string.h"
+#include "starboard/system.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+using nb::analytics::AllocationGroup;
+using nb::analytics::AllocationRecord;
+using nb::analytics::AllocationVisitor;
+using nb::analytics::GetProcessMemoryStats;
+using nb::analytics::MemoryStats;
+using nb::analytics::MemoryTracker;
+
+class PrintTool::CvalsMap {
+ public:
+ typedef base::CVal<base::cval::SizeInBytes> CValType;
+
+ ~CvalsMap() {
+ while (!map_.empty()) {
+ MapCvals::iterator it = map_.begin();
+ delete it->second;
+ map_.erase(it);
+ }
+ }
+
+ void Update(const std::string& name, size_t value) {
+ std::string cval_name = GetCvalName(name);
+ CValType*& val = map_[cval_name];
+ if (!val) {
+ // Create if not found.
+ val = new CValType(cval_name, 0,
+ "Automatically generated by the "
+ "browser::memory_tracker system.");
+ }
+ (*val) = value;
+ }
+
+ static std::string GetCvalName(const std::string& name) {
+ std::stringstream ss;
+ ss << "Memory.Scope." << name;
+ return ss.str();
+ }
+
+ private:
+ typedef std::map<std::string, CValType*> MapCvals;
+ MapCvals map_;
+};
+
+PrintTool::PrintTool() : cvals_map_(new CvalsMap) {}
+
+PrintTool::~PrintTool() {}
+
+void PrintTool::Run(Params* params) {
+ const std::string kSeperator =
+ "--------------------------------------------------";
+
+ while (!params->finished()) {
+ std::vector<const AllocationGroup*> vector_output;
+ params->memory_tracker()->GetAllocationGroups(&vector_output);
+
+ typedef std::map<std::string, const AllocationGroup*> Map;
+ typedef Map::const_iterator MapIt;
+
+ Map output;
+ for (size_t i = 0; i < vector_output.size(); ++i) {
+ const AllocationGroup* group = vector_output[i];
+ output[group->name()] = group;
+ }
+
+ int32 num_allocs = 0;
+ int64 total_bytes = 0;
+
+ struct F {
+ static void PrintRow(std::stringstream* ss, const std::string& v1,
+ const std::string& v2, const std::string& v3) {
+ ss->width(25);
+ *ss << std::left << v1;
+ ss->width(13);
+ *ss << std::right << v2 << " ";
+ ss->width(10);
+ *ss << std::right << v3 << "\n";
+ }
+ };
+
+ if (params->memory_tracker()->IsMemoryTrackingEnabled()) {
+ // If this isn't true then it would cause an infinite loop. The
+ // following will likely crash.
+ SB_DCHECK(false) << "Unexpected, memory tracking should be disabled.";
+ }
+
+ std::stringstream ss;
+
+ ss << kNewLine;
+ ss << "TimeNow " << params->TimeInMinutesString()
+ << " (minutes):" << kNewLine << kNewLine;
+
+ ss << kSeperator << kNewLine;
+ MemoryStats memstats = GetProcessMemoryStats();
+
+ F::PrintRow(&ss, "MALLOC STAT", "IN USE BYTES", "");
+ ss << kSeperator << kNewLine;
+ F::PrintRow(&ss, "Total CPU Reserved",
+ NumberFormatWithCommas(memstats.total_cpu_memory), "");
+
+ F::PrintRow(&ss, "Total CPU Used",
+ NumberFormatWithCommas(memstats.used_cpu_memory), "");
+
+ F::PrintRow(&ss, "Total GPU Reserved",
+ NumberFormatWithCommas(memstats.total_gpu_memory), "");
+
+ F::PrintRow(&ss, "Total GPU Used",
+ NumberFormatWithCommas(memstats.used_gpu_memory), "");
+
+ ss << kSeperator << kNewLine << kNewLine;
+
+ ss << kSeperator << kNewLine;
+ F::PrintRow(&ss, "MEMORY REGION", "IN USE BYTES", "NUM ALLOCS");
+ ss << kSeperator << kNewLine;
+
+ for (MapIt it = output.begin(); it != output.end(); ++it) {
+ const AllocationGroup* group = it->second;
+ if (!group) {
+ continue;
+ }
+
+ int32 num_group_allocs = -1;
+ int64 total_group_bytes = -1;
+
+ group->GetAggregateStats(&num_group_allocs, &total_group_bytes);
+ SB_DCHECK(-1 != num_group_allocs);
+ SB_DCHECK(-1 != total_group_bytes);
+
+ cvals_map_->Update(group->name(), static_cast<size_t>(total_group_bytes));
+
+ num_allocs += num_group_allocs;
+ total_bytes += total_group_bytes;
+
+ F::PrintRow(&ss, it->first, NumberFormatWithCommas(total_group_bytes),
+ NumberFormatWithCommas(num_group_allocs));
+ }
+
+ cvals_map_->Update("Total", static_cast<size_t>(total_bytes));
+
+ ss << kNewLine;
+
+ F::PrintRow(&ss, "Total (in groups above)",
+ NumberFormatWithCommas(total_bytes),
+ NumberFormatWithCommas(num_allocs));
+
+ ss << kSeperator << kNewLine;
+ ss << kNewLine << kNewLine;
+
+ params->logger()->Output(ss.str().c_str());
+ // Output once every 5 seconds.
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
+ }
+}
+
+PrintCSVTool::PrintCSVTool(int sampling_interval_ms, int sampling_time_ms)
+ : sample_interval_ms_(sampling_interval_ms),
+ sampling_time_ms_(sampling_time_ms) {}
+
+std::string PrintCSVTool::ToCsvString(const MapAllocationSamples& samples_in) {
+ typedef MapAllocationSamples Map;
+ typedef Map::const_iterator MapIt;
+
+ size_t largest_sample_size = 0;
+ size_t smallest_sample_size = INT_MAX;
+
+ // Sanitize samples_in and store as samples.
+ MapAllocationSamples samples;
+ for (MapIt it = samples_in.begin(); it != samples_in.end(); ++it) {
+ std::string name = it->first;
+ const AllocationSamples& value = it->second;
+
+ if (value.allocated_bytes_.size() != value.number_allocations_.size()) {
+ SB_NOTREACHED() << "Error at " << __FILE__ << ":" << __LINE__;
+ return "ERROR";
+ }
+
+ const size_t n = value.allocated_bytes_.size();
+ if (n > largest_sample_size) {
+ largest_sample_size = n;
+ }
+ if (n < smallest_sample_size) {
+ smallest_sample_size = n;
+ }
+
+ const bool duplicate_found = (samples.end() != samples.find(name));
+ if (duplicate_found) {
+ SB_NOTREACHED() << "Error, duplicate found for entry: " << name
+ << kNewLine;
+ }
+ // Store value as a sanitized sample.
+ samples[name] = value;
+ }
+
+ SB_DCHECK(largest_sample_size == smallest_sample_size);
+
+ std::stringstream ss;
+
+ // Begin output to CSV.
+ // Sometimes we need to skip the CPU memory entry.
+ const MapIt total_cpu_memory_it = samples.find(UntrackedMemoryKey());
+
+ // Preamble
+ ss << kNewLine << "//////////////////////////////////////////////";
+ ss << kNewLine << "// CSV of bytes / allocation" << kNewLine;
+ // HEADER.
+ ss << "Name" << kDelimiter << kQuote << "Bytes/Alloc" << kQuote << kNewLine;
+ // DATA.
+ for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+ if (total_cpu_memory_it == it) {
+ continue;
+ }
+
+ const AllocationSamples& samples = it->second;
+ if (samples.allocated_bytes_.empty() ||
+ samples.number_allocations_.empty()) {
+ SB_NOTREACHED() << "Should not be here";
+ return "ERROR";
+ }
+ const int64 n_allocs = samples.number_allocations_.back();
+ const int64 n_bytes = samples.allocated_bytes_.back();
+ int64 bytes_per_alloc = 0;
+ if (n_allocs > 0) {
+ bytes_per_alloc = n_bytes / n_allocs;
+ }
+ const std::string& name = it->first;
+ ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter
+ << bytes_per_alloc << kNewLine;
+ }
+ ss << kNewLine;
+
+ // Preamble
+ ss << kNewLine << "//////////////////////////////////////////////" << kNewLine
+ << "// CSV of bytes allocated per region (MB's)." << kNewLine
+ << "// Units are in Megabytes. This is designed" << kNewLine
+ << "// to be used in a stacked graph." << kNewLine;
+
+ // HEADER.
+ for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+ if (total_cpu_memory_it == it) {
+ continue;
+ }
+ // Strip out any characters that could make parsing the csv difficult.
+ const std::string name = SanitizeCSVKey(it->first);
+ ss << kQuote << name << kQuote << kDelimiter;
+ }
+ // Save the total for last.
+ if (total_cpu_memory_it != samples.end()) {
+ const std::string& name = SanitizeCSVKey(total_cpu_memory_it->first);
+ ss << kQuote << name << kQuote << kDelimiter;
+ }
+ ss << kNewLine;
+
+ // Print out the values of each of the samples.
+ for (size_t i = 0; i < smallest_sample_size; ++i) {
+ for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+ if (total_cpu_memory_it == it) {
+ continue;
+ }
+ const int64 alloc_bytes = it->second.allocated_bytes_[i];
+ // Convert to float megabytes with decimals of precision.
+ double n = alloc_bytes / (1000 * 10);
+ n = n / (100.);
+ ss << n << kDelimiter;
+ }
+ if (total_cpu_memory_it != samples.end()) {
+ const int64 alloc_bytes = total_cpu_memory_it->second.allocated_bytes_[i];
+ // Convert to float megabytes with decimals of precision.
+ double n = alloc_bytes / (1000 * 10);
+ n = n / (100.);
+ ss << n << kDelimiter;
+ }
+ ss << kNewLine;
+ }
+
+ ss << kNewLine;
+ // Preamble
+ ss << kNewLine << "//////////////////////////////////////////////";
+ ss << kNewLine << "// CSV of number of allocations per region." << kNewLine;
+
+ // HEADER
+ for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+ if (total_cpu_memory_it == it) {
+ continue;
+ }
+ const std::string& name = SanitizeCSVKey(it->first);
+ ss << kQuote << name << kQuote << kDelimiter;
+ }
+ ss << kNewLine;
+ for (size_t i = 0; i < smallest_sample_size; ++i) {
+ for (MapIt it = samples.begin(); it != samples.end(); ++it) {
+ if (total_cpu_memory_it == it) {
+ continue;
+ }
+ const int64 n_allocs = it->second.number_allocations_[i];
+ ss << n_allocs << kDelimiter;
+ }
+ ss << kNewLine;
+ }
+ std::string output = ss.str();
+ return output;
+}
+
+const char* PrintCSVTool::UntrackedMemoryKey() { return "Untracked Memory"; }
+
+void PrintCSVTool::Run(Params* params) {
+ params->logger()->Output("\nMemoryTrackerPrintCSVThread is sampling...\n");
+ int sample_count = 0;
+ MapAllocationSamples map_samples;
+
+ while (!TimeExpiredYet(*params) && !params->finished()) {
+ // Sample total memory used by the system.
+ MemoryStats mem_stats = GetProcessMemoryStats();
+ int64 untracked_used_memory =
+ mem_stats.used_cpu_memory + mem_stats.used_gpu_memory;
+
+ std::vector<const AllocationGroup*> vector_output;
+ params->memory_tracker()->GetAllocationGroups(&vector_output);
+
+ // Sample all known memory scopes.
+ for (size_t i = 0; i < vector_output.size(); ++i) {
+ const AllocationGroup* group = vector_output[i];
+ const std::string& name = group->name();
+
+ const bool first_creation =
+ map_samples.find(group->name()) == map_samples.end();
+
+ AllocationSamples* new_entry = &(map_samples[name]);
+
+ // Didn't see it before so create new entry.
+ if (first_creation) {
+ // Make up for lost samples...
+ new_entry->allocated_bytes_.resize(sample_count, 0);
+ new_entry->number_allocations_.resize(sample_count, 0);
+ }
+
+ int32 num_allocs = -1;
+ int64 allocation_bytes = -1;
+ group->GetAggregateStats(&num_allocs, &allocation_bytes);
+
+ new_entry->allocated_bytes_.push_back(allocation_bytes);
+ new_entry->number_allocations_.push_back(num_allocs);
+
+ untracked_used_memory -= allocation_bytes;
+ }
+
+ // Now push in remaining total.
+ AllocationSamples* process_sample = &(map_samples[UntrackedMemoryKey()]);
+ if (untracked_used_memory < 0) {
+ // On some platforms, total GPU memory may not be correctly reported.
+ // However the allocations from the GPU memory may be reported. In this
+ // case untracked_used_memory will go negative. To protect the memory
+ // reporting the untracked_used_memory is set to 0 so that it doesn't
+ // cause an error in reporting.
+ untracked_used_memory = 0;
+ }
+ process_sample->allocated_bytes_.push_back(untracked_used_memory);
+ process_sample->number_allocations_.push_back(-1);
+
+ ++sample_count;
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds(sample_interval_ms_));
+ }
+
+ std::stringstream ss;
+ ss.precision(2);
+ ss << "Time now: " << params->TimeInMinutesString() << ",\n";
+ ss << ToCsvString(map_samples);
+ params->logger()->Output(ss.str().c_str());
+ params->logger()->Flush();
+ // Prevents the "thread exited code 0" from being interleaved into the
+ // output. This happens if flush is not implemented correctly in the system.
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
+}
+
+bool PrintCSVTool::TimeExpiredYet(const Params& params) {
+ base::TimeDelta dt = params.time_since_start();
+ int64 dt_ms = dt.InMilliseconds();
+ const bool expired_time = dt_ms > sampling_time_ms_;
+ return expired_time;
+}
+
+MemorySizeBinner::MemorySizeBinner(const std::string& memory_scope_name)
+ : memory_scope_name_(memory_scope_name) {}
+
+const AllocationGroup* FindAllocationGroup(const std::string& name,
+ MemoryTracker* memory_tracker) {
+ std::vector<const AllocationGroup*> groups;
+ memory_tracker->GetAllocationGroups(&groups);
+ // Find by exact string match.
+ for (size_t i = 0; i < groups.size(); ++i) {
+ const AllocationGroup* group = groups[i];
+ if (group->name().compare(name) == 0) {
+ return group;
+ }
+ }
+ return NULL;
+}
+
+void MemorySizeBinner::Run(Params* params) {
+ const AllocationGroup* target_group = NULL;
+
+ while (!params->finished()) {
+ if (target_group == NULL && !memory_scope_name_.empty()) {
+ target_group =
+ FindAllocationGroup(memory_scope_name_, params->memory_tracker());
+ }
+
+ std::stringstream ss;
+ ss.precision(2);
+ if (target_group || memory_scope_name_.empty()) {
+ AllocationSizeBinner visitor_binner = AllocationSizeBinner(target_group);
+ params->memory_tracker()->Accept(&visitor_binner);
+
+ size_t min_size = 0;
+ size_t max_size = 0;
+
+ visitor_binner.GetLargestSizeRange(&min_size, &max_size);
+
+ FindTopSizes top_size_visitor =
+ FindTopSizes(min_size, max_size, target_group);
+ params->memory_tracker()->Accept(&top_size_visitor);
+
+ ss << kNewLine;
+ ss << "TimeNow " << params->TimeInMinutesString() << " (minutes):";
+ ss << kNewLine;
+ if (!memory_scope_name_.empty()) {
+ ss << "Tracking Memory Scope \"" << memory_scope_name_ << "\", ";
+ } else {
+ ss << "Tracking whole program, ";
+ }
+ ss << "first row is allocation size range, second row is number of "
+ << kNewLine << "allocations in that range." << kNewLine;
+ ss << visitor_binner.ToCSVString();
+ ss << kNewLine;
+ ss << "Largest allocation range: \"" << min_size << "..." << max_size
+ << "\"" << kNewLine;
+ ss << "Printing out top allocations from this range: " << kNewLine;
+ ss << top_size_visitor.ToString(5) << kNewLine;
+ } else {
+ ss << "No allocations for \"" << memory_scope_name_ << "\".";
+ }
+
+ params->logger()->Output(ss.str().c_str());
+ params->logger()->Flush();
+
+ // Sleep until the next sample.
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
+ }
+}
+
+size_t AllocationSizeBinner::GetBucketIndexForAllocationSize(size_t size) {
+ for (int i = 0; i < 32; ++i) {
+ size_t val = 0x1 << i;
+ if (val > size) {
+ return i;
+ }
+ }
+ SB_NOTREACHED();
+ return 32;
+}
+
+void AllocationSizeBinner::GetSizeRange(size_t size, size_t* min_value,
+ size_t* max_value) {
+ size_t idx = GetBucketIndexForAllocationSize(size);
+ IndexToSizeRange(idx, min_value, max_value);
+}
+
+void AllocationSizeBinner::IndexToSizeRange(size_t idx, size_t* min_value,
+ size_t* max_value) {
+ if (idx == 0) {
+ *min_value = 0;
+ *max_value = 0;
+ return;
+ }
+ *min_value = 0x1 << (idx - 1);
+ *max_value = (*min_value << 1) - 1;
+ return;
+}
+
+size_t AllocationSizeBinner::GetIndexRepresentingMostMemoryConsumption() const {
+ int64 largest_allocation_total = 0;
+ size_t largest_allocation_total_idx = 0;
+
+ for (size_t i = 0; i < allocation_histogram_.size(); ++i) {
+ size_t alloc_size = 0x1 << i;
+ size_t count = allocation_histogram_[i];
+ int64 allocation_total =
+ static_cast<int64>(alloc_size) * static_cast<int64>(count);
+
+ if (largest_allocation_total < allocation_total) {
+ largest_allocation_total = allocation_total;
+ largest_allocation_total_idx = i;
+ }
+ }
+ return largest_allocation_total_idx;
+}
+
+void AllocationSizeBinner::GetLargestSizeRange(size_t* min_value,
+ size_t* max_value) const {
+ size_t index = GetIndexRepresentingMostMemoryConsumption();
+ IndexToSizeRange(index, min_value, max_value);
+}
+
+AllocationSizeBinner::AllocationSizeBinner(const AllocationGroup* group_filter)
+ : group_filter_(group_filter) {
+ allocation_histogram_.resize(33);
+}
+
+bool AllocationSizeBinner::PassesFilter(
+ const AllocationRecord& alloc_record) const {
+ if (group_filter_ == NULL) {
+ return true;
+ }
+
+ return alloc_record.allocation_group == group_filter_;
+}
+
+bool AllocationSizeBinner::Visit(const void* /*memory*/,
+ const AllocationRecord& alloc_record) {
+ if (PassesFilter(alloc_record)) {
+ const size_t idx = GetBucketIndexForAllocationSize(alloc_record.size);
+ allocation_histogram_[idx]++;
+ }
+ return true;
+}
+
+std::string AllocationSizeBinner::ToCSVString() const {
+ size_t first_idx = 0;
+ size_t end_idx = allocation_histogram_.size();
+
+ // Determine the start index by skipping all consecutive head entries
+ // that are 0.
+ while (first_idx < allocation_histogram_.size()) {
+ const size_t num_allocs = allocation_histogram_[first_idx];
+ if (num_allocs > 0) {
+ break;
+ }
+ first_idx++;
+ }
+
+ // Determine the end index by skipping all consecutive tail entries
+ // that are 0.
+ while (end_idx > 0) {
+ if (end_idx < allocation_histogram_.size()) {
+ const size_t num_allocs = allocation_histogram_[end_idx];
+ if (num_allocs > 0) {
+ ++end_idx;
+ break;
+ }
+ }
+ end_idx--;
+ }
+
+ std::stringstream ss;
+ for (size_t i = first_idx; i < end_idx; ++i) {
+ size_t min = 0;
+ size_t max = 0;
+ IndexToSizeRange(i, &min, &max);
+ std::stringstream name_ss;
+ name_ss << kQuote << min << "..." << max << kQuote;
+ ss << name_ss.str() << kDelimiter;
+ }
+ ss << kNewLine;
+
+ for (size_t i = first_idx; i < end_idx; ++i) {
+ const size_t num_allocs = allocation_histogram_[i];
+ ss << num_allocs << kDelimiter;
+ }
+ ss << kNewLine;
+ return ss.str();
+}
+
+FindTopSizes::FindTopSizes(size_t minimum_size, size_t maximum_size,
+ const AllocationGroup* group)
+ : minimum_size_(minimum_size),
+ maximum_size_(maximum_size),
+ group_filter_(group) {}
+
+bool FindTopSizes::Visit(const void* /*memory*/,
+ const AllocationRecord& alloc_record) {
+ if (PassesFilter(alloc_record)) {
+ size_counter_[alloc_record.size]++;
+ }
+ return true;
+}
+
+std::string FindTopSizes::ToString(size_t max_elements_to_print) const {
+ std::vector<GroupAllocation> group_allocs = GetTopAllocations();
+ const size_t n = std::min(max_elements_to_print, group_allocs.size());
+
+ if (!group_allocs.empty()) {
+ std::stringstream ss;
+
+ for (size_t i = 0; i < n; ++i) {
+ GroupAllocation g = group_allocs[i];
+ size_t total_size = g.allocation_count * g.allocation_size;
+ ss << " " << total_size
+ << " bytes allocated with object size: " << g.allocation_size
+ << " bytes in " << g.allocation_count << " instances " << kNewLine;
+ }
+ return ss.str();
+ } else {
+ return std::string();
+ }
+}
+
+std::vector<FindTopSizes::GroupAllocation> FindTopSizes::GetTopAllocations()
+ const {
+ std::vector<GroupAllocation> group_allocs;
+ // Push objects to a vector.
+ for (SizeCounterMap::const_iterator it = size_counter_.begin();
+ it != size_counter_.end(); ++it) {
+ GroupAllocation alloc = {it->first, it->second};
+ group_allocs.push_back(alloc);
+ }
+
+ std::sort(group_allocs.begin(), group_allocs.end(),
+ GroupAllocation::LessAllocationSize);
+ // Biggest first.
+ std::reverse(group_allocs.begin(), group_allocs.end());
+ return group_allocs;
+}
+
+bool FindTopSizes::PassesFilter(const AllocationRecord& alloc_record) const {
+ if (alloc_record.size < minimum_size_) return false;
+ if (alloc_record.size > maximum_size_) return false;
+ if (!group_filter_) return true; // No group filter when null.
+ return group_filter_ == alloc_record.allocation_group;
+}
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_impl.h b/src/cobalt/browser/memory_tracker/tool/tool_impl.h
new file mode 100644
index 0000000..5141d6a
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/tool_impl.h
@@ -0,0 +1,246 @@
+// 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_BROWSER_MEMORY_TRACKER_TOOL_TOOL_IMPL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_TOOL_IMPL_H_
+
+#include <deque>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/debug/stack_trace.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/simple_thread.h"
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/buffered_file_writer.h"
+#include "nb/analytics/memory_tracker.h"
+#include "nb/analytics/memory_tracker_helpers.h"
+#include "nb/concurrent_map.h"
+#include "nb/string_interner.h"
+#include "nb/thread_local_object.h"
+#include "starboard/memory_reporter.h"
+
+namespace starboard {
+class ScopedFile;
+} // namespace starboard
+
+namespace nb {
+namespace analytics {
+class AllocationGroup;
+class AllocationRecord;
+class MemoryTracker;
+} // namespace analytics
+} // namespace nb
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Interface for logging. This allows dependency inject to override in the case
+// of future tests.
+class AbstractLogger {
+ public:
+ virtual ~AbstractLogger() {}
+ virtual void Output(const char* str) = 0;
+ virtual void Flush() = 0;
+};
+
+// Params holds important data needed by the MemoryTracker tools during
+// their run cycle.
+class Params;
+
+// Base class for all tools.
+class AbstractTool {
+ public:
+ virtual ~AbstractTool() {}
+ virtual std::string tool_name() const = 0;
+ virtual void Run(Params* params) = 0;
+};
+
+// Start() is called when this object is created, and Cancel() & Join() are
+// called during destruction.
+class PrintTool : public AbstractTool {
+ public:
+ PrintTool();
+ ~PrintTool() OVERRIDE;
+
+ // Overridden so that the thread can exit gracefully.
+ virtual void Run(Params* params) OVERRIDE;
+ virtual std::string tool_name() const OVERRIDE {
+ return "MemoryTrackerPrintThread";
+ }
+
+ private:
+ class CvalsMap;
+ scoped_ptr<CvalsMap> cvals_map_;
+};
+
+// Generates CSV values of the engine.
+// There are three sections of data including:
+// 1. average bytes / alloc
+// 2. # Bytes allocated per memory scope.
+// 3. # Allocations per memory scope.
+// This data can be pasted directly into a Google spreadsheet and visualized.
+// Note that this thread will implicitly call Start() is called during
+// construction and Cancel() & Join() during destruction.
+class PrintCSVTool : public AbstractTool {
+ public:
+ // This tool will only produce on CSV dump of the engine. This is useful
+ // for profiling startup memory consumption.
+ PrintCSVTool(int sampling_interval_ms, int sampling_time_ms);
+
+ // Overridden so that the thread can exit gracefully.
+ virtual void Run(Params* params) OVERRIDE;
+ virtual std::string tool_name() const OVERRIDE {
+ return "MemoryTrackerPrintCSV";
+ }
+
+ private:
+ struct AllocationSamples {
+ std::vector<int32_t> number_allocations_;
+ std::vector<int64_t> allocated_bytes_;
+ };
+ typedef std::map<std::string, AllocationSamples> MapAllocationSamples;
+ static std::string ToCsvString(const MapAllocationSamples& samples);
+ static const char* UntrackedMemoryKey();
+ bool TimeExpiredYet(const Params& params);
+
+ const int sample_interval_ms_;
+ const int sampling_time_ms_;
+};
+
+struct AllocationSamples {
+ std::vector<int32_t> number_allocations_;
+ std::vector<int64_t> allocated_bytes_;
+};
+typedef std::map<std::string, AllocationSamples> MapAllocationSamples;
+typedef std::vector<base::TimeDelta> TimeStamps;
+
+struct TimeSeries {
+ MapAllocationSamples samples_;
+ TimeStamps time_stamps_;
+};
+
+// This tool inspects a memory scope and reports on the memory usage.
+// The output will be a CSV file printed to stdout representing
+// the number of memory allocations for objects. The objects are binned
+// according to the size of the memory allocation. Objects within the same
+// power of two are binned together. For example 1024 will be binned with 1025.
+class MemorySizeBinner : public AbstractTool {
+ public:
+ // memory_scope_name represents the memory scope that is to be investigated.
+ explicit MemorySizeBinner(const std::string& memory_scope_name);
+
+ virtual void Run(Params* params) OVERRIDE;
+ virtual std::string tool_name() const OVERRIDE {
+ return "MemoryTrackerCompressedTimeSeries";
+ }
+
+ private:
+ std::string memory_scope_name_;
+};
+
+// Bins the size according from all the encountered allocations.
+// If AllocationGroup* is non-null, then this is used as a filter such that
+// ONLY allocations belonging to that AllocationGroup are considered.
+class AllocationSizeBinner : public nb::analytics::AllocationVisitor {
+ public:
+ typedef std::vector<int> AllocationHistogram;
+ // Maps the input size to a bin number. This function performs a 2^n
+ // mapping. Here is a table of some size mappings:
+ // Example:
+ // GetIndex(0) == 0;
+ // GetIndex(1) == 1;
+ // GetIndex(2) == 2;
+ // GetIndex(3) == 2;
+ // GetIndex(4) == 3;
+ // ...
+ // GetIndex(15) == 4;
+ // GetIndex(16) == 5;
+ static size_t GetBucketIndexForAllocationSize(size_t size);
+ static void GetSizeRange(size_t size, size_t* min_value, size_t* max_value);
+ static void IndexToSizeRange(size_t size, size_t* min_value,
+ size_t* max_value);
+
+ explicit AllocationSizeBinner(
+ const nb::analytics::AllocationGroup* group_filter);
+ virtual bool Visit(
+ const void* memory,
+ const nb::analytics::AllocationRecord& alloc_record) OVERRIDE;
+
+ size_t GetIndexRepresentingMostMemoryConsumption() const;
+ void GetLargestSizeRange(size_t* min_value, size_t* max_value) const;
+ bool PassesFilter(const nb::analytics::AllocationRecord& alloc_record) const;
+ // Outputs CSV formatted string of the data values.
+ // The header containing the binning range is printed first, then the
+ // the number of allocations in that bin.
+ // Example:
+ // "16...32","32...64","64...128","128...256",...
+ // 831,3726,3432,10285,...
+ //
+ // In this example there are 831 allocations of size 16-32 bytes.
+ std::string ToCSVString() const;
+ const AllocationHistogram& allocation_histogram() const {
+ return allocation_histogram_;
+ }
+
+ private:
+ AllocationHistogram allocation_histogram_;
+ // Only these allocations are tracked.
+ const nb::analytics::AllocationGroup* group_filter_;
+};
+
+// Finds the top allocations by size.
+class FindTopSizes : public nb::analytics::AllocationVisitor {
+ public:
+ FindTopSizes(size_t minimum_size, size_t maximum_size,
+ const nb::analytics::AllocationGroup* group);
+
+ virtual bool Visit(
+ const void* memory,
+ const nb::analytics::AllocationRecord& alloc_record) OVERRIDE;
+
+ struct GroupAllocation {
+ size_t allocation_size;
+ size_t allocation_count;
+
+ static bool LessAllocationSize(GroupAllocation a, GroupAllocation b) {
+ size_t total_size_a = a.allocation_size * a.allocation_count;
+ size_t total_size_b = b.allocation_size * b.allocation_count;
+ return total_size_a < total_size_b;
+ }
+ };
+
+ std::string ToString(size_t max_elements_to_print) const;
+ std::vector<GroupAllocation> GetTopAllocations() const;
+
+ private:
+ typedef std::map<size_t, size_t> SizeCounterMap;
+
+ bool PassesFilter(const nb::analytics::AllocationRecord& alloc_record) const;
+ size_t minimum_size_;
+ size_t maximum_size_;
+ const nb::analytics::AllocationGroup* group_filter_;
+ SizeCounterMap size_counter_;
+};
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_TOOL_IMPL_H_
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool_test.cc b/src/cobalt/browser/memory_tracker/tool/tool_impl_test.cc
similarity index 92%
rename from src/cobalt/browser/memory_tracker/memory_tracker_tool_test.cc
rename to src/cobalt/browser/memory_tracker/tool/tool_impl_test.cc
index 4d1913e..1a963e9 100644
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool_test.cc
+++ b/src/cobalt/browser/memory_tracker/tool/tool_impl_test.cc
@@ -13,7 +13,7 @@
// limitations under the License.
#include "build/build_config.h" // defines OS_STARBOARD
-#include "cobalt/browser/memory_tracker/memory_tracker_tool_impl.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,7 +31,7 @@
TEST(MemoryTrackerToolTest, AllocationSizeBinner) {
const size_t index =
AllocationSizeBinner::GetBucketIndexForAllocationSize(24);
- EXPECT_EQ(5, index);
+ EXPECT_EQ(5, static_cast<int>(index));
const size_t kAllocSize = 24;
@@ -44,8 +44,8 @@
size_t min_val = 0;
size_t max_val = 0;
AllocationSizeBinner::GetSizeRange(kAllocSize, &min_val, &max_val);
- EXPECT_EQ(16, min_val);
- EXPECT_EQ(31, max_val);
+ EXPECT_EQ(16, static_cast<int>(min_val));
+ EXPECT_EQ(31, static_cast<int>(max_val));
// 0 -> [0,0]
// 1 -> [1,1]
@@ -62,8 +62,8 @@
size_t min_value = 0;
size_t max_value = 0;
binner.GetLargestSizeRange(&min_value, &max_value);
- EXPECT_EQ(min_value, 8);
- EXPECT_EQ(max_value, 15);
+ EXPECT_EQ(min_value, static_cast<int>(8));
+ EXPECT_EQ(max_value, static_cast<int>(15));
std::string csv_string = binner.ToCSVString();
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_thread.cc b/src/cobalt/browser/memory_tracker/tool/tool_thread.cc
new file mode 100644
index 0000000..23ce566
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/tool_thread.cc
@@ -0,0 +1,86 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/tool_thread.h"
+
+#include "base/time.h"
+#include "cobalt/browser/memory_tracker/tool/params.h"
+#include "cobalt/browser/memory_tracker/tool/tool_impl.h"
+#include "nb/analytics/memory_tracker.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+namespace {
+// NoMemoryTracking will disable memory tracking while in the current scope of
+// execution. When the object is destroyed it will reset the previous state
+// of allocation tracking.
+// Example:
+// void Foo() {
+// NoMemoryTracking no_memory_tracking_in_scope;
+// int* ptr = new int(); // ptr is not tracked.
+// delete ptr;
+// return; // Previous memory tracking state is restored.
+// }
+class NoMemoryTracking {
+ public:
+ explicit NoMemoryTracking(nb::analytics::MemoryTracker* owner)
+ : prev_val_(false), owner_(owner) {
+ if (owner_) {
+ prev_val_ = owner_->IsMemoryTrackingEnabled();
+ owner_->SetMemoryTrackingEnabled(false);
+ }
+ }
+ ~NoMemoryTracking() {
+ if (owner_) {
+ owner_->SetMemoryTrackingEnabled(prev_val_);
+ }
+ }
+
+ private:
+ bool prev_val_;
+ nb::analytics::MemoryTracker* owner_;
+};
+
+} // namespace.
+
+ToolThread::ToolThread(nb::analytics::MemoryTracker* memory_tracker,
+ AbstractTool* tool, AbstractLogger* logger)
+ : Super(tool->tool_name()),
+ params_(
+ new Params(memory_tracker, logger, base::Time::NowFromSystemTime())),
+ tool_(tool) {
+ Start();
+}
+
+ToolThread::~ToolThread() {
+ Join();
+ tool_.reset();
+ params_.reset();
+}
+
+void ToolThread::Join() {
+ params_->set_finished(true);
+ Super::Join();
+}
+
+void ToolThread::Run() {
+ NoMemoryTracking no_mem_tracking_in_this_scope(params_->memory_tracker());
+ // This tool will run until the finished_ if flipped to false.
+ tool_->Run(params_.get());
+}
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_thread.h b/src/cobalt/browser/memory_tracker/tool/tool_thread.h
new file mode 100644
index 0000000..503248a
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/tool_thread.h
@@ -0,0 +1,56 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_TOOL_THREAD_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_TOOL_THREAD_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/simple_thread.h"
+
+namespace nb {
+namespace analytics {
+class MemoryTracker;
+} // namespace analytics
+} // namespace nb
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+class AbstractLogger;
+class AbstractTool;
+class Params;
+
+// ToolThread sets up the the thread environment to run an AbstractTool.
+class ToolThread : public base::SimpleThread {
+ public:
+ typedef base::SimpleThread Super;
+ ToolThread(nb::analytics::MemoryTracker* memory_tracker, AbstractTool* tool,
+ AbstractLogger* logger);
+ virtual ~ToolThread();
+
+ virtual void Join() OVERRIDE;
+ virtual void Run() OVERRIDE;
+
+ private:
+ scoped_ptr<Params> params_;
+ scoped_ptr<AbstractTool> tool_;
+};
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_TOOL_THREAD_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/util.cc b/src/cobalt/browser/memory_tracker/tool/util.cc
new file mode 100644
index 0000000..09068cb
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/util.cc
@@ -0,0 +1,136 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/util.h"
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "base/time.h"
+#include "starboard/string.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+const char kQuote[] = "\"";
+const char kDelimiter[] = ",";
+const char kNewLine[] = "\n";
+
+std::string RemoveString(const std::string& haystack, const char* needle) {
+ const size_t kNotFound = std::string::npos;
+
+ // Base case. No modification needed.
+ size_t pos = haystack.find(needle);
+ if (pos == kNotFound) {
+ return haystack;
+ }
+ const size_t n = SbStringGetLength(needle);
+ std::string output;
+ output.reserve(haystack.size());
+
+ // Copy string, omitting the portion containing the "needle".
+ std::copy(haystack.begin(), haystack.begin() + pos,
+ std::back_inserter(output));
+ std::copy(haystack.begin() + pos + n, haystack.end(),
+ std::back_inserter(output));
+
+ // Recursively remove same needle in haystack.
+ return RemoveString(output, needle);
+}
+
+std::string SanitizeCSVKey(std::string key) {
+ key = RemoveString(key, kQuote);
+ key = RemoveString(key, kDelimiter);
+ key = RemoveString(key, kNewLine);
+ return key;
+}
+
+std::string InsertCommasIntoNumberString(const std::string& input) {
+ typedef std::vector<char> CharVector;
+ typedef CharVector::iterator CharIt;
+
+ CharVector chars(input.begin(), input.end());
+ std::reverse(chars.begin(), chars.end());
+
+ CharIt curr_it = chars.begin();
+ CharIt mid = std::find(chars.begin(), chars.end(), '.');
+ if (mid == chars.end()) {
+ mid = curr_it;
+ }
+
+ CharVector out(curr_it, mid);
+
+ int counter = 0;
+ for (CharIt it = mid; it != chars.end(); ++it) {
+ if (counter != 0 && (counter % 3 == 0)) {
+ out.push_back(',');
+ }
+ if (*it != '.') {
+ counter++;
+ }
+ out.push_back(*it);
+ }
+
+ std::reverse(out.begin(), out.end());
+ std::stringstream ss;
+ for (size_t i = 0; i < out.size(); ++i) {
+ ss << out[i];
+ }
+ return ss.str();
+}
+
+Timer::Timer(base::TimeDelta dt)
+ : start_time_(base::TimeTicks::Now()), time_before_expiration_(dt) {}
+
+void Timer::Restart() { start_time_ = base::TimeTicks::Now(); }
+
+bool Timer::UpdateAndIsExpired() {
+ base::TimeTicks now_time = base::TimeTicks::Now();
+ base::TimeDelta dt = now_time - start_time_;
+ if (dt > time_before_expiration_) {
+ start_time_ = now_time;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+const char* BaseNameFast(const char* file_name) {
+ // Case: Linux.
+ const char* end_pos = file_name + SbStringGetLength(file_name);
+ const char* last_forward_slash = SbStringFindLastCharacter(file_name, '/');
+ if (last_forward_slash) {
+ if (end_pos != last_forward_slash) {
+ ++last_forward_slash;
+ }
+ return last_forward_slash;
+ }
+
+ // Case: Windows.
+ const char* last_backward_slash = SbStringFindLastCharacter(file_name, '\\');
+ if (last_backward_slash) {
+ if (end_pos != last_backward_slash) {
+ ++last_backward_slash;
+ }
+ return last_backward_slash;
+ }
+ return file_name;
+}
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/memory_tracker/tool/util.h b/src/cobalt/browser/memory_tracker/tool/util.h
new file mode 100644
index 0000000..3d44f6a
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/util.h
@@ -0,0 +1,153 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_
+#define COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_
+
+#include <sstream>
+#include <string>
+
+#include "base/time.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Used for CSV generation.
+extern const char kQuote[];
+extern const char kDelimiter[];
+extern const char kNewLine[];
+
+// This is a simple algorithm to remove the "needle" from the haystack. Note
+// that this function is simple and not well optimized.
+std::string RemoveString(const std::string& haystack, const char* needle);
+
+// Not optimized but works ok for a tool that dumps out in user time.
+std::string SanitizeCSVKey(std::string key);
+
+// Converts "2345.54" => "2,345.54".
+std::string InsertCommasIntoNumberString(const std::string& input);
+
+template <typename T>
+std::string NumberFormatWithCommas(T val) {
+ // Convert value to string.
+ std::stringstream ss;
+ ss << val;
+ std::string s = InsertCommasIntoNumberString(ss.str());
+ return s;
+}
+
+// Removes odd elements and resizes vector.
+template <typename VectorType>
+void RemoveOddElements(VectorType* v) {
+ typedef typename VectorType::iterator iterator;
+
+ iterator read_it = v->end();
+ iterator write_it = v->end();
+ for (size_t i = 0; i * 2 < v->size(); ++i) {
+ write_it = v->begin() + i;
+ read_it = v->begin() + (i * 2);
+ *write_it = *read_it;
+ }
+ if (write_it != v->end()) {
+ write_it++;
+ }
+ v->erase(write_it, v->end());
+}
+
+// Simple timer class that fires periodically after dt time has elapsed.
+class Timer {
+ public:
+ explicit Timer(base::TimeDelta dt);
+ void Restart();
+ bool UpdateAndIsExpired(); // Returns true if the expiration was triggered.
+
+ private:
+ base::TimeTicks start_time_;
+ base::TimeDelta time_before_expiration_;
+};
+
+// Generates a linear fit in the form of slope / y-intercept form.
+// Returns true if linear fit was calculated. False otherwise. Reasons for
+// returning false include passing in an empty range, such that
+// begin_it == end_it, or all x-values being the same.
+//
+// Algorithm adapted from:
+// https://en.wikipedia.org/wiki/Simple_linear_regression#Fitting_the_regression_line
+// Example:
+// std::vector<std::pair<int, int> > data;
+// for (int i = 0; i < 10; ++i) {
+// data.push_back(std::pair<int, int>(i+1, 2*i));
+// }
+// double slope = 0;
+// double y_intercept = 0;
+// GetLinearFit(data.begin(), data.end(), &slope, &y_intercept);
+// std::cout << "slope: " << slope << "\n";
+// std::cout << "y_intercept: " << y_intercept<< "\n";
+template <typename PairIterator>
+bool GetLinearFit(PairIterator begin_it, PairIterator end_it, double* out_slope,
+ double* out_yintercept) {
+ if (begin_it == end_it) {
+ return false;
+ }
+
+ size_t n = 0;
+ double x_avg = 0;
+ double y_avg = 0;
+
+ for (PairIterator it = begin_it; it != end_it; ++it) {
+ x_avg += it->first;
+ y_avg += it->second;
+ n++;
+ }
+
+ x_avg /= n;
+ y_avg /= n;
+
+ double numerator = 0;
+ double denominator = 0;
+
+ for (PairIterator it = begin_it; it != end_it; ++it) {
+ double x_variance = it->first - x_avg;
+ double y_variance = it->second - y_avg;
+ numerator += (x_variance * y_variance);
+ denominator += (x_variance * x_variance);
+ }
+
+ if (denominator == 0.0) {
+ return false; // Failed to generate any value.
+ }
+
+ double slope = numerator / denominator;
+ double yintercept = y_avg - slope * x_avg;
+
+ *out_slope = slope;
+ *out_yintercept = yintercept;
+ return true;
+}
+
+// Returns a substring with the directory path removed from the filename.
+// Example:
+// F::BaseNameFast("directory/filename.cc") => "filename.cc"
+// F::BaseNameFast("directory\filename.cc") => "filename.cc"
+//
+// Note that base::FilePath::BaseName() isn't used because of performance
+// reasons.
+const char* BaseNameFast(const char* file_name);
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_MEMORY_TRACKER_TOOL_UTIL_H_
diff --git a/src/cobalt/browser/memory_tracker/tool/util_test.cc b/src/cobalt/browser/memory_tracker/tool/util_test.cc
new file mode 100644
index 0000000..07e9d63
--- /dev/null
+++ b/src/cobalt/browser/memory_tracker/tool/util_test.cc
@@ -0,0 +1,112 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/browser/memory_tracker/tool/util.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace browser {
+namespace memory_tracker {
+
+// Tests the expectation that AllocationSizeBinner will correctly bin
+// allocations.
+TEST(MemoryTrackerUtilTest, RemoveString) {
+ std::string value = "abba";
+ value = RemoveString(value, "bb");
+ EXPECT_STREQ("aa", value.c_str());
+}
+
+// Tests the expectation that AllocationSizeBinner will correctly bin
+// allocations.
+TEST(MemoryTrackerUtilTest, InsertCommasIntoNumberString) {
+ std::string value = "2345.54";
+ std::string value_with_commas = InsertCommasIntoNumberString(value);
+
+ EXPECT_STREQ("2,345.54", value_with_commas.c_str());
+}
+
+TEST(MemoryTrackerUtilTest, NumberFormatWithCommas) {
+ int value = 1000;
+ std::string value_with_commas = NumberFormatWithCommas<int>(value);
+
+ EXPECT_STREQ("1,000", value_with_commas.c_str());
+}
+
+// Tests the expectation that RemoveOddElements() removes the odd elements of
+// a vector and resizes it.
+TEST(MemoryTrackerUtilTest, RemoveOddElements) {
+ std::vector<int> values;
+
+ // EVEN TEST.
+ values.push_back(0);
+ values.push_back(1);
+ values.push_back(2);
+ values.push_back(3);
+
+ RemoveOddElements(&values);
+
+ ASSERT_EQ(2, values.size());
+ EXPECT_EQ(0, values[0]);
+ EXPECT_EQ(2, values[1]);
+
+ values.clear();
+
+ // ODD TEST
+ values.push_back(0);
+ values.push_back(1);
+ values.push_back(2);
+ values.push_back(3);
+ values.push_back(4);
+ RemoveOddElements(&values);
+
+ ASSERT_EQ(3, values.size());
+ EXPECT_EQ(0, values[0]);
+ EXPECT_EQ(2, values[1]);
+ EXPECT_EQ(4, values[2]);
+}
+
+// Tests the expectation that GetLinearFit() generates the expected linear
+// regression values.
+TEST(MemoryTrackerUtilTest, GetLinearFit) {
+ std::vector<std::pair<int, int> > data;
+ for (int i = 0; i < 10; ++i) {
+ data.push_back(std::pair<int, int>(i+1, 2*i));
+ }
+ double slope = 0;
+ double y_intercept = 0;
+ GetLinearFit(data.begin(), data.end(), &slope, &y_intercept);
+
+ EXPECT_DOUBLE_EQ(2.0f, slope);
+ EXPECT_DOUBLE_EQ(-2.0f, y_intercept);
+}
+
+// Test the expectation that BaseNameFast() works correctly for both windows
+// and linux path types.
+TEST(MemoryTrackerUtilTest, BaseNameFast) {
+ const char* linux_path = "directory/filename.cc";
+ const char* win_path = "directory\\filename.cc";
+
+ EXPECT_STREQ("filename.cc", BaseNameFast(linux_path));
+ EXPECT_STREQ("filename.cc", BaseNameFast(win_path));
+}
+
+} // namespace memory_tracker
+} // namespace browser
+} // namespace cobalt
diff --git a/src/cobalt/browser/resource_provider_array_buffer_allocator.cc b/src/cobalt/browser/resource_provider_array_buffer_allocator.cc
index 4712185..7f2ee02 100644
--- a/src/cobalt/browser/resource_provider_array_buffer_allocator.cc
+++ b/src/cobalt/browser/resource_provider_array_buffer_allocator.cc
@@ -14,6 +14,8 @@
#include "cobalt/browser/resource_provider_array_buffer_allocator.h"
+#include "starboard/common/scoped_ptr.h"
+
namespace cobalt {
namespace browser {
@@ -24,9 +26,9 @@
DCHECK(gpu_memory_buffer_space_);
DCHECK(gpu_memory_buffer_space_->GetMemory());
- gpu_memory_pool_.reset(new nb::MemoryPool(
- gpu_memory_buffer_space_->GetMemory(),
- gpu_memory_buffer_space_->GetSizeInBytes(), true /* thread_safe */));
+ gpu_memory_pool_.set(starboard::make_scoped_ptr(
+ new nb::MemoryPool(gpu_memory_buffer_space_->GetMemory(),
+ gpu_memory_buffer_space_->GetSizeInBytes())));
}
void* ResourceProviderArrayBufferAllocator::Allocate(size_t size) {
diff --git a/src/cobalt/browser/resource_provider_array_buffer_allocator.h b/src/cobalt/browser/resource_provider_array_buffer_allocator.h
index d4368c9..c9de006 100644
--- a/src/cobalt/browser/resource_provider_array_buffer_allocator.h
+++ b/src/cobalt/browser/resource_provider_array_buffer_allocator.h
@@ -15,10 +15,10 @@
#ifndef COBALT_BROWSER_RESOURCE_PROVIDER_ARRAY_BUFFER_ALLOCATOR_H_
#define COBALT_BROWSER_RESOURCE_PROVIDER_ARRAY_BUFFER_ALLOCATOR_H_
-#include "base/memory/scoped_ptr.h"
#include "cobalt/dom/array_buffer.h"
#include "cobalt/render_tree/resource_provider.h"
#include "nb/memory_pool.h"
+#include "starboard/common/locked_ptr.h"
namespace cobalt {
namespace browser {
@@ -37,7 +37,7 @@
void Free(void* p) OVERRIDE;
scoped_ptr<render_tree::RawImageMemory> gpu_memory_buffer_space_;
- scoped_ptr<nb::MemoryPool> gpu_memory_pool_;
+ starboard::LockedPtr<nb::MemoryPool> gpu_memory_pool_;
};
} // namespace browser
diff --git a/src/cobalt/browser/splash_screen.cc b/src/cobalt/browser/splash_screen.cc
index ce8a580..ae60aa5 100644
--- a/src/cobalt/browser/splash_screen.cc
+++ b/src/cobalt/browser/splash_screen.cc
@@ -24,27 +24,31 @@
const char SplashScreen::Options::kDefaultSplashScreenURL[] =
"h5vcc-embedded://splash_screen.html";
-SplashScreen::SplashScreen(
- const WebModule::OnRenderTreeProducedCallback&
- render_tree_produced_callback,
- network::NetworkModule* network_module, const math::Size& window_dimensions,
- render_tree::ResourceProvider* resource_provider, float layout_refresh_rate,
- const SplashScreen::Options& options)
- : render_tree_produced_callback_(render_tree_produced_callback)
- , is_ready_(true, false) {
- WebModule::Options web_module_options(window_dimensions);
+SplashScreen::SplashScreen(const WebModule::OnRenderTreeProducedCallback&
+ render_tree_produced_callback,
+ network::NetworkModule* network_module,
+ const math::Size& window_dimensions,
+ render_tree::ResourceProvider* resource_provider,
+ float layout_refresh_rate,
+ const SplashScreen::Options& options)
+ : render_tree_produced_callback_(render_tree_produced_callback),
+ is_ready_(true, false) {
+ WebModule::Options web_module_options;
web_module_options.name = "SplashScreenWebModule";
// We want the splash screen to load and appear as quickly as possible, so
// we set it and its image decoding thread to be high priority.
web_module_options.thread_priority = base::kThreadPriority_High;
web_module_options.loader_thread_priority = base::kThreadPriority_High;
+ web_module_options.animated_image_decode_thread_priority =
+ base::kThreadPriority_High;
web_module_.reset(new WebModule(
options.url,
base::Bind(&SplashScreen::OnRenderTreeProduced, base::Unretained(this)),
base::Bind(&SplashScreen::OnError, base::Unretained(this)),
base::Bind(&SplashScreen::OnWindowClosed, base::Unretained(this)),
+ base::Closure(), // window_minimize_callback
&stub_media_module_, network_module, window_dimensions, resource_provider,
stub_media_module_.system_window(), layout_refresh_rate,
web_module_options));
diff --git a/src/cobalt/browser/starboard/event_handler.cc b/src/cobalt/browser/starboard/event_handler.cc
index 6888ca0..a8ad309 100644
--- a/src/cobalt/browser/starboard/event_handler.cc
+++ b/src/cobalt/browser/starboard/event_handler.cc
@@ -16,6 +16,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "cobalt/base/accessibility_settings_changed_event.h"
#include "cobalt/base/deep_link_event.h"
#include "cobalt/network/network_event.h"
#include "cobalt/system_window/application_event.h"
@@ -49,38 +50,50 @@
g_the_event_handler->DispatchEvent(starboard_event);
}
+void EventHandler::DispatchEventInternal(base::Event* event) const {
+ event_dispatcher_->DispatchEvent(make_scoped_ptr<base::Event>(event));
+}
+
void EventHandler::DispatchEvent(const SbEvent* starboard_event) const {
// Create a Cobalt event from the Starboard event, if recognized.
- scoped_ptr<base::Event> cobalt_event;
- if (starboard_event->type == kSbEventTypePause) {
- cobalt_event.reset(new system_window::ApplicationEvent(
- system_window::ApplicationEvent::kPause));
- } else if (starboard_event->type == kSbEventTypeUnpause) {
- cobalt_event.reset(new system_window::ApplicationEvent(
- system_window::ApplicationEvent::kUnpause));
- } else if (starboard_event->type == kSbEventTypeSuspend) {
- cobalt_event.reset(new system_window::ApplicationEvent(
- system_window::ApplicationEvent::kSuspend));
- } else if (starboard_event->type == kSbEventTypeResume) {
- cobalt_event.reset(new system_window::ApplicationEvent(
- system_window::ApplicationEvent::kResume));
- } else if (starboard_event->type == kSbEventTypeNetworkConnect) {
- cobalt_event.reset(
- new network::NetworkEvent(network::NetworkEvent::kConnection));
- } else if (starboard_event->type == kSbEventTypeNetworkDisconnect) {
- cobalt_event.reset(
- new network::NetworkEvent(network::NetworkEvent::kDisconnection));
- } else if (starboard_event->type == kSbEventTypeLink) {
- const char* link = static_cast<const char*>(starboard_event->data);
- cobalt_event.reset(new base::DeepLinkEvent(link));
- }
-
- // Dispatch the Cobalt event, if created.
- if (cobalt_event) {
- event_dispatcher_->DispatchEvent(cobalt_event.Pass());
- } else {
- DLOG(WARNING) << "Unhandled Starboard event of type: "
- << starboard_event->type;
+ switch (starboard_event->type) {
+ case kSbEventTypePause:
+ DispatchEventInternal(new system_window::ApplicationEvent(
+ system_window::ApplicationEvent::kPause));
+ break;
+ case kSbEventTypeUnpause:
+ DispatchEventInternal(new system_window::ApplicationEvent(
+ system_window::ApplicationEvent::kUnpause));
+ break;
+ case kSbEventTypeSuspend:
+ DispatchEventInternal(new system_window::ApplicationEvent(
+ system_window::ApplicationEvent::kSuspend));
+ break;
+ case kSbEventTypeResume:
+ DispatchEventInternal(new system_window::ApplicationEvent(
+ system_window::ApplicationEvent::kResume));
+ break;
+ case kSbEventTypeNetworkConnect:
+ DispatchEventInternal(
+ new network::NetworkEvent(network::NetworkEvent::kConnection));
+ break;
+ case kSbEventTypeNetworkDisconnect:
+ DispatchEventInternal(
+ new network::NetworkEvent(network::NetworkEvent::kDisconnection));
+ break;
+ case kSbEventTypeLink: {
+ const char* link = static_cast<const char*>(starboard_event->data);
+ DispatchEventInternal(new base::DeepLinkEvent(link));
+ break;
+ }
+#if SB_API_VERSION >= 4
+ case kSbEventTypeAccessiblitySettingsChanged:
+ DispatchEventInternal(new base::AccessibilitySettingsChangedEvent());
+ break;
+#endif // SB_API_VERSION >= 4
+ default:
+ DLOG(WARNING) << "Unhandled Starboard event of type: "
+ << starboard_event->type;
}
}
diff --git a/src/cobalt/browser/starboard/event_handler.h b/src/cobalt/browser/starboard/event_handler.h
index dddb0f1..13391d2 100644
--- a/src/cobalt/browser/starboard/event_handler.h
+++ b/src/cobalt/browser/starboard/event_handler.h
@@ -35,6 +35,8 @@
// of the system via |event_dispatcher_|.
void DispatchEvent(const SbEvent* event) const;
+ void DispatchEventInternal(base::Event*) const;
+
// The event dispatcher that dispatches Cobalt events to the rest of the
// system.
base::EventDispatcher* event_dispatcher_;
diff --git a/src/cobalt/browser/storage_upgrade_handler.cc b/src/cobalt/browser/storage_upgrade_handler.cc
index b40acf6..9be2cda 100644
--- a/src/cobalt/browser/storage_upgrade_handler.cc
+++ b/src/cobalt/browser/storage_upgrade_handler.cc
@@ -42,7 +42,8 @@
void StorageUpgradeHandler::OnUpgrade(storage::StorageManager* storage,
const char* data, int size) {
- storage::upgrade::UpgradeReader upgrade_reader(data, size);
+ storage::upgrade::UpgradeReader upgrade_reader;
+ upgrade_reader.Parse(data, size);
int num_cookies = upgrade_reader.GetNumCookies();
int num_local_storage_entries = upgrade_reader.GetNumLocalStorageEntries();
DLOG(INFO) << "Upgrading legacy save data: " << num_cookies << " cookies, "
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index e894b9e..59d9850 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -161,6 +161,12 @@
// Specifies that javascript jit should be disabled.
const char kDisableJavaScriptJit[] = "disable_javascript_jit";
+// Specifies the javascript gc threshold. When this amount of garbage has
+// collected then the garbage collector will begin running.
+const char kJavaScriptGcThresholdInBytes[] = "javascript_gc_threshold_in_bytes";
+
+const char kSkiaTextureAtlasDimensions[] = "skia_atlas_texture_dimensions";
+
} // namespace switches
} // namespace browser
} // namespace cobalt
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index faeaf51..6b30b2b 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -56,6 +56,8 @@
extern const char kSurfaceCacheSizeInBytes[];
extern const char kViewport[];
extern const char kDisableJavaScriptJit[];
+extern const char kJavaScriptGcThresholdInBytes[];
+extern const char kSkiaTextureAtlasDimensions[];
} // namespace switches
} // namespace browser
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 0354f78..f9cfc51 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -23,14 +23,9 @@
#include "base/message_loop_proxy.h"
#include "base/optional.h"
#include "base/stringprintf.h"
-#include "cobalt/accessibility/screen_reader.h"
-#include "cobalt/accessibility/starboard_tts_engine.h"
-#include "cobalt/accessibility/tts_engine.h"
-#include "cobalt/accessibility/tts_logger.h"
#include "cobalt/base/c_val.h"
#include "cobalt/base/poller.h"
#include "cobalt/base/tokens.h"
-#include "cobalt/browser/memory_settings/memory_settings.h"
#include "cobalt/browser/stack_size_constants.h"
#include "cobalt/browser/switches.h"
#include "cobalt/browser/web_module_stat_tracker.h"
@@ -45,6 +40,8 @@
#include "cobalt/dom/url.h"
#include "cobalt/dom_parser/parser.h"
#include "cobalt/h5vcc/h5vcc.h"
+#include "cobalt/loader/image/animated_image_tracker.h"
+#include "cobalt/media_session/media_session_client.h"
#include "cobalt/script/javascript_engine.h"
#include "cobalt/storage/storage_manager.h"
#include "cobalt/system_window/system_window.h"
@@ -111,28 +108,6 @@
base::CVal<base::cval::SizeInBytes, base::CValPublic> js_reserved_memory_;
};
-#if SB_HAS(SPEECH_SYNTHESIS)
-bool IsTextToSpeechEnabled() {
-#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
- // Check for a command-line override to enable TTS.
- CommandLine* command_line = CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(browser::switches::kUseTTS)) {
- return true;
- }
-#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
- // Check if the tts feature is enabled in Starboard.
- SbAccessibilityTextToSpeechSettings tts_settings = {0};
- // Check platform settings.
- if (SbAccessibilityGetTextToSpeechSettings(&tts_settings)) {
- return tts_settings.has_text_to_speech_setting &&
- tts_settings.is_text_to_speech_enabled;
- }
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
- return false;
-}
-#endif // SB_HAS(SPEECH_SYNTHESIS)
-
// StartupTimer is designed to measure time since the startup of the app.
// It is loader initialized to have the most accurate start time as possible.
class StartupTimer {
@@ -177,7 +152,8 @@
// output parameter and signals |got_result|.
void ExecuteJavascript(const std::string& script_utf8,
const base::SourceLocation& script_location,
- base::WaitableEvent* got_result, std::string* result);
+ base::WaitableEvent* got_result, std::string* result,
+ bool *out_succeeded);
// Clears disables timer related objects
// so that the message loop can easily exit
@@ -212,6 +188,12 @@
private:
class DocumentLoadedObserver;
+ // Purge all resource caches owned by the WebModule.
+ void PurgeResourceCaches();
+
+ // Disable callbacks in all resource caches owned by the WebModule.
+ void DisableCallbacksInResourceCaches();
+
// Injects a list of custom window attributes into the WebModule's window
// object.
void InjectCustomWindowAttributes(
@@ -268,6 +250,8 @@
// URL.
scoped_ptr<loader::LoaderFactory> loader_factory_;
+ scoped_ptr<loader::image::AnimatedImageTracker> animated_image_tracker_;
+
// ImageCache that is used to manage image cache logic.
scoped_ptr<loader::image::ImageCache> image_cache_;
@@ -337,12 +321,6 @@
// Triggers layout whenever the document changes.
scoped_ptr<layout::LayoutManager> layout_manager_;
- // TTSEngine that the ScreenReader speaks to.
- scoped_ptr<accessibility::TTSEngine> tts_engine_;
-
- // Utters the text contents of the focused element.
- scoped_ptr<accessibility::ScreenReader> screen_reader_;
-
#if defined(ENABLE_DEBUG_CONSOLE)
// Allows the debugger to add render components to the web module.
// Used for DOM node highlighting and overlay messages.
@@ -362,6 +340,8 @@
scoped_ptr<base::ConsoleCommandManager::CommandHandler>
partial_layout_command_handler_;
#endif // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
+
+ scoped_ptr<media_session::MediaSessionClient> media_session_client_;
};
class WebModule::Impl::DocumentLoadedObserver : public dom::DocumentObserver {
@@ -427,6 +407,9 @@
new loader::LoaderFactory(fetcher_factory_.get(), resource_provider_,
data.options.loader_thread_priority));
+ animated_image_tracker_.reset(new loader::image::AnimatedImageTracker(
+ data.options.animated_image_decode_thread_priority));
+
DCHECK_LE(0, data.options.image_cache_capacity);
image_cache_ = loader::image::CreateImageCache(
base::StringPrintf("%s.ImageCache", name_.c_str()),
@@ -461,7 +444,8 @@
new browser::WebModuleStatTracker(name_, data.options.track_event_stats));
DCHECK(web_module_stat_tracker_);
- javascript_engine_ = script::JavaScriptEngine::CreateEngine();
+ javascript_engine_ =
+ script::JavaScriptEngine::CreateEngine(data.options.javascript_options);
DCHECK(javascript_engine_);
#if defined(COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING)
@@ -475,8 +459,7 @@
base::Unretained(this)),
base::TimeDelta::FromMilliseconds(kPollerPeriodMs)));
- global_environment_ = javascript_engine_->CreateGlobalEnvironment(
- data.options.javascript_options);
+ global_environment_ = javascript_engine_->CreateGlobalEnvironment();
DCHECK(global_environment_);
execution_state_ =
@@ -489,14 +472,16 @@
media_source_registry_.reset(new dom::MediaSource::Registry);
+ media_session_client_ = media_session::MediaSessionClient::Create();
+
window_ = new dom::Window(
data.window_dimensions.width(), data.window_dimensions.height(),
css_parser_.get(), dom_parser_.get(), fetcher_factory_.get(),
- &resource_provider_, image_cache_.get(),
+ &resource_provider_, animated_image_tracker_.get(), image_cache_.get(),
reduced_image_cache_capacity_manager_.get(), remote_typeface_cache_.get(),
mesh_cache_.get(), local_storage_database_.get(), data.media_module,
data.media_module, execution_state_.get(), script_runner_.get(),
- media_source_registry_.get(),
+ global_environment_->script_value_factory(), media_source_registry_.get(),
web_module_stat_tracker_->dom_stat_tracker(), data.initial_url,
data.network_module->GetUserAgent(),
data.network_module->preferred_language(),
@@ -507,9 +492,10 @@
base::Bind(&WebModule::Impl::OnCspPolicyChanged, base::Unretained(this)),
base::Bind(&WebModule::Impl::OnRanAnimationFrameCallbacks,
base::Unretained(this)),
- data.window_close_callback, data.system_window_,
- data.options.input_poller, data.options.csp_insecure_allowed_token,
- data.dom_max_element_depth);
+ data.window_close_callback, data.window_minimize_callback,
+ data.system_window_, data.options.input_poller,
+ media_session_client_->GetMediaSession(),
+ data.options.csp_insecure_allowed_token, data.dom_max_element_depth);
DCHECK(window_);
window_weak_ = base::AsWeakPtr(window_.get());
@@ -566,26 +552,6 @@
window_->document()->AddObserver(document_load_observer_.get());
}
- // If a TTSEngine was provided through the options, use it.
- accessibility::TTSEngine* tts_engine = data.options.tts_engine;
- if (!tts_engine) {
-#if SB_HAS(SPEECH_SYNTHESIS)
- if (IsTextToSpeechEnabled()) {
- // Create a StarboardTTSEngine if TTS is enabled.
- tts_engine_.reset(new accessibility::StarboardTTSEngine());
- }
-#endif // SB_HAS(SPEECH_SYNTHESIS)
-#if !defined(COBALT_BUILD_TYPE_GOLD)
- if (!tts_engine_) {
- tts_engine_.reset(new accessibility::TTSLogger());
- }
-#endif // !defined(COBALT_BUILD_TYPE_GOLD)
- tts_engine = tts_engine_.get();
- }
- if (tts_engine) {
- screen_reader_.reset(new accessibility::ScreenReader(
- window_->document(), tts_engine, &mutation_observer_task_manager_));
- }
is_running_ = true;
}
@@ -596,6 +562,7 @@
global_environment_->SetReportEvalCallback(base::Closure());
window_->DispatchEvent(new dom::Event(base::Tokens::unload()));
document_load_observer_.reset();
+ media_session_client_.reset();
#if defined(ENABLE_DEBUG_CONSOLE)
debug_overlay_.reset();
@@ -606,10 +573,8 @@
// callback to occur into a DOM object that is being kept alive by a JS engine
// reference even after the DOM tree has been destroyed. This can result in a
// crash when the callback attempts to access a stale Document pointer.
- remote_typeface_cache_->DisableCallbacks();
- image_cache_->DisableCallbacks();
+ DisableCallbacksInResourceCaches();
- screen_reader_.reset();
layout_manager_.reset();
environment_settings_.reset();
window_weak_.reset();
@@ -625,6 +590,7 @@
local_storage_database_.reset();
mesh_cache_.reset();
remote_typeface_cache_.reset();
+ animated_image_tracker_.reset();
image_cache_.reset();
fetcher_factory_.reset();
dom_parser_.reset();
@@ -658,11 +624,12 @@
void WebModule::Impl::ExecuteJavascript(
const std::string& script_utf8, const base::SourceLocation& script_location,
- base::WaitableEvent* got_result, std::string* result) {
+ base::WaitableEvent* got_result, std::string* result, bool* out_succeeded) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(is_running_);
DCHECK(script_runner_);
- *result = script_runner_->Execute(script_utf8, script_location);
+ *result = script_runner_->Execute(script_utf8, script_location,
+ out_succeeded);
got_result->Signal();
}
@@ -768,16 +735,27 @@
for (Options::InjectedWindowAttributes::const_iterator iter =
attributes.begin();
iter != attributes.end(); ++iter) {
- global_environment_->Bind(iter->first, iter->second.Run());
+ global_environment_->Bind(
+ iter->first,
+ iter->second.Run(window_, &mutation_observer_task_manager_));
}
}
void WebModule::Impl::SuspendLoaders() {
TRACE_EVENT0("cobalt::browser", "WebModule::Impl::SuspendLoaders()");
+ // Purge the resource caches before running any suspend logic. This will force
+ // any pending callbacks that the caches are batching to run.
+ PurgeResourceCaches();
+
// Stop the generation of render trees.
layout_manager_->Suspend();
+ // Purge the cached resources prior to the suspend. That may cancel pending
+ // loads, allowing the suspend to occur faster and preventing unnecessary
+ // callbacks.
+ window_->document()->PurgeCachedResources();
+
// Clear out the loader factory's resource provider, possibly aborting any
// in-progress loads.
loader_factory_->Suspend();
@@ -789,14 +767,12 @@
// Ensure the document is not holding onto any more image cached resources so
// that they are eligible to be purged.
- window_->document()->PurgeCachedResourceReferencesRecursively();
+ window_->document()->PurgeCachedResources();
- // Clear out all image resources from the image cache. We need to do this
- // after we abort all in-progress loads, and after we clear all document
- // references, or they will still be referenced and won't be cleared from the
- // cache.
- image_cache_->Purge();
- remote_typeface_cache_->Purge();
+ // Clear out all resource caches. We need to do this after we abort all
+ // in-progress loads, and after we clear all document references, or they will
+ // still be referenced and won't be cleared from the cache.
+ PurgeResourceCaches();
#if defined(ENABLE_DEBUG_CONSOLE)
// The debug overlay may be holding onto a render tree, clear that out.
@@ -841,6 +817,18 @@
SbLogRaw(ss.str().c_str());
}
+void WebModule::Impl::PurgeResourceCaches() {
+ image_cache_->Purge();
+ remote_typeface_cache_->Purge();
+ mesh_cache_->Purge();
+}
+
+void WebModule::Impl::DisableCallbacksInResourceCaches() {
+ image_cache_->DisableCallbacks();
+ remote_typeface_cache_->DisableCallbacks();
+ mesh_cache_->DisableCallbacks();
+}
+
WebModule::DestructionObserver::DestructionObserver(WebModule* web_module)
: web_module_(web_module) {}
@@ -848,11 +836,10 @@
web_module_->impl_.reset();
}
-WebModule::Options::Options(const math::Size& ui_dimensions)
+WebModule::Options::Options()
: name("WebModule"),
layout_trigger(layout::LayoutManager::kOnDocumentMutation),
- image_cache_capacity(
- static_cast<int>(memory_settings::GetImageCacheSize(ui_dimensions))),
+ image_cache_capacity(32 * 1024 * 1024),
remote_typeface_cache_capacity(
COBALT_REMOTE_TYPEFACE_CACHE_SIZE_IN_BYTES),
mesh_cache_capacity(COBALT_MESH_CACHE_SIZE_IN_BYTES),
@@ -862,13 +849,14 @@
image_cache_capacity_multiplier_when_playing_video(1.0f),
thread_priority(base::kThreadPriority_Normal),
loader_thread_priority(base::kThreadPriority_Low),
- tts_engine(NULL) {}
+ animated_image_decode_thread_priority(base::kThreadPriority_Low) {}
WebModule::WebModule(
const GURL& initial_url,
const OnRenderTreeProducedCallback& render_tree_produced_callback,
const OnErrorCallback& error_callback,
const base::Closure& window_close_callback,
+ const base::Closure& window_minimize_callback,
media::MediaModule* media_module, network::NetworkModule* network_module,
const math::Size& window_dimensions,
render_tree::ResourceProvider* resource_provider,
@@ -877,9 +865,9 @@
: thread_(options.name.c_str()) {
ConstructionData construction_data(
initial_url, render_tree_produced_callback, error_callback,
- window_close_callback, media_module, network_module, window_dimensions,
- resource_provider, kDOMMaxElementDepth, system_window,
- layout_refresh_rate, options);
+ window_close_callback, window_minimize_callback, media_module,
+ network_module, window_dimensions, resource_provider, kDOMMaxElementDepth,
+ system_window, layout_refresh_rate, options);
// Start the dedicated thread and create the internal implementation
// object on that thread.
@@ -969,7 +957,8 @@
std::string WebModule::ExecuteJavascript(
const std::string& script_utf8,
- const base::SourceLocation& script_location) {
+ const base::SourceLocation& script_location,
+ bool* out_succeeded) {
TRACE_EVENT0("cobalt::browser", "WebModule::ExecuteJavascript()");
DCHECK(message_loop());
DCHECK(impl_);
@@ -979,7 +968,8 @@
message_loop()->PostTask(
FROM_HERE, base::Bind(&WebModule::Impl::ExecuteJavascript,
base::Unretained(impl_.get()), script_utf8,
- script_location, &got_result, &result));
+ script_location, &got_result, &result,
+ out_succeeded));
got_result.Wait();
return result;
}
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index c8fdad9..0054f19 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -77,14 +77,16 @@
class WebModule {
public:
struct Options {
- typedef base::Callback<scoped_refptr<script::Wrappable>()>
+ typedef base::Callback<scoped_refptr<script::Wrappable>(
+ const scoped_refptr<dom::Window>& window,
+ dom::MutationObserverTaskManager* mutation_observer_task_manager)>
CreateObjectFunction;
typedef base::hash_map<std::string, CreateObjectFunction>
InjectedWindowAttributes;
// All optional parameters defined in this structure should have their
// values initialized in the default constructor to useful defaults.
- explicit Options(const math::Size& ui_dimensions);
+ Options();
// The name of the WebModule. This is useful for debugging purposes as in
// the case where multiple WebModule objects exist, it can be used to
@@ -157,8 +159,11 @@
// base::kThreadPriority_Low.
base::ThreadPriority loader_thread_priority;
- // TTSEngine instance to use for text-to-speech.
- accessibility::TTSEngine* tts_engine;
+ // Specifies the priority tha the web module's animated image decoding
+ // thread will be assigned. This thread is responsible for decoding,
+ // blending and constructing individual frames from animated images. The
+ // default value is base::kThreadPriority_Low.
+ base::ThreadPriority animated_image_decode_thread_priority;
// InputPoller to use for constantly polling the input key position or
// state. For example, this is used to support 3D camera movements.
@@ -176,6 +181,7 @@
const OnRenderTreeProducedCallback& render_tree_produced_callback,
const OnErrorCallback& error_callback,
const base::Closure& window_close_callback,
+ const base::Closure& window_minimize_callback,
media::MediaModule* media_module,
network::NetworkModule* network_module,
const math::Size& window_dimensions,
@@ -191,7 +197,8 @@
// thread will block until the JavaScript has executed and the output results
// are available.
std::string ExecuteJavascript(const std::string& script_utf8,
- const base::SourceLocation& script_location);
+ const base::SourceLocation& script_location,
+ bool* out_succeeded);
#if defined(ENABLE_WEBDRIVER)
// Creates a new webdriver::WindowDriver that interacts with the Window that
@@ -224,6 +231,7 @@
const OnRenderTreeProducedCallback& render_tree_produced_callback,
const OnErrorCallback& error_callback,
const base::Closure& window_close_callback,
+ const base::Closure& window_minimize_callback,
media::MediaModule* media_module,
network::NetworkModule* network_module,
const math::Size& window_dimensions,
@@ -234,6 +242,7 @@
render_tree_produced_callback(render_tree_produced_callback),
error_callback(error_callback),
window_close_callback(window_close_callback),
+ window_minimize_callback(window_minimize_callback),
media_module(media_module),
network_module(network_module),
window_dimensions(window_dimensions),
@@ -247,6 +256,7 @@
OnRenderTreeProducedCallback render_tree_produced_callback;
OnErrorCallback error_callback;
const base::Closure& window_close_callback;
+ const base::Closure& window_minimize_callback;
media::MediaModule* media_module;
network::NetworkModule* network_module;
math::Size window_dimensions;
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 4dfb22e..663e4ba 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -54,6 +54,7 @@
'<(DEPTH)/cobalt/math/math.gyp:*',
'<(DEPTH)/cobalt/media/sandbox/sandbox.gyp:*',
'<(DEPTH)/cobalt/media_session/media_session.gyp:*',
+ '<(DEPTH)/cobalt/media_session/media_session_test.gyp:*',
'<(DEPTH)/cobalt/network/network.gyp:*',
'<(DEPTH)/cobalt/render_tree/render_tree.gyp:*',
'<(DEPTH)/cobalt/renderer/renderer.gyp:*',
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 28b10ee..aa6da0c 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-34413
\ No newline at end of file
+49488
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index e500d8c..cc21f05 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -33,10 +33,77 @@
# implement spherical video playback.
'enable_map_to_mesh%': 0,
+ # Enables embedding Cobalt as a shared library within another app. This
+ # requires a 'lib' starboard implementation for the corresponding platform.
+ 'cobalt_enable_lib%': 0,
+
# Contains the current font package selection. This can be used to trade
- # font quality, coverage, and latency with smaller font package size.
- # See content/fonts/README.md for more details.
- 'cobalt_font_package%': 'unlimited',
+ # font quality, coverage, and latency for different font package sizes.
+ # The font package can be one of the following options:
+ # 'expanded' -- The largest package. It includes everything in the
+ # 'standard' package, along with 'bold' weight CJK. It is
+ # recommended that 'local_font_cache_size_in_bytes' be
+ # increased to 24MB when using this package to account for
+ # the extra memory required by bold CJK. This package is
+ # ~46.2MB.
+ # 'standard' -- The default package. It includes all non-CJK fallback
+ # fonts in both 'normal' and 'bold' weights, 'normal' weight
+ # CJK ('bold' weight CJK is synthesized from it), and all
+ # FCC fonts. This package is ~26.9MB.
+ # 'limited_with_jp' -- A significantly smaller package than 'standard'.
+ # This package removes the 'bold' weighted non-CJK fallback
+ # fonts (the 'normal' weight is still included and is used
+ # to synthesize bold), removes the FCC fonts (which must be
+ # downloaded from the web), and replaces standard CJK with
+ # low quality CJK. However, higher quality Japanese is still
+ # included. Because low quality CJK cannot synthesize bold,
+ # bold glyphs are unavailable in Chinese and Korean. This
+ # package is ~10.9MB.
+ # 'limited' -- A smaller package than 'limited_with_jp'. The two packages
+ # are identical with the exception that 'limited' does not
+ # include the higher quality Japanese font; instead it
+ # relies on low quality CJK for all CJK characters. Because
+ # low quality CJK cannot synthesize bold, bold glyphs are
+ # unavailable in Chinese, Japanese, and Korean. This package
+ # is ~7.7MB.
+ # 'minimal' -- The smallest possible font package. It only includes
+ # Roboto's Basic Latin characters. Everything else must be
+ # downloaded from the web. This package is ~16.4KB.
+ # NOTE: When bold is needed, but unavailable, it is typically synthesized,
+ # resulting in lower quality glyphs than those generated directly from
+ # a bold font. However, this does not occur with low quality CJK,
+ # which is not high enough quality to synthesize. Its glyphs always
+ # have a 'normal' weight.
+ 'cobalt_font_package%': 'standard',
+
+ # Font package overrides can be used to modify the files included within the
+ # selected package. The following values are available:
+ # -1 -- The package value for the specified category is not overridden.
+ # 0 -- The package value is overridden and no fonts for the specified
+ # category are included.
+ # 1 -- The package value is overridden and fonts from the specified
+ # category with a weight of 'normal' and a style of 'normal' are
+ # included.
+ # 2 -- The package value is overridden and fonts from the specified
+ # category with a weight of either 'normal' or bold' and a style of
+ # 'normal' are included.
+ # 3 -- The package value is overridden and fonts from the specified
+ # category with a weight of either 'normal' or 'bold' and a style of
+ # either 'normal' or 'italic' are included.
+ # 4 -- The package value is overridden and all available fonts from the
+ # specified category are included. This may include additional
+ # weights beyond 'normal' and 'bold'.
+ # See content/fonts/README.md for details on the specific values used by
+ # each of the packages use for the various font categories.
+ 'cobalt_font_package_override_named_sans_serif%': -1,
+ 'cobalt_font_package_override_named_serif%': -1,
+ 'cobalt_font_package_override_named_fcc_fonts%': -1,
+ 'cobalt_font_package_override_fallback_lang_non_cjk%': -1,
+ 'cobalt_font_package_override_fallback_lang_cjk%': -1,
+ 'cobalt_font_package_override_fallback_lang_cjk_low_quality%': -1,
+ 'cobalt_font_package_override_fallback_lang_jp%': -1,
+ 'cobalt_font_package_override_fallback_emoji%': -1,
+ 'cobalt_font_package_override_fallback_symbols%': -1,
# Build version number.
'cobalt_version%': 0,
@@ -67,6 +134,19 @@
# still be available and valid, but it will do nothing.
'rasterizer_type%': 'hardware',
+ # If set to 1, will enable support for rendering only the regions of the
+ # display that are modified due to animations, instead of re-rendering the
+ # entire scene each frame. This feature can reduce startup time where
+ # usually there is a small loading spinner animating on the screen. On GLES
+ # renderers, Cobalt will attempt to implement this support by using
+ # eglSurfaceAttrib(..., EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED), otherwise
+ # the dirty region will be silently disabled. On Blitter API platforms,
+ # if this is enabled, we explicitly create an extra offscreen full-size
+ # intermediate surface to render into. Note that some GLES driver
+ # implementations may internally allocate an extra full screen surface to
+ # support this feature.
+ 'render_dirty_region_only%': 1,
+
# Modify this value to adjust the default rasterizer setting for your
# platform.
'default_renderer_options_dependency%': '<(DEPTH)/cobalt/renderer/default_options_starboard.gyp:default_options',
@@ -76,7 +156,11 @@
# swapping frames may take some additional processing time, so it may be
# better to specify a lower delay. For example, '33' instead of '33.33'
# for 30 Hz refresh.
- 'cobalt_minimum_frame_time_in_milliseconds%': '0',
+ 'cobalt_minimum_frame_time_in_milliseconds%': '16.4',
+
+ # Cobalt will call eglSwapInterval() and specify this value before calling
+ # eglSwapBuffers() each frame.
+ 'cobalt_egl_swap_interval%': 1,
# The variables allow changing the target type on platforms where the
# native code may require an additional packaging step (ex. Android).
@@ -88,6 +172,9 @@
# Set to 1 to build with DIAL support.
'in_app_dial%': 0,
+ # Set to 1 to enable a custom MediaSessionClient.
+ 'custom_media_session_client%': 0,
+
# Set to 1 to enable H5vccAccountManager.
'enable_account_manager%': 0,
@@ -166,14 +253,14 @@
# surface cache facilitates the reuse of temporary offscreen surfaces
# within a single frame. This setting is only relevant when using the
# hardware-accelerated Skia rasterizer.
- 'scratch_surface_cache_size_in_bytes%': 7 * 1024 * 1024,
+ 'scratch_surface_cache_size_in_bytes%': 0,
# 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.
'surface_cache_size_in_bytes%': 0,
- # Determines the capacity of the image cache which manages image surfaces
+ # Determines the capacity of the image cache, which manages image surfaces
# downloaded from a web page. While it depends on the platform, often (and
# ideally) these images are cached within GPU memory.
# Set to -1 to automatically calculate the value at runtime, based on
@@ -181,9 +268,18 @@
# SbSystemGetTotalGPUMemory().
'image_cache_size_in_bytes%': -1,
- # Determines the capacity of the remote typefaces cache which manages all
- # typefaces downloaded from a web page.
- 'remote_typeface_cache_size_in_bytes%': 5 * 1024 * 1024,
+ # Determines the capacity of the local font cache, which manages all fonts
+ # loaded from local files. Newly encountered sections of font files are
+ # lazily loaded into the cache, enabling subsequent requests to the same
+ # file sections to be handled via direct memory access. Once the limit is
+ # reached, further requests are handled via file stream.
+ # Setting the value to 0 disables memory caching and causes all font file
+ # accesses to be done using file streams.
+ 'local_font_cache_size_in_bytes%': 16 * 1024 * 1024,
+
+ # Determines the capacity of the remote font cache, which manages all
+ # fonts downloaded from a web page.
+ 'remote_font_cache_size_in_bytes%': 4 * 1024 * 1024,
# Determines the capacity of the mesh cache. Each mesh is held compressed
# in main memory, to be inflated into a GPU buffer when needed for
@@ -213,8 +309,10 @@
# result in visible stutter. Such thrashing is more likely to occur when CJK
# language glyphs are rendered and when the size of the glyphs in pixels is
# larger, such as for higher resolution displays.
- 'skia_glyph_atlas_width%': '2048',
- 'skia_glyph_atlas_height%': '2048',
+ # The negative default values indicates to the engine that these settings
+ # should be automatically set.
+ 'skia_glyph_atlas_width%': '-1',
+ 'skia_glyph_atlas_height%': '-1',
# Determines the size of garbage collection threshold. After this many bytes
# have been allocated, the mozjs garbage collector will run. Lowering this
@@ -266,10 +364,12 @@
# as expected, rather than requiring it to be set for each platform.
#'javascript_engine%': 'mozjs',
- # Enable jit by default. It can be set to 0 to run in interpreter-only mode.
+ # Disable jit and run in interpreter-only mode by default. It can be set to
+ # 1 to run in jit mode. We have found that disabling jit often results in
+ # faster JavaScript execution and lower memory usage.
# Setting this to 1 on a platform or engine for which there is no JIT
# implementation is a no-op.
- 'cobalt_enable_jit%': 1,
+ 'cobalt_enable_jit%': 0,
# Customize variables used by Chromium's build/common.gypi.
@@ -286,7 +386,57 @@
# Platforms may redefine to 'poll' if necessary.
# Other mechanisms, e.g. devpoll, kqueue, select, are not yet supported.
'sb_libevent_method%': 'epoll',
+
+ # Use media source extension implementation that is conformed to the
+ # Candidate Recommandation of July 5th 2016.
'cobalt_media_source_2016%': 0,
+
+ # Note that the following media buffer related variables are only used when
+ # |cobalt_media_source_2016| is set to 1.
+
+ # This can be set to "memory" or "file". When it is set to "memory", the
+ # media buffers will be stored in main memory allocated by SbMemory
+ # functions. When it is set to "file", the media buffers will be stored in
+ # a temporary file in the system cache folder acquired by calling
+ # SbSystemGetPath() with "kSbSystemPathCacheDirectory". Note that when its
+ # value is "file" the media stack will still allocate memory to cache the
+ # the buffers in use.
+ 'cobalt_media_buffer_storage_type%': 'memory',
+ # The amount of memory that will be used to store media buffers allocated
+ # during system startup. To allocate a large chunk at startup helps with
+ # reducing frafmentation and can avoid failures to allocate incrementally.
+ # This can be set to 0.
+ 'cobalt_media_buffer_initial_capacity%': 26 * 1024 * 1024,
+ # When the media stack needs more memory to store media buffers, it will
+ # allocate extra memory in units of |cobalt_media_buffer_allocation_unit|.
+ # This can be set to 0, in which case the media stack will allocate extra
+ # memory on demand. When |cobalt_media_buffer_initial_capacity| and this
+ # value are both set to 0, the media stack will allocate individual buffers
+ # directly using SbMemory functions.
+ 'cobalt_media_buffer_allocation_unit%': 0 * 1024 * 1024,
+
+ # Specifies the maximum amount of memory used by audio or text buffers of
+ # media source before triggering a garbage collection. A large value will
+ # cause more memory being used by audio buffers but will also make
+ # JavaScript app less likely to re-download audio data. Note that the
+ # JavaScript app may experience significant difficulty if this value is too
+ # low.
+ 'cobalt_media_buffer_non_video_budget%': 5 * 1024 * 1024,
+
+ # Specifies the maximum amount of memory used by video buffers of media
+ # source before triggering a garbage collection when the video resolution is
+ # lower than 1080p (1920x1080). A large value will cause more memory being
+ # used by video buffers but will also make JavaScript app less likely to
+ # re-download video data. Note that the JavaScript app may experience
+ # significant difficulty if this value is too low.
+ 'cobalt_media_buffer_video_budget_1080p%': 16 * 1024 * 1024,
+ # Specifies the maximum amount of memory used by video buffers of media
+ # source before triggering a garbage collection when the video resolution is
+ # lower than 4k (3840x2160). A large value will cause more memory being
+ # used by video buffers but will also make JavaScript app less likely to
+ # re-download video data. Note that the JavaScript app may experience
+ # significant difficulty if this value is too low.
+ 'cobalt_media_buffer_video_budget_4k%': 60 * 1024 * 1024,
},
'target_defaults': {
@@ -304,6 +454,11 @@
},
'defines': [
'COBALT',
+ 'COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET=<(cobalt_media_buffer_non_video_budget)',
+ 'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P=<(cobalt_media_buffer_video_budget_1080p)',
+ 'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K=<(cobalt_media_buffer_video_budget_4k)',
+ 'COBALT_MEDIA_BUFFER_INITIAL_CAPACITY=<(cobalt_media_buffer_initial_capacity)',
+ 'COBALT_MEDIA_BUFFER_ALLOCATION_UNIT=<(cobalt_media_buffer_allocation_unit)',
],
'cflags': [ '<@(compiler_flags)' ],
'ldflags': [ '<@(linker_flags)' ],
@@ -328,6 +483,24 @@
'libraries': [ '<@(platform_libraries)' ],
'conditions': [
+ ['cobalt_media_source_2016 == 1', {
+ 'defines': [
+ 'COBALT_MEDIA_SOURCE_2016=1',
+ ],
+ }, {
+ 'defines': [
+ 'COBALT_MEDIA_SOURCE_2012=1',
+ ],
+ }],
+ ['cobalt_media_buffer_storage_type == "memory"', {
+ 'defines': [
+ 'COBALT_MEDIA_BUFFER_STORAGE_TYPE_MEMORY=1',
+ ],
+ }, {
+ 'defines': [
+ 'COBALT_MEDIA_BUFFER_STORAGE_TYPE_FILE=1',
+ ],
+ }],
['final_executable_type=="shared_library"', {
'target_conditions': [
['_toolset=="target"', {
@@ -356,15 +529,6 @@
'COBALT_WEBKIT_SHARED=1',
],
}],
- ['cobalt_media_source_2016 == 1', {
- 'defines': [
- 'COBALT_MEDIA_SOURCE_2016=1',
- ],
- }, {
- 'defines': [
- 'COBALT_MEDIA_SOURCE_2012=1',
- ],
- }],
['OS == "lb_shell"', {
'defines': [
'__LB_SHELL__',
@@ -390,9 +554,6 @@
}],
],
}], # OS == "starboard"
- ['target_arch in ["xb1", "xb360"]', {
- 'defines': ['_USE_MATH_DEFINES'], # For #define M_PI
- }],
['in_app_dial == 1', {
'defines': [
'DIAL_SERVER',
@@ -424,6 +585,7 @@
'COBALT_BOX_DUMP_ENABLED',
'COBALT_BUILD_TYPE_DEBUG',
'COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING',
+ 'COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR',
'_DEBUG',
'ENABLE_DEBUG_COMMAND_LINE_SWITCHES',
'ENABLE_DEBUG_C_VAL',
@@ -449,6 +611,7 @@
'ALLOCATOR_STATS_TRACKING',
'COBALT_BUILD_TYPE_DEVEL',
'COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING',
+ 'COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR',
'ENABLE_DEBUG_COMMAND_LINE_SWITCHES',
'ENABLE_DEBUG_C_VAL',
'ENABLE_DEBUG_CONSOLE',
@@ -472,6 +635,7 @@
'ALLOCATOR_STATS_TRACKING',
'COBALT_BUILD_TYPE_QA',
'COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING',
+ 'COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR',
'ENABLE_DEBUG_COMMAND_LINE_SWITCHES',
'ENABLE_DEBUG_C_VAL',
'ENABLE_DEBUG_CONSOLE',
@@ -506,8 +670,10 @@
# For configurations other than Gold, set the flag that lets test data files
# be copied and carried along with the build.
+ # Clients must copy over all content; to avoid having to copy over extra data, we
+ # omit the test data
'conditions': [
- ['cobalt_config != "gold"', {
+ ['cobalt_config != "gold" and cobalt_enable_lib == 0', {
'variables' : {
'cobalt_copy_debug_console': 1,
'cobalt_copy_test_data': 1,
diff --git a/src/cobalt/build/config/starboard.py b/src/cobalt/build/config/starboard.py
index 799c9f9..562c166 100644
--- a/src/cobalt/build/config/starboard.py
+++ b/src/cobalt/build/config/starboard.py
@@ -67,7 +67,7 @@
'use_asan': use_asan,
# Whether to build with clang's Thread Sanitizer instrumentation.
'use_tsan': use_tsan,
- # Whether to emable VR.
+ # Whether to enable VR.
'enable_vr': vr_enabled,
}
return variables
diff --git a/src/cobalt/build/gyp_cobalt b/src/cobalt/build/gyp_cobalt
index 698d9b7..69180fb 100755
--- a/src/cobalt/build/gyp_cobalt
+++ b/src/cobalt/build/gyp_cobalt
@@ -17,6 +17,7 @@
import argparse
import logging
import os
+import shlex
import sys
import textwrap
@@ -48,6 +49,7 @@
host_os_names = {
'linux2': 'linux',
'linux3': 'linux',
+ 'darwin': 'linux',
'win32': 'win',
}
@@ -162,6 +164,21 @@
variables.update(self.platform_config.GetVariables(config_name))
_AppendVariables(variables, args)
+ # Add the symbolizer path for ASAN to allow translation of callstacks.
+ asan_symbolizer_path = ''
+ if variables.get('use_asan', 0) == 1:
+ compiler_commandline = shlex.split(os.environ.get('CC', ''))
+ for path in compiler_commandline:
+ if os.path.isfile(path):
+ test_path = os.path.join(os.path.dirname(path), 'llvm-symbolizer')
+ if os.path.isfile(test_path):
+ asan_symbolizer_path = test_path
+ break
+ asan_variables = {
+ 'asan_symbolizer_path': asan_symbolizer_path,
+ }
+ _AppendVariables(asan_variables, args)
+
# Add config specific generator variables.
generator_config = '{}_{}'.format(self.options.platform, config_name)
diff --git a/src/cobalt/build/gyp_utils.py b/src/cobalt/build/gyp_utils.py
index fa38e7d..3f1ac0b 100644
--- a/src/cobalt/build/gyp_utils.py
+++ b/src/cobalt/build/gyp_utils.py
@@ -28,8 +28,8 @@
from cobalt.tools import paths
from starboard.tools import platform
-_CLANG_REVISION = '264915-1'
-_CLANG_VERSION = '3.9.0'
+_CLANG_REVISION = '298539-1'
+_CLANG_VERSION = '5.0.0'
_SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
@@ -71,7 +71,7 @@
try:
revinfo_cmd = ['gclient', 'revinfo', '-a']
- if sys.platform.startswith('linux'):
+ if sys.platform.startswith('linux') or sys.platform == 'darwin':
use_shell = False
else:
# Windows needs shell to find gclient in the PATH.
diff --git a/src/cobalt/content/fonts/10megabytes/fonts.xml b/src/cobalt/content/fonts/10megabytes/fonts.xml
deleted file mode 100644
index 2305fbf..0000000
--- a/src/cobalt/content/fonts/10megabytes/fonts.xml
+++ /dev/null
@@ -1,217 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<familyset version="1">
-<!--
- Fallback fonts are chosen based on a match: full BCP-47 language tag
- including script, then just language, and finally order (the first font
- containing the glyph). Order of appearance is also the tiebreaker for weight
- matching.
-
- NOTE: When the optional family attribute "fallback" is included for a
- family, then it dictates whether or not that family is added to the fallback
- list. However, when it is not included, then the lack of a "name" attribute
- causes the family to be added to the fallback list.
-
- The pages attribute indicates which character pages are contained within
- the font. It is used with character fallback to allow the system to quickly
- determine that a character cannot appear in a font without requiring the
- full character map to be loaded into memory. Character pages are zero
- indexed, and each page contains 256 characters, so character 1000 would be
- contained within page 3.
--->
- <!-- first family is default -->
- <family name="sans-serif">
- <font weight="400" style="normal" font_name="Roboto Regular" postscript_name="Roboto-Regular">Roboto-Regular.ttf</font>
- </family>
- <!-- Note that aliases must come after the fonts they reference. -->
- <alias name="arial" to="sans-serif" />
- <alias name="helvetica" to="sans-serif" />
- <alias name="roboto" to="sans-serif" />
- <alias name="tahoma" to="sans-serif" />
- <alias name="verdana" to="sans-serif" />
- <alias name="courier" to="serif-monospace" />
- <alias name="courier new" to="serif-monospace" />
- <!-- fallback fonts -->
- <family fallback="true" name="Noto Naskh Arabic UI" pages="0,6-8,32,37,46,251-254">
- <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,18-19,45,171,254">
- <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,5,32,37,251,254">
- <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,2-3,14,32,37,254">
- <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,5,251,254">
- <font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,5,16,45,254">
- <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,2,9,28,32,34,37,168,254">
- <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
- </family>
- <!-- Gujarati should come after Devanagari -->
- <family fallback="true" pages="0,9-10,32,34,37,168,254">
- <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
- </family>
- <!-- Gurmukhi should come after Devanagari -->
- <family fallback="true" pages="0,9-10,32,34,37-38,168,254">
- <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,9,11,32,34,37,254">
- <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,3,9,13,32,34,37,254">
- <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,9,32,34,37,254">
- <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,9,12,32,34,37,254">
- <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,9,12,32,34,37,254">
- <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,9,11,32,34,37,254">
- <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,9,13,32,34,37,254">
- <font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,23,25,32,37">
- <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,3,14,32,37">
- <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,6-7,32,37,253-254">
- <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,3,170">
- <font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,27,32,37,254">
- <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,166,254,360-362">
- <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,27,254">
- <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,26,32,37,169,254">
- <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,23,254">
- <font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0-3,20-22,24,254">
- <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,19,254">
- <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,3,29,37,44,254">
- <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,44,254">
- <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,23,254">
- <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,32,37,169,254">
- <font weight="400" style="normal">NotoSansJavanese-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,169,254">
- <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,28,37,254">
- <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,9,25,254">
- <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,2,164,254">
- <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,6,8,254">
- <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,170-171,254">
- <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,25,254">
- <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,6-7,32,46,253-254">
- <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,28,254">
- <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,169,254">
- <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,32,37,168,254">
- <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,27-28,254">
- <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,9,32,37,168,254">
- <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,3,6-7,32,34,37-38,254">
- <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,23,254">
- <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,26,32,34,37,254">
- <font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,32,37,167,170,254">
- <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,15,32,37,254">
- <font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,3,32,45,254">
- <font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,165-166,254">
- <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,48,160-164,254-255">
- <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
- </family>
- <family fallback="true" pages="32-43,497-498">
- <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
- </family>
- <family fallback="true" lang="ja" pages="0,32,34-35,46-159,249-250,254-255,498,512-523,525-527,530-538,540-543,545-547,550,552,554-559,561,563-568,570,572-573,575-579,582-584,586-594,596-608,610-612,614-618,620,622-625,627-628,630-631,633-638,640,642-646,649-655,658,660-664,666,669-678,681,695-696,760-761">
- <font weight="400" style="normal">NotoSansJP-Regular.otf</font>
- </family>
- <family fallback="true" pages="0,32-33,35-39,41,43,48,50,254,496-502,4068,4072">
- <font weight="400" style="normal">NotoEmoji-Regular.ttf</font>
- </family>
- <family fallback="true" pages="35,37-39,43,496,498">
- <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
- </family>
- <family fallback="true" pages="0,14,17,32,48-51,77-159,172-215,249-250,254-255,260">
- <font weight="400" style="normal">DroidSansFallback.ttf</font>
- </family>
- <!--
- Tai Le and Mongolian are intentionally kept last, to make sure they don't override
- the East Asian punctuation for Chinese.
- -->
- <family fallback="true" pages="0,3,16,25,48,254">
- <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
- </family>
- <family fallback="true" pages="0,24,32,36-37,48,254">
- <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
- </family>
-</familyset>
diff --git a/src/cobalt/content/fonts/README.md b/src/cobalt/content/fonts/README.md
index e3bada1..3bd0d89 100644
--- a/src/cobalt/content/fonts/README.md
+++ b/src/cobalt/content/fonts/README.md
@@ -1,32 +1,207 @@
## Description
-This directory contains a few different set of font packages one can select
-to be packaged with cobalt.
+This directory contains fonts that can be packaged with Cobalt.
## How to use
To use this:
-1. Select one of the profiles below.
+1. Select one of the packages below.
2. Add a variable named `cobalt_font_package` in your platform's
-`gyp_configuration.gypi` file.
+ `gyp_configuration.gypi` file.
+3. Optional: Add package overrides, which can be used to add or remove fonts
+ from the package.
Example:
+ This example uses the 'limited' package, but overrides it to include bold
+ non-CJK language fallback and to not include any CJK language fallback.
'variables': {
- 'cobalt_font_package': '10megabytes',
+ 'cobalt_font_package': 'limited',
+ 'cobalt_font_package_override_fallback_lang_non_cjk': 2,
+ 'cobalt_font_package_override_fallback_lang_cjk_low_quality': 0,
}
+## Packages
+* 'expanded' -- The largest package. It includes everything in the 'standard'
+ package, along with 'bold' weight CJK. It is recommended that
+ 'local_font_cache_size_in_bytes' be increased to 24MB when
+ using this package to account for the extra memory required by
+ bold CJK. This package is ~46.2MB.
+
+ Package category values:
+ 'package_named_sans_serif': 4,
+ 'package_named_serif': 4,
+ 'package_named_fcc_fonts': 2,
+ 'package_fallback_lang_non_cjk': 2,
+ 'package_fallback_lang_cjk': 2,
+ 'package_fallback_lang_cjk_low_quality': 0,
+ 'package_fallback_lang_jp': 0,
+ 'package_fallback_emoji': 1,
+ 'package_fallback_symbols': 1,
+
+* 'standard' -- The default package. It includes all non-CJK fallback fonts in
+ both 'normal' and 'bold' weights, 'normal' weight CJK ('bold'
+ weight CJK is synthesized from it), and all FCC fonts. This
+ package is ~26.9MB.
+
+ Package category values:
+ 'package_named_sans_serif': 3,
+ 'package_named_serif': 3,
+ 'package_named_fcc_fonts': 2,
+ 'package_fallback_lang_non_cjk': 2,
+ 'package_fallback_lang_cjk': 1,
+ 'package_fallback_lang_cjk_low_quality': 0,
+ 'package_fallback_lang_jp': 0,
+ 'package_fallback_emoji': 1,
+ 'package_fallback_symbols': 1,
+
+* 'limited_with_jp' -- A significantly smaller package than 'standard'. This
+ package removes the 'bold' weighted non-CJK fallback fonts (the
+ 'normal' weight is still included and is used to synthesize
+ bold), removes the FCC fonts (which must be downloaded from the
+ web), and replaces standard CJK with low quality CJK. However,
+ higher quality Japanese is still included. Because low quality
+ CJK cannot synthesize bold, bold glyphs are unavailable in
+ Chinese and Korean. This package is ~10.9MB.
+
+ Package category values:
+ 'package_named_sans_serif': 2,
+ 'package_named_serif': 0,
+ 'package_named_fcc_fonts': 0,
+ 'package_fallback_lang_non_cjk': 1,
+ 'package_fallback_lang_cjk': 0,
+ 'package_fallback_lang_cjk_low_quality': 1,
+ 'package_fallback_lang_jp': 1,
+ 'package_fallback_emoji': 1,
+ 'package_fallback_symbols': 1,
+
+* 'limited' -- A smaller package than 'limited_with_jp'. The two packages are
+ identical with the exception that 'limited' does not include
+ the higher quality Japanese font; instead it relies on low
+ quality CJK for all CJK characters. Because low quality CJK
+ cannot synthesize bold, bold glyphs are unavailable in Chinese,
+ Japanese, and Korean. This package is ~7.7MB.
+
+ Package category values:
+ 'package_named_sans_serif': 2,
+ 'package_named_serif': 0,
+ 'package_named_fcc_fonts': 0,
+ 'package_fallback_lang_non_cjk': 1,
+ 'package_fallback_lang_cjk': 0,
+ 'package_fallback_lang_cjk_low_quality': 1,
+ 'package_fallback_lang_jp': 0,
+ 'package_fallback_emoji': 1,
+ 'package_fallback_symbols': 1,
+
+* 'minimal' -- The smallest possible font package. It only includes Roboto's
+ Basic Latin characters. Everything else must be downloaded from
+ the web. This package is ~16.4KB.
+
+ Package category values:
+ 'package_named_sans_serif': 0,
+ 'package_named_serif': 0,
+ 'package_named_fcc_fonts': 0,
+ 'package_fallback_lang_non_cjk': 0,
+ 'package_fallback_lang_cjk': 0,
+ 'package_fallback_lang_cjk_low_quality': 0,
+ 'package_fallback_lang_jp': 0,
+ 'package_fallback_emoji': 0,
+ 'package_fallback_symbols': 0,
+
+NOTE: When bold is needed, but unavailable, it is typically synthesized,
+ resulting in lower quality glyphs than those generated directly from a
+ bold font. However, this does not occur with low quality CJK, which is
+ not high enough quality to synthesize. Its glyphs always have a regular
+ weight.
-## Profiles
+## Package font categories
+Each package contains values for the following categories, which specifies the
+fonts from each category included within the package:
+ * 'package_named_sans_serif':
+ Named sans-serif fonts.
-1. `unlimited`: This font set is preferred, and default. This will enable the
-use the fonts with highest quality and coverage, without the network latency of
-fetching fonts from the server.
-2. `10megabytes`: Use this set of fonts if the target space allocated for fonts
-is approximately 10 megabytes. This directory contains DroidSansFallback, which
-will render many Chinese, and Korean characters at lower quality. The benefit
-of using this font is space savings at the cost of reduced quality.
-3. `minimal`: Use this if minimizing space is a goal, and Cobalt should rely
-on web fonts.
+ * 'package_named_serif':
+ Named serif fonts.
+
+ * 'package_named_fcc_fonts':
+ All FCC-required fonts that are not included within sans-serif or serif:
+ monospace, serif-monospace, casual, cursive, and sans-serif-smallcaps.
+
+ * 'package_fallback_lang_non_cjk':
+ All non-CJK language-specific fallback fonts.
+
+ * 'package_fallback_lang_cjk':
+ Higher quality CJK language-specific fallback fonts.
+
+ * 'package_fallback_lang_cjk_low_quality':
+ Low quality CJK language-specific fallback fonts. These should only be
+ included when 'package_fallback_lang_cjk' has a value of '0'. This is the
+ only category of fonts that is not synthetically boldable.
+
+ * 'package_fallback_lang_jp':
+ Higher quality Japanese language-specific fallback fonts. These should
+ only be included when 'package_fallback_lang_cjk' has a value of '0'.
+
+ * 'package_fallback_emoji':
+ Emoji-related fallback fonts.
+
+ * 'package_fallback_symbols':
+ Symbol-related fallback fonts.
+
+
+## Package font category values
+The following explains the meaning behind the values that packages use for each
+of the font categories:
+ * 0 -- No fonts from the specified category are included.
+ * 1 -- Fonts from the specified category with a weight of 'normal' and a
+ style of 'normal' are included.
+ * 2 -- Fonts from the the specified category with a weight of either 'normal'
+ or 'bold' and a style of 'normal' are included.
+ * 3 -- Fonts from the the specified category with a weight of either 'normal'
+ or 'bold' and a style of either 'normal' or 'italic' are included.
+ * 4 -- All available fonts from the specified category are included. This may
+ include additional weights beyond 'normal' and 'bold'.
+
+
+## Overriding packages
+Font package overrides can be used to modify the files included within the
+selected package. The following override values are available for each font
+category:
+ * -1 -- The package value for the specified type is not overridden.
+ * 0 -- The package value is overridden and no fonts from the specified
+ category are included.
+ * 1 -- The package value is overridden and fonts from the specified category
+ with a weight of 'normal' and a style of 'normal' are included.
+ * 2 -- The package value is overridden and fonts from the specified category
+ with a weight of either 'normal' or bold' and a style of 'normal' are
+ included.
+ * 3 -- The package value is overridden and fonts from the specified category
+ with a weight of either 'normal' or 'bold' and a style of either
+ 'normal' or 'italic' are included.
+ * 4 -- The package value is overridden and all available fonts from the
+ specified category are included. This may include additional weights
+ beyond 'normal' and 'bold'.
+
+
+## Override mappings
+The mapping between the override category name and the package category name:
+ * 'cobalt_font_package_override_named_sans_serif' ==>
+ 'package_named_sans_serif'
+ * 'cobalt_font_package_override_named_serif' ==>
+ 'package_named_serif'
+ * 'cobalt_font_package_override_named_fcc_fonts' ==>
+ 'package_named_fcc_fonts'
+ * 'cobalt_font_package_override_fallback_lang_non_cjk' ==>
+ 'package_fallback_lang_non_cjk'
+ * 'cobalt_font_package_override_fallback_lang_cjk' ==>
+ 'package_fallback_lang_cjk'
+ * 'cobalt_font_package_override_fallback_lang_cjk_low_quality' ==>
+ 'package_fallback_lang_cjk_low_quality'
+ * 'cobalt_font_package_override_fallback_lang_jp' ==>
+ 'package_fallback_lang_jp'
+ * 'cobalt_font_package_override_fallback_emoji' ==>
+ 'package_fallback_emoji'
+ * 'cobalt_font_package_override_fallback_symbols' ==>
+ 'package_fallback_symbols'
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKR-Regular.otf b/src/cobalt/content/fonts/all_fonts/NotoSansKR-Regular.otf
deleted file mode 100644
index 5d5b429..0000000
--- a/src/cobalt/content/fonts/all_fonts/NotoSansKR-Regular.otf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSC-Regular.otf b/src/cobalt/content/fonts/all_fonts/NotoSansSC-Regular.otf
deleted file mode 100644
index 20f40ce..0000000
--- a/src/cobalt/content/fonts/all_fonts/NotoSansSC-Regular.otf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTC-Regular.otf b/src/cobalt/content/fonts/all_fonts/NotoSansTC-Regular.otf
deleted file mode 100644
index e7d7c8f..0000000
--- a/src/cobalt/content/fonts/all_fonts/NotoSansTC-Regular.otf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Black.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-Black.ttf
deleted file mode 100644
index e8a835a..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-Black.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-BlackItalic.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-BlackItalic.ttf
deleted file mode 100644
index 49bc496..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-BlackItalic.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Light.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-Light.ttf
deleted file mode 100644
index e50ce97..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-Light.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-LightItalic.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-LightItalic.ttf
deleted file mode 100644
index a2bfefe..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-LightItalic.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Medium.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-Medium.ttf
deleted file mode 100644
index f144b6d..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-Medium.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-MediumItalic.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-MediumItalic.ttf
deleted file mode 100644
index e6e8319..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-MediumItalic.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Thin.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-Thin.ttf
deleted file mode 100644
index 77b470e..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-Thin.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-ThinItalic.ttf b/src/cobalt/content/fonts/all_fonts/Roboto-ThinItalic.ttf
deleted file mode 100644
index 5562612..0000000
--- a/src/cobalt/content/fonts/all_fonts/Roboto-ThinItalic.ttf
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/CarroisGothicSC-Regular.ttf b/src/cobalt/content/fonts/font_files/CarroisGothicSC-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/CarroisGothicSC-Regular.ttf
rename to src/cobalt/content/fonts/font_files/CarroisGothicSC-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/ComingSoon.ttf b/src/cobalt/content/fonts/font_files/ComingSoon.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/ComingSoon.ttf
rename to src/cobalt/content/fonts/font_files/ComingSoon.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/CutiveMono.ttf b/src/cobalt/content/fonts/font_files/CutiveMono.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/CutiveMono.ttf
rename to src/cobalt/content/fonts/font_files/CutiveMono.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/DancingScript-Bold.ttf b/src/cobalt/content/fonts/font_files/DancingScript-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/DancingScript-Bold.ttf
rename to src/cobalt/content/fonts/font_files/DancingScript-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/DancingScript-Regular.ttf b/src/cobalt/content/fonts/font_files/DancingScript-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/DancingScript-Regular.ttf
rename to src/cobalt/content/fonts/font_files/DancingScript-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/DroidSansFallback.ttf b/src/cobalt/content/fonts/font_files/DroidSansFallback.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/DroidSansFallback.ttf
rename to src/cobalt/content/fonts/font_files/DroidSansFallback.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/DroidSansMono.ttf b/src/cobalt/content/fonts/font_files/DroidSansMono.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/DroidSansMono.ttf
rename to src/cobalt/content/fonts/font_files/DroidSansMono.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoEmoji-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoEmoji-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoEmoji-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoEmoji-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoNaskhArabicUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoNaskhArabicUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoNaskhArabicUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoNaskhArabicUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoNaskhArabicUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoNaskhArabicUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoNaskhArabicUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoNaskhArabicUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansArmenian-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansArmenian-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansArmenian-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansArmenian-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansArmenian-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansArmenian-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansArmenian-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansArmenian-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBalinese-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBalinese-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBalinese-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBalinese-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBamum-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBamum-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBamum-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBamum-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBatak-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBatak-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBatak-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBatak-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBengaliUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansBengaliUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBengaliUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBengaliUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBengaliUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBengaliUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBengaliUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBengaliUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBuginese-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBuginese-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBuginese-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBuginese-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansBuhid-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansBuhid-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansBuhid-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansBuhid-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/NotoSansCJK-Bold.ttc b/src/cobalt/content/fonts/font_files/NotoSansCJK-Bold.ttc
new file mode 100644
index 0000000..09707f9
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/NotoSansCJK-Bold.ttc
Binary files differ
diff --git a/src/cobalt/content/fonts/font_files/NotoSansCJK-Regular.ttc b/src/cobalt/content/fonts/font_files/NotoSansCJK-Regular.ttc
new file mode 100644
index 0000000..d9a2262
--- /dev/null
+++ b/src/cobalt/content/fonts/font_files/NotoSansCJK-Regular.ttc
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansCanadianAboriginal-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansCanadianAboriginal-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansCanadianAboriginal-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansCanadianAboriginal-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansCham-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansCham-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansCham-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansCham-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansCham-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansCham-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansCham-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansCham-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansCherokee-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansCherokee-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansCherokee-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansCherokee-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansCoptic-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansCoptic-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansCoptic-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansCoptic-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansDevanagariUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansDevanagariUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansDevanagariUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansDevanagariUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansDevanagariUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansDevanagariUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansDevanagariUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansDevanagariUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansEthiopic-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansEthiopic-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansEthiopic-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansEthiopic-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansEthiopic-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansEthiopic-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansEthiopic-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansEthiopic-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGeorgian-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansGeorgian-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGeorgian-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGeorgian-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGeorgian-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansGeorgian-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGeorgian-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGeorgian-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGlagolitic-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansGlagolitic-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGlagolitic-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGlagolitic-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGujaratiUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansGujaratiUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGujaratiUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGujaratiUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGujaratiUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansGujaratiUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGujaratiUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGujaratiUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGurmukhiUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansGurmukhiUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGurmukhiUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGurmukhiUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansGurmukhiUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansGurmukhiUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansGurmukhiUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansGurmukhiUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansHanunoo-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansHanunoo-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansHanunoo-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansHanunoo-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansHebrew-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansHebrew-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansHebrew-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansHebrew-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansHebrew-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansHebrew-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansHebrew-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansHebrew-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansJP-Regular.otf b/src/cobalt/content/fonts/font_files/NotoSansJP-Regular.otf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansJP-Regular.otf
rename to src/cobalt/content/fonts/font_files/NotoSansJP-Regular.otf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansJavanese-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansJavanese-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansJavanese-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansJavanese-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKannadaUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansKannadaUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansKannadaUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansKannadaUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKannadaUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansKannadaUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansKannadaUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansKannadaUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKayahLi-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansKayahLi-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansKayahLi-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansKayahLi-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKhmerUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansKhmerUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansKhmerUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansKhmerUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansKhmerUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansKhmerUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansKhmerUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansKhmerUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansLaoUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansLaoUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansLaoUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansLaoUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansLaoUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansLaoUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansLaoUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansLaoUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansLepcha-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansLepcha-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansLepcha-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansLepcha-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansLimbu-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansLimbu-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansLimbu-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansLimbu-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansLisu-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansLisu-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansLisu-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansLisu-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMalayalamUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansMalayalamUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMalayalamUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMalayalamUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMalayalamUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansMalayalamUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMalayalamUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMalayalamUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMandaic-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansMandaic-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMandaic-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMandaic-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMeeteiMayek-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansMeeteiMayek-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMeeteiMayek-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMeeteiMayek-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMongolian-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansMongolian-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMongolian-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMongolian-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMyanmarUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansMyanmarUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMyanmarUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMyanmarUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansMyanmarUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansMyanmarUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansMyanmarUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansMyanmarUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansNKo-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansNKo-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansNKo-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansNKo-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansNewTaiLue-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansNewTaiLue-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansNewTaiLue-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansNewTaiLue-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansOlChiki-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansOlChiki-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansOlChiki-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansOlChiki-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansOriyaUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansOriyaUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansOriyaUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansOriyaUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansOriyaUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansOriyaUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansOriyaUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansOriyaUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansRejang-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansRejang-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansRejang-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansRejang-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSaurashtra-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansSaurashtra-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSaurashtra-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSaurashtra-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSinhala-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansSinhala-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSinhala-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSinhala-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSinhala-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansSinhala-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSinhala-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSinhala-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSundanese-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansSundanese-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSundanese-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSundanese-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSylotiNagri-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansSylotiNagri-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSylotiNagri-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSylotiNagri-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSymbols-Regular-Subsetted.ttf b/src/cobalt/content/fonts/font_files/NotoSansSymbols-Regular-Subsetted.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSymbols-Regular-Subsetted.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSymbols-Regular-Subsetted.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSymbols-Regular-Subsetted2.ttf b/src/cobalt/content/fonts/font_files/NotoSansSymbols-Regular-Subsetted2.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSymbols-Regular-Subsetted2.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSymbols-Regular-Subsetted2.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansSyriacEstrangela-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansSyriacEstrangela-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansSyriacEstrangela-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansSyriacEstrangela-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTagbanwa-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTagbanwa-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTagbanwa-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTagbanwa-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTaiLe-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTaiLe-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTaiLe-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTaiLe-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTaiTham-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTaiTham-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTaiTham-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTaiTham-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTaiViet-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTaiViet-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTaiViet-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTaiViet-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTamilUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansTamilUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTamilUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTamilUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTamilUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTamilUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTamilUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTamilUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTeluguUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansTeluguUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTeluguUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTeluguUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTeluguUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTeluguUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTeluguUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTeluguUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansThaana-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansThaana-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansThaana-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansThaana-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansThaana-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansThaana-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansThaana-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansThaana-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansThaiUI-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansThaiUI-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansThaiUI-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansThaiUI-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansThaiUI-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansThaiUI-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansThaiUI-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansThaiUI-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTibetan-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSansTibetan-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTibetan-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTibetan-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTibetan-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTibetan-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTibetan-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTibetan-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansTifinagh-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansTifinagh-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansTifinagh-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansTifinagh-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansVai-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansVai-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansVai-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansVai-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSansYi-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSansYi-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSansYi-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSansYi-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSerif-Bold.ttf b/src/cobalt/content/fonts/font_files/NotoSerif-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSerif-Bold.ttf
rename to src/cobalt/content/fonts/font_files/NotoSerif-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSerif-BoldItalic.ttf b/src/cobalt/content/fonts/font_files/NotoSerif-BoldItalic.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSerif-BoldItalic.ttf
rename to src/cobalt/content/fonts/font_files/NotoSerif-BoldItalic.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSerif-Italic.ttf b/src/cobalt/content/fonts/font_files/NotoSerif-Italic.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSerif-Italic.ttf
rename to src/cobalt/content/fonts/font_files/NotoSerif-Italic.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/NotoSerif-Regular.ttf b/src/cobalt/content/fonts/font_files/NotoSerif-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/NotoSerif-Regular.ttf
rename to src/cobalt/content/fonts/font_files/NotoSerif-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Bold.ttf b/src/cobalt/content/fonts/font_files/Roboto-Bold.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/Roboto-Bold.ttf
rename to src/cobalt/content/fonts/font_files/Roboto-Bold.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-BoldItalic.ttf b/src/cobalt/content/fonts/font_files/Roboto-BoldItalic.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/Roboto-BoldItalic.ttf
rename to src/cobalt/content/fonts/font_files/Roboto-BoldItalic.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Italic.ttf b/src/cobalt/content/fonts/font_files/Roboto-Italic.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/Roboto-Italic.ttf
rename to src/cobalt/content/fonts/font_files/Roboto-Italic.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Regular-Subsetted.ttf b/src/cobalt/content/fonts/font_files/Roboto-Regular-Subsetted.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/Roboto-Regular-Subsetted.ttf
rename to src/cobalt/content/fonts/font_files/Roboto-Regular-Subsetted.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/all_fonts/Roboto-Regular.ttf b/src/cobalt/content/fonts/font_files/Roboto-Regular.ttf
similarity index 100%
rename from src/cobalt/content/fonts/all_fonts/Roboto-Regular.ttf
rename to src/cobalt/content/fonts/font_files/Roboto-Regular.ttf
Binary files differ
diff --git a/src/cobalt/content/fonts/minimal/fonts.xml b/src/cobalt/content/fonts/minimal/fonts.xml
deleted file mode 100644
index c261fc2..0000000
--- a/src/cobalt/content/fonts/minimal/fonts.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<familyset version="1">
-<!--
- Fallback fonts are chosen based on a match: full BCP-47 language tag
- including script, then just language, and finally order (the first font
- containing the glyph). Order of appearance is also the tiebreaker for weight
- matching.
-
- NOTE: When the optional family attribute "fallback" is included for a
- family, then it dictates whether or not that family is added to the fallback
- list. However, when it is not included, then the lack of a "name" attribute
- causes the family to be added to the fallback list.
-
- The pages attribute indicates which character pages are contained within
- the font. It is used with character fallback to allow the system to quickly
- determine that a character cannot appear in a font without requiring the
- full character map to be loaded into memory. Character pages are zero
- indexed, and each page contains 256 characters, so character 1000 would be
- contained within page 3.
--->
- <!-- first family is default -->
- <family>
- <font weight="400" style="normal">Roboto-Regular-Subsetted.ttf</font>
- </family>
-</familyset>
diff --git a/src/cobalt/content/fonts/android_system/fonts.xml b/src/cobalt/content/fonts/xml/android/fonts.xml
similarity index 89%
rename from src/cobalt/content/fonts/android_system/fonts.xml
rename to src/cobalt/content/fonts/xml/android/fonts.xml
index 55219d9..6a714c9 100644
--- a/src/cobalt/content/fonts/android_system/fonts.xml
+++ b/src/cobalt/content/fonts/xml/android/fonts.xml
@@ -10,27 +10,23 @@
list. However, when it is not included, then the lack of a "name" attribute
causes the family to be added to the fallback list.
- The pages attribute indicates which character pages are contained within
- the font. It is used with character fallback to allow the system to quickly
- determine that a character cannot appear in a font without requiring the
- full character map to be loaded into memory. Character pages are zero
- indexed, and each page contains 256 characters, so character 1000 would be
- contained within page 3.
+ The optional family attribute "pages" indicates which character pages are
+ contained within the family. It is used with character fallback to allow the
+ system to quickly determine that a character cannot appear in a font without
+ requiring the full character map to be loaded into memory. Character pages
+ are zero indexed, and each page contains 256 characters, so character 1000
+ would be contained within page 3.
+
+ The optional font attribute "disable_synthetic_bolding" prevents the font
+ from being synthetically bolded. By default synthetic bolding is done when
+ there is no bold font available and a bold weight (>500) is specified.
-->
<!-- first family is default -->
<family name="sans-serif">
- <font weight="100" style="normal" font_name="Roboto Thin" postscript_name="Roboto-Thin">Roboto-Thin.ttf</font>
- <font weight="100" style="italic" font_name="Roboto Thin Italic" postscript_name="Roboto-Thin-Italic">Roboto-ThinItalic.ttf</font>
- <font weight="300" style="normal" font_name="Roboto Light" postscript_name="Roboto-Light">Roboto-Light.ttf</font>
- <font weight="300" style="italic" font_name="Roboto Light Italic" postscript_name="Roboto-Light-Italic">Roboto-LightItalic.ttf</font>
<font weight="400" style="normal" font_name="Roboto Regular" postscript_name="Roboto-Regular">Roboto-Regular.ttf</font>
<font weight="400" style="italic" font_name="Roboto Regular Italic" postscript_name="Roboto-Italic">Roboto-Italic.ttf</font>
- <font weight="500" style="normal" font_name="Roboto Medium" postscript_name="Roboto-Medium">Roboto-Medium.ttf</font>
- <font weight="500" style="italic" font_name="Roboto Medium Italic" postscript_name="Roboto-Medium-Italic">Roboto-MediumItalic.ttf</font>
<font weight="700" style="normal" font_name="Roboto Bold" postscript_name="Roboto-Bold">Roboto-Bold.ttf</font>
<font weight="700" style="italic" font_name="Roboto Bold Italic" postscript_name="Roboto-BoldItalic">Roboto-BoldItalic.ttf</font>
- <font weight="900" style="normal" font_name="Roboto Black" postscript_name="Roboto-Black">Roboto-Black.ttf</font>
- <font weight="900" style="italic" font_name="Roboto Black Italic" postscript_name="Roboto-Black-Italic">Roboto-BlackItalic.ttf</font>
</family>
<!-- Note that aliases must come after the fonts they reference. -->
<alias name="arial" to="sans-serif" />
@@ -38,14 +34,11 @@
<alias name="roboto" to="sans-serif" />
<alias name="tahoma" to="sans-serif" />
<alias name="verdana" to="sans-serif" />
- <!-- This font should only be used if there are no other fonts in our final
- image. To ensure this, it has no name and fallback is set to false. -->
- <family fallback="false">
+ <!-- This is the default font when Roboto is unavailable. -->
+ <family fallback="true" pages="0">
<font weight="400" style="normal">Roboto-Regular-Subsetted.ttf</font>
</family>
<family name="sans-serif-condensed">
- <font weight="300" style="normal" font_name="Roboto Condensed Light" postscript_name="Roboto-Condensed-Light">RobotoCondensed-Light.ttf</font>
- <font weight="300" style="italic" font_name="Roboto Condensed Light Italic" postscript_name="Roboto-Condensed-Light-Italic">RobotoCondensed-LightItalic.ttf</font>
<font weight="400" style="normal" font_name="Roboto Condensed Regular" postscript_name="Roboto-Condensed-Regular">RobotoCondensed-Regular.ttf</font>
<font weight="400" style="italic" font_name="Roboto Condensed Regular Italic" postscript_name="Roboto-Condensed-Regular-Italic">RobotoCondensed-Italic.ttf</font>
<font weight="700" style="normal" font_name="Roboto Condensed Bold" postscript_name="Roboto-Condensed-Bold">RobotoCondensed-Bold.ttf</font>
@@ -312,8 +305,13 @@
<family fallback="true" pages="35,37-39,43,496,498">
<font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
</family>
+ <!--
+ Synthetic bolding is explicitly disabled for DroidSansFallback. The
+ quality of its synthetically bolded glyphs are not good enough to meet
+ Cobalt's standards.
+ -->
<family fallback="true" pages="0,14,17,32,48-159,172-215,249-250,254-255,260">
- <font weight="400" style="normal">DroidSansFallback.ttf</font>
+ <font weight="400" style="normal" disable_synthetic_bolding="true">DroidSansFallback.ttf</font>
</family>
<family fallback="true" lang="ja" pages="0,2-4,32-38,48,50-51,78-159,249-250,255">
<font weight="400" style="normal">MTLmr3m.ttf</font>
diff --git a/src/cobalt/content/fonts/unlimited/fonts.xml b/src/cobalt/content/fonts/xml/common/fonts.xml
similarity index 85%
rename from src/cobalt/content/fonts/unlimited/fonts.xml
rename to src/cobalt/content/fonts/xml/common/fonts.xml
index 6385638..2ff5097 100644
--- a/src/cobalt/content/fonts/unlimited/fonts.xml
+++ b/src/cobalt/content/fonts/xml/common/fonts.xml
@@ -10,27 +10,23 @@
list. However, when it is not included, then the lack of a "name" attribute
causes the family to be added to the fallback list.
- The pages attribute indicates which character pages are contained within
- the font. It is used with character fallback to allow the system to quickly
- determine that a character cannot appear in a font without requiring the
- full character map to be loaded into memory. Character pages are zero
- indexed, and each page contains 256 characters, so character 1000 would be
- contained within page 3.
+ The optional family attribute "pages" indicates which character pages are
+ contained within the family. It is used with character fallback to allow the
+ system to quickly determine that a character cannot appear in a font without
+ requiring the full character map to be loaded into memory. Character pages
+ are zero indexed, and each page contains 256 characters, so character 1000
+ would be contained within page 3.
+
+ The optional font attribute "disable_synthetic_bolding" prevents the font
+ from being synthetically bolded. By default synthetic bolding is done when
+ there is no bold font available and a bold weight (>500) is specified.
-->
<!-- first family is default -->
<family name="sans-serif">
- <font weight="100" style="normal" font_name="Roboto Thin" postscript_name="Roboto-Thin">Roboto-Thin.ttf</font>
- <font weight="100" style="italic" font_name="Roboto Thin Italic" postscript_name="Roboto-Thin-Italic">Roboto-ThinItalic.ttf</font>
- <font weight="300" style="normal" font_name="Roboto Light" postscript_name="Roboto-Light">Roboto-Light.ttf</font>
- <font weight="300" style="italic" font_name="Roboto Light Italic" postscript_name="Roboto-Light-Italic">Roboto-LightItalic.ttf</font>
<font weight="400" style="normal" font_name="Roboto Regular" postscript_name="Roboto-Regular">Roboto-Regular.ttf</font>
<font weight="400" style="italic" font_name="Roboto Regular Italic" postscript_name="Roboto-Italic">Roboto-Italic.ttf</font>
- <font weight="500" style="normal" font_name="Roboto Medium" postscript_name="Roboto-Medium">Roboto-Medium.ttf</font>
- <font weight="500" style="italic" font_name="Roboto Medium Italic" postscript_name="Roboto-Medium-Italic">Roboto-MediumItalic.ttf</font>
<font weight="700" style="normal" font_name="Roboto Bold" postscript_name="Roboto-Bold">Roboto-Bold.ttf</font>
<font weight="700" style="italic" font_name="Roboto Bold Italic" postscript_name="Roboto-BoldItalic">Roboto-BoldItalic.ttf</font>
- <font weight="900" style="normal" font_name="Roboto Black" postscript_name="Roboto-Black">Roboto-Black.ttf</font>
- <font weight="900" style="italic" font_name="Roboto Black Italic" postscript_name="Roboto-Black-Italic">Roboto-BlackItalic.ttf</font>
</family>
<!-- Note that aliases must come after the fonts they reference. -->
<alias name="arial" to="sans-serif" />
@@ -38,6 +34,10 @@
<alias name="roboto" to="sans-serif" />
<alias name="tahoma" to="sans-serif" />
<alias name="verdana" to="sans-serif" />
+ <!-- This is the default font when Roboto is unavailable. -->
+ <family fallback="true" pages="0">
+ <font weight="400" style="normal">Roboto-Regular-Subsetted.ttf</font>
+ </family>
<family name="serif">
<font weight="400" style="normal" font_name="Noto Serif" postscript_name="NotoSerif">NotoSerif-Regular.ttf</font>
<font weight="400" style="italic" font_name="Noto Serif Italic" postscript_name="NotoSerif-Italic">NotoSerif-Italic.ttf</font>
@@ -259,18 +259,21 @@
<family fallback="true" pages="32-43,497-498">
<font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
</family>
- <family fallback="true" lang="zh-Hans" pages="0,2,32-39,41,43,46-159,249-250,254-255,497-498,512-513,518,524,531-533,553,565,572,574,577,584-586,597,602,606,610,612,614-615,617,619-620,632,639,644,646-647,651-652,654,662,664,671,679-682,687,689,691-696,698-702,704-718">
- <font weight="400" style="normal">NotoSansSC-Regular.otf</font>
+ <family fallback="true" lang="zh-Hans" pages="0-4,17,30,32-39,41,43,46-159,169,172-215,249-251,254-255,497-498,512-658,660-682,685,687,689,691-696,698-702,704-718,760-761">
+ <font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
</family>
- <family fallback="true" lang="zh-Hant" pages="0,32,34,46-48,50,52-159,249-250,254-255,498,512-657,660-661,663-678,685,760-761">
- <font weight="400" style="normal">NotoSansTC-Regular.otf</font>
+ <family fallback="true" lang="zh-Hant" pages="0-4,17,30,32-39,41,43,46-159,169,172-215,249-251,254-255,497-498,512-658,660-682,685,687,689,691-696,698-702,704-718,760-761">
+ <font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
+ </family>
+ <family fallback="true" lang="ja" pages="0-4,17,30,32-39,41,43,46-159,169,172-215,249-251,254-255,497-498,512-658,660-682,685,687,689,691-696,698-702,704-718,760-761">
+ <font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font>
+ </family>
+ <family fallback="true" lang="ko" pages="0-4,17,30,32-39,41,43,46-159,169,172-215,249-251,254-255,497-498,512-658,660-682,685,687,689,691-696,698-702,704-718,760-761">
+ <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
</family>
<family fallback="true" lang="ja" pages="0,32,34-35,46-159,249-250,254-255,498,512-523,525-527,530-538,540-543,545-547,550,552,554-559,561,563-568,570,572-573,575-579,582-584,586-594,596-608,610-612,614-618,620,622-625,627-628,630-631,633-638,640,642-646,649-655,658,660-664,666,669-678,681,695-696,760-761">
<font weight="400" style="normal">NotoSansJP-Regular.otf</font>
</family>
- <family fallback="true" lang="ko" pages="0,17,32,48-50,169,172-215,255">
- <font weight="400" style="normal">NotoSansKR-Regular.otf</font>
- </family>
<family fallback="true" pages="0,32-33,35-39,41,43,48,50,254,496-502,4068,4072">
<font weight="400" style="normal">NotoEmoji-Regular.ttf</font>
</family>
@@ -278,6 +281,14 @@
<font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
</family>
<!--
+ Synthetic bolding is explicitly disabled for DroidSansFallback. The
+ quality of its synthetically bolded glyphs are not good enough to meet
+ Cobalt's standards.
+ -->
+ <family fallback="true" pages="0,14,17,32,48-51,77-159,172-215,249-250,254-255,260">
+ <font weight="400" style="normal" disable_synthetic_bolding="true">DroidSansFallback.ttf</font>
+ </family>
+ <!--
Tai Le and Mongolian are intentionally kept last, to make sure they don't override
the East Asian punctuation for Chinese.
-->
@@ -285,6 +296,6 @@
<font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
</family>
<family fallback="true" pages="0,24,32,36-37,48,254">
- <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
+ <font weight="400" style="normal" >NotoSansMongolian-Regular.ttf</font>
</family>
</familyset>
diff --git a/src/cobalt/csp/csp.gyp b/src/cobalt/csp/csp.gyp
index 87ace1d..a1e9144 100644
--- a/src/cobalt/csp/csp.gyp
+++ b/src/cobalt/csp/csp.gyp
@@ -41,6 +41,7 @@
],
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/network/network.gyp:network',
'<(DEPTH)/net/net.gyp:net',
'<(DEPTH)/googleurl/googleurl.gyp:googleurl',
],
diff --git a/src/cobalt/csp/source_list.cc b/src/cobalt/csp/source_list.cc
index 33f3fe7..1f6fcd1 100644
--- a/src/cobalt/csp/source_list.cc
+++ b/src/cobalt/csp/source_list.cc
@@ -14,14 +14,22 @@
#include "cobalt/csp/source_list.h"
+#include <algorithm>
+
#include "base/base64.h"
#include "base/string_number_conversions.h"
+#include "cobalt/network/socket_address_parser.h"
+#include "googleurl/src/url_canon_ip.h"
+#include "googleurl/src/url_constants.h"
#include "net/base/escape.h"
+#include "net/base/net_util.h"
+#include "starboard/socket.h"
namespace cobalt {
namespace csp {
namespace {
+
bool IsSourceListNone(const char* begin, const char* end) {
SkipWhile<IsAsciiWhitespace>(&begin, end);
@@ -56,9 +64,14 @@
url.SchemeIs("filesystem"));
}
+bool IsInsecureScheme(const GURL& url) {
+ return url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kWsScheme);
+}
+
} // namespace
-SourceList::SourceList(ContentSecurityPolicy* policy,
+SourceList::SourceList(const LocalNetworkCheckerInterface* checker,
+ ContentSecurityPolicy* policy,
const std::string& directive_name)
: policy_(policy),
directive_name_(directive_name),
@@ -66,7 +79,11 @@
allow_star_(false),
allow_inline_(false),
allow_eval_(false),
- hash_algorithms_used_(0) {}
+ allow_insecure_connections_to_local_network_(false),
+ allow_insecure_connections_to_localhost_(false),
+ allow_insecure_connections_to_private_range_(false),
+ hash_algorithms_used_(0),
+ local_network_checker_(checker) {}
bool SourceList::Matches(
const GURL& url,
@@ -85,6 +102,59 @@
}
}
+ if (!url.is_valid() || url.is_empty()) {
+ return false;
+ }
+ // Since url is valid, we can now use possibly_invalid_spec() below.
+ const std::string& valid_spec = url.possibly_invalid_spec();
+
+ const url_parse::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
+
+ if (!url.has_host() || !parsed.host.is_valid() ||
+ !parsed.host.is_nonempty()) {
+ return false;
+ }
+
+ if (!IsInsecureScheme(url)) {
+ return false;
+ }
+
+ if (allow_insecure_connections_to_localhost_) {
+ std::string host;
+#if SB_HAS(IPV6)
+ host = url.HostNoBrackets();
+#else
+ host.append(valid_spec.c_str() + parsed.host.begin,
+ valid_spec.c_str() + parsed.host.begin + parsed.host.len);
+#endif
+ if (net::IsLocalhost(host)) {
+ return true;
+ }
+ }
+
+ /* allow mixed content for local network or private ranges? */
+ if (!(allow_insecure_connections_to_local_network_ ||
+ allow_insecure_connections_to_private_range_)) {
+ return false;
+ }
+
+ SbSocketAddress destination;
+ // Note that GetDestinationForHost will only pass if host is a numeric IP.
+ if (!cobalt::network::ParseSocketAddress(valid_spec.c_str(), parsed.host,
+ &destination)) {
+ return false;
+ }
+
+ if (allow_insecure_connections_to_private_range_ &&
+ local_network_checker_->IsIPInPrivateRange(destination)) {
+ return true;
+ }
+
+ if (allow_insecure_connections_to_local_network_ &&
+ local_network_checker_->IsIPInLocalNetwork(destination)) {
+ return true;
+ }
+
return false;
}
@@ -181,6 +251,21 @@
return true;
}
+ if (directive_name_.compare(ContentSecurityPolicy::kConnectSrc) == 0) {
+ if (LowerCaseEqualsASCII(begin, end, "'cobalt-insecure-localhost'")) {
+ AddSourceLocalhost();
+ return true;
+ }
+ if (LowerCaseEqualsASCII(begin, end, "'cobalt-insecure-local-network'")) {
+ AddSourceLocalNetwork();
+ return true;
+ }
+ if (LowerCaseEqualsASCII(begin, end, "'cobalt-insecure-private-range'")) {
+ AddSourcePrivateRange();
+ return true;
+ }
+ }
+
std::string nonce;
if (!ParseNonce(begin, end, &nonce)) {
return false;
@@ -504,6 +589,18 @@
return ok;
}
+void SourceList::AddSourceLocalhost() {
+ allow_insecure_connections_to_localhost_ = true;
+}
+
+void SourceList::AddSourceLocalNetwork() {
+ allow_insecure_connections_to_local_network_ = true;
+}
+
+void SourceList::AddSourcePrivateRange() {
+ allow_insecure_connections_to_private_range_ = true;
+}
+
void SourceList::AddSourceSelf() { allow_self_ = true; }
void SourceList::AddSourceStar() { allow_star_ = true; }
diff --git a/src/cobalt/csp/source_list.h b/src/cobalt/csp/source_list.h
index c1e906f..f3b3d9c 100644
--- a/src/cobalt/csp/source_list.h
+++ b/src/cobalt/csp/source_list.h
@@ -20,9 +20,12 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
#include "base/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
#include "cobalt/csp/content_security_policy.h"
#include "cobalt/csp/source.h"
+#include "cobalt/network/local_network.h"
#include "googleurl/src/gurl.h"
namespace cobalt {
@@ -30,7 +33,17 @@
class SourceList {
public:
- SourceList(ContentSecurityPolicy* policy, const std::string& directive_name);
+ struct LocalNetworkCheckerInterface {
+ virtual bool IsIPInLocalNetwork(
+ const SbSocketAddress& destination) const = 0;
+ virtual bool IsIPInPrivateRange(
+ const SbSocketAddress& destination) const = 0;
+
+ virtual ~LocalNetworkCheckerInterface() {}
+ };
+
+ SourceList(const LocalNetworkCheckerInterface* checker,
+ ContentSecurityPolicy* policy, const std::string& directive_name);
void Parse(const base::StringPiece& begin);
bool Matches(const GURL& url,
@@ -56,6 +69,9 @@
bool ParseHash(const char* begin, const char* end, DigestValue* hash,
HashAlgorithm* hash_algorithm);
+ void AddSourceLocalhost();
+ void AddSourceLocalNetwork();
+ void AddSourcePrivateRange();
void AddSourceSelf();
void AddSourceStar();
void AddSourceUnsafeInline();
@@ -70,12 +86,19 @@
bool allow_star_;
bool allow_inline_;
bool allow_eval_;
+ bool allow_insecure_connections_to_local_network_;
+ bool allow_insecure_connections_to_localhost_;
+ bool allow_insecure_connections_to_private_range_;
base::hash_set<std::string> nonces_;
// TODO: This is a hash_set in blink. Need to implement
// a hash for HashValue.
std::set<HashValue> hashes_;
uint8 hash_algorithms_used_;
+ const LocalNetworkCheckerInterface* local_network_checker_;
+
+ FRIEND_TEST_ALL_PREFIXES(SourceListTest, TestInsecureLocalNetworkDefault);
+
DISALLOW_COPY_AND_ASSIGN(SourceList);
};
diff --git a/src/cobalt/csp/source_list_directive.cc b/src/cobalt/csp/source_list_directive.cc
index bc67e2a..532f969 100644
--- a/src/cobalt/csp/source_list_directive.cc
+++ b/src/cobalt/csp/source_list_directive.cc
@@ -17,10 +17,21 @@
namespace cobalt {
namespace csp {
+bool SourceListDirective::LocalNetworkChecker::IsIPInLocalNetwork(
+ const SbSocketAddress& destination) const {
+ return ::cobalt::network::IsIPInLocalNetwork(destination);
+}
+
+bool SourceListDirective::LocalNetworkChecker::IsIPInPrivateRange(
+ const SbSocketAddress& destination) const {
+ return ::cobalt::network::IsIPInPrivateRange(destination);
+}
+
SourceListDirective::SourceListDirective(const std::string& name,
const std::string& value,
ContentSecurityPolicy* policy)
- : Directive(name, value, policy), source_list_(policy, name) {
+ : Directive(name, value, policy),
+ source_list_(&local_network_checker_, policy, name) {
source_list_.Parse(base::StringPiece(value));
}
diff --git a/src/cobalt/csp/source_list_directive.h b/src/cobalt/csp/source_list_directive.h
index 117003d..ecb3656 100644
--- a/src/cobalt/csp/source_list_directive.h
+++ b/src/cobalt/csp/source_list_directive.h
@@ -26,6 +26,12 @@
class SourceListDirective : public Directive {
public:
+ class LocalNetworkChecker : public SourceList::LocalNetworkCheckerInterface {
+ public:
+ bool IsIPInLocalNetwork(const SbSocketAddress& destination) const OVERRIDE;
+ bool IsIPInPrivateRange(const SbSocketAddress& destination) const OVERRIDE;
+ };
+
SourceListDirective(const std::string& name, const std::string& value,
ContentSecurityPolicy* policy);
@@ -39,6 +45,8 @@
uint8 hash_algorithms_used() const;
private:
+ // Note: Ensure |local_network_checker_| is initialized before |source_list_|.
+ LocalNetworkChecker local_network_checker_;
SourceList source_list_;
DISALLOW_COPY_AND_ASSIGN(SourceListDirective);
};
diff --git a/src/cobalt/csp/source_list_test.cc b/src/cobalt/csp/source_list_test.cc
index d4ddf1f..febdbce 100644
--- a/src/cobalt/csp/source_list_test.cc
+++ b/src/cobalt/csp/source_list_test.cc
@@ -14,15 +14,30 @@
#include "cobalt/csp/source_list.h"
+#include "base/memory/scoped_ptr.h"
#include "cobalt/csp/content_security_policy.h"
#include "cobalt/csp/source.h"
+#include "cobalt/network/local_network.h"
#include "googleurl/src/gurl.h"
+#include "net/base/net_util.h"
+#include "starboard/memory.h"
+#include "starboard/socket.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace csp {
-namespace {
+using ::testing::_;
+using ::testing::Return;
+
+class MockLocalNetworkChecker
+ : public SourceList::LocalNetworkCheckerInterface {
+ public:
+ MOCK_CONST_METHOD1(IsIPInLocalNetwork, bool(const SbSocketAddress&));
+ MOCK_CONST_METHOD1(IsIPInPrivateRange, bool(const SbSocketAddress&));
+};
+
void ParseSourceList(SourceList* source_list, const std::string& sources) {
base::StringPiece characters(sources);
source_list->Parse(characters);
@@ -36,12 +51,13 @@
}
scoped_ptr<ContentSecurityPolicy> csp_;
+ MockLocalNetworkChecker checker_;
ViolationCallback violation_callback_;
};
TEST_F(SourceListTest, BasicMatchingNone) {
std::string sources = "'none'";
- SourceList source_list(csp_.get(), "script-src");
+ SourceList source_list(&checker_, csp_.get(), "script-src");
ParseSourceList(&source_list, sources);
EXPECT_FALSE(source_list.Matches(GURL("http://example.com/")));
@@ -50,7 +66,7 @@
TEST_F(SourceListTest, BasicMatchingStar) {
std::string sources = "*";
- SourceList source_list(csp_.get(), "script-src");
+ SourceList source_list(&checker_, csp_.get(), "script-src");
ParseSourceList(&source_list, sources);
EXPECT_TRUE(source_list.Matches(GURL("http://example.com/")));
@@ -66,7 +82,7 @@
TEST_F(SourceListTest, BasicMatchingSelf) {
std::string sources = "'self'";
- SourceList source_list(csp_.get(), "script-src");
+ SourceList source_list(&checker_, csp_.get(), "script-src");
ParseSourceList(&source_list, sources);
EXPECT_FALSE(source_list.Matches(GURL("http://example.com/")));
@@ -76,7 +92,7 @@
TEST_F(SourceListTest, BlobMatchingSelf) {
std::string sources = "'self'";
- SourceList source_list(csp_.get(), "script-src");
+ SourceList source_list(&checker_, csp_.get(), "script-src");
ParseSourceList(&source_list, sources);
EXPECT_TRUE(source_list.Matches(GURL("https://example.test/")));
@@ -89,7 +105,7 @@
TEST_F(SourceListTest, BlobMatchingBlob) {
std::string sources = "blob:";
- SourceList source_list(csp_.get(), "script-src");
+ SourceList source_list(&checker_, csp_.get(), "script-src");
ParseSourceList(&source_list, sources);
EXPECT_FALSE(source_list.Matches(GURL("https://example.test/")));
@@ -98,7 +114,7 @@
TEST_F(SourceListTest, BasicMatching) {
std::string sources = "http://example1.com:8000/foo/ https://example2.com/";
- SourceList source_list(csp_.get(), "script-src");
+ SourceList source_list(&checker_, csp_.get(), "script-src");
ParseSourceList(&source_list, sources);
EXPECT_TRUE(source_list.Matches(GURL("http://example1.com:8000/foo/")));
@@ -115,7 +131,7 @@
TEST_F(SourceListTest, WildcardMatching) {
std::string sources =
"http://example1.com:*/foo/ https://*.example2.com/bar/ http://*.test/";
- SourceList source_list(csp_.get(), "script-src");
+ SourceList source_list(&checker_, csp_.get(), "script-src");
ParseSourceList(&source_list, sources);
EXPECT_TRUE(source_list.Matches(GURL("http://example1.com/foo/")));
@@ -141,7 +157,7 @@
TEST_F(SourceListTest, RedirectMatching) {
std::string sources = "http://example1.com/foo/ http://example2.com/bar/";
- SourceList source_list(csp_.get(), "script-src");
+ SourceList source_list(&checker_, csp_.get(), "script-src");
ParseSourceList(&source_list, sources);
EXPECT_TRUE(source_list.Matches(GURL("http://example1.com/foo/"),
@@ -161,7 +177,353 @@
ContentSecurityPolicy::kDidRedirect));
}
-} // namespace
+TEST_F(SourceListTest, TestInsecureLocalhostDefaultInsecureV4) {
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://locaLHost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost./")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://locaLHost./")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost.localdomain/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost.locaLDomain/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost.localdomain./")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://127.0.0.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://127.0.0.1:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://127.0.1.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://127.1.0.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://127.0.0.255/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://127.0.255.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://127.255.0.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://example.localhost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://example.localhost:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://example.localhost./")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://example.locaLHost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://example.locaLHost./")));
+
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://locaLHost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://locaLHost./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost.localdomain/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost.locaLDomain/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost.localdomain./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.1:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.1.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.1.0.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.255/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.255.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.255.0.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://example.locaLHost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://example.locaLHost./")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecureLocalhostDefaultInsecureV6) {
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost6/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost6:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost6./")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost6.localdomain6/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost6.localdomain6:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://localhost6.localdomain6./")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://[::1]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://[::1]:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://[0:0:0:0:0:0:0:1]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://[0:0:0:0:0:0:0:1]:80/")));
+ EXPECT_FALSE(source_list.Matches(
+ GURL("http://[0000:0000:0000:0000:0000:0000:0000:0001]/")));
+ EXPECT_FALSE(source_list.Matches(
+ GURL("http://[0000:0000:0000:0000:0000:0000:0000:0001]:80/")));
+
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost6/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost6:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost6./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost6.localdomain6/")));
+ EXPECT_FALSE(
+ source_list.Matches(GURL("https://localhost6.localdomain6:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost6.localdomain6./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[::1]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[::1]:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[0:0:0:0:0:0:0:1]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[0:0:0:0:0:0:0:1]:80/")));
+ EXPECT_FALSE(source_list.Matches(
+ GURL("https://[0000:0000:0000:0000:0000:0000:0000:0001]/")));
+ EXPECT_FALSE(source_list.Matches(
+ GURL("https://[0000:0000:0000:0000:0000:0000:0000:0001]:80/")));
+}
+#endif
+
+TEST_F(SourceListTest, TestInsecureLocalhostInsecureV4) {
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ std::string sources = "'cobalt-insecure-localhost'";
+ ParseSourceList(&source_list, sources);
+
+ EXPECT_TRUE(source_list.Matches(GURL("http://localhost/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://localhost:80/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://locaLHost/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://localhost.localdomain/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://localhost.locaLDomain/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://127.0.0.1/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://127.0.0.1:80/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://127.0.1.0/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://127.1.0.0/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://127.0.0.255/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://127.0.255.0/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://127.255.0.0/")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecureLocalhostInsecureV6) {
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ std::string sources = "'cobalt-insecure-localhost'";
+ ParseSourceList(&source_list, sources);
+
+ EXPECT_TRUE(source_list.Matches(GURL("http://localhost6/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://localhost6:80/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://localhost6.localdomain6/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://localhost6.localdomain6:80/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://[::1]/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://[::1]:80/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://[0:0:0:0:0:0:0:1]/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://[0:0:0:0:0:0:0:1]:80/")));
+ EXPECT_TRUE(source_list.Matches(
+ GURL("http://[0000:0000:0000:0000:0000:0000:0000:0001]/")));
+ EXPECT_TRUE(source_list.Matches(
+ GURL("http://[0000:0000:0000:0000:0000:0000:0000:0001]:80/")));
+}
+#endif
+
+TEST_F(SourceListTest, TestInsecureLocalhostSecureV4) {
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ std::string sources = "'cobalt-insecure-localhost'";
+ ParseSourceList(&source_list, sources);
+
+ // Per CA/Browser forum, issuance of internal names is now prohibited.
+ // See: https://cabforum.org/internal-names/
+ // But, test it anyway.
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://locaLHost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://locaLHost./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost.localdomain/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost.locaLDomain/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost.localdomain./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.1:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.1.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.1.0.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.255/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.255.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.255.0.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://example.localhost./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://example.locaLHost/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://example.locaLHost./")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecureLocalhostSecureV6) {
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ std::string sources = "'cobalt-insecure-localhost'";
+ ParseSourceList(&source_list, sources);
+
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost6/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost6:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost6./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost6.localdomain6/")));
+ EXPECT_FALSE(
+ source_list.Matches(GURL("https://localhost6.localdomain6:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://localhost6.localdomain6./")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[::1]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[::1]:80/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[0:0:0:0:0:0:0:1]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[0:0:0:0:0:0:0:1]:80/")));
+ EXPECT_FALSE(source_list.Matches(
+ GURL("https://[0000:0000:0000:0000:0000:0000:0000:0001]/")));
+ EXPECT_FALSE(source_list.Matches(
+ GURL("https://[0000:0000:0000:0000:0000:0000:0000:0001]:80/")));
+}
+#endif
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeDefaultV4) {
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+ // These test fail by default, since cobalt-insecure-private-range is not set.
+ EXPECT_FALSE(source_list.Matches(GURL("http://10.0.0.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://172.16.1.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://192.168.1.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://0.0.0.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://255.255.255.255/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://10.0.0.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://172.16.1.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://192.168.1.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://0.0.0.0/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://255.255.255.255/")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecurePrivateRangeDefaultV6) {
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+ // These test fail by default, since cobalt-insecure-private-range is not set.
+ EXPECT_FALSE(source_list.Matches(GURL("http://[fd00::]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://[fd00:1:2:3:4:5::]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[fd00::]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[fd00:1:2:3:4:5::]/")));
+
+ EXPECT_FALSE(source_list.Matches(
+ GURL("https://[2606:2800:220:1:248:1893:25c8:1946]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://[FE80::]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[FE80::]/")));
+}
+#endif
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeV4Private) {
+ std::string sources = "'cobalt-insecure-private-range'";
+
+ EXPECT_CALL(checker_, IsIPInPrivateRange(_)).WillRepeatedly(Return(true));
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ ParseSourceList(&source_list, sources);
+ EXPECT_TRUE(source_list.Matches(GURL("http://10.0.0.1/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://172.16.1.1/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://192.168.1.1/")));
+}
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeV4NotPrivate) {
+ std::string sources = "'cobalt-insecure-private-range'";
+ EXPECT_CALL(checker_, IsIPInPrivateRange(_)).WillRepeatedly(Return(false));
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ ParseSourceList(&source_list, sources);
+ EXPECT_FALSE(source_list.Matches(GURL("http://255.255.255.255/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://0.0.0.0/")));
+}
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeV4Secure) {
+ // These are secure calls.
+ std::string sources = "'cobalt-insecure-private-range'";
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ ParseSourceList(&source_list, sources);
+ EXPECT_FALSE(source_list.Matches(GURL("https://10.0.0.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://172.16.1.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://192.168.1.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://255.255.255.255/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://0.0.0.0/")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecurePrivateRangeV6ULA) {
+ std::string sources = "'cobalt-insecure-private-range'";
+ // These are insecure calls.
+ EXPECT_CALL(checker_, IsIPInPrivateRange(_)).WillRepeatedly(Return(true));
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ ParseSourceList(&source_list, sources);
+ EXPECT_TRUE(source_list.Matches(GURL("http://[fd00::]/")));
+ EXPECT_TRUE(source_list.Matches(GURL("http://[fd00:1:2:3:4:5::]/")));
+}
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeV6NotULA) {
+ std::string sources = "'cobalt-insecure-private-range'";
+ EXPECT_CALL(checker_, IsIPInPrivateRange(_)).WillRepeatedly(Return(false));
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ ParseSourceList(&source_list, sources);
+ EXPECT_FALSE(source_list.Matches(GURL("http://[2620::]/")));
+}
+
+TEST_F(SourceListTest, TestInsecurePrivateRangeV6Secure) {
+ std::string sources = "'cobalt-insecure-private-range'";
+ // These are secure calls.
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ ParseSourceList(&source_list, sources);
+ EXPECT_FALSE(source_list.Matches(GURL("https://[fd00::]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[fd00:1:2:3:4:5::]/")));
+ EXPECT_FALSE(source_list.Matches(
+ GURL("https://[2606:2800:220:1:248:1893:25c8:1946]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[FE80::]/")));
+}
+#endif
+
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV4Local) {
+ std::string sources = "'cobalt-insecure-local-network'";
+
+ // These are insecure calls.
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ EXPECT_CALL(checker_, IsIPInLocalNetwork(_)).WillRepeatedly(Return(false));
+ ParseSourceList(&source_list, sources);
+ EXPECT_FALSE(source_list.Matches(GURL("http://127.0.0.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://172.16.1.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://143.195.170.2/")));
+}
+
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV4NotLocal) {
+ std::string sources = "'cobalt-insecure-local-network'";
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ ParseSourceList(&source_list, sources);
+ EXPECT_CALL(checker_, IsIPInLocalNetwork(_)).WillRepeatedly(Return(true));
+
+ EXPECT_TRUE(source_list.Matches(GURL("http://143.195.170.1/")));
+}
+
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV4Secure) {
+ std::string sources = "'cobalt-insecure-local-network'";
+
+ // These are secure calls.
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ ParseSourceList(&source_list, sources);
+ EXPECT_FALSE(source_list.Matches(GURL("https://127.0.0.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://172.16.1.1/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://143.195.170.2/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://143.195.170.1/")));
+}
+
+#if SB_HAS(IPV6)
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV6Local) {
+ std::string sources = "'cobalt-insecure-local-network'";
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+ EXPECT_CALL(checker_, IsIPInLocalNetwork(_)).WillRepeatedly(Return(true));
+
+ ParseSourceList(&source_list, sources);
+
+ // These are insecure calls.
+ EXPECT_TRUE(source_list.Matches(
+ GURL("http://[2606:2800:220:1:248:1893:25c8:1946]/")));
+}
+
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV6NotLocal) {
+ std::string sources = "'cobalt-insecure-local-network'";
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+
+ EXPECT_CALL(checker_, IsIPInLocalNetwork(_)).WillRepeatedly(Return(false));
+
+ ParseSourceList(&source_list, sources);
+
+ // These are insecure calls.
+ EXPECT_FALSE(source_list.Matches(GURL("http://[2606:1:2:3:4::1]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("http://[::1]/")));
+}
+
+TEST_F(SourceListTest, TestInsecureLocalNetworkDefaultV6Secure) {
+ std::string sources = "'cobalt-insecure-local-network'";
+
+ // These are secure calls.
+ SourceList source_list(&checker_, csp_.get(), "connect-src");
+ ParseSourceList(&source_list, sources);
+
+ EXPECT_FALSE(source_list.Matches(GURL("https://[::1]/")));
+ EXPECT_FALSE(source_list.Matches(GURL("https://[2606:1:2:3:4::1]/")));
+ EXPECT_FALSE(source_list.Matches(
+ GURL("https://[2606:2800:220:1:248:1893:25c8:1946]/")));
+}
+#endif
} // namespace csp
} // namespace cobalt
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y
index bb25ee0..86a9ca0 100644
--- a/src/cobalt/css_parser/grammar.y
+++ b/src/cobalt/css_parser/grammar.y
@@ -6480,18 +6480,22 @@
| kPropertyValueEntryPointToken maybe_whitespace maybe_declaration {
scoped_ptr<PropertyDeclaration> property_declaration($3);
if (property_declaration != NULL) {
- DCHECK_EQ(1, property_declaration->property_values.size()) <<
- "Cannot parse shorthand properties as single property values.";
- if (property_declaration->is_important) {
- parser_impl->LogWarning(
- @1, "!important is not allowed when setting single property values.");
+ if (property_declaration->property_values.size() != 1) {
+ parser_impl->LogError(
+ @1, "Cannot parse shorthand properties as single property values.");
} else {
- if (!property_declaration->property_values[0].value) {
+ if (property_declaration->is_important) {
parser_impl->LogWarning(
- @1, "declaration must have a property value.");
+ @1,
+ "!important is not allowed when setting single property values.");
} else {
- parser_impl->set_property_value(
- property_declaration->property_values[0].value);
+ if (!property_declaration->property_values[0].value) {
+ parser_impl->LogWarning(
+ @1, "declaration must have a property value.");
+ } else {
+ parser_impl->set_property_value(
+ property_declaration->property_values[0].value);
+ }
}
}
}
diff --git a/src/cobalt/css_parser/parser_test.cc b/src/cobalt/css_parser/parser_test.cc
index 2991fe6..e51b2d0 100644
--- a/src/cobalt/css_parser/parser_test.cc
+++ b/src/cobalt/css_parser/parser_test.cc
@@ -8402,8 +8402,9 @@
EXPECT_EQ(cssom::MapToMeshFunction::kUrls,
mtm_function->mesh_spec().mesh_type());
- EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
- EXPECT_EQ(1.5f, mtm_function->vertical_fov());
+ EXPECT_EQ(static_cast<float>(M_PI),
+ mtm_function->horizontal_fov_in_radians());
+ EXPECT_EQ(1.5f, mtm_function->vertical_fov_in_radians());
EXPECT_EQ(mtm_function->stereo_mode()->value(),
cssom::KeywordValue::kMonoscopic);
@@ -8434,8 +8435,9 @@
EXPECT_EQ(cssom::MapToMeshFunction::kEquirectangular,
mtm_function->mesh_spec().mesh_type());
- EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
- EXPECT_EQ(1.5f, mtm_function->vertical_fov());
+ EXPECT_EQ(static_cast<float>(M_PI),
+ mtm_function->horizontal_fov_in_radians());
+ EXPECT_EQ(1.5f, mtm_function->vertical_fov_in_radians());
EXPECT_EQ(mtm_function->stereo_mode()->value(),
cssom::KeywordValue::kMonoscopic);
@@ -8462,8 +8464,9 @@
dynamic_cast<const cssom::MapToMeshFunction*>(filter_list->value()[0]);
ASSERT_TRUE(mtm_function);
- EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
- EXPECT_EQ(1.2f, mtm_function->vertical_fov());
+ EXPECT_EQ(static_cast<float>(M_PI),
+ mtm_function->horizontal_fov_in_radians());
+ EXPECT_EQ(1.2f, mtm_function->vertical_fov_in_radians());
EXPECT_EQ(mtm_function->stereo_mode()->value(),
cssom::KeywordValue::kMonoscopic);
diff --git a/src/cobalt/cssom/css_computed_style_declaration_test.cc b/src/cobalt/cssom/css_computed_style_declaration_test.cc
index dc77b98..4eb82c3 100644
--- a/src/cobalt/cssom/css_computed_style_declaration_test.cc
+++ b/src/cobalt/cssom/css_computed_style_declaration_test.cc
@@ -17,7 +17,7 @@
#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/dom/testing/fake_exception_state.h"
#include "cobalt/script/exception_state.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -27,29 +27,7 @@
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 SetSimpleExceptionWithArgs(script::MessageType /*message_type*/,
- int /*dummy*/, ...) 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
+using dom::testing::FakeExceptionState;
TEST(CSSComputedStyleDeclarationTest, CSSTextSetterRaisesException) {
scoped_refptr<CSSComputedStyleDeclaration> style =
diff --git a/src/cobalt/cssom/map_to_mesh_function.cc b/src/cobalt/cssom/map_to_mesh_function.cc
index c27f867..800ffa6 100644
--- a/src/cobalt/cssom/map_to_mesh_function.cc
+++ b/src/cobalt/cssom/map_to_mesh_function.cc
@@ -44,8 +44,8 @@
}
}
- result.append(base::StringPrintf(", %.7grad", horizontal_fov()));
- result.append(base::StringPrintf(" %.7grad, ", vertical_fov()));
+ result.append(base::StringPrintf(", %.7grad", horizontal_fov_in_radians()));
+ result.append(base::StringPrintf(" %.7grad, ", vertical_fov_in_radians()));
result.append("matrix3d(");
for (int col = 0; col <= 3; ++col) {
@@ -107,8 +107,8 @@
bool MapToMeshFunction::operator==(const MapToMeshFunction& rhs) const {
if (mesh_spec().mesh_type() != rhs.mesh_spec().mesh_type() ||
- horizontal_fov() != rhs.horizontal_fov() ||
- horizontal_fov() != rhs.horizontal_fov() ||
+ horizontal_fov_in_radians() != rhs.horizontal_fov_in_radians() ||
+ vertical_fov_in_radians() != rhs.vertical_fov_in_radians() ||
!stereo_mode()->Equals(*rhs.stereo_mode())) {
return false;
}
diff --git a/src/cobalt/cssom/map_to_mesh_function.h b/src/cobalt/cssom/map_to_mesh_function.h
index 974161c..21a7f67 100644
--- a/src/cobalt/cssom/map_to_mesh_function.h
+++ b/src/cobalt/cssom/map_to_mesh_function.h
@@ -98,12 +98,13 @@
DISALLOW_COPY_AND_ASSIGN(MeshSpec);
};
- MapToMeshFunction(scoped_ptr<MeshSpec> mesh_spec, float horizontal_fov,
- float vertical_fov, const glm::mat4& transform,
+ MapToMeshFunction(scoped_ptr<MeshSpec> mesh_spec,
+ float horizontal_fov_in_radians,
+ float vertical_fov_in_radians, const glm::mat4& transform,
const scoped_refptr<KeywordValue>& stereo_mode)
: mesh_spec_(mesh_spec.Pass()),
- horizontal_fov_(horizontal_fov),
- vertical_fov_(vertical_fov),
+ horizontal_fov_in_radians_(horizontal_fov_in_radians),
+ vertical_fov_in_radians_(vertical_fov_in_radians),
transform_(transform),
stereo_mode_(stereo_mode) {
DCHECK(mesh_spec_);
@@ -113,25 +114,25 @@
// Alternate constructor for mesh URL lists.
MapToMeshFunction(const scoped_refptr<PropertyValue>& mesh_url,
ResolutionMatchedMeshListBuilder resolution_matched_meshes,
- float horizontal_fov, float vertical_fov,
- const glm::mat4& transform,
+ float horizontal_fov_in_radians,
+ float vertical_fov_in_radians, const glm::mat4& transform,
const scoped_refptr<KeywordValue>& stereo_mode)
: mesh_spec_(
new MeshSpec(kUrls, mesh_url, resolution_matched_meshes.Pass())),
- horizontal_fov_(horizontal_fov),
- vertical_fov_(vertical_fov),
+ horizontal_fov_in_radians_(horizontal_fov_in_radians),
+ vertical_fov_in_radians_(vertical_fov_in_radians),
transform_(transform),
stereo_mode_(stereo_mode) {
DCHECK(stereo_mode_);
}
// Alternate constructor for built-in meshes.
- MapToMeshFunction(MeshSpecType spec_type, float horizontal_fov,
- float vertical_fov, const glm::mat4& transform,
+ MapToMeshFunction(MeshSpecType spec_type, float horizontal_fov_in_radians,
+ float vertical_fov_in_radians, const glm::mat4& transform,
const scoped_refptr<KeywordValue>& stereo_mode)
: mesh_spec_(new MeshSpec(spec_type)),
- horizontal_fov_(horizontal_fov),
- vertical_fov_(vertical_fov),
+ horizontal_fov_in_radians_(horizontal_fov_in_radians),
+ vertical_fov_in_radians_(vertical_fov_in_radians),
transform_(transform),
stereo_mode_(stereo_mode) {
DCHECK(stereo_mode_);
@@ -140,8 +141,8 @@
~MapToMeshFunction() OVERRIDE {}
const MeshSpec& mesh_spec() const { return *mesh_spec_; }
- float horizontal_fov() const { return horizontal_fov_; }
- float vertical_fov() const { return vertical_fov_; }
+ float horizontal_fov_in_radians() const { return horizontal_fov_in_radians_; }
+ float vertical_fov_in_radians() const { return vertical_fov_in_radians_; }
const glm::mat4& transform() const { return transform_; }
const scoped_refptr<KeywordValue>& stereo_mode() const {
return stereo_mode_;
@@ -158,8 +159,8 @@
private:
const scoped_ptr<MeshSpec> mesh_spec_;
- const float horizontal_fov_;
- const float vertical_fov_;
+ const float horizontal_fov_in_radians_;
+ const float vertical_fov_in_radians_;
const glm::mat4 transform_;
const scoped_refptr<KeywordValue> stereo_mode_;
diff --git a/src/cobalt/debug/debug_web_server.cc b/src/cobalt/debug/debug_web_server.cc
index 32711db..fac8535 100644
--- a/src/cobalt/debug/debug_web_server.cc
+++ b/src/cobalt/debug/debug_web_server.cc
@@ -89,11 +89,39 @@
ip_addr.FromSockAddr(reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
DCHECK(result);
#elif defined(OS_STARBOARD)
+
+#if SB_API_VERSION >= 4
+ SbSocketAddress local_ip;
+ SbMemorySet(&(local_ip.address), 0, sizeof(local_ip.address));
+
+ bool result = false;
+
+ // Prefer IPv4 addresses, as they're easier to type for debugging.
+ SbSocketAddressType address_types[] = {kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6};
+
+ for (std::size_t i = 0; i != SB_ARRAY_SIZE(address_types); ++i) {
+ SbSocketAddress destination;
+ SbMemorySet(&(destination.address), 0, sizeof(destination.address));
+ destination.type = address_types[i];
+ if (!SbSocketGetInterfaceAddress(&destination, &local_ip, NULL)) {
+ continue;
+ }
+
+ if (ip_addr.FromSbSocketAddress(&local_ip)) {
+ result = true;
+ break;
+ }
+ }
+
+ DCHECK(result);
+#else
SbSocketAddress sb_address;
bool result = SbSocketGetLocalInterfaceAddress(&sb_address);
DCHECK(result);
result = ip_addr.FromSbSocketAddress(&sb_address);
- DCHECK(result);
+#endif // SB_API_VERSION >= 4
+
#else
#error "Not Implemented."
#endif
diff --git a/src/cobalt/doc/branching.md b/src/cobalt/doc/branching.md
new file mode 100644
index 0000000..ae41373
--- /dev/null
+++ b/src/cobalt/doc/branching.md
@@ -0,0 +1,82 @@
+# Cobalt Branching
+
+*(This document assumes you are already familiar
+with [Cobalt Versioning][versioning] practices.)*
+
+The Cobalt project uses git branches for two main purposes:
+
+ 1. To solidify a release as it is hardened for production.
+ 2. To isolate `master` branch developers from risky or disruptive work.
+
+
+## Release Branches
+
+A "Cobalt release" is an official, tested version of Cobalt that is intended to
+be deployable to production. We branch for releases to allow development to
+continue on the `master` branch while stabilizing and finalizing a set of sources
+for release.
+
+
+### Release Timeline
+
+ 1. Feature work is done in the `master` branch.
+
+ 2. Once all feature work is completed,
+ a [Release Number](versioning.md#Release-Number) is reserved and a branch
+ is created. The branch will be named `rc_<release-number>` to indicate that
+ the branch represents a release candidate for version `<release-number>`,
+ and not the final release.
+
+ An RC announcement will be made to [cobalt-dev@googlegroups.com][cobalt-dev].
+
+ It should be understood that RC branches are being stabilized and are
+ subject to changes.
+
+ 3. As bugs are discovered and feedback received from partners, fixes will be
+ cherry-picked into the release candidate branch. Each update to
+ `rc_<release-number>` will have an
+ increasing [Build ID](versioning.md#Build-ID).
+
+ 4. As time goes on, the number of cherry-picks will decrease in number and
+ scope.
+
+ 5. Once the branch is deemed to be feature-complete and stable, it will be
+ **renamed** to `release_<release-number>`, representing an actual Cobalt
+ release. This means that the RC branch will be removed and the release
+ branch will permanently take its place.
+
+ A release announcement will be made
+ to [cobalt-dev@googlegroups.com][cobalt-dev].
+
+ 6. The Cobalt project will strongly resist updating released versions, but may
+ need to back-port fixes from time to time. This is especially true if there
+ are any severe security flaws that need to be addressed. So cherry-picks
+ are still possible into `release_<release-number>` release branches.
+
+ In these rare cases, announcements will be made
+ to [cobalt-dev@googlegroups.com][cobalt-dev].
+
+
+## Work Branches
+
+If a set of work is deemed to be particularly risky or disruptive, or if a
+serious contributor wants a sandbox to prepare an extensive patch, a work branch
+may be created to facilitate such development.
+
+Work branch names are of the form `work_<topic>`, where `<topic>` is the purpose
+for which the branch was created. Work branches are generally in use by a
+specific and limited set of people, and may disappear at any time.
+
+
+## Capitalization, Branch Names, and You
+
+Some older branches followed an `ALL_CAPS_SNAKE_CASE` naming convention. Going
+forward, branch names will follow a `lower_snake_case` naming convention.
+
+
+## Other Reading
+
+ * [Cobalt Versioning][versioning]
+
+[cobalt-dev]: https://groups.google.com/forum/#!forum/cobalt-dev "cobalt-dev@googlegroups.com"
+[versioning]: versioning.md "Cobalt Versioning"
diff --git a/src/cobalt/doc/performance_tuning.md b/src/cobalt/doc/performance_tuning.md
new file mode 100644
index 0000000..b4cfd88
--- /dev/null
+++ b/src/cobalt/doc/performance_tuning.md
@@ -0,0 +1,373 @@
+# Performance Tuning
+
+Cobalt is designed to choose sensible parameters for all performance-related
+options and parameters, however sometimes these need to be explicitly set
+to allow Cobalt to run optimally for a specific platform. This document
+discusses some of the tweakable parameters in Cobalt that can have an
+affect on performance.
+
+A number of tweaks are listed below in no particular order. Each item
+has a set of tags keywords to make it easy to search for items related
+to a specific type of performance metric (e.g. "framerate").
+
+Many of the tweaks involve adding a new gyp variable to your platform's
+`gyp_configuration.gypi` file. The default values for these variables are
+defined in [`base.gypi`](../build/config/base.gypi).
+
+### Use a Release Build
+
+Cobalt has a number of different build configurations (e.g. "debug", "devel",
+"qa" and "gold" in slowest-to-fastest order), with varying degrees of
+optimizations enabled. For example, while "devel" has compiler optimizations
+enabled, it does not disable DCHECKS (debug assertions) which can decrease
+Cobalt's performance. The "qa" build is most similar to "gold", but it still
+has some debug features enabled (such as the debug console which can consume
+memory, and decrease performance while it is visible). For the best
+performance, build Cobalt in the "gold" configuration.
+
+**Tags:** *framerate, startup, browse-to-watch, cpu memory, input latency.*
+
+
+### Framerate throttling
+
+If you're willing to accept a lower framerate, there is potential that
+JavaScript execution can be made to run faster (which can improve startup
+time, browse-to-watch time, and input latency). Without any special
+settings in place, the renderer will attempt to render each frame as fast
+as it can, limited only by the display's refresh rate, which is usually 60Hz.
+By artificially throttling this rate to a lower value, like 30Hz, CPU
+resources can be freed to work on other tasks. You can enable framerate
+throttling by setting a value for `cobalt_minimum_frame_time_in_milliseconds`
+in your platform's `gyp_configuration.gypi` file. Setting it to 33, for
+example, will throttle Cobalt's renderer to 30 frames per second.
+
+**Tags:** *gyp_configuration.gypi, framerate, startup, browse-to-watch,
+ input latency.*
+
+
+### Image cache capacity
+
+Cobalt's image cache is used to cache decoded image data. The image data
+in the image cache is stored as a texture, and so it will occupy GPU memory.
+The image cache capacity dictates how long images will be kept resident in
+memory even if they are not currently visible on the web page. By reducing
+this value, you can lower GPU memory usage, at the cost of having Cobalt
+make more network requests and image decodes for previously seen images.
+Cobalt will automatically set the image cache capacity to a reasonable value,
+but if you wish to override this, you can do so by setting the
+`image_cache_size_in_bytes` variable in your `gyp_configuration.gypi` file. For
+the YouTube web app, we have found that at 1080p, 32MB will allow around
+5 thumbnail shelves to stay resident at a time, with 720p and 4K resolutions
+using proportionally less and more memory, respectively.
+
+**Tags:** *gyp_configuration.gypi, cpu memory, gpu memory.*
+
+
+### Image cache capacity multiplier during video playback
+
+Cobalt provides a feature where the image cache capacity will be reduced
+as soon as video playback begins. This can be useful for reducing peak
+GPU memory usage, which usually occurs during video playback. The
+downside to lowering the image cache during video playback is that it
+may need to evict some images when the capacity changes, and so it is
+more likely that Cobalt will have to re-download and decode images after
+returning from video playback. Note that this feature is not well tested.
+The feature can be activated by setting
+`image_cache_capacity_multiplier_when_playing_video` to a value between
+`0.0` and `1.0` in your `gyp_configuration.gypi` file. The image cache
+capacity will be multiplied by this value during video playback.
+
+**Tags:** *gyp_configuration.gypi, gpu memory.*
+
+
+### Scratch Surface cache capacity
+
+This only affects GLES renderers. While rasterizing a frame, it is
+occasionally necessary to render to a temporary offscreen surface and then
+apply that surface to the original render target. Offscreen surface
+rendering may also need to be performed multiple times per frame. The
+scratch surface cache will keep allocated a set of scratch textures that
+will be reused (within and across frames) for offscreen rendering. Reusing
+offscreen surfaces allows render target allocations, which can be expensive
+on some platforms, to be minimized. However, it has been found that some
+platforms (especially those with tiled renderers, like the Raspberry Pi's
+Broadcom VideoCore), reading and writing again and again to the same texture
+can result in performance degradation. Memory may also be potentially saved
+by disabling this cache, since when it is enabled, if the cache is filled, it
+may be occupying memory that it is not currently using. This setting can
+be adjusted by setting `surface_cache_size_in_bytes` in your
+`gyp_configuration.gypi` file. A value of `0` will disable the surface cache.
+
+**Tags:** *gyp_configuration.gypi, gpu memory, framerate.*
+
+
+### Glyph atlas size
+
+This only affects GLES renderers. Skia sets up glyph atlases to which
+it software rasterizes glyphs the first time they are encountered, and
+from which the glyphs are used as textures for hardware accelerated glyph
+rendering to the render target. Adjusting this value will adjust
+GPU memory usage, but at the cost of performance as text glyphs will be
+less likely to be cached already. Note that if experimenting with
+modifications to this setting, be sure to test many languages, as some
+are more demanding (e.g. Chinese and Japanese) on the glyph cache than
+others. This value can be adjusted by changing the values of
+the `skia_glyph_atlas_width` and `skia_glyph_atlas_height` variables in your
+`gyp_configuration.gypi` file. Note that by default, these will be
+automatically configured by Cobalt to values found to be optimal for
+the application's resolution.
+
+**Tags:** *gyp_configuration.gypi, gpu memory, input latency, framerate.*
+
+
+### Software surface cache capacity
+
+This only affects Starboard Blitter API renderers. The Starboard Blitter API
+has only limited support for rendering special effects, so often Cobalt will
+have to fallback to a software rasterizer for rendering certain visual
+elements (most notably, text). In order to avoid expensive software
+renders, the results are cached and re-used across frames. The software
+surface cache is crucial to achieving an acceptable framerate on Blitter API
+platforms. The size of this cache is specified by the
+`software_surface_cache_size_in_bytes` variable in `gyp_configuration.gypi`.
+
+**Tags:** *gyp_configuration.gypi, gpu memory, framerate.*
+
+
+### Toggle Just-In-Time JavaScript Compilation
+
+Just-in-time (JIT) compilation of JavaScript is well known to significantly
+improve the speed of JavaScript execution. However, in the context of Cobalt
+and its web apps (like YouTube's HTML5 TV application), JITting may not be
+the best or fastest thing to do. Enabling JIT can result in Cobalt using
+more memory (to store compiled code) and can also actually slow down
+JavaScript execution (e.g. time must now be spent compiling code). It is
+recommended that JIT support be left disabled, but you can experiment with
+it by setting the `cobalt_enable_jit` `gyp_configuration.gypi` variable to `1`
+to enable JIT, or `0` to disable it.
+
+**Tags:** *gyp_configuration.gypi, startup, browse-to-watch, input latency,
+ cpu memory.*
+
+
+### Garbage collection trigger threshold
+
+The SpiderMonkey JavaScript engine provides a parameter that describes how
+aggressive it will be at performing garbage collections to reduce memory
+usage. By lowering this value, garbage collection will occur more often,
+thus reducing performance, but memory usage will be lowered. We have found
+that performance reductions are modest, so it is not unreasonable to set this
+value to something low like 1MB if your platform is low on memory. This
+setting can be adjusted by setting the value of
+`mozjs_garbage_collection_threshold_in_bytes` in your `gyp_configuration.gypi`
+file.
+
+**Tags:** *gyp_configuration.gypi, startup, browse-to-watch, input latency,
+ cpu memory.*
+
+
+### Ensure that you are not requesting Cobalt to render unchanging frames
+
+Some platforms require that the display buffer is swapped frequently, and
+so in these cases Cobalt will render the scene every frame, even if it is
+not changing, which consumes CPU resources. This behavior is defined by the
+value of `SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER` in your platform's
+`configuration_public.h` file. Unless your platform is restricted in this
+aspect, you should ensure that `SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER`
+is set to `0`.
+
+**Tags:** *configuration_public.h, startup, browse-to-watch, input latency,
+ framerate.*
+
+
+### Ensure that Cobalt is rendering only regions that change
+
+Cobalt has logic to detect which part of the frame has been affected by
+animations and can be configured to only render to that region. However,
+this feature requires support from the driver for GLES platforms. In
+particular, `eglChooseConfig()` will first be called with
+`EGL_SWAP_BEHAVIOR_PRESERVED_BIT` set in its attribute list. If this
+fails, Cobalt will call eglChooseConfig() again without
+`EGL_SWAP_BEHAVIOR_PRESERVED_BIT` set and dirty region rendering will
+be disabled. By having Cobalt render only small parts of the screen,
+CPU (and GPU) resources can be freed to work on other tasks. This can
+especially affect startup time since usually only a small part of the
+screen is updating (e.g. displaying an animated spinner). Thus, if
+possible, ensure that your EGL/GLES driver supports
+`EGL_SWAP_BEHAVIOR_PRESERVED_BIT`. Note that it is possible (but not
+necessary) that GLES drivers will implement this feature by allocating a new
+offscreen buffer, which can significantly affect GPU memory usage.
+
+**Tags:** *startup, framerate, gpu memory.*
+
+
+### Ensure that thread priorities are respected
+
+Cobalt makes use of thread priorities to ensure that animations remain smooth
+even while JavaScript is being executed, and to ensure that JavaScript is
+processed (e.g. in response to a key press) before images are decoded. Thus
+having support for priorities can improve the overall performance of the
+application. To enable thread priority support, you should set the value
+of `SB_HAS_THREAD_PRIORITY_SUPPORT` to `1` in your `configuration_public.h`
+file, and then also ensure that your platform's implementation of
+`SbThreadCreate()` properly forwards the priority parameter down to the
+platform.
+
+**Tags:** *configuration_public.h, framerate, startup, browse-to-watch,
+ input latency.*
+
+
+### Tweak compiler/linker optimization flags
+
+Huge performance improvements can be obtained by ensuring that the right
+optimizations are enabled by your compiler and linker flag settings. You
+can set these up within `gyp_configuration.gypi` by adjusting the list
+variables `compiler_flags` and `linker_flags`. See also
+`compiler_flags_gold` and `linker_flags_gold` which describe flags that
+apply only to gold builds where performance is critical. Note that
+unless you explicitly set this up, it is unlikely that compiler/linker
+flags will carry over from external shell environment settings; they
+must be set explicitly in `gyp_configuration.gypi`.
+
+#### Link Time Optimization (LTO)
+If your toolchain supports it, it is recommended that you enable the LTO
+optimization, as it has been reported to yield significant performance
+improvements in many high profile projects.
+
+#### The GCC '-mplt' flag for MIPS architectures
+The '-mplt' flag has been found to improve all around performance by
+~20% on MIPS architecture platforms. If your platform has a MIPS
+architecture, it is suggested that you enable this flag in gold builds.
+
+**Tags:** *gyp_configuration.gypi, framerate, startup, browse-to-watch,
+ input latency.*
+
+
+### Close "Stats for Nerds" when measuring performance
+
+The YouTube web app offers a feature called "Stats for Nerds" that enables
+a stats overlay to appear on the screen during video playback. Rendering
+this overlay requires a significant amount of processing, so it is
+recommended that all performance evaluation is done without the
+"Stats for Nerds" overlay active. This can greatly affect browse-to-watch
+time and potentially affect the video frame drop rate.
+
+**Tags:** *browse-to-watch, framerate, youtube.*
+
+
+### Close the debug console when measuring performance
+
+Cobalt provides a debug console in non-gold builds to allow the display
+of variables overlayed on top of the application. This can be helpful
+for debugging issues and keeping track of things like app lifetime, but
+the debug console consumes significant resources when it is visible in order
+to render it, so it should be hidden when performance is being evaluated.
+
+**Tags:** *framerate, startup, browse-to-watch, input latency.*
+
+
+### Toggle between dlmalloc and system allocator
+
+Cobalt includes dlmalloc and can be configured to use it to handle all
+memory allocations. It should be carefully evaluated however whether
+dlmalloc performs better or worse than your system allocator, in terms
+of both memory fragmentation efficiency as well as runtime performance.
+To use dlmalloc, you should adjust your starboard_platform.gyp file to
+use the Starboard [`starboard/memory.h`](../../starboard/memory.h) function
+implementations defined in
+[`starboard/shared/dlmalloc/`](../../starboard/shared/dlmalloc). To use
+your system allocator, you should adjust your starboard_platform.gyp file
+to use the Starboard [`starboard/memory.h`](../../starboard/memory.h) function
+implementations defined in
+[`starboard/shared/iso/`](../../starboard/shared/iso).
+
+**Tags:** *framerate, startup, browse-to-watch, input latency, cpu memory.*
+
+
+### Media buffer allocation strategy
+
+During video playback, memory is reserved by Cobalt to contain the encoded
+media data (separated into video and audio), and we refer to this memory
+as the media buffers. By default, Cobalt pre-allocates the memory and
+wraps it with a custom allocator, in order to avoid fragmentation of main
+memory. However, depending on your platform and your system allocator,
+overall memory usage may improve if media buffer allocations were made
+normally via the system allocator instead. TODO: Unfortunately, there is
+currently no flip to switch to toggle one method or the other, however
+we should look into this because switching to using the system allocator
+has been found to reduce memory usage by ~5MB. In the meantime, it can
+be hacked by modifying
+[`cobalt/media/shell_media_platform_starboard.cc`](../media/shell_media_platform_starboard.cc).
+Note also that if you choose to pre-allocate memory, for 1080p video it has been
+found that 24MB is a good media buffer size. The pre-allocated media buffer
+capacity size can be adjusted by modifying the value of
+`SB_MEDIA_MAIN_BUFFER_BUDGET` in your platform's `configuration_public.h` file.
+
+**Tags:** *configuration_public.h, cpu memory.*
+
+
+### Avoid using a the YouTube web app FPS counter (i.e. "?fps=1")
+
+The YouTube web app is able to display a Frames Per Second (FPS) counter in the
+corner when the URL parameter "fps=1" is set. Unfortunately, activating this
+timer will cause Cobalt to re-layout and re-render the scene frequently in
+order to update the FPS counter. Instead, we recommend instead to either
+measure the framerate in the GLES driver and periodically printing it, or
+hacking Cobalt to measure the framerate and periodically print it. In order to
+hack in an FPS counter, you will want to look at the
+`HardwareRasterizer::Impl::Submit()` function in
+[`cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc`](../renderer/rasterizer/skia/hardware_rasterizer.cc).
+The work required to update the counter has the potential to affect many
+aspects of performance. TODO: Cobalt should add a command line switch to
+enable printing of the framerate in gold builds.
+
+**Tags:** *framerate, startup, browse-to-watch, input latency,*
+
+
+### Implement hardware image decoding
+
+The Starboard header file [`starboard/image.h`](../../starboard/image.h) defines
+functions that allow platforms to implement hardware-accelerated image
+decoding, if available. In particular, if `SbImageIsDecodeSupported()` returns
+true for the specified mime type and output format, then instead of using the
+software-based libpng or libjpeg libraries, Cobalt will instead call
+`SbImageDecode()`. `SbImageDecode()` is expected to return a decoded image as
+a `SbDecodeTarget` option, from which Cobalt will extract a GL texture or
+Blitter API surface object when rendering. If non-CPU hardware is used to
+decode images, it would alleviate the load on the CPU, and possibly also
+increase the speed at which images can be decoded.
+
+**Tags:** *startup, browse-to-watch, input latency.*
+
+
+### Use Chromium's about:tracing tool to debug Cobalt performance
+
+Cobalt has support for generating profiling data that is viewable through
+Chromium's about:tracing tool. This feature is available in all Cobalt
+configurations except for "gold" ("qa" is the best build to use for performance
+investigations here). There are currently two ways to tell Cobalt
+to generate this data:
+
+1. The command line option, "--timed_trace=XX" will instruct Cobalt to trace
+ upon startup, for XX seconds (e.g. "--timed_trace=25"). When completed,
+ the output will be written to the file `timed_trace.json`.
+2. Using the debug console (hit CTRL+O on a keyboard once or twice), type in the
+ command "d.trace()" and hit enter. Cobalt will begin a trace. After
+ some time has passed (and presumably you have performed some actions), you
+ can open the debug console again and type "d.trace()" again to end the trace.
+ The trace output will be written to the file `triggered_trace.json`.
+
+The directory the output files will be placed within is the directory that the
+Starboard function `SbSystemGetPath()` returns with a `path_id` of
+`kSbSystemPathDebugOutputDirectory`, so you may need to check your
+implementation of `SbSystemGetPath()` to discover where this is.
+
+Once the trace file is created, it can be opened in Chrome by navigating to
+`about:tracing` or `chrome://tracing`, clicking the "Load" button near the top
+left, and then opening the JSON file created earlier.
+
+Of particular interest in the output view is the `MainWebModule` thread where
+JavaScript and layout are executed, and `Rasterizer` where per-frame rendering
+takes place.
+
+**Tags:** *framerate, startup, browse-to-watch, input latency.*
diff --git a/src/cobalt/doc/spherical_video.md b/src/cobalt/doc/spherical_video.md
new file mode 100644
index 0000000..2a5adf4
--- /dev/null
+++ b/src/cobalt/doc/spherical_video.md
@@ -0,0 +1,40 @@
+# Enabling Spherical Video in Cobalt
+
+Cobalt supports playback of 360 spherical videos. Cobalt does not expose
+this support to web applications through WebGL (which is currently
+unimplemented in Cobalt), but rather through a custom `map-to-mesh` CSS
+filter and custom [`window.camera3D` Web API](../dom/camera_3d.idl). Support
+for spherical video in Cobalt requires a GLES rasterizer (i.e. it is not
+supported for the Starboard Blitter API), and Starboard platform support for
+the player
+[decode-to-texture output mode](../starboard/doc/howto_decode_to_texture.md).
+
+## Enabling spherical video support
+
+Spherical video support requires `map-to-mesh` support, which must be
+explicitly enabled in your Starboard platform's `gyp_configuration.gypi` file.
+
+In particular, your platform's `gyp_configuration.gypi` file should contain the
+line,
+
+```
+'enable_map_to_mesh': 1,
+```
+
+When `enable_map_to_mesh` is set to 1, Cobalt will make the `map-to-mesh`
+CSS filter parseable. The web app can then detect whether the browser,
+Cobalt, supports spherical video by evaluating the following JavaScript:
+
+```
+function checkForMapToMeshSupport() {
+ return 'CSS' in window && 'supports' in window.CSS &&
+ CSS.supports(
+ 'filter',
+ 'map-to-mesh(url(p.msh), 100deg 60deg,' +
+ 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1),' +
+ 'monoscopic)');
+}
+```
+
+Finally, it is required that your platform provides
+[decode-to-texture support](../starboard/doc/howto_decode_to_texture.md).
diff --git a/src/cobalt/doc/versioning.md b/src/cobalt/doc/versioning.md
new file mode 100644
index 0000000..b945062
--- /dev/null
+++ b/src/cobalt/doc/versioning.md
@@ -0,0 +1,43 @@
+# Cobalt Versioning
+
+A Cobalt version consists of two components: The Release Number and the Build
+ID. Some real historical Cobalt version examples:
+
+ * `2.15147`
+ * `3.16138`
+ * `4.16134`
+ * `6.18971`
+
+You get the idea. The number before the dot is the "Release Number." The number
+after the dot is the "Build ID."
+
+## Release Number
+
+A "Cobalt release" is an official, tested version of Cobalt that is intended to
+be deployable to production. The "Release Number" is a single counting number,
+starting at "1" for our first release, and increasing by one for every release
+thereafter. This number is checked into [`src/cobalt/version.h`](../version.h),
+and represents **coarse** information about the state of the source tree when we
+decided to do a release.
+
+It is important to note that there are no point releases, or major or minor
+releases. Each release gets its own unique counting number.
+
+## Build ID
+
+The Cobalt Build ID represents **fine-grained** information about the state of
+the source tree for a given build. An internal Cobalt build server generates a
+monotonically increasing number for each unique set of sources that it
+sees. When an open-source release is published,
+a [`src/cobalt/build/build.id`](../build/build.id) file is included that
+specifies the build ID of that source release. The Cobalt team can reproduce the
+exact sources for a given Build ID.
+
+Note that since the Build ID always increases with time, it means that the
+latest version of an older Cobalt release can have a higher Build ID than the
+latest version of a new Cobalt release. An example from above: Cobalt `4.16134`
+was produced earlier than Cobalt `3.16138`, thus has a lower Build ID.
+
+## Other Reading
+
+ * [Cobalt Branching](branching.md)
diff --git a/src/cobalt/dom/array_buffer.cc b/src/cobalt/dom/array_buffer.cc
index 82046d1..9b2d29a 100644
--- a/src/cobalt/dom/array_buffer.cc
+++ b/src/cobalt/dom/array_buffer.cc
@@ -20,6 +20,7 @@
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/dom/dom_settings.h"
#include "cobalt/script/javascript_engine.h"
+#include "nb/memory_scope.h"
namespace cobalt {
namespace dom {
@@ -95,6 +96,7 @@
void ArrayBuffer::Data::Initialize(script::EnvironmentSettings* settings,
size_t size) {
+ TRACK_MEMORY_SCOPE("DOM");
if (settings) {
DOMSettings* dom_settings =
base::polymorphic_downcast<dom::DOMSettings*>(settings);
@@ -143,6 +145,7 @@
}
void ArrayBuffer::Cache::TryToOffload() {
+ TRACK_MEMORY_SCOPE("DOM");
if (total_size_in_main_memory_ <= maximum_size_in_main_memory_) {
return;
}
@@ -200,6 +203,7 @@
scoped_refptr<ArrayBuffer> ArrayBuffer::Slice(
script::EnvironmentSettings* settings, int start, int end) const {
+ TRACK_MEMORY_SCOPE("DOM");
int clamped_start;
int clamped_end;
ClampRange(start, end, static_cast<int>(byte_length()), &clamped_start,
diff --git a/src/cobalt/dom/array_buffer_view.cc b/src/cobalt/dom/array_buffer_view.cc
index d3b0868..a9d35d2 100644
--- a/src/cobalt/dom/array_buffer_view.cc
+++ b/src/cobalt/dom/array_buffer_view.cc
@@ -17,6 +17,11 @@
namespace cobalt {
namespace dom {
+const char* ArrayBufferView::kWrongByteOffsetMultipleErrorFormat =
+ "Byte offset should be a multiple of %d.";
+const char* ArrayBufferView::kWrongByteLengthMultipleErrorFormat =
+ "Byte length should be a multiple of %d.";
+
ArrayBufferView::ArrayBufferView(const scoped_refptr<ArrayBuffer>& buffer)
: buffer_(buffer), byte_offset_(0), byte_length_(buffer->byte_length()) {}
diff --git a/src/cobalt/dom/array_buffer_view.h b/src/cobalt/dom/array_buffer_view.h
index 5bdb1b4..3db1a07 100644
--- a/src/cobalt/dom/array_buffer_view.h
+++ b/src/cobalt/dom/array_buffer_view.h
@@ -42,6 +42,9 @@
uint32 byte_length);
~ArrayBufferView();
+ static const char* kWrongByteOffsetMultipleErrorFormat;
+ static const char* kWrongByteLengthMultipleErrorFormat;
+
private:
scoped_refptr<ArrayBuffer> buffer_;
uint32 byte_offset_;
diff --git a/src/cobalt/dom/blob.cc b/src/cobalt/dom/blob.cc
new file mode 100644
index 0000000..0a47116
--- /dev/null
+++ b/src/cobalt/dom/blob.cc
@@ -0,0 +1,99 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/blob.h"
+
+#include "base/lazy_instance.h"
+#include "cobalt/dom/blob_property_bag.h"
+
+namespace cobalt {
+namespace dom {
+
+namespace {
+
+const uint8* DataStart(const Blob::BlobPart& part) {
+ if (part.IsType<scoped_refptr<ArrayBuffer> >()) {
+ return part.AsType<scoped_refptr<ArrayBuffer> >()->data();
+ } else if (part.IsType<scoped_refptr<ArrayBufferView> >()) {
+ const scoped_refptr<ArrayBufferView>& view =
+ part.AsType<scoped_refptr<ArrayBufferView> >();
+ return view->buffer()->data() + view->byte_offset();
+ } else if (part.IsType<scoped_refptr<DataView> >()) {
+ const scoped_refptr<DataView>& view =
+ part.AsType<scoped_refptr<DataView> >();
+ return view->buffer()->data() + view->byte_offset();
+ } else if (part.IsType<scoped_refptr<Blob> >()) {
+ return part.AsType<scoped_refptr<Blob> >()->data();
+ }
+
+ return NULL;
+}
+
+uint64 DataLength(const Blob::BlobPart& part) {
+ if (part.IsType<scoped_refptr<ArrayBuffer> >()) {
+ return part.AsType<scoped_refptr<ArrayBuffer> >()->byte_length();
+ } else if (part.IsType<scoped_refptr<ArrayBufferView> >()) {
+ return part.AsType<scoped_refptr<ArrayBufferView> >()->byte_length();
+ } else if (part.IsType<scoped_refptr<DataView> >()) {
+ return part.AsType<scoped_refptr<DataView> >()->byte_length();
+ } else if (part.IsType<scoped_refptr<Blob> >()) {
+ return part.AsType<scoped_refptr<Blob> >()->size();
+ }
+
+ return 0;
+}
+
+base::LazyInstance<BlobPropertyBag> empty_blob_property_bag =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+Blob::Blob(script::EnvironmentSettings* settings,
+ const scoped_refptr<ArrayBuffer>& buffer) {
+ if (buffer) {
+ buffer_ = buffer->Slice(settings, 0);
+ } else {
+ buffer_ = new ArrayBuffer(settings, 0);
+ }
+}
+
+// TODO: Do the appropriate steps to convert the type member to lowercase ASCII
+// so the media type can be exposed and used, as described in:
+// https://www.w3.org/TR/FileAPI/#constructorBlob
+Blob::Blob(script::EnvironmentSettings* settings,
+ script::Sequence<BlobPart> blobParts, const BlobPropertyBag& options)
+ : buffer_(new ArrayBuffer(settings, 0)), type_(options.type()) {
+ size_t byte_length = 0;
+ for (script::Sequence<BlobPart>::size_type i = 0; i < blobParts.size(); i++) {
+ byte_length += DataLength(blobParts.at(i));
+ }
+ buffer_ = new ArrayBuffer(settings, static_cast<uint32>(byte_length));
+
+ uint8* destination = buffer_->data();
+ size_t offset = 0;
+ for (script::Sequence<BlobPart>::size_type i = 0; i < blobParts.size(); i++) {
+ const uint8* source = DataStart(blobParts.at(i));
+ uint64 count = DataLength(blobParts.at(i));
+
+ std::copy(source, source + count, destination + offset);
+ offset += count;
+ }
+}
+
+const BlobPropertyBag& Blob::EmptyBlobPropertyBag() {
+ return empty_blob_property_bag.Get();
+}
+
+} // namespace dom
+} // namespace cobalt
diff --git a/src/cobalt/dom/blob.h b/src/cobalt/dom/blob.h
index 6a49b8b..2ca6d52 100644
--- a/src/cobalt/dom/blob.h
+++ b/src/cobalt/dom/blob.h
@@ -15,43 +15,57 @@
#ifndef COBALT_DOM_BLOB_H_
#define COBALT_DOM_BLOB_H_
+#include <algorithm>
+#include <string>
#include <vector>
#include "cobalt/dom/array_buffer.h"
+#include "cobalt/dom/array_buffer_view.h"
+#include "cobalt/dom/data_view.h"
#include "cobalt/dom/url_registry.h"
#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/sequence.h"
+#include "cobalt/script/union_type.h"
#include "cobalt/script/wrappable.h"
#include "googleurl/src/gurl.h"
namespace cobalt {
namespace dom {
+// This ensures this header can be included without depending on generated
+// dictionaries.
+class BlobPropertyBag;
+
// 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.
+// https://www.w3.org/TR/FileAPI/#dfn-Blob
class Blob : public script::Wrappable {
public:
typedef UrlRegistry<Blob> Registry;
+ typedef script::UnionType4<
+ scoped_refptr<ArrayBuffer>, scoped_refptr<ArrayBufferView>,
+ scoped_refptr<DataView>, scoped_refptr<Blob> > BlobPart;
Blob(script::EnvironmentSettings* settings,
- const scoped_refptr<ArrayBuffer>& buffer = NULL)
- : buffer_(
- buffer ? buffer->Slice(settings, 0)
- : scoped_refptr<ArrayBuffer>(new ArrayBuffer(settings, 0))) {
- }
+ const scoped_refptr<ArrayBuffer>& buffer = NULL);
+
+ Blob(script::EnvironmentSettings* settings,
+ script::Sequence<BlobPart> blobParts,
+ const BlobPropertyBag& options = EmptyBlobPropertyBag());
const uint8* data() { return buffer_->data(); }
- uint64 size() { return static_cast<uint64>(buffer_->byte_length()); }
+ uint64 size() const { return static_cast<uint64>(buffer_->byte_length()); }
DEFINE_WRAPPABLE_TYPE(Blob);
private:
+ static const BlobPropertyBag& EmptyBlobPropertyBag();
+
scoped_refptr<ArrayBuffer> buffer_;
+ const std::string type_;
DISALLOW_COPY_AND_ASSIGN(Blob);
};
diff --git a/src/cobalt/dom/blob.idl b/src/cobalt/dom/blob.idl
index e96b035..1f495fe 100644
--- a/src/cobalt/dom/blob.idl
+++ b/src/cobalt/dom/blob.idl
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// https://www.w3.org/TR/2015/WD-FileAPI-20150421/#dfn-Blob
+// https://www.w3.org/TR/FileAPI/#dfn-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),
+ Constructor,
+ // TODO: Accept DOMString blob parts in the following union.
+ Constructor(sequence<(ArrayBuffer or ArrayBufferView or DataView or Blob)> blobParts, optional BlobPropertyBag options),
ConstructorCallWith=EnvironmentSettings
]
interface Blob {
readonly attribute unsigned long long size;
-};
+ // TODO: Expose type and use for blob fetching and decoding.
+ // readonly attribute DOMString type;
+};
\ No newline at end of file
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/blob_property_bag.idl
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/blob_property_bag.idl
index d335495..7020c15 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/blob_property_bag.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
-
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+// https://www.w3.org/TR/FileAPI/#dfn-BlobPropertyBag
+dictionary BlobPropertyBag {
+ DOMString type = "";
+};
\ No newline at end of file
diff --git a/src/cobalt/dom/blob_test.cc b/src/cobalt/dom/blob_test.cc
index 9a31b78..1e015a5 100644
--- a/src/cobalt/dom/blob_test.cc
+++ b/src/cobalt/dom/blob_test.cc
@@ -38,7 +38,7 @@
scoped_refptr<DataView> data_view =
new DataView(array_buffer, &exception_state);
data_view->SetInt16(0, static_cast<int16>(0x0607), &exception_state);
-
+ data_view->SetInt16(3, static_cast<int16>(0xABCD), &exception_state);
scoped_refptr<Blob> blob_with_buffer = new Blob(NULL, array_buffer);
ASSERT_EQ(5, blob_with_buffer->size());
@@ -47,8 +47,24 @@
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]);
+ EXPECT_EQ(0xAB, blob_with_buffer->data()[3]);
+ EXPECT_EQ(0xCD, blob_with_buffer->data()[4]);
+
+ scoped_refptr<DataView> data_view_mid_3 =
+ new DataView(array_buffer, 1, 3, &exception_state);
+ script::Sequence<Blob::BlobPart> parts;
+ parts.push_back(Blob::BlobPart(blob_with_buffer));
+ parts.push_back(Blob::BlobPart(data_view_mid_3));
+ parts.push_back(Blob::BlobPart(array_buffer));
+ scoped_refptr<Blob> blob_with_parts = new Blob(NULL, parts);
+
+ ASSERT_EQ(13UL, blob_with_parts->size());
+ ASSERT_TRUE(blob_with_parts->data());
+ EXPECT_EQ(0x6, blob_with_parts->data()[0]);
+ EXPECT_EQ(0x7, blob_with_parts->data()[5]);
+ EXPECT_EQ(0, blob_with_parts->data()[6]);
+ EXPECT_EQ(0xAB, blob_with_parts->data()[11]);
+ EXPECT_EQ(0xCD, blob_with_parts->data()[12]);
}
// Tests that further changes to a buffer from which a blob was constructed
diff --git a/src/cobalt/dom/camera_3d.cc b/src/cobalt/dom/camera_3d.cc
index 8229d07..376f981 100644
--- a/src/cobalt/dom/camera_3d.cc
+++ b/src/cobalt/dom/camera_3d.cc
@@ -31,5 +31,7 @@
void Camera3D::ClearAllKeyMappings() { impl_->ClearAllKeyMappings(); }
+void Camera3D::Reset() { impl_->Reset(); }
+
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/camera_3d.h b/src/cobalt/dom/camera_3d.h
index 9063ac9..03d04cb 100644
--- a/src/cobalt/dom/camera_3d.h
+++ b/src/cobalt/dom/camera_3d.h
@@ -49,6 +49,9 @@
// Clears all key mappings created by previous calls to |CreateKeyMapping|.
void ClearAllKeyMappings();
+ // Resets the camera's orientation.
+ void Reset();
+
// Custom, not in any spec.
scoped_refptr<Camera3DImpl> impl() { return impl_; }
diff --git a/src/cobalt/dom/camera_3d.idl b/src/cobalt/dom/camera_3d.idl
index 1f9e89f..51b40a1 100644
--- a/src/cobalt/dom/camera_3d.idl
+++ b/src/cobalt/dom/camera_3d.idl
@@ -26,4 +26,7 @@
float degrees_per_second);
void clearKeyMapping(long keyCode);
void clearAllKeyMappings();
+
+ // Resets the camera's orientation.
+ void reset();
};
diff --git a/src/cobalt/dom/camera_3d_impl.cc b/src/cobalt/dom/camera_3d_impl.cc
index d6ca39b..90a502e 100644
--- a/src/cobalt/dom/camera_3d_impl.cc
+++ b/src/cobalt/dom/camera_3d_impl.cc
@@ -45,6 +45,11 @@
keycode_map_.clear();
}
+void Camera3DImpl::Reset() {
+ base::AutoLock lock(mutex_);
+ orientation_ = Orientation();
+}
+
namespace {
const float kPiF = static_cast<float>(M_PI);
@@ -54,7 +59,8 @@
} // namespace
glm::mat4 Camera3DImpl::QueryViewPerspectiveMatrix(
- float width_to_height_aspect_ratio) {
+ float width_over_height_aspect_ratio, float max_horizontal_fov_rad,
+ float max_vertical_fov_rad) {
base::AutoLock lock(mutex_);
AccumulateOrientation();
@@ -69,12 +75,15 @@
glm::rotate(-DegreesToRadians(orientation_.yaw), glm::vec3(0, 1, 0));
// Setup a (right-handed) perspective projection matrix.
- const float kVerticalFOVInDegrees = 60.0f;
+ float vertical_fov_rad =
+ std::min(max_vertical_fov_rad,
+ 2 * static_cast<float>(atan(tan(max_horizontal_fov_rad * 0.5f) /
+ width_over_height_aspect_ratio)));
+
const float kNearZ = 0.01f;
const float kFarZ = 1000.0f;
- glm::mat4 projection =
- glm::perspectiveRH(DegreesToRadians(kVerticalFOVInDegrees),
- width_to_height_aspect_ratio, kNearZ, kFarZ);
+ glm::mat4 projection = glm::perspectiveRH(
+ vertical_fov_rad, width_over_height_aspect_ratio, kNearZ, kFarZ);
return projection * camera_rotations;
}
diff --git a/src/cobalt/dom/camera_3d_impl.h b/src/cobalt/dom/camera_3d_impl.h
index 1e368fe..311d2ed 100644
--- a/src/cobalt/dom/camera_3d_impl.h
+++ b/src/cobalt/dom/camera_3d_impl.h
@@ -47,10 +47,14 @@
void ClearKeyMapping(int keycode);
void ClearAllKeyMappings();
+ void Reset();
+
// Returns the camera's view-perspective matrix, setup according to the passed
// in width/height aspect ratio. It is likely that this function will be
// called from another thread, like a renderer thread.
- glm::mat4 QueryViewPerspectiveMatrix(float width_to_height_aspect_ratio);
+ glm::mat4 QueryViewPerspectiveMatrix(float width_over_height_aspect_ratio,
+ float max_horizontal_fov_rad,
+ float max_vertical_fov_rad);
private:
struct KeycodeMappingInfo {
diff --git a/src/cobalt/dom/comment_test.cc b/src/cobalt/dom/comment_test.cc
index 34af72f..1e9cf83 100644
--- a/src/cobalt/dom/comment_test.cc
+++ b/src/cobalt/dom/comment_test.cc
@@ -40,7 +40,7 @@
CommentTest::CommentTest()
: html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, "") {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
document_ = new Document(&html_element_context_);
}
diff --git a/src/cobalt/dom/csp_delegate.cc b/src/cobalt/dom/csp_delegate.cc
index 04d34a1..fc9d41f 100644
--- a/src/cobalt/dom/csp_delegate.cc
+++ b/src/cobalt/dom/csp_delegate.cc
@@ -83,6 +83,9 @@
case kXhr:
can_load = csp_->AllowConnectToSource(url, redirect_status);
break;
+ case kWebSocket:
+ can_load = csp_->AllowConnectToSource(url, redirect_status);
+ break;
default:
NOTREACHED() << "Invalid resource type " << type;
break;
diff --git a/src/cobalt/dom/csp_delegate.h b/src/cobalt/dom/csp_delegate.h
index 51e43c2..2660ea7 100644
--- a/src/cobalt/dom/csp_delegate.h
+++ b/src/cobalt/dom/csp_delegate.h
@@ -38,6 +38,7 @@
kScript,
kStyle,
kXhr,
+ kWebSocket,
};
CspDelegate();
diff --git a/src/cobalt/dom/csp_delegate_test.cc b/src/cobalt/dom/csp_delegate_test.cc
index 32ee612..fccdcaf 100644
--- a/src/cobalt/dom/csp_delegate_test.cc
+++ b/src/cobalt/dom/csp_delegate_test.cc
@@ -47,6 +47,7 @@
{CspDelegate::kScript, "script-src"},
{CspDelegate::kStyle, "style-src"},
{CspDelegate::kXhr, "connect-src"},
+ {CspDelegate::kWebSocket, "connect-src"},
};
class MockViolationReporter : public CspViolationReporter {
diff --git a/src/cobalt/dom/data_view_test.cc b/src/cobalt/dom/data_view_test.cc
index 9d03500..d32bdcf 100644
--- a/src/cobalt/dom/data_view_test.cc
+++ b/src/cobalt/dom/data_view_test.cc
@@ -68,25 +68,18 @@
TEST(DataViewTest, ExceptionInConstructors) {
StrictMock<MockExceptionState> exception_state;
- script::MessageType message_type;
scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 10);
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
- .WillOnce(SaveArg<0>(&message_type));
+ EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kTypeError, _, _));
scoped_refptr<DataView> data_view = new DataView(NULL, &exception_state);
- EXPECT_EQ(script::kTypeError, GetSimpleExceptionType(message_type));
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
- .WillOnce(SaveArg<0>(&message_type));
+ EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
data_view = new DataView(array_buffer, array_buffer->byte_length() + 1,
&exception_state);
- EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
- .WillOnce(SaveArg<0>(&message_type));
+ EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
data_view = new DataView(array_buffer, 0, array_buffer->byte_length() + 1,
&exception_state);
- EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
}
TEST(DataViewTest, Getters) {
@@ -120,18 +113,16 @@
TEST(DataViewTest, GettersWithException) {
StrictMock<MockExceptionState> exception_state;
- script::MessageType message_type;
scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 13);
scoped_refptr<DataView> data_view =
new DataView(array_buffer, &exception_state);
#define DEFINE_DATA_VIEW_GETTER_WITH_EXCEPTION_TEST(DomType, CppType) \
{ \
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) \
- .WillOnce(SaveArg<0>(&message_type)); \
+ EXPECT_CALL(exception_state, \
+ SetSimpleExceptionVA(script::kRangeError, _, _)); \
data_view->Get##DomType(array_buffer->byte_length() - sizeof(CppType) + 1, \
&exception_state); \
- EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); \
}
DATA_VIEW_ACCESSOR_FOR_EACH(DEFINE_DATA_VIEW_GETTER_WITH_EXCEPTION_TEST)
#undef DEFINE_DATA_VIEW_GETTER_WITH_EXCEPTION_TEST
@@ -186,18 +177,16 @@
TEST(DataViewTest, SettersWithException) {
StrictMock<MockExceptionState> exception_state;
- script::MessageType message_type;
scoped_refptr<ArrayBuffer> array_buffer = new ArrayBuffer(NULL, 13);
scoped_refptr<DataView> data_view =
new DataView(array_buffer, &exception_state);
#define DEFINE_DATA_VIEW_SETTER_WITH_EXCEPTION_TEST(DomType, CppType) \
{ \
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _)) \
- .WillOnce(SaveArg<0>(&message_type)); \
+ EXPECT_CALL(exception_state, \
+ SetSimpleExceptionVA(script::kRangeError, _, _)); \
data_view->Set##DomType(array_buffer->byte_length() - sizeof(CppType) + 1, \
0, &exception_state); \
- EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type)); \
}
DATA_VIEW_ACCESSOR_FOR_EACH(DEFINE_DATA_VIEW_SETTER_WITH_EXCEPTION_TEST)
#undef DEFINE_DATA_VIEW_SETTER_WITH_EXCEPTION_TEST
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index 131b243..4869925 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -161,9 +161,20 @@
return implementation_;
}
+// Algorithm for GetElementsByTagName:
+// https://www.w3.org/TR/dom/#concept-getelementsbytagname
scoped_refptr<HTMLCollection> Document::GetElementsByTagName(
const std::string& local_name) const {
- return HTMLCollection::CreateWithElementsByTagName(this, local_name);
+ // 2. If the document is not an HTML document, then return an HTML collection
+ // whose name is local name. If it is an HTML document, then return an,
+ // HTML collection whose name is local name converted to ASCII lowercase.
+ if (IsXMLDocument()) {
+ return HTMLCollection::CreateWithElementsByLocalName(this, local_name);
+ } else {
+ const std::string lower_local_name = StringToLowerASCII(local_name);
+ return HTMLCollection::CreateWithElementsByLocalName(this,
+ lower_local_name);
+ }
}
scoped_refptr<HTMLCollection> Document::GetElementsByClassName(
@@ -212,10 +223,10 @@
return new UIEvent(Event::Uninitialized);
}
- DOMException::Raise(DOMException::kNotSupportedErr,
- "document.createEvent does not support \""
- + interface_name + "\".",
- exception_state);
+ DOMException::Raise(
+ DOMException::kNotSupportedErr,
+ "document.createEvent does not support \"" + interface_name + "\".",
+ exception_state);
// Return value will be ignored.
return NULL;
@@ -268,11 +279,6 @@
void Document::set_body(const scoped_refptr<HTMLBodyElement>& body) {
// 1. If the new value is not a body or frameset element, then throw a
// HierarchyRequestError exception and abort these steps.
- if (body->tag_name() != HTMLBodyElement::kTagName) {
- // TODO: Throw JS HierarchyRequestError.
- return;
- }
-
// 2. Otherwise, if the new value is the same as the body element, do nothing.
// Abort these steps.
scoped_refptr<HTMLBodyElement> current_body = this->body();
@@ -379,11 +385,26 @@
}
void Document::SetActiveElement(Element* active_element) {
+ // Invalidate matching rules on old and new active element.
+ if (active_element_) {
+ HTMLElement* html_element = active_element_->AsHTMLElement();
+ if (html_element) {
+ html_element->InvalidateMatchingRulesRecursively();
+ }
+ }
if (active_element) {
+ HTMLElement* html_element = active_element->AsHTMLElement();
+ if (html_element) {
+ html_element->InvalidateMatchingRulesRecursively();
+ }
active_element_ = base::AsWeakPtr(active_element);
} else {
active_element_.reset();
}
+
+ // Record mutation and trigger layout.
+ is_computed_style_dirty_ = true;
+ RecordMutation();
FOR_EACH_OBSERVER(DocumentObserver, observers_, OnFocusChanged());
}
@@ -450,7 +471,7 @@
scoped_refptr<HTMLHtmlElement> current_html = html();
if (current_html) {
- current_html->InvalidateComputedStylesRecursively();
+ current_html->InvalidateComputedStylesOfNodeAndDescendants();
}
RecordMutation();
@@ -465,7 +486,10 @@
}
void Document::OnTypefaceLoadEvent() {
- InvalidateLayoutBoxesFromNodeAndDescendants();
+ scoped_refptr<HTMLHtmlElement> current_html = html();
+ if (current_html) {
+ current_html->InvalidateLayoutBoxesOfNodeAndDescendants();
+ }
RecordMutation();
}
@@ -581,7 +605,10 @@
mode_token_iterator != mode_tokens.end(); ++mode_token_iterator) {
const std::string& mode_token = *mode_token_iterator;
if (mode_token == "wipe") {
- InvalidateLayoutBoxesFromNodeAndDescendants();
+ scoped_refptr<HTMLHtmlElement> current_html = html();
+ if (current_html) {
+ current_html->InvalidateLayoutBoxesOfNodeAndDescendants();
+ }
DLOG(INFO) << "Partial Layout state wiped";
} else if (mode_token == "off") {
partial_layout_is_enabled_ = false;
@@ -610,7 +637,7 @@
scoped_refptr<HTMLHtmlElement> current_html = html();
if (current_html) {
- current_html->InvalidateComputedStylesRecursively();
+ current_html->InvalidateComputedStylesOfNodeAndDescendants();
}
}
@@ -648,20 +675,22 @@
}
}
-void Document::InvalidateLayout() {
- // Set all invalidation flags and recursively invalidate the computed style.
- is_selector_tree_dirty_ = true;
+void Document::PurgeCachedResources() {
+ // Set the computed style to dirty so that it'll be able to update any
+ // elements that had images purged when it resumes.
is_computed_style_dirty_ = true;
- are_font_faces_dirty_ = true;
- are_keyframes_dirty_ = true;
scoped_refptr<HTMLHtmlElement> current_html = html();
if (current_html) {
- current_html->InvalidateComputedStylesRecursively();
+ current_html->PurgeCachedBackgroundImagesOfNodeAndDescendants();
}
+}
- // Finally, also destroy all cached layout boxes.
- InvalidateLayoutBoxesFromNodeAndDescendants();
+void Document::InvalidateLayoutBoxes() {
+ scoped_refptr<HTMLHtmlElement> current_html = html();
+ if (current_html) {
+ current_html->InvalidateLayoutBoxesOfNodeAndDescendants();
+ }
}
void Document::DisableJit() {
@@ -730,7 +759,7 @@
// the keyframes map changed.
scoped_refptr<HTMLHtmlElement> current_html = html();
if (current_html) {
- current_html->InvalidateComputedStylesRecursively();
+ current_html->InvalidateComputedStylesOfNodeAndDescendants();
}
}
}
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index 36d4e3b..f1e121b 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -325,8 +325,8 @@
// Exposed for test purposes.
void UpdateSelectorTree();
- // Invalidates the document's cached layout tree and associated data.
- void InvalidateLayout();
+ void PurgeCachedResources();
+ void InvalidateLayoutBoxes();
// Disable just-in-time compilation of JavaScript code.
void DisableJit();
diff --git a/src/cobalt/dom/document_test.cc b/src/cobalt/dom/document_test.cc
index 1900ec6..8544240 100644
--- a/src/cobalt/dom/document_test.cc
+++ b/src/cobalt/dom/document_test.cc
@@ -64,7 +64,7 @@
: css_parser_(css_parser::Parser::Create()),
dom_stat_tracker_(new DomStatTracker("DocumentTest")),
html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
dom_stat_tracker_.get(), "") {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
}
@@ -113,7 +113,7 @@
scoped_refptr<Element> element = document->CreateElement("element");
EXPECT_EQ(Node::kElementNode, element->node_type());
- EXPECT_EQ("element", element->node_name());
+ EXPECT_EQ("ELEMENT", element->node_name());
EXPECT_EQ(document, element->node_document());
EXPECT_EQ(NULL, element->parent_node());
@@ -121,7 +121,7 @@
EXPECT_EQ(NULL, element->last_child());
element = document->CreateElement("ELEMENT");
- EXPECT_EQ("element", element->node_name());
+ EXPECT_EQ("ELEMENT", element->node_name());
}
TEST_F(DocumentTest, CreateTextNode) {
diff --git a/src/cobalt/dom/document_type_test.cc b/src/cobalt/dom/document_type_test.cc
index daffa4e..e2e7efe 100644
--- a/src/cobalt/dom/document_type_test.cc
+++ b/src/cobalt/dom/document_type_test.cc
@@ -25,7 +25,7 @@
protected:
DocumentTypeTest()
: html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, ""),
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, ""),
document_(new Document(&html_element_context_)) {}
~DocumentTypeTest() OVERRIDE {}
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 44f9329..a815619 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -34,7 +34,9 @@
'audio_track_list.h',
'benchmark_stat_names.cc',
'benchmark_stat_names.h',
+ 'blob.cc',
'blob.h',
+ 'blob_property_bag.h',
'camera_3d.cc',
'camera_3d.h',
'camera_3d_impl.cc',
@@ -267,7 +269,7 @@
],
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
- '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_dictionaries',
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
'<(DEPTH)/cobalt/csp/csp.gyp:csp',
'<(DEPTH)/cobalt/cssom/cssom.gyp:cssom',
'<(DEPTH)/cobalt/dom/dom_exception.gyp:dom_exception',
@@ -319,7 +321,7 @@
# Additionally, ensure that the include directories for generated
# headers are put on the include directories for targets that depend
# on this one.
- '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_dictionaries',
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
]
},
@@ -335,6 +337,11 @@
'testing/stub_script_runner.cc',
'testing/stub_script_runner.h',
],
+ 'dependencies': [
+ # TODO: Remove the dependency below, it works around the fact that
+ # ScriptValueFactory has non-virtual method CreatePromise().
+ '<(DEPTH)/cobalt/script/engine.gyp:engine',
+ ],
},
],
}
diff --git a/src/cobalt/dom/dom_implementation_test.cc b/src/cobalt/dom/dom_implementation_test.cc
index 5b0a7bf..f161a30 100644
--- a/src/cobalt/dom/dom_implementation_test.cc
+++ b/src/cobalt/dom/dom_implementation_test.cc
@@ -26,7 +26,7 @@
TEST(DOMImplementationTest, CreateDocumentShouldCreateXMLDocument) {
HTMLElementContext html_element_context(NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, "");
+ NULL, NULL, NULL, "");
scoped_refptr<DOMImplementation> dom_implementation =
new DOMImplementation(&html_element_context);
scoped_refptr<Document> document =
diff --git a/src/cobalt/dom/dom_parser.cc b/src/cobalt/dom/dom_parser.cc
index 0ab0f7c..7b60408 100644
--- a/src/cobalt/dom/dom_parser.cc
+++ b/src/cobalt/dom/dom_parser.cc
@@ -34,23 +34,21 @@
DOMParser::DOMParser(HTMLElementContext* html_element_context)
: html_element_context_(html_element_context) {}
-scoped_refptr<Document> DOMParser::ParseFromString(const std::string& str,
- const SupportedType& type) {
+scoped_refptr<Document> DOMParser::ParseFromString(
+ const std::string& str, DOMParserSupportedType type) {
switch (type) {
- case kTextHtml:
+ case kDOMParserSupportedTypeTextHtml:
return html_element_context_->dom_parser()->ParseDocument(
str, html_element_context_, GetInlineSourceLocation());
- case kTextXml:
- case kApplicationXml:
- case kApplicationXhtmlXml:
- case kImageSvgXml:
+ case kDOMParserSupportedTypeTextXml:
+ case kDOMParserSupportedTypeApplicationXml:
+ case kDOMParserSupportedTypeApplicationXhtmlXml:
+ case kDOMParserSupportedTypeImageSvgXml:
return html_element_context_->dom_parser()->ParseXMLDocument(
str, html_element_context_, GetInlineSourceLocation());
- case kMaxSupportedType:
- default:
- LOG(WARNING) << "DOMParse.ParseFromString received invalid type value.";
- return NULL;
}
+ LOG(WARNING) << "DOMParse.ParseFromString received invalid type value.";
+ return NULL;
}
} // namespace dom
diff --git a/src/cobalt/dom/dom_parser.h b/src/cobalt/dom/dom_parser.h
index 5c33df1..9aef630 100644
--- a/src/cobalt/dom/dom_parser.h
+++ b/src/cobalt/dom/dom_parser.h
@@ -18,6 +18,7 @@
#include <string>
#include "base/memory/ref_counted.h"
+#include "cobalt/dom/dom_parser_supported_type.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/wrappable.h"
@@ -33,21 +34,12 @@
// https://www.w3.org/TR/DOM-Parsing/#the-domparser-interface
class DOMParser : public script::Wrappable {
public:
- enum SupportedType {
- kTextHtml,
- kTextXml,
- kApplicationXml,
- kApplicationXhtmlXml,
- kImageSvgXml,
- kMaxSupportedType,
- };
-
explicit DOMParser(script::EnvironmentSettings* environment_settings);
explicit DOMParser(HTMLElementContext* html_element_context);
// Web API: DOMParser
scoped_refptr<Document> ParseFromString(const std::string& str,
- const SupportedType& type);
+ DOMParserSupportedType type);
DEFINE_WRAPPABLE_TYPE(DOMParser);
diff --git a/src/cobalt/dom/dom_parser.idl b/src/cobalt/dom/dom_parser.idl
index 75f6f0a..7ff1879 100644
--- a/src/cobalt/dom/dom_parser.idl
+++ b/src/cobalt/dom/dom_parser.idl
@@ -14,17 +14,9 @@
// https://www.w3.org/TR/DOM-Parsing/#the-domparser-interface
-enum SupportedType {
- "text/html",
- "text/xml",
- "application/xml",
- "application/xhtml+xml",
- "image/svg+xml"
-};
-
[
Constructor,
ConstructorCallWith=EnvironmentSettings
] interface DOMParser {
- [NewObject] Document parseFromString(DOMString str, SupportedType type);
+ [NewObject] Document parseFromString(DOMString str, DOMParserSupportedType type);
};
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/dom_parser_supported_type.idl
similarity index 68%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/dom_parser_supported_type.idl
index d335495..a041a81 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/dom_parser_supported_type.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/DOM-Parsing/#the-domparser-interface
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum DOMParserSupportedType {
+ "text/html",
+ "text/xml",
+ "application/xml",
+ "application/xhtml+xml",
+ "image/svg+xml"
+};
diff --git a/src/cobalt/dom/dom_parser_test.cc b/src/cobalt/dom/dom_parser_test.cc
index a9cf512..21d7302 100644
--- a/src/cobalt/dom/dom_parser_test.cc
+++ b/src/cobalt/dom/dom_parser_test.cc
@@ -45,8 +45,9 @@
html_element_context_(
&fetcher_factory_, &stub_css_parser_, dom_parser_parser_.get(),
NULL /* can_play_type_handler */, NULL /* web_media_player_factory */,
- &stub_script_runner_, NULL /* media_source_registry */,
- NULL /* resource_provider */, NULL /* image_cache */,
+ &stub_script_runner_, NULL /* script_value_factory */,
+ NULL /* media_source_registry */, NULL /* resource_provider */,
+ NULL /* animated_image_tracker */, NULL /* image_cache */,
NULL /* reduced_image_cache_capacity_manager */,
NULL /* remote_typeface_cache */, NULL /* mesh_cache */,
NULL /* dom_stat_tracker */, "" /* language */),
@@ -69,15 +70,9 @@
" </Period>\n"
"</MPD>\n";
scoped_refptr<Document> document =
- dom_parser_->ParseFromString(input, DOMParser::kTextXml);
+ dom_parser_->ParseFromString(input, kDOMParserSupportedTypeTextXml);
EXPECT_TRUE(document->IsXMLDocument());
}
-TEST_F(DOMParserTest, InvalidType) {
- scoped_refptr<Document> document =
- dom_parser_->ParseFromString("", DOMParser::kMaxSupportedType);
- EXPECT_FALSE(document);
-}
-
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/dom_string_map.cc b/src/cobalt/dom/dom_string_map.cc
index 9f7f318..e2d71c2 100644
--- a/src/cobalt/dom/dom_string_map.cc
+++ b/src/cobalt/dom/dom_string_map.cc
@@ -136,8 +136,8 @@
if (attribute_name) {
return element_->GetAttribute(*attribute_name);
} else {
- exception_state->SetSimpleExceptionWithArgs(script::kPropertySyntaxError, 0,
- property_name.c_str());
+ exception_state->SetSimpleException(script::kSyntaxError,
+ property_name.c_str());
return base::nullopt;
}
}
@@ -150,8 +150,8 @@
if (attribute_name) {
element_->SetAttribute(*attribute_name, value);
} else {
- exception_state->SetSimpleExceptionWithArgs(script::kPropertySyntaxError, 0,
- property_name.c_str());
+ exception_state->SetSimpleException(script::kSyntaxError,
+ property_name.c_str());
}
}
diff --git a/src/cobalt/dom/dom_string_map_test.cc b/src/cobalt/dom/dom_string_map_test.cc
index bb5599a..cf8a9a5 100644
--- a/src/cobalt/dom/dom_string_map_test.cc
+++ b/src/cobalt/dom/dom_string_map_test.cc
@@ -43,7 +43,7 @@
DOMStringMapTest::DOMStringMapTest()
: html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, ""),
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, ""),
document_(new Document(&html_element_context_)),
element_(new Element(document_, base::Token("element"))),
dom_string_map_(new DOMStringMap(element_)) {}
@@ -163,12 +163,12 @@
TEST_F(DOMStringMapTest, InvalidPropertyName) {
EXPECT_CALL(exception_state_,
- SetSimpleExceptionVA(script::kPropertySyntaxError, _));
+ SetSimpleExceptionVA(script::kSyntaxError, _, _));
dom_string_map_->AnonymousNamedSetter("hyphen-lowercase", "Los Angeles",
&exception_state_);
EXPECT_CALL(exception_state_,
- SetSimpleExceptionVA(script::kPropertySyntaxError, _));
+ SetSimpleExceptionVA(script::kSyntaxError, _, _));
dom_string_map_->AnonymousNamedGetter("hyphen-lowercase", &exception_state_);
}
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index 7985a1a..7ec5d03 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -50,6 +50,7 @@
'location_test.cc',
'media_query_list_test.cc',
'mutation_observer_test.cc',
+ 'named_node_map_test.cc',
'node_dispatch_event_test.cc',
'node_list_live_test.cc',
'node_list_test.cc',
diff --git a/src/cobalt/dom/dom_token_list.cc b/src/cobalt/dom/dom_token_list.cc
index 18e2daf..0bc8b8e 100644
--- a/src/cobalt/dom/dom_token_list.cc
+++ b/src/cobalt/dom/dom_token_list.cc
@@ -107,7 +107,6 @@
}
}
- const size_t old_size = tokens_.size();
for (std::vector<std::string>::const_iterator it = tokens.begin();
it != tokens.end(); ++it) {
// 3. For each token in tokens, in given order, that is not in tokens,
@@ -119,9 +118,7 @@
}
// 4. Run the update steps.
- if (tokens_.size() != old_size) {
- RunUpdateSteps();
- }
+ RunUpdateSteps();
}
// Algorithm for Remove:
@@ -140,7 +137,6 @@
}
}
- const size_t old_size = tokens_.size();
for (std::vector<std::string>::const_iterator it = tokens.begin();
it != tokens.end(); ++it) {
// 3. For each token in tokens, remove token from tokens.
@@ -149,9 +145,7 @@
}
// 4. Run the update steps.
- if (tokens_.size() != old_size) {
- RunUpdateSteps();
- }
+ RunUpdateSteps();
}
// Algorithm for AnonymousStringifier:
diff --git a/src/cobalt/dom/dom_token_list_test.cc b/src/cobalt/dom/dom_token_list_test.cc
index e7fe553..a40f8be 100644
--- a/src/cobalt/dom/dom_token_list_test.cc
+++ b/src/cobalt/dom/dom_token_list_test.cc
@@ -38,7 +38,7 @@
DOMTokenListTest::DOMTokenListTest()
: html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, "") {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
document_ = new Document(&html_element_context_);
}
diff --git a/src/cobalt/dom/element.cc b/src/cobalt/dom/element.cc
index a8ed9a4..521069e 100644
--- a/src/cobalt/dom/element.cc
+++ b/src/cobalt/dom/element.cc
@@ -72,9 +72,9 @@
++(element_count_log.Get().count);
}
-Element::Element(Document* document, base::Token tag_name)
+Element::Element(Document* document, base::Token local_name)
: Node(document),
- tag_name_(tag_name),
+ local_name_(local_name),
animations_(new web_animations::AnimationSet()) {
++(element_count_log.Get().count);
}
@@ -97,14 +97,18 @@
void Element::set_text_content(
const base::optional<std::string>& text_content) {
TRACK_MEMORY_SCOPE("DOM");
- // Remove all children and replace them with a single Text node.
- while (HasChildNodes()) {
- RemoveChild(first_child());
- }
+ // https://www.w3.org/TR/dom/#dom-node-textcontent
+ // 1. Let node be null.
+ scoped_refptr<Node> new_node;
+
+ // 2. If new value is not the empty string, set node to a new Text node whose
+ // data is new value.
std::string new_text_content = text_content.value_or("");
if (!new_text_content.empty()) {
- AppendChild(new Text(node_document(), new_text_content));
+ new_node = new Text(node_document(), new_text_content);
}
+ // 3. Replace all with node within the context object.
+ ReplaceAll(new_node);
}
bool Element::HasAttributes() const { return !attribute_map_.empty(); }
@@ -308,6 +312,25 @@
OnRemoveAttribute(name);
}
+// Algorithm for tag_name:
+// https://www.w3.org/TR/dom/#dom-element-tagname
+base::Token Element::tag_name() const {
+ // 1. If context object's namespace prefix is not null, let qualified name be
+ // its namespace prefix, followed by a ":" (U+003A), followed by its local
+ // name. Otherwise, let qualified name be its local name.
+ std::string qualified_name = local_name_.c_str();
+
+ // 2. If the context object is in the HTML namespace and its node document is
+ // an HTML document, let qualified name be converted to ASCII uppercase.
+ Document* document = node_document();
+ if (document && !document->IsXMLDocument()) {
+ StringToUpperASCII(&qualified_name);
+ }
+
+ // 3. Return qualified name.
+ return base::Token(qualified_name);
+}
+
// Algorithm for HasAttribute:
// https://www.w3.org/TR/2014/WD-dom-20140710/#dom-element-hasattribute
bool Element::HasAttribute(const std::string& name) const {
@@ -327,9 +350,21 @@
return iter != attribute_map_.end();
}
+// Algorithm for GetElementsByTagName:
+// https://www.w3.org/TR/dom/#concept-getelementsbytagname
scoped_refptr<HTMLCollection> Element::GetElementsByTagName(
- const std::string& tag_name) const {
- return HTMLCollection::CreateWithElementsByTagName(this, tag_name);
+ const std::string& local_name) const {
+ Document* document = node_document();
+ // 2. If the document is not an HTML document, then return an HTML collection
+ // whose name is local name. If it is an HTML document, then return an,
+ // HTML collection whose name is local name converted to ASCII lowercase.
+ if (document && document->IsXMLDocument()) {
+ return HTMLCollection::CreateWithElementsByLocalName(this, local_name);
+ } else {
+ const std::string lower_local_name = StringToLowerASCII(local_name);
+ return HTMLCollection::CreateWithElementsByLocalName(this,
+ lower_local_name);
+ }
}
scoped_refptr<HTMLCollection> Element::GetElementsByClassName(
@@ -520,7 +555,7 @@
scoped_refptr<Node> Element::Duplicate() const {
TRACK_MEMORY_SCOPE("DOM");
- Element* new_element = new Element(node_document(), tag_name());
+ Element* new_element = new Element(node_document(), local_name_);
new_element->CopyAttributes(*this);
return new_element;
}
@@ -585,7 +620,7 @@
}
std::string Element::GetDebugName() {
- std::string name = tag_name_.c_str();
+ std::string name = local_name_.c_str();
if (HasAttribute("id")) {
name += "#";
name += id_attribute_.c_str();
diff --git a/src/cobalt/dom/element.h b/src/cobalt/dom/element.h
index ed1b288..1e64100 100644
--- a/src/cobalt/dom/element.h
+++ b/src/cobalt/dom/element.h
@@ -56,7 +56,7 @@
AttributeMap;
explicit Element(Document* document);
- Element(Document* document, base::Token tag_name);
+ Element(Document* document, base::Token local_name);
// Web API: Node
//
@@ -72,7 +72,7 @@
// Web API: Element
//
- base::Token tag_name() const { return tag_name_; }
+ base::Token tag_name() const;
base::Token id() const { return id_attribute_; }
void set_id(const std::string& value) { SetAttribute("id", value); }
@@ -91,7 +91,7 @@
bool HasAttribute(const std::string& name) const;
scoped_refptr<HTMLCollection> GetElementsByTagName(
- const std::string& tag_name) const;
+ const std::string& local_name) const;
scoped_refptr<HTMLCollection> GetElementsByClassName(
const std::string& class_name) const;
@@ -125,6 +125,9 @@
// Custom, not in any spec.
//
+
+ base::Token local_name() const { return local_name_; }
+
// Returns whether the element has no children at all except comments or
// processing instructions.
// https://www.w3.org/TR/selectors4/#empty-pseudo
@@ -184,8 +187,8 @@
// Callback for error when parsing inner / outer HTML.
void HTMLParseError(const std::string& error);
- // Tag name of the element.
- base::Token tag_name_;
+ // Local name of the element.
+ base::Token local_name_;
// A map that holds the actual element attributes.
AttributeMap attribute_map_;
// The "id" attribute for this element. Stored here in addition to being
diff --git a/src/cobalt/dom/element_test.cc b/src/cobalt/dom/element_test.cc
index 7c927fc..1e84b7a 100644
--- a/src/cobalt/dom/element_test.cc
+++ b/src/cobalt/dom/element_test.cc
@@ -32,6 +32,7 @@
#include "cobalt/dom/testing/gtest_workarounds.h"
#include "cobalt/dom/testing/html_collection_testing.h"
#include "cobalt/dom/text.h"
+#include "cobalt/dom/xml_document.h"
#include "cobalt/dom_parser/parser.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -52,6 +53,7 @@
scoped_ptr<DomStatTracker> dom_stat_tracker_;
HTMLElementContext html_element_context_;
scoped_refptr<Document> document_;
+ scoped_refptr<XMLDocument> xml_document_;
};
ElementTest::ElementTest()
@@ -60,12 +62,14 @@
dom_stat_tracker_(new DomStatTracker("ElementTest")),
html_element_context_(NULL, css_parser_.get(), dom_parser_.get(), NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- dom_stat_tracker_.get(), "") {
+ NULL, NULL, dom_stat_tracker_.get(), "") {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
document_ = new Document(&html_element_context_);
+ xml_document_ = new XMLDocument(&html_element_context_);
}
ElementTest::~ElementTest() {
+ xml_document_ = NULL;
document_ = NULL;
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
}
@@ -80,9 +84,9 @@
ASSERT_TRUE(element);
EXPECT_EQ(Node::kElementNode, element->node_type());
- EXPECT_EQ("element", element->node_name());
-
- EXPECT_EQ("element", element->tag_name());
+ EXPECT_EQ("ELEMENT", element->node_name());
+ EXPECT_EQ("ELEMENT", element->tag_name());
+ EXPECT_EQ("element", element->local_name());
EXPECT_EQ("", element->id());
EXPECT_EQ("", element->class_name());
@@ -119,6 +123,18 @@
EXPECT_EQ(NULL, text->AsElement());
}
+TEST_F(ElementTest, TagName) {
+ scoped_refptr<Element> element_in_html =
+ new Element(document_, base::Token("eLeMeNt"));
+ EXPECT_EQ("ELEMENT", element_in_html->tag_name());
+ EXPECT_EQ("eLeMeNt", element_in_html->local_name());
+
+ scoped_refptr<Element> element_in_xml =
+ new Element(xml_document_, base::Token("eLeMeNt"));
+ EXPECT_EQ("eLeMeNt", element_in_xml->tag_name());
+ EXPECT_EQ("eLeMeNt", element_in_html->local_name());
+}
+
TEST_F(ElementTest, AttributeMethods) {
scoped_refptr<Element> element =
new Element(document_, base::Token("element"));
@@ -375,8 +391,14 @@
" <element_b2>Text</element_b2>\n"
"</element_a>";
EXPECT_EQ(kExpectedHTML, root->inner_html());
+}
+TEST_F(ElementTest, SetInnerHTML) {
// Setting inner HTML should remove all previous children.
+ scoped_refptr<Element> root = new Element(document_, base::Token("root"));
+ scoped_refptr<Element> element_a =
+ root->AppendChild(new Element(document_, base::Token("element_a")))
+ ->AsElement();
root->set_inner_html("");
EXPECT_FALSE(root->HasChildNodes());
diff --git a/src/cobalt/dom/event_queue_test.cc b/src/cobalt/dom/event_queue_test.cc
index 0a46e27..3697091 100644
--- a/src/cobalt/dom/event_queue_test.cc
+++ b/src/cobalt/dom/event_queue_test.cc
@@ -18,6 +18,7 @@
#include "cobalt/dom/event.h"
#include "cobalt/dom/event_target.h"
#include "cobalt/dom/testing/mock_event_listener.h"
+#include "cobalt/script/testing/fake_script_value.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::AllOf;
@@ -28,7 +29,7 @@
namespace cobalt {
namespace dom {
-using testing::FakeScriptValue;
+using ::cobalt::script::testing::FakeScriptValue;
using testing::MockEventListener;
class EventQueueTest : public ::testing::Test {
@@ -54,8 +55,8 @@
scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
EventQueue event_queue(event_target.get());
- event_target->AddEventListener("event",
- FakeScriptValue(event_listener.get()), false);
+ event_target->AddEventListener(
+ "event", FakeScriptValue<EventListener>(event_listener.get()), false);
ExpectHandleEventCallWithEventAndTarget(event_listener.get(), event,
event_target);
@@ -70,8 +71,8 @@
EventQueue event_queue(event_target.get());
event->set_target(event_target);
- event_target->AddEventListener("event",
- FakeScriptValue(event_listener.get()), false);
+ event_target->AddEventListener(
+ "event", FakeScriptValue<EventListener>(event_listener.get()), false);
ExpectHandleEventCallWithEventAndTarget(event_listener.get(), event,
event_target);
@@ -86,8 +87,8 @@
EventQueue event_queue(event_target.get());
event->set_target(event_target);
- event_target->AddEventListener("event",
- FakeScriptValue(event_listener.get()), false);
+ event_target->AddEventListener(
+ "event", FakeScriptValue<EventListener>(event_listener.get()), false);
event_listener->ExpectNoHandleEventCall();
event_queue.Enqueue(event);
@@ -108,7 +109,7 @@
event->set_target(event_target_2);
event_target_2->AddEventListener(
- "event", FakeScriptValue(event_listener.get()), false);
+ "event", FakeScriptValue<EventListener>(event_listener.get()), false);
ExpectHandleEventCallWithEventAndTarget(event_listener.get(), event,
event_target_2);
diff --git a/src/cobalt/dom/event_target_test.cc b/src/cobalt/dom/event_target_test.cc
index f055f07..0fc5137 100644
--- a/src/cobalt/dom/event_target_test.cc
+++ b/src/cobalt/dom/event_target_test.cc
@@ -17,6 +17,7 @@
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/dom/dom_exception.h"
#include "cobalt/dom/testing/mock_event_listener.h"
+#include "cobalt/script/testing/fake_script_value.h"
#include "cobalt/script/testing/mock_exception_state.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -35,8 +36,8 @@
using ::testing::SaveArg;
using ::testing::StrictMock;
using ::testing::_;
+using script::testing::FakeScriptValue;
using script::testing::MockExceptionState;
-using testing::FakeScriptValue;
using testing::MockEventListener;
base::optional<bool> DispatchEventOnCurrentTarget(
@@ -67,8 +68,8 @@
scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
event_listener->ExpectHandleEventCall(event, event_target);
- event_target->AddEventListener("fired",
- FakeScriptValue(event_listener.get()), false);
+ event_target->AddEventListener(
+ "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
}
@@ -79,8 +80,8 @@
scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
event_listener->ExpectNoHandleEventCall();
- event_target->AddEventListener("notfired",
- FakeScriptValue(event_listener.get()), false);
+ event_target->AddEventListener(
+ "notfired", FakeScriptValue<EventListener>(event_listener.get()), false);
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
}
@@ -103,11 +104,14 @@
event_listenernot_fired->ExpectNoHandleEventCall();
event_target->AddEventListener(
- "fired", FakeScriptValue(event_listenerfired_1.get()), false);
+ "fired",
+ FakeScriptValue<EventListener>(event_listenerfired_1.get()), false);
event_target->AddEventListener(
- "notfired", FakeScriptValue(event_listenernot_fired.get()), false);
+ "notfired",
+ FakeScriptValue<EventListener>(event_listenernot_fired.get()), false);
event_target->AddEventListener(
- "fired", FakeScriptValue(event_listenerfired_2.get()), true);
+ "fired",
+ FakeScriptValue<EventListener>(event_listenerfired_2.get()), true);
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
}
@@ -119,18 +123,18 @@
scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
event_listener->ExpectHandleEventCall(event, event_target);
- event_target->AddEventListener("fired",
- FakeScriptValue(event_listener.get()), false);
+ event_target->AddEventListener(
+ "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
event_listener->ExpectNoHandleEventCall();
event_target->RemoveEventListener(
- "fired", FakeScriptValue(event_listener.get()), false);
+ "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
event_listener->ExpectHandleEventCall(event, event_target);
- event_target->AddEventListener("fired",
- FakeScriptValue(event_listener.get()), false);
+ event_target->AddEventListener(
+ "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
}
@@ -147,26 +151,30 @@
MockEventListener::Create();
event_target->AddEventListener(
- "fired", FakeScriptValue(non_attribute_event_listener.get()), false);
+ "fired",
+ FakeScriptValue<EventListener>(non_attribute_event_listener.get()),
+ false);
non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
attribute_event_listener1->ExpectHandleEventCall(event, event_target);
event_target->SetAttributeEventListener(
- base::Token("fired"), FakeScriptValue(attribute_event_listener1.get()));
+ base::Token("fired"),
+ FakeScriptValue<EventListener>(attribute_event_listener1.get()));
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
attribute_event_listener1->ExpectNoHandleEventCall();
attribute_event_listener2->ExpectHandleEventCall(event, event_target);
event_target->SetAttributeEventListener(
- base::Token("fired"), FakeScriptValue(attribute_event_listener2.get()));
+ base::Token("fired"),
+ FakeScriptValue<EventListener>(attribute_event_listener2.get()));
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
non_attribute_event_listener->ExpectHandleEventCall(event, event_target);
attribute_event_listener1->ExpectNoHandleEventCall();
attribute_event_listener2->ExpectNoHandleEventCall();
event_target->SetAttributeEventListener(base::Token("fired"),
- FakeScriptValue(NULL));
+ FakeScriptValue<EventListener>(NULL));
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
}
@@ -180,29 +188,29 @@
event_listener->ExpectHandleEventCall(event_1, event_target);
event_listener->ExpectHandleEventCall(event_2, event_target);
- event_target->AddEventListener("fired_1",
- FakeScriptValue(event_listener.get()), false);
- event_target->AddEventListener("fired_2",
- FakeScriptValue(event_listener.get()), false);
+ event_target->AddEventListener(
+ "fired_1", FakeScriptValue<EventListener>(event_listener.get()), false);
+ event_target->AddEventListener(
+ "fired_2", FakeScriptValue<EventListener>(event_listener.get()), false);
EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
event_listener->ExpectHandleEventCall(event_1, event_target);
event_target->RemoveEventListener(
- "fired_2", FakeScriptValue(event_listener.get()), false);
+ "fired_2", FakeScriptValue<EventListener>(event_listener.get()), false);
EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
event_listener->ExpectHandleEventCall(event_1, event_target);
// The capture flag is not the same so the event will not be removed.
event_target->RemoveEventListener(
- "fired_1", FakeScriptValue(event_listener.get()), true);
+ "fired_1", FakeScriptValue<EventListener>(event_listener.get()), true);
EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
event_listener->ExpectNoHandleEventCall();
event_target->RemoveEventListener(
- "fired_1", FakeScriptValue(event_listener.get()), false);
+ "fired_1", FakeScriptValue<EventListener>(event_listener.get()), false);
EXPECT_TRUE(event_target->DispatchEvent(event_1, &exception_state));
EXPECT_TRUE(event_target->DispatchEvent(event_2, &exception_state));
}
@@ -222,9 +230,11 @@
event_listenerfired_2->ExpectHandleEventCall(event, event_target);
event_target->AddEventListener(
- "fired", FakeScriptValue(event_listenerfired_1.get()), false);
+ "fired",
+ FakeScriptValue<EventListener>(event_listenerfired_1.get()), false);
event_target->AddEventListener(
- "fired", FakeScriptValue(event_listenerfired_2.get()), true);
+ "fired",
+ FakeScriptValue<EventListener>(event_listenerfired_2.get()), true);
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
}
@@ -242,9 +252,11 @@
event_listenerfired_2->ExpectNoHandleEventCall();
event_target->AddEventListener(
- "fired", FakeScriptValue(event_listenerfired_1.get()), false);
+ "fired",
+ FakeScriptValue<EventListener>(event_listenerfired_1.get()), false);
event_target->AddEventListener(
- "fired", FakeScriptValue(event_listenerfired_2.get()), true);
+ "fired",
+ FakeScriptValue<EventListener>(event_listenerfired_2.get()), true);
EXPECT_TRUE(event_target->DispatchEvent(event, &exception_state));
}
@@ -256,7 +268,8 @@
MockEventListener::Create();
event_target->AddEventListener(
- "fired", FakeScriptValue(event_listenerfired.get()), false);
+ "fired",
+ FakeScriptValue<EventListener>(event_listenerfired.get()), false);
event = new Event(base::Token("fired"), Event::kNotBubbles,
Event::kNotCancelable);
event_listenerfired->ExpectHandleEventCall(
@@ -296,8 +309,8 @@
base::polymorphic_downcast<DOMException*>(exception.get())->code());
exception = NULL;
- event_target->AddEventListener("fired",
- FakeScriptValue(event_listener.get()), false);
+ event_target->AddEventListener(
+ "fired", FakeScriptValue<EventListener>(event_listener.get()), false);
event = new Event(base::Token("fired"), Event::kNotBubbles,
Event::kNotCancelable);
// Dispatch event again when it is being dispatched.
@@ -311,7 +324,7 @@
scoped_refptr<EventTarget> event_target = new EventTarget;
scoped_refptr<Event> event = new Event(base::Token("fired"));
scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
- FakeScriptValue script_object(event_listener.get());
+ FakeScriptValue<EventListener> script_object(event_listener.get());
InSequence in_sequence;
event_listener->ExpectHandleEventCall(event, event_target);
@@ -328,7 +341,7 @@
scoped_refptr<EventTarget> event_target = new EventTarget;
scoped_refptr<Event> event = new Event(base::Token("fired"));
scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
- FakeScriptValue script_object(event_listener.get());
+ FakeScriptValue<EventListener> script_object(event_listener.get());
InSequence in_sequence;
event_listener->ExpectHandleEventCall(event, event_target);
@@ -345,7 +358,7 @@
scoped_refptr<EventTarget> event_target = new EventTarget;
scoped_refptr<Event> event = new Event(base::Token("fired"));
scoped_ptr<MockEventListener> event_listener = MockEventListener::Create();
- FakeScriptValue script_object(event_listener.get());
+ FakeScriptValue<EventListener> script_object(event_listener.get());
InSequence in_sequence;
event_listener->ExpectHandleEventCall(event, event_target);
diff --git a/src/cobalt/dom/font_cache.cc b/src/cobalt/dom/font_cache.cc
index f9645bd..28da2c0 100644
--- a/src/cobalt/dom/font_cache.cc
+++ b/src/cobalt/dom/font_cache.cc
@@ -373,7 +373,8 @@
break;
}
const scoped_refptr<render_tree::Typeface>& typeface(
- resource_provider()->GetLocalTypefaceByFaceNameIfAvailable(font_face));
+ resource_provider()->GetLocalTypefaceByFaceNameIfAvailable(
+ font_face.c_str()));
if (!typeface) {
break;
}
diff --git a/src/cobalt/dom/font_list.h b/src/cobalt/dom/font_list.h
index 062d399..3cbc097 100644
--- a/src/cobalt/dom/font_list.h
+++ b/src/cobalt/dom/font_list.h
@@ -161,6 +161,9 @@
// From render_tree::FontProvider
+ const render_tree::FontStyle& style() const OVERRIDE { return style_; }
+ float size() const OVERRIDE { return size_; }
+
// Returns the first font in the font list that supports the specified
// UTF-32 character or a fallback font provided by the font cache if none of
// them do.
@@ -171,9 +174,6 @@
const scoped_refptr<render_tree::Font>& GetCharacterFont(
int32 utf32_character, render_tree::GlyphIndex* glyph_index) OVERRIDE;
- render_tree::FontStyle style() const { return style_; }
- float size() const { return size_; }
-
private:
const scoped_refptr<render_tree::Font>& GetFallbackCharacterFont(
int32 utf32_character, render_tree::GlyphIndex* glyph_index);
diff --git a/src/cobalt/dom/html_collection.cc b/src/cobalt/dom/html_collection.cc
index 40bffac..5bff1e3 100644
--- a/src/cobalt/dom/html_collection.cc
+++ b/src/cobalt/dom/html_collection.cc
@@ -156,10 +156,10 @@
node->AsElement()->class_list()->Contains(class_name);
}
-// Used to implement HTMLCollection::kElementsByTagName.
-bool IsElementWithTagName(const std::string& tag_name, Node* node) {
+// Used to implement HTMLCollection::kElementsByLocalName.
+bool IsElementWithLocalName(const std::string& local_name, Node* node) {
return node->IsElement() &&
- (tag_name == "*" || node->AsElement()->tag_name() == tag_name);
+ (local_name == "*" || node->AsElement()->local_name() == local_name);
}
} // namespace
@@ -188,13 +188,13 @@
}
// static
-scoped_refptr<HTMLCollection> HTMLCollection::CreateWithElementsByTagName(
+scoped_refptr<HTMLCollection> HTMLCollection::CreateWithElementsByLocalName(
const scoped_refptr<const Node>& base, const std::string& name) {
if (!base) {
return NULL;
}
return new NodeCollection<NodeDescendantsIterator>(
- base, base::Bind(&IsElementWithTagName, name));
+ base, base::Bind(&IsElementWithLocalName, name));
}
HTMLCollection::HTMLCollection() { GlobalStats::GetInstance()->Add(this); }
diff --git a/src/cobalt/dom/html_collection.h b/src/cobalt/dom/html_collection.h
index 0bf427e..ac21b8b 100644
--- a/src/cobalt/dom/html_collection.h
+++ b/src/cobalt/dom/html_collection.h
@@ -44,8 +44,8 @@
// A collections of all descendants with a given class name.
static scoped_refptr<HTMLCollection> CreateWithElementsByClassName(
const scoped_refptr<const Node>& base, const std::string& name);
- // A collections of all descendants with a given tag name.
- static scoped_refptr<HTMLCollection> CreateWithElementsByTagName(
+ // A collections of all descendants with a given local name.
+ static scoped_refptr<HTMLCollection> CreateWithElementsByLocalName(
const scoped_refptr<const Node>& base, const std::string& name);
// Web API: HTMLCollection
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index 90f8bd7..e1e9485 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -53,6 +53,7 @@
#include "cobalt/dom/html_unknown_element.h"
#include "cobalt/dom/html_video_element.h"
#include "cobalt/dom/rule_matching.h"
+#include "cobalt/loader/image/animated_image_tracker.h"
namespace cobalt {
namespace dom {
@@ -482,7 +483,7 @@
scoped_refptr<HTMLElement> new_html_element =
document->html_element_context()
->html_element_factory()
- ->CreateHTMLElement(document, tag_name());
+ ->CreateHTMLElement(document, local_name());
new_html_element->CopyAttributes(*this);
new_html_element->CopyDirectionality(*this);
new_html_element->style_->AssignFrom(*this->style_);
@@ -622,11 +623,6 @@
}
}
-void HTMLElement::InvalidateComputedStylesRecursively() {
- computed_style_valid_ = false;
- Node::InvalidateComputedStylesRecursively();
-}
-
void HTMLElement::UpdateComputedStyleRecursively(
const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
parent_computed_style_declaration,
@@ -666,38 +662,50 @@
}
}
-void HTMLElement::InvalidateLayoutBoxesFromNodeAndAncestors() {
- layout_boxes_.reset();
- ReleaseImagesAndInvalidateComputedStyleIfNecessary();
- Node::InvalidateLayoutBoxesFromNodeAndAncestors();
+void HTMLElement::PurgeCachedBackgroundImagesOfNodeAndDescendants() {
+ ClearActiveBackgroundImages();
+ if (!cached_background_images_.empty()) {
+ cached_background_images_.clear();
+ computed_style_valid_ = false;
+ }
+ PurgeCachedBackgroundImagesOfDescendants();
}
-void HTMLElement::InvalidateLayoutBoxesFromNodeAndDescendants() {
- layout_boxes_.reset();
- ReleaseImagesAndInvalidateComputedStyleIfNecessary();
- Node::InvalidateLayoutBoxesFromNodeAndDescendants();
+void HTMLElement::InvalidateComputedStylesOfNodeAndDescendants() {
+ computed_style_valid_ = false;
+ InvalidateComputedStylesOfDescendants();
}
-void HTMLElement::InvalidateLayoutBoxSizesFromNode() {
+void HTMLElement::InvalidateLayoutBoxesOfNodeAndAncestors() {
+ layout_boxes_.reset();
+ InvalidateLayoutBoxesOfAncestors();
+}
+
+void HTMLElement::InvalidateLayoutBoxesOfNodeAndDescendants() {
+ layout_boxes_.reset();
+ InvalidateLayoutBoxesOfDescendants();
+}
+
+void HTMLElement::InvalidateLayoutBoxSizes() {
if (layout_boxes_) {
layout_boxes_->InvalidateSizes();
}
}
-void HTMLElement::InvalidateLayoutBoxCrossReferencesFromNode() {
+void HTMLElement::InvalidateLayoutBoxCrossReferences() {
if (layout_boxes_) {
layout_boxes_->InvalidateCrossReferences();
}
}
-void HTMLElement::InvalidateRenderTreeNodesFromNode() {
+void HTMLElement::InvalidateLayoutBoxRenderTreeNodes() {
if (layout_boxes_) {
layout_boxes_->InvalidateRenderTreeNodes();
}
}
-HTMLElement::HTMLElement(Document* document, base::Token tag_name)
- : Element(document, tag_name),
+HTMLElement::HTMLElement(Document* document, base::Token local_name)
+ : Element(document, local_name),
dom_stat_tracker_(document->html_element_context()->dom_stat_tracker()),
directionality_(kNoExplicitDirectionality),
style_(new cssom::CSSDeclaredStyleDeclaration(
@@ -760,8 +768,8 @@
}
if (directionality_ != previous_directionality) {
- InvalidateLayoutBoxesFromNodeAndAncestors();
- InvalidateLayoutBoxesFromNodeAndDescendants();
+ InvalidateLayoutBoxesOfNodeAndAncestors();
+ InvalidateLayoutBoxesOfDescendants();
}
}
@@ -817,20 +825,29 @@
// Flags tracking which cached values must be invalidated.
struct UpdateComputedStyleInvalidationFlags {
UpdateComputedStyleInvalidationFlags()
- : invalidate_descendant_computed_styles(false),
+ : purge_cached_background_images_of_descendants(false),
+ invalidate_computed_styles_of_descendants(false),
invalidate_layout_boxes(false),
invalidate_sizes(false),
invalidate_cross_references(false),
invalidate_render_tree_nodes(false) {}
- bool invalidate_descendant_computed_styles;
+ bool purge_cached_background_images_of_descendants;
+ bool invalidate_computed_styles_of_descendants;
bool invalidate_layout_boxes;
bool invalidate_sizes;
bool invalidate_cross_references;
bool invalidate_render_tree_nodes;
};
-bool NewComputedStyleInvalidatesDescendantComputedStyles(
+bool NewComputedStylePurgesCachedBackgroundImagesOfDescendants(
+ const scoped_refptr<const cssom::CSSComputedStyleData>& old_computed_style,
+ const scoped_refptr<cssom::CSSComputedStyleData>& new_computed_style) {
+ return old_computed_style->display() != cssom::KeywordValue::GetNone() &&
+ new_computed_style->display() == cssom::KeywordValue::GetNone();
+}
+
+bool NewComputedStyleInvalidatesComputedStylesOfDescendants(
const scoped_refptr<const cssom::CSSComputedStyleData>& old_computed_style,
const scoped_refptr<cssom::CSSComputedStyleData>& new_computed_style) {
return !non_trivial_static_fields.Get()
@@ -865,15 +882,27 @@
new_computed_style);
}
-void UpdateInvalidationFlagsForNewComputedStyle(
+enum IsPseudoElement {
+ kIsNotPseudoElement,
+ kIsPseudoElement,
+};
+
+void UpdateInvalidationFlagsFromNewComputedStyle(
const scoped_refptr<const cssom::CSSComputedStyleData>& old_computed_style,
const scoped_refptr<cssom::CSSComputedStyleData>& new_computed_style,
- bool animations_modified, UpdateComputedStyleInvalidationFlags* flags) {
+ bool animations_modified, IsPseudoElement is_pseudo_element,
+ UpdateComputedStyleInvalidationFlags* flags) {
if (old_computed_style) {
- if (!flags->invalidate_descendant_computed_styles &&
- NewComputedStyleInvalidatesDescendantComputedStyles(
+ if (!flags->purge_cached_background_images_of_descendants &&
+ is_pseudo_element == kIsNotPseudoElement &&
+ NewComputedStylePurgesCachedBackgroundImagesOfDescendants(
old_computed_style, new_computed_style)) {
- flags->invalidate_descendant_computed_styles = true;
+ flags->purge_cached_background_images_of_descendants = true;
+ }
+ if (!flags->invalidate_computed_styles_of_descendants &&
+ NewComputedStyleInvalidatesComputedStylesOfDescendants(
+ old_computed_style, new_computed_style)) {
+ flags->invalidate_computed_styles_of_descendants = true;
flags->invalidate_layout_boxes = true;
} else if (!flags->invalidate_layout_boxes) {
if (NewComputedStyleInvalidatesLayoutBoxes(old_computed_style,
@@ -965,9 +994,9 @@
computed_style(), &css_transitions_, &css_animations_,
document->keyframes_map(), &animations_modified);
- UpdateInvalidationFlagsForNewComputedStyle(
+ UpdateInvalidationFlagsFromNewComputedStyle(
computed_style(), new_computed_style, animations_modified,
- &invalidation_flags);
+ kIsNotPseudoElement, &invalidation_flags);
css_computed_style_declaration_->SetData(new_computed_style);
@@ -1003,9 +1032,9 @@
pseudo_elements_[pseudo_element_type]->css_animations(),
document->keyframes_map(), &animations_modified);
- UpdateInvalidationFlagsForNewComputedStyle(
+ UpdateInvalidationFlagsFromNewComputedStyle(
pseudo_elements_[pseudo_element_type]->computed_style(),
- pseudo_element_computed_style, animations_modified,
+ pseudo_element_computed_style, animations_modified, kIsPseudoElement,
&invalidation_flags);
pseudo_elements_[pseudo_element_type]
@@ -1014,29 +1043,45 @@
}
}
- if (invalidation_flags.invalidate_descendant_computed_styles) {
- InvalidateComputedStylesRecursively();
+ if (invalidation_flags.purge_cached_background_images_of_descendants) {
+ PurgeCachedBackgroundImagesOfDescendants();
+ }
+ if (invalidation_flags.invalidate_computed_styles_of_descendants) {
+ InvalidateComputedStylesOfDescendants();
}
if (invalidation_flags.invalidate_layout_boxes) {
- InvalidateLayoutBoxesFromNodeAndAncestors();
- InvalidateLayoutBoxesFromNodeAndDescendants();
+ InvalidateLayoutBoxesOfNodeAndAncestors();
+ InvalidateLayoutBoxesOfDescendants();
} else {
if (invalidation_flags.invalidate_sizes) {
- InvalidateLayoutBoxSizesFromNode();
+ InvalidateLayoutBoxSizes();
}
if (invalidation_flags.invalidate_cross_references) {
- InvalidateLayoutBoxCrossReferencesFromNode();
+ InvalidateLayoutBoxCrossReferences();
}
if (invalidation_flags.invalidate_render_tree_nodes) {
- InvalidateRenderTreeNodesFromNode();
+ InvalidateLayoutBoxRenderTreeNodes();
}
}
computed_style_valid_ = true;
}
+void HTMLElement::ClearActiveBackgroundImages() {
+ if (html_element_context() &&
+ html_element_context()->animated_image_tracker()) {
+ for (std::vector<GURL>::iterator it = active_background_images_.begin();
+ it != active_background_images_.end(); ++it) {
+ html_element_context()->animated_image_tracker()->DecreaseURLCount(*it);
+ }
+ }
+ active_background_images_.clear();
+}
+
void HTMLElement::UpdateCachedBackgroundImagesFromComputedStyle() {
+ ClearActiveBackgroundImages();
+
// Don't fetch or cache the image if the display of this element is turned
// off.
if (computed_style()->display() != cssom::KeywordValue::GetNone()) {
@@ -1055,19 +1100,26 @@
continue;
}
- cssom::AbsoluteURLValue* absolute_url =
- base::polymorphic_downcast<cssom::AbsoluteURLValue*>(
- property_list_value->value()[i].get());
- if (absolute_url->value().is_valid()) {
- scoped_refptr<loader::image::CachedImage> cached_image =
- html_element_context()->image_cache()->CreateCachedResource(
- absolute_url->value());
- base::Closure loaded_callback = base::Bind(
- &HTMLElement::OnBackgroundImageLoaded, base::Unretained(this));
- cached_images.push_back(
- new loader::image::CachedImageReferenceWithCallbacks(
- cached_image, loaded_callback, base::Closure()));
+ // Skip invalid URL.
+ GURL absolute_url = base::polymorphic_downcast<cssom::AbsoluteURLValue*>(
+ property_list_value->value()[i].get())
+ ->value();
+ if (!absolute_url.is_valid()) {
+ continue;
}
+
+ active_background_images_.push_back(absolute_url);
+ html_element_context()->animated_image_tracker()->IncreaseURLCount(
+ absolute_url);
+
+ scoped_refptr<loader::image::CachedImage> cached_image =
+ html_element_context()->image_cache()->CreateCachedResource(
+ absolute_url);
+ base::Closure loaded_callback = base::Bind(
+ &HTMLElement::OnBackgroundImageLoaded, base::Unretained(this));
+ cached_images.push_back(
+ new loader::image::CachedImageReferenceWithCallbacks(
+ cached_image, loaded_callback, base::Closure()));
}
cached_background_images_ = cached_images.Pass();
@@ -1079,7 +1131,7 @@
void HTMLElement::OnBackgroundImageLoaded() {
node_document()->RecordMutation();
- InvalidateRenderTreeNodesFromNode();
+ InvalidateLayoutBoxRenderTreeNodes();
}
bool HTMLElement::IsRootElement() {
@@ -1088,12 +1140,5 @@
return AsHTMLHtmlElement() != NULL;
}
-void HTMLElement::ReleaseImagesAndInvalidateComputedStyleIfNecessary() {
- if (!cached_background_images_.empty()) {
- cached_background_images_.clear();
- computed_style_valid_ = false;
- }
-}
-
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index 995ed52..88bbccd 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -16,6 +16,7 @@
#define COBALT_DOM_HTML_ELEMENT_H_
#include <string>
+#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
@@ -202,8 +203,14 @@
return css_computed_style_declaration_->data();
}
- // Invalidates the cached computed style of this element and its descendants.
- void InvalidateComputedStylesRecursively() OVERRIDE;
+ void PurgeCachedBackgroundImagesOfNodeAndDescendants() OVERRIDE;
+ void InvalidateComputedStylesOfNodeAndDescendants() OVERRIDE;
+ void InvalidateLayoutBoxesOfNodeAndAncestors() OVERRIDE;
+ void InvalidateLayoutBoxesOfNodeAndDescendants() OVERRIDE;
+ void InvalidateLayoutBoxSizes() OVERRIDE;
+ void InvalidateLayoutBoxCrossReferences() OVERRIDE;
+ void InvalidateLayoutBoxRenderTreeNodes() OVERRIDE;
+
// Updates the cached computed style of this element and its descendants.
void UpdateComputedStyleRecursively(
const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
@@ -230,11 +237,6 @@
}
LayoutBoxes* layout_boxes() const { return layout_boxes_.get(); }
- void InvalidateLayoutBoxesFromNodeAndAncestors() OVERRIDE;
- void InvalidateLayoutBoxesFromNodeAndDescendants() OVERRIDE;
- void InvalidateLayoutBoxSizesFromNode() OVERRIDE;
- void InvalidateLayoutBoxCrossReferencesFromNode() OVERRIDE;
- void InvalidateRenderTreeNodesFromNode() OVERRIDE;
// Determines whether this element is focusable.
bool IsFocusable() const { return HasAttribute("tabindex"); }
@@ -254,15 +256,11 @@
DEFINE_WRAPPABLE_TYPE(HTMLElement);
protected:
- HTMLElement(Document* document, base::Token tag_name);
+ HTMLElement(Document* document, base::Token local_name);
~HTMLElement() OVERRIDE;
void CopyDirectionality(const HTMLElement& other);
- // Releases image resources and invalidates computed style if there are images
- // associated with this html element in the image cache.
- void ReleaseImagesAndInvalidateComputedStyleIfNecessary() OVERRIDE;
-
// HTMLElement keeps a pointer to the dom stat tracker to ensure that it can
// make stat updates even after its weak pointer to its document has been
// deleted. This is protected because some derived classes need access to it.
@@ -286,6 +284,10 @@
// directionality does not invalidate the computed style.
void SetDirectionality(const std::string& value);
+ // Clear the list of active background images, and notify the animated image
+ // tracker to stop the animations.
+ void ClearActiveBackgroundImages();
+
void UpdateCachedBackgroundImagesFromComputedStyle();
// This will be called when the image data associated with this element's
@@ -336,6 +338,8 @@
scoped_ptr<PseudoElement> pseudo_elements_[kMaxPseudoElementType];
base::WeakPtr<DOMStringMap> dataset_;
+ std::vector<GURL> active_background_images_;
+
// |cached_background_images_| contains a list of CachedImage references for
// all images referenced by the computed value for the background_image CSS
// property and a image loaded handle to add and remove image loaded callback.
diff --git a/src/cobalt/dom/html_element_context.cc b/src/cobalt/dom/html_element_context.cc
index b069028..3cf96f0 100644
--- a/src/cobalt/dom/html_element_context.cc
+++ b/src/cobalt/dom/html_element_context.cc
@@ -24,8 +24,10 @@
Parser* dom_parser, media::CanPlayTypeHandler* can_play_type_handler,
media::WebMediaPlayerFactory* web_media_player_factory,
script::ScriptRunner* script_runner,
+ script::ScriptValueFactory* script_value_factory,
MediaSourceRegistry* media_source_registry,
render_tree::ResourceProvider** resource_provider,
+ loader::image::AnimatedImageTracker* animated_image_tracker,
loader::image::ImageCache* image_cache,
loader::image::ReducedCacheCapacityManager*
reduced_image_cache_capacity_manager,
@@ -38,8 +40,10 @@
can_play_type_handler_(can_play_type_handler),
web_media_player_factory_(web_media_player_factory),
script_runner_(script_runner),
+ script_value_factory_(script_value_factory),
media_source_registry_(media_source_registry),
resource_provider_(resource_provider),
+ animated_image_tracker_(animated_image_tracker),
image_cache_(image_cache),
reduced_image_cache_capacity_manager_(
reduced_image_cache_capacity_manager),
diff --git a/src/cobalt/dom/html_element_context.h b/src/cobalt/dom/html_element_context.h
index fa71993..4650c31 100644
--- a/src/cobalt/dom/html_element_context.h
+++ b/src/cobalt/dom/html_element_context.h
@@ -25,11 +25,13 @@
#include "cobalt/dom/url_registry.h"
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/loader/font/remote_typeface_cache.h"
+#include "cobalt/loader/image/animated_image_tracker.h"
#include "cobalt/loader/image/image_cache.h"
#include "cobalt/loader/mesh/mesh_cache.h"
#include "cobalt/media/can_play_type_handler.h"
#include "cobalt/media/web_media_player_factory.h"
#include "cobalt/script/script_runner.h"
+#include "cobalt/script/script_value_factory.h"
namespace cobalt {
namespace dom {
@@ -44,20 +46,21 @@
public:
typedef UrlRegistry<MediaSource> MediaSourceRegistry;
- HTMLElementContext(loader::FetcherFactory* fetcher_factory,
- cssom::CSSParser* css_parser, Parser* dom_parser,
- media::CanPlayTypeHandler* can_play_type_handler,
- media::WebMediaPlayerFactory* web_media_player_factory,
- script::ScriptRunner* script_runner,
- MediaSourceRegistry* media_source_registry,
- render_tree::ResourceProvider** resource_provider,
- loader::image::ImageCache* image_cache,
- loader::image::ReducedCacheCapacityManager*
- reduced_image_cache_capacity_manager,
- loader::font::RemoteTypefaceCache* remote_typeface_cache,
- loader::mesh::MeshCache* mesh_cache,
- DomStatTracker* dom_stat_tracker,
- const std::string& language);
+ HTMLElementContext(
+ loader::FetcherFactory* fetcher_factory, cssom::CSSParser* css_parser,
+ Parser* dom_parser, media::CanPlayTypeHandler* can_play_type_handler,
+ media::WebMediaPlayerFactory* web_media_player_factory,
+ script::ScriptRunner* script_runner,
+ script::ScriptValueFactory* script_value_factory,
+ MediaSourceRegistry* media_source_registry,
+ render_tree::ResourceProvider** resource_provider,
+ loader::image::AnimatedImageTracker* animated_image_tracker,
+ loader::image::ImageCache* image_cache,
+ loader::image::ReducedCacheCapacityManager*
+ reduced_image_cache_capacity_manager,
+ loader::font::RemoteTypefaceCache* remote_typeface_cache,
+ loader::mesh::MeshCache* mesh_cache, DomStatTracker* dom_stat_tracker,
+ const std::string& language);
~HTMLElementContext();
loader::FetcherFactory* fetcher_factory() { return fetcher_factory_; }
@@ -73,7 +76,12 @@
return web_media_player_factory_;
}
- script::ScriptRunner* script_runner() { return script_runner_; }
+ script::ScriptRunner* script_runner() const { return script_runner_; }
+
+ script::ScriptValueFactory* script_value_factory() const {
+ return script_value_factory_;
+ }
+
MediaSourceRegistry* media_source_registry() {
return media_source_registry_;
}
@@ -82,6 +90,10 @@
return resource_provider_;
}
+ loader::image::AnimatedImageTracker* animated_image_tracker() const {
+ return animated_image_tracker_;
+ }
+
loader::image::ImageCache* image_cache() const { return image_cache_; }
loader::font::RemoteTypefaceCache* remote_typeface_cache() const {
@@ -112,8 +124,10 @@
media::CanPlayTypeHandler* can_play_type_handler_;
media::WebMediaPlayerFactory* const web_media_player_factory_;
script::ScriptRunner* const script_runner_;
+ script::ScriptValueFactory* const script_value_factory_;
MediaSourceRegistry* const media_source_registry_;
render_tree::ResourceProvider** resource_provider_;
+ loader::image::AnimatedImageTracker* const animated_image_tracker_;
loader::image::ImageCache* const image_cache_;
loader::image::ReducedCacheCapacityManager* const
reduced_image_cache_capacity_manager_;
diff --git a/src/cobalt/dom/html_element_factory.cc b/src/cobalt/dom/html_element_factory.cc
index a061e2f..e4cfeee 100644
--- a/src/cobalt/dom/html_element_factory.cc
+++ b/src/cobalt/dom/html_element_factory.cc
@@ -49,8 +49,8 @@
template <typename T>
scoped_refptr<HTMLElement> CreateHTMLElementWithTagNameT(
- const std::string& tag_name, Document* document) {
- return new T(document, base::Token(tag_name));
+ const std::string& local_name, Document* document) {
+ return new T(document, base::Token(local_name));
}
} // namespace
diff --git a/src/cobalt/dom/html_element_factory_test.cc b/src/cobalt/dom/html_element_factory_test.cc
index 5c1828d..0b27f6f 100644
--- a/src/cobalt/dom/html_element_factory_test.cc
+++ b/src/cobalt/dom/html_element_factory_test.cc
@@ -55,7 +55,8 @@
&fetcher_factory_, &stub_css_parser_, dom_parser_.get(),
NULL /* can_play_type_handler */,
NULL /* web_media_player_factory */, &stub_script_runner_,
- NULL /* media_source_registry */, NULL /* resource_provider */,
+ NULL /* script_value_factory */, NULL /* media_source_registry */,
+ NULL /* resource_provider */, NULL /* animated_image_tracker */,
NULL /* image_cache */,
NULL /* reduced_image_cache_capacity_manager */,
NULL /* remote_typeface_cache */, NULL /* mesh_cache */,
diff --git a/src/cobalt/dom/html_element_test.cc b/src/cobalt/dom/html_element_test.cc
index db94236..685ccab 100644
--- a/src/cobalt/dom/html_element_test.cc
+++ b/src/cobalt/dom/html_element_test.cc
@@ -108,7 +108,7 @@
HTMLElementTest()
: dom_stat_tracker_(new DomStatTracker("HTMLElementTest")),
html_element_context_(NULL, &css_parser_, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
dom_stat_tracker_.get(), ""),
document_(new Document(&html_element_context_)) {}
~HTMLElementTest() OVERRIDE {}
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 06b08da..5e98f55 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -19,6 +19,7 @@
#include "base/bind.h"
#include "base/compiler_specific.h"
+#include "base/debug/trace_event.h"
#include "base/guid.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
@@ -37,6 +38,7 @@
#include "cobalt/dom/media_key_message_event.h"
#include "cobalt/dom/media_key_needed_event.h"
#include "cobalt/dom/media_source.h"
+#include "cobalt/dom/media_source_ready_state.h"
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/media/fetcher_buffered_data_source.h"
#include "cobalt/media/web_media_player_factory.h"
@@ -144,11 +146,13 @@
pending_load_(false),
sent_stalled_event_(false),
sent_end_event_(false) {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::HTMLMediaElement");
MLOG();
html_media_element_count_log.Get().count++;
}
HTMLMediaElement::~HTMLMediaElement() {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::~HTMLMediaElement");
MLOG();
ClearMediaSource();
html_media_element_count_log.Get().count--;
@@ -165,6 +169,7 @@
}
void HTMLMediaElement::set_src(const std::string& src) {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::set_src");
MLOG() << src;
SetAttribute("src", src);
ClearMediaPlayer();
@@ -199,6 +204,7 @@
}
void HTMLMediaElement::Load() {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Load");
// LoadInternal may result in a 'beforeload' event, which can make arbitrary
// DOM mutations.
scoped_refptr<HTMLMediaElement> protect(this);
@@ -486,6 +492,7 @@
}
void HTMLMediaElement::Play() {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Play");
MLOG();
// 4.8.10.9. Playing the media resource
if (!player_ || network_state_ == kNetworkEmpty) {
@@ -512,6 +519,7 @@
}
void HTMLMediaElement::Pause() {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::Pause");
MLOG();
// 4.8.10.9. Playing the media resource
if (!player_ || network_state_ == kNetworkEmpty) {
@@ -608,11 +616,13 @@
#endif // defined(COBALT_MEDIA_SOURCE_2016)
void HTMLMediaElement::ScheduleEvent(const scoped_refptr<Event>& event) {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ScheduleEvent");
MLOG() << event->type();
event_queue_.Enqueue(event);
}
void HTMLMediaElement::CreateMediaPlayer() {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::CreateMediaPlayer");
MLOG();
if (src().empty()) {
reduced_image_cache_capacity_request_ = base::nullopt;
@@ -629,9 +639,7 @@
// Set a new reduced cache capacity.
reduced_image_cache_capacity_request_.emplace(
html_element_context()->reduced_image_cache_capacity_manager());
- // Empty all non-referenced images from the image cache to make room for
- // the video decoder.
- html_element_context()->image_cache()->Purge();
+
// Ensure that all resource destructions are flushed and the memory is
// reclaimed.
if (*html_element_context()->resource_provider()) {
@@ -649,10 +657,11 @@
}
#endif // !defined(COBALT_MEDIA_SOURCE_2016)
node_document()->OnDOMMutation();
- InvalidateLayoutBoxesFromNodeAndAncestors();
+ InvalidateLayoutBoxesOfNodeAndAncestors();
}
void HTMLMediaElement::ScheduleLoad() {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ScheduleLoad");
if (!pending_load_) {
PrepareForLoad();
pending_load_ = true;
@@ -665,6 +674,7 @@
}
void HTMLMediaElement::PrepareForLoad() {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::PrepareForLoad");
// Perform the cleanup required for the resource load algorithm to run.
StopPeriodicTimers();
load_timer_.Stop();
@@ -728,6 +738,7 @@
}
void HTMLMediaElement::LoadInternal() {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::LoadInternal");
DCHECK(node_document());
// Select media resource.
@@ -858,6 +869,7 @@
}
void HTMLMediaElement::ClearMediaPlayer() {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::ClearMediaPlayer");
MLOG();
ClearMediaSource();
@@ -879,6 +891,9 @@
void HTMLMediaElement::NoneSupported() {
MLOG();
+
+ DLOG(WARNING) << "HTMLMediaElement::NoneSupported() error.";
+
StopPeriodicTimers();
load_state_ = kWaitingForSource;
@@ -921,6 +936,7 @@
// This is kept as a one shot timer to be in sync with the original code. It
// should be replaced by PostTask if this is any future rewrite.
void HTMLMediaElement::OnLoadTimer() {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::OnLoadTimer");
scoped_refptr<HTMLMediaElement> protect(this);
if (pending_load_) {
@@ -1202,7 +1218,8 @@
// Always notify the media engine of a seek if the source is not closed. This
// ensures that the source is always in a flushed state when the 'seeking'
// event fires.
- if (media_source_ && media_source_->ready_state() != MediaSource::kClosed) {
+ if (media_source_ &&
+ media_source_->ready_state() != kMediaSourceReadyStateClosed) {
no_seek_required = false;
}
@@ -1499,6 +1516,7 @@
#if defined(COBALT_MEDIA_SOURCE_2016)
void HTMLMediaElement::SourceOpened(media::ChunkDemuxer* chunk_demuxer) {
+ TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::SourceOpened");
BeginProcessingMediaPlayerCallback();
DCHECK(media_source_);
media_source_->SetChunkDemuxerAndOpen(chunk_demuxer);
@@ -1507,7 +1525,7 @@
#else // defined(COBALT_MEDIA_SOURCE_2016)
void HTMLMediaElement::SourceOpened() {
BeginProcessingMediaPlayerCallback();
- SetSourceState(MediaSource::kOpen);
+ SetSourceState(kMediaSourceReadyStateOpen);
EndProcessingMediaPlayerCallback();
}
#endif // defined(COBALT_MEDIA_SOURCE_2016)
@@ -1528,6 +1546,15 @@
return map_to_mesh_filter;
}
+#if defined(COBALT_MEDIA_SOURCE_2016)
+void HTMLMediaElement::EncryptedMediaInitData(
+ media::EmeInitDataType /*init_data_type*/,
+ const unsigned char* /*init_data*/, unsigned int /*init_data_length*/) {
+ // TODO: Implement as per EME 2017.
+ NOTIMPLEMENTED();
+}
+#endif // defined(COBALT_MEDIA_SOURCE_2016)
+
void HTMLMediaElement::KeyAdded(const std::string& key_system,
const std::string& session_id) {
MLOG() << key_system;
@@ -1596,18 +1623,18 @@
media_source_ = NULL;
}
#else // defined(COBALT_MEDIA_SOURCE_2016)
- SetSourceState(MediaSource::kClosed);
+ SetSourceState(kMediaSourceReadyStateClosed);
#endif // defined(COBALT_MEDIA_SOURCE_2016)
}
#if !defined(COBALT_MEDIA_SOURCE_2016)
-void HTMLMediaElement::SetSourceState(MediaSource::ReadyState ready_state) {
+void HTMLMediaElement::SetSourceState(MediaSourceReadyState ready_state) {
MLOG() << ready_state;
if (!media_source_) {
return;
}
media_source_->SetReadyState(ready_state);
- if (ready_state == MediaSource::kClosed) {
+ if (ready_state == kMediaSourceReadyStateClosed) {
media_source_ = NULL;
}
}
@@ -1618,7 +1645,7 @@
// the video render tree differently depending on whether we are in punch-out
// or decode-to-texture.
node_document()->OnDOMMutation();
- InvalidateLayoutBoxesFromNodeAndAncestors();
+ InvalidateLayoutBoxesOfNodeAndAncestors();
}
} // namespace dom
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index ce08323..73b7a8e 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -231,6 +231,11 @@
#endif // defined(COBALT_MEDIA_SOURCE_2016)
std::string SourceURL() const OVERRIDE;
bool PreferDecodeToTexture() const OVERRIDE;
+#if defined(COBALT_MEDIA_SOURCE_2016)
+ void EncryptedMediaInitData(media::EmeInitDataType init_data_type,
+ const unsigned char* init_data,
+ unsigned int init_data_length) OVERRIDE;
+#endif // defined(COBALT_MEDIA_SOURCE_2016)
void KeyAdded(const std::string& key_system,
const std::string& session_id) OVERRIDE;
void KeyError(const std::string& key_system, const std::string& session_id,
@@ -243,7 +248,7 @@
unsigned int init_data_length) OVERRIDE;
void ClearMediaSource();
#if !defined(COBALT_MEDIA_SOURCE_2016)
- void SetSourceState(MediaSource::ReadyState ready_state);
+ void SetSourceState(MediaSourceReadyState ready_state);
#endif // !defined(COBALT_MEDIA_SOURCE_2016)
// Called whenever the player's output mode (e.g. punch-out,
diff --git a/src/cobalt/dom/html_meta_element.cc b/src/cobalt/dom/html_meta_element.cc
index 325a366..9e2ea63 100644
--- a/src/cobalt/dom/html_meta_element.cc
+++ b/src/cobalt/dom/html_meta_element.cc
@@ -19,6 +19,7 @@
#include "cobalt/csp/content_security_policy.h"
#include "cobalt/dom/csp_delegate.h"
#include "cobalt/dom/document.h"
+#include "cobalt/dom/html_head_element.h"
#include "cobalt/dom/node.h"
namespace cobalt {
@@ -56,7 +57,9 @@
bool HTMLMetaElement::IsDescendantOfHeadElement() const {
for (scoped_refptr<Node> node = parent_node(); node;
node = node->parent_node()) {
- if (node->node_name() == "head") return true;
+ if (node->AsElement() && node->AsElement()->AsHTMLElement() &&
+ node->AsElement()->AsHTMLElement()->AsHTMLHeadElement())
+ return true;
}
return false;
}
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index 840458d..c7a3362 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -517,7 +517,8 @@
// script was obtained, the script block's type as the scripting language, and
// the script settings object of the script element's Document's Window
// object.
- html_element_context()->script_runner()->Execute(content, script_location);
+ html_element_context()->script_runner()->Execute(content, script_location,
+ NULL /* output: succeeded */);
// 5. 6. Not needed by Cobalt.
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/media_key_status.idl
similarity index 64%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/media_key_status.idl
index d335495..583215a 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/media_key_status.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/2016/CR-encrypted-media-20160705/#mediakeystatusmap-interface
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum MediaKeyStatus {
+ "usable",
+ "expired",
+ "released",
+ "output-restricted",
+ "output-downscaled",
+ "status-pending",
+ "internal-error"
+};
diff --git a/src/cobalt/dom/media_key_status_map.h b/src/cobalt/dom/media_key_status_map.h
index abcee44..414b0f6 100644
--- a/src/cobalt/dom/media_key_status_map.h
+++ b/src/cobalt/dom/media_key_status_map.h
@@ -27,17 +27,6 @@
class MediaKeyStatusMap : public script::Wrappable {
public:
- enum MediaKeyStatus {
- kUsable,
- kExpired,
- kReleased,
- kOutputRestricted,
- kOutputDownscaled,
- kStatusPending,
- kInternalError,
- kMaxMediaKeyStatus,
- };
-
typedef script::UnionType2<scoped_refptr<ArrayBufferView>,
scoped_refptr<ArrayBuffer> > BufferSource;
uint32_t size() const { return 0; }
diff --git a/src/cobalt/dom/media_key_system_configuration.idl b/src/cobalt/dom/media_key_system_configuration.idl
index 6bbde5c..0ac303b 100644
--- a/src/cobalt/dom/media_key_system_configuration.idl
+++ b/src/cobalt/dom/media_key_system_configuration.idl
@@ -14,12 +14,6 @@
// https://www.w3.org/TR/2016/CR-encrypted-media-20160705/#mediakeysystemconfiguration-dictionary
-enum MediaKeysRequirement {
- "required",
- "optional",
- "not-allowed"
-};
-
dictionary MediaKeySystemConfiguration {
DOMString label = "";
sequence<DOMString> initDataTypes = [];
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/media_keys_requirement.idl
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/media_keys_requirement.idl
index d335495..cf125bb 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/media_keys_requirement.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/2016/CR-encrypted-media-20160705/#mediakeysystemconfiguration-dictionary
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum MediaKeysRequirement {
+ "required",
+ "optional",
+ "not-allowed"
+};
diff --git a/src/cobalt/dom/media_source.cc b/src/cobalt/dom/media_source.cc
index 335fb8d..20cc705 100644
--- a/src/cobalt/dom/media_source.cc
+++ b/src/cobalt/dom/media_source.cc
@@ -73,7 +73,7 @@
} // namespace
MediaSource::MediaSource()
- : ready_state_(kClosed),
+ : ready_state_(kMediaSourceReadyStateClosed),
player_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
source_buffers_(new SourceBufferList(&event_queue_)) {}
@@ -90,7 +90,7 @@
double MediaSource::duration(script::ExceptionState* exception_state) const {
UNREFERENCED_PARAMETER(exception_state);
- if (ready_state_ == kClosed) {
+ if (ready_state_ == kMediaSourceReadyStateClosed) {
return std::numeric_limits<float>::quiet_NaN();
}
@@ -104,7 +104,7 @@
DOMException::Raise(DOMException::kInvalidAccessErr, exception_state);
return;
}
- if (ready_state_ != kOpen) {
+ if (ready_state_ != kMediaSourceReadyStateOpen) {
DOMException::Raise(DOMException::kInvalidAccessErr, exception_state);
return;
}
@@ -131,7 +131,7 @@
return NULL;
}
- if (!player_ || ready_state_ != kOpen) {
+ if (!player_ || ready_state_ != kMediaSourceReadyStateOpen) {
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
// Return value should be ignored.
return NULL;
@@ -182,18 +182,16 @@
player_->SourceRemoveId(source_buffer->id());
}
-MediaSource::ReadyState MediaSource::ready_state() const {
- return ready_state_;
-}
+MediaSourceReadyState MediaSource::ready_state() const { return ready_state_; }
void MediaSource::EndOfStream(script::ExceptionState* exception_state) {
// If there is no error string provided, treat it as empty.
- EndOfStream(kNoError, exception_state);
+ EndOfStream(kMediaSourceEndOfStreamErrorNoError, exception_state);
}
-void MediaSource::EndOfStream(EndOfStreamError error,
+void MediaSource::EndOfStream(MediaSourceEndOfStreamError error,
script::ExceptionState* exception_state) {
- if (!player_ || ready_state_ != kOpen) {
+ if (!player_ || ready_state_ != kMediaSourceReadyStateOpen) {
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
return;
}
@@ -201,18 +199,18 @@
WebMediaPlayer::EndOfStreamStatus eos_status =
WebMediaPlayer::kEndOfStreamStatusNoError;
- if (error == kNoError) {
+ if (error == kMediaSourceEndOfStreamErrorNoError) {
eos_status = WebMediaPlayer::kEndOfStreamStatusNoError;
- } else if (error == kNetwork) {
+ } else if (error == kMediaSourceEndOfStreamErrorNetwork) {
eos_status = WebMediaPlayer::kEndOfStreamStatusNetworkError;
- } else if (error == kDecode) {
+ } else if (error == kMediaSourceEndOfStreamErrorDecode) {
eos_status = WebMediaPlayer::kEndOfStreamStatusDecodeError;
} else {
DOMException::Raise(DOMException::kInvalidAccessErr, exception_state);
return;
}
- SetReadyState(kEnded);
+ SetReadyState(kMediaSourceReadyStateEnded);
player_->SourceEndOfStream(eos_status);
}
@@ -243,7 +241,7 @@
scoped_refptr<TimeRanges> MediaSource::GetBuffered(
const SourceBuffer* source_buffer,
script::ExceptionState* exception_state) {
- if (!player_ || ready_state_ == kClosed) {
+ if (!player_ || ready_state_ == kMediaSourceReadyStateClosed) {
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
// Return value should be ignored.
return NULL;
@@ -263,7 +261,7 @@
bool MediaSource::SetTimestampOffset(const SourceBuffer* source_buffer,
double timestamp_offset,
script::ExceptionState* exception_state) {
- if (!player_ || ready_state_ == kClosed) {
+ if (!player_ || ready_state_ == kMediaSourceReadyStateClosed) {
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
// Return value should be ignored.
return false;
@@ -284,13 +282,13 @@
return;
}
- if (!player_ || ready_state_ == kClosed) {
+ if (!player_ || ready_state_ == kMediaSourceReadyStateClosed) {
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
return;
}
- if (ready_state_ == kEnded) {
- SetReadyState(kOpen);
+ if (ready_state_ == kMediaSourceReadyStateEnded) {
+ SetReadyState(kMediaSourceReadyStateOpen);
}
TRACE_EVENT1("media_stack", "MediaSource::Append()", "size", size);
@@ -314,7 +312,7 @@
void MediaSource::Abort(const SourceBuffer* source_buffer,
script::ExceptionState* exception_state) {
- if (!player_ || ready_state_ != kOpen) {
+ if (!player_ || ready_state_ != kMediaSourceReadyStateOpen) {
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
return;
}
@@ -323,27 +321,28 @@
DCHECK(aborted);
}
-void MediaSource::SetReadyState(ReadyState ready_state) {
+void MediaSource::SetReadyState(MediaSourceReadyState ready_state) {
if (ready_state_ == ready_state) {
return;
}
- ReadyState old_state = ready_state_;
+ MediaSourceReadyState old_state = ready_state_;
ready_state_ = ready_state;
- if (ready_state_ == kClosed) {
+ if (ready_state_ == kMediaSourceReadyStateClosed) {
source_buffers_->Clear();
player_ = NULL;
ScheduleEvent(base::Tokens::sourceclose());
return;
}
- if (old_state == kOpen && ready_state_ == kEnded) {
+ if (old_state == kMediaSourceReadyStateOpen &&
+ ready_state_ == kMediaSourceReadyStateEnded) {
ScheduleEvent(base::Tokens::sourceended());
return;
}
- if (ready_state_ == kOpen) {
+ if (ready_state_ == kMediaSourceReadyStateOpen) {
ScheduleEvent(base::Tokens::sourceopen());
}
}
diff --git a/src/cobalt/dom/media_source.h b/src/cobalt/dom/media_source.h
index 352983d..d266621 100644
--- a/src/cobalt/dom/media_source.h
+++ b/src/cobalt/dom/media_source.h
@@ -26,6 +26,8 @@
#include "base/memory/ref_counted.h"
#include "cobalt/dom/event_queue.h"
#include "cobalt/dom/event_target.h"
+#include "cobalt/dom/media_source_end_of_stream_error.h"
+#include "cobalt/dom/media_source_ready_state.h"
#include "cobalt/dom/source_buffer.h"
#include "cobalt/dom/source_buffer_list.h"
#include "cobalt/dom/url_registry.h"
@@ -48,12 +50,6 @@
public:
typedef UrlRegistry<MediaSource> Registry;
- // Web API: MediaSource
- //
- enum EndOfStreamError { kNoError, kNetwork, kDecode };
-
- enum ReadyState { kClosed, kOpen, kEnded };
-
// Custom, not in any spec.
//
MediaSource();
@@ -69,10 +65,10 @@
void RemoveSourceBuffer(const scoped_refptr<SourceBuffer>& source_buffer,
script::ExceptionState* exception_state);
- ReadyState ready_state() const;
+ MediaSourceReadyState ready_state() const;
void EndOfStream(script::ExceptionState* exception_state);
- void EndOfStream(EndOfStreamError error,
+ void EndOfStream(MediaSourceEndOfStreamError error,
script::ExceptionState* exception_state);
static bool IsTypeSupported(script::EnvironmentSettings* settings,
@@ -99,7 +95,7 @@
script::ExceptionState* exception_state);
// Methods used by HTMLMediaElement
- void SetReadyState(ReadyState ready_state);
+ void SetReadyState(MediaSourceReadyState ready_state);
DEFINE_WRAPPABLE_TYPE(MediaSource);
@@ -107,7 +103,7 @@
// From EventTarget.
std::string GetDebugName() OVERRIDE { return "MediaSource"; }
- ReadyState ready_state_;
+ MediaSourceReadyState ready_state_;
::media::WebMediaPlayer* player_;
EventQueue event_queue_;
scoped_refptr<SourceBufferList> source_buffers_;
diff --git a/src/cobalt/dom/media_source.idl b/src/cobalt/dom/media_source.idl
index ee69d48..e1c230d 100644
--- a/src/cobalt/dom/media_source.idl
+++ b/src/cobalt/dom/media_source.idl
@@ -15,30 +15,19 @@
// https://www.w3.org/TR/media-source/#idl-def-mediasource
// https://www.w3.org/TR/2016/CR-media-source-20160705/#idl-def-MediaSource
-enum EndOfStreamError {
- "network",
- "decode"
-};
-
-enum ReadyState {
- "closed",
- "open",
- "ended"
-};
-
[Constructor] interface MediaSource : EventTarget {
// All the source buffers created by this object.
readonly attribute SourceBufferList sourceBuffers;
// Subset of sourceBuffers that provide data for the selected/enabled tracks.
readonly attribute SourceBufferList activeSourceBuffers;
- readonly attribute ReadyState readyState;
+ readonly attribute MediaSourceReadyState readyState;
[RaisesException] attribute unrestricted double duration;
[RaisesException] SourceBuffer addSourceBuffer(DOMString type);
[RaisesException] void removeSourceBuffer(SourceBuffer sourceBuffer);
- [RaisesException] void endOfStream(optional EndOfStreamError error);
+ [RaisesException] void endOfStream(optional MediaSourceEndOfStreamError error);
[Conditional=COBALT_MEDIA_SOURCE_2016, RaisesException]
void setLiveSeekableRange(double start, double end);
diff --git a/src/cobalt/dom/media_source/media_source.cc b/src/cobalt/dom/media_source/media_source.cc
index 614d305..63abdec 100644
--- a/src/cobalt/dom/media_source/media_source.cc
+++ b/src/cobalt/dom/media_source/media_source.cc
@@ -110,13 +110,13 @@
MediaSource::MediaSource()
: chunk_demuxer_(NULL),
- ready_state_(kClosed),
+ ready_state_(kMediaSourceReadyStateClosed),
ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
source_buffers_(new SourceBufferList(&event_queue_)),
active_source_buffers_(new SourceBufferList(&event_queue_)),
live_seekable_range_(new TimeRanges) {}
-MediaSource::~MediaSource() { SetReadyState(kClosed); }
+MediaSource::~MediaSource() { SetReadyState(kMediaSourceReadyStateClosed); }
scoped_refptr<SourceBufferList> MediaSource::source_buffers() const {
return source_buffers_;
@@ -126,14 +126,12 @@
return active_source_buffers_;
}
-MediaSource::ReadyState MediaSource::ready_state() const {
- return ready_state_;
-}
+MediaSourceReadyState MediaSource::ready_state() const { return ready_state_; }
double MediaSource::duration(script::ExceptionState* exception_state) const {
UNREFERENCED_PARAMETER(exception_state);
- if (ready_state_ == kClosed) {
+ if (ready_state_ == kMediaSourceReadyStateClosed) {
return std::numeric_limits<float>::quiet_NaN();
}
@@ -262,23 +260,23 @@
void MediaSource::EndOfStream(script::ExceptionState* exception_state) {
// If there is no error string provided, treat it as empty.
- EndOfStream(kNoError, exception_state);
+ EndOfStream(kMediaSourceEndOfStreamErrorNoError, exception_state);
}
-void MediaSource::EndOfStream(EndOfStreamError error,
+void MediaSource::EndOfStream(MediaSourceEndOfStreamError error,
script::ExceptionState* exception_state) {
if (!IsOpen() || IsUpdating()) {
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
return;
}
- SetReadyState(kEnded);
+ SetReadyState(kMediaSourceReadyStateEnded);
PipelineStatus pipeline_status = PIPELINE_OK;
- if (error == kNetwork) {
+ if (error == kMediaSourceEndOfStreamErrorNetwork) {
pipeline_status = CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR;
- } else if (error == kDecode) {
+ } else if (error == kMediaSourceEndOfStreamErrorDecode) {
pipeline_status = CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR;
}
@@ -352,12 +350,14 @@
DCHECK(!chunk_demuxer_);
DCHECK(attached_element_);
chunk_demuxer_ = chunk_demuxer;
- SetReadyState(kOpen);
+ SetReadyState(kMediaSourceReadyStateOpen);
}
-void MediaSource::Close() { SetReadyState(kClosed); }
+void MediaSource::Close() { SetReadyState(kMediaSourceReadyStateClosed); }
-bool MediaSource::IsClosed() const { return ready_state_ == kClosed; }
+bool MediaSource::IsClosed() const {
+ return ready_state_ == kMediaSourceReadyStateClosed;
+}
scoped_refptr<TimeRanges> MediaSource::GetBufferedRange() const {
std::vector<scoped_refptr<TimeRanges> > ranges(
@@ -386,7 +386,7 @@
scoped_refptr<TimeRanges> intersection_ranges =
new TimeRanges(0, highest_end_time);
- bool ended = ready_state() == kEnded;
+ bool ended = ready_state() == kMediaSourceReadyStateEnded;
for (size_t i = 0; i < ranges.size(); ++i) {
scoped_refptr<TimeRanges> source_ranges = ranges[i].get();
@@ -471,15 +471,17 @@
}
void MediaSource::OpenIfInEndedState() {
- if (ready_state_ != kEnded) {
+ if (ready_state_ != kMediaSourceReadyStateEnded) {
return;
}
- SetReadyState(kOpen);
+ SetReadyState(kMediaSourceReadyStateOpen);
chunk_demuxer_->UnmarkEndOfStream();
}
-bool MediaSource::IsOpen() const { return ready_state_ == kOpen; }
+bool MediaSource::IsOpen() const {
+ return ready_state_ == kMediaSourceReadyStateOpen;
+}
void MediaSource::SetSourceBufferActive(SourceBuffer* source_buffer,
bool is_active) {
@@ -513,8 +515,8 @@
return attached_element_;
}
-void MediaSource::SetReadyState(ReadyState ready_state) {
- if (ready_state == kClosed) {
+void MediaSource::SetReadyState(MediaSourceReadyState ready_state) {
+ if (ready_state == kMediaSourceReadyStateClosed) {
chunk_demuxer_ = NULL;
}
@@ -522,7 +524,7 @@
return;
}
- ReadyState old_state = ready_state_;
+ MediaSourceReadyState old_state = ready_state_;
ready_state_ = ready_state;
if (IsOpen()) {
@@ -530,7 +532,8 @@
return;
}
- if (old_state == kOpen && ready_state_ == kEnded) {
+ if (old_state == kMediaSourceReadyStateOpen &&
+ ready_state_ == kMediaSourceReadyStateEnded) {
ScheduleEvent(base::Tokens::sourceended());
return;
}
diff --git a/src/cobalt/dom/media_source/media_source.h b/src/cobalt/dom/media_source/media_source.h
index e205317..234768d 100644
--- a/src/cobalt/dom/media_source/media_source.h
+++ b/src/cobalt/dom/media_source/media_source.h
@@ -57,6 +57,8 @@
#include "cobalt/dom/html_media_element.h"
#include "cobalt/dom/media_source/source_buffer.h"
#include "cobalt/dom/media_source/source_buffer_list.h"
+#include "cobalt/dom/media_source_end_of_stream_error.h"
+#include "cobalt/dom/media_source_ready_state.h"
#include "cobalt/dom/time_ranges.h"
#include "cobalt/dom/url_registry.h"
#include "cobalt/dom/video_track.h"
@@ -74,12 +76,6 @@
public:
typedef UrlRegistry<MediaSource> Registry;
- // Web API: MediaSource
- //
- enum EndOfStreamError { kNoError, kNetwork, kDecode };
-
- enum ReadyState { kClosed, kOpen, kEnded };
-
// Custom, not in any spec.
//
MediaSource();
@@ -89,7 +85,7 @@
//
scoped_refptr<SourceBufferList> source_buffers() const;
scoped_refptr<SourceBufferList> active_source_buffers() const;
- ReadyState ready_state() const;
+ MediaSourceReadyState ready_state() const;
double duration(script::ExceptionState* exception_state) const;
void set_duration(double duration, script::ExceptionState* exception_state);
scoped_refptr<SourceBuffer> AddSourceBuffer(
@@ -98,7 +94,7 @@
script::ExceptionState* exception_state);
void EndOfStream(script::ExceptionState* exception_state);
- void EndOfStream(EndOfStreamError error,
+ void EndOfStream(MediaSourceEndOfStreamError error,
script::ExceptionState* exception_state);
void SetLiveSeekableRange(double start, double end,
script::ExceptionState* exception_state);
@@ -128,12 +124,12 @@
DEFINE_WRAPPABLE_TYPE(MediaSource);
private:
- void SetReadyState(ReadyState ready_state);
+ void SetReadyState(MediaSourceReadyState ready_state);
bool IsUpdating() const;
void ScheduleEvent(base::Token eventName);
media::ChunkDemuxer* chunk_demuxer_;
- ReadyState ready_state_;
+ MediaSourceReadyState ready_state_;
EventQueue event_queue_;
base::WeakPtr<HTMLMediaElement> attached_element_;
diff --git a/src/cobalt/dom/media_source/source_buffer.cc b/src/cobalt/dom/media_source/source_buffer.cc
index 9ea4ce5..0f1981d 100644
--- a/src/cobalt/dom/media_source/source_buffer.cc
+++ b/src/cobalt/dom/media_source/source_buffer.cc
@@ -93,7 +93,7 @@
media_source_(media_source),
track_defaults_(new TrackDefaultList(NULL)),
event_queue_(event_queue),
- mode_(kSegments),
+ mode_(kSourceBufferAppendModeSegments),
updating_(false),
timestamp_offset_(0),
audio_tracks_(new AudioTrackList(media_source->GetMediaElement())),
@@ -113,7 +113,7 @@
id_, base::Bind(&SourceBuffer::InitSegmentReceived, this));
}
-void SourceBuffer::set_mode(AppendMode mode,
+void SourceBuffer::set_mode(SourceBufferAppendMode mode,
script::ExceptionState* exception_state) {
if (media_source_ == NULL) {
DOMException::Raise(DOMException::kInvalidStateErr, exception_state);
@@ -131,7 +131,7 @@
return;
}
- chunk_demuxer_->SetSequenceMode(id_, mode == kSequence);
+ chunk_demuxer_->SetSequenceMode(id_, mode == kSourceBufferAppendModeSequence);
mode_ = mode;
}
@@ -471,7 +471,7 @@
ScheduleEvent(base::Tokens::error());
ScheduleEvent(base::Tokens::updateend());
- media_source_->EndOfStream(MediaSource::kDecode, NULL);
+ media_source_->EndOfStream(kMediaSourceEndOfStreamErrorDecode, NULL);
}
void SourceBuffer::OnRemoveTimer() {
diff --git a/src/cobalt/dom/media_source/source_buffer.h b/src/cobalt/dom/media_source/source_buffer.h
index 9685837..7e508bc 100644
--- a/src/cobalt/dom/media_source/source_buffer.h
+++ b/src/cobalt/dom/media_source/source_buffer.h
@@ -59,6 +59,7 @@
#include "cobalt/dom/audio_track_list.h"
#include "cobalt/dom/event_queue.h"
#include "cobalt/dom/event_target.h"
+#include "cobalt/dom/source_buffer_append_mode.h"
#include "cobalt/dom/time_ranges.h"
#include "cobalt/dom/track_default_list.h"
#include "cobalt/dom/video_track_list.h"
@@ -76,13 +77,6 @@
// https://www.w3.org/TR/2016/CR-media-source-20160705/#sourcebuffer
class SourceBuffer : public dom::EventTarget {
public:
- // Web API: SourceBuffer
- //
- enum AppendMode {
- kSegments,
- kSequence,
- };
-
// Custom, not in any spec.
//
SourceBuffer(const std::string& id, MediaSource* media_source,
@@ -90,11 +84,12 @@
// Web API: SourceBuffer
//
- AppendMode mode(script::ExceptionState* exception_state) const {
+ SourceBufferAppendMode mode(script::ExceptionState* exception_state) const {
UNREFERENCED_PARAMETER(exception_state);
return mode_;
}
- void set_mode(AppendMode mode, script::ExceptionState* exception_state);
+ void set_mode(SourceBufferAppendMode mode,
+ script::ExceptionState* exception_state);
bool updating() const { return updating_; }
scoped_refptr<TimeRanges> buffered(
script::ExceptionState* exception_state) const;
@@ -173,7 +168,7 @@
scoped_refptr<TrackDefaultList> track_defaults_;
EventQueue* event_queue_;
- AppendMode mode_;
+ SourceBufferAppendMode mode_;
bool updating_;
double timestamp_offset_;
scoped_refptr<AudioTrackList> audio_tracks_;
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/media_source_end_of_stream_error.idl
similarity index 63%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/media_source_end_of_stream_error.idl
index d335495..ca51be0 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/media_source_end_of_stream_error.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/media-source/#idl-def-mediasource
+// https://www.w3.org/TR/2016/CR-media-source-20160705/#idl-def-MediaSource
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum MediaSourceEndOfStreamError {
+ "network",
+ "decode",
+
+ // not part of spec, used in Cobalt implementation
+ "no-error",
+};
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/media_source_ready_state.idl
similarity index 68%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/media_source_ready_state.idl
index d335495..7528b74 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/media_source_ready_state.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/media-source/#idl-def-mediasource
+// https://www.w3.org/TR/2016/CR-media-source-20160705/#idl-def-MediaSource
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum MediaSourceReadyState {
+ "closed",
+ "open",
+ "ended"
+};
diff --git a/src/cobalt/dom/mutation_observer.cc b/src/cobalt/dom/mutation_observer.cc
index f8a5a82..b237491 100644
--- a/src/cobalt/dom/mutation_observer.cc
+++ b/src/cobalt/dom/mutation_observer.cc
@@ -18,9 +18,24 @@
#include "cobalt/dom/dom_settings.h"
#include "cobalt/dom/mutation_observer_task_manager.h"
#include "cobalt/dom/node.h"
+#include "cobalt/script/exception_message.h"
namespace cobalt {
namespace dom {
+namespace {
+// script::ExceptionState that will DCHECK on exception.
+class NativeExceptionState : public script::ExceptionState {
+ public:
+ void SetException(const scoped_refptr<script::ScriptException>&) OVERRIDE {
+ NOTREACHED();
+ }
+
+ void SetSimpleExceptionVA(script::SimpleExceptionType, const char*,
+ va_list) OVERRIDE {
+ NOTREACHED();
+ }
+};
+} // namespace
// Abstract base class for a MutationCallback.
class MutationObserver::CallbackInternal {
public:
@@ -87,17 +102,8 @@
void MutationObserver::Observe(const scoped_refptr<Node>& target,
const MutationObserverInit& options) {
- if (!target) {
- // |target| is not nullable, so if this is NULL that indicates a bug in the
- // bindings layer.
- NOTREACHED();
- return;
- }
- if (!target->RegisterMutationObserver(make_scoped_refptr(this), options)) {
- // TODO: Throw TypeError.
- NOTREACHED();
- }
- TrackObservedNode(target);
+ NativeExceptionState exception_state;
+ ObserveInternal(target, options, &exception_state);
}
void MutationObserver::Disconnect() {
@@ -164,5 +170,22 @@
observed_nodes_.push_back(base::AsWeakPtr(node.get()));
}
+void MutationObserver::ObserveInternal(
+ const scoped_refptr<Node>& target, const MutationObserverInit& options,
+ script::ExceptionState* exception_state) {
+ if (!target) {
+ // |target| is not nullable, so if this is NULL that indicates a bug in the
+ // bindings layer.
+ NOTREACHED();
+ return;
+ }
+ if (!target->RegisterMutationObserver(make_scoped_refptr(this), options)) {
+ // This fails if the options are invalid.
+ exception_state->SetSimpleException(script::kTypeError, "Invalid options.");
+ return;
+ }
+ TrackObservedNode(target);
+}
+
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/mutation_observer.h b/src/cobalt/dom/mutation_observer.h
index 87817e6..ea7acb8 100644
--- a/src/cobalt/dom/mutation_observer.h
+++ b/src/cobalt/dom/mutation_observer.h
@@ -24,6 +24,7 @@
#include "cobalt/dom/mutation_record.h"
#include "cobalt/script/callback_function.h"
#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/exception_state.h"
#include "cobalt/script/script_value.h"
#include "cobalt/script/sequence.h"
#include "cobalt/script/wrappable.h"
@@ -63,7 +64,15 @@
~MutationObserver();
void Observe(const scoped_refptr<Node>& target,
+ const MutationObserverInit& options,
+ script::ExceptionState* exception_state) {
+ ObserveInternal(target, options, exception_state);
+ }
+
+ // Call this from native code. Will DCHECK on exception.
+ void Observe(const scoped_refptr<Node>& target,
const MutationObserverInit& options);
+
void Disconnect();
MutationRecordSequence TakeRecords();
@@ -87,6 +96,10 @@
private:
void TrackObservedNode(const scoped_refptr<dom::Node>& node);
+ void ObserveInternal(const scoped_refptr<Node>& target,
+ const MutationObserverInit& options,
+ script::ExceptionState* exception_state);
+
scoped_ptr<CallbackInternal> callback_;
typedef std::vector<base::WeakPtr<dom::Node> > WeakNodeVector;
WeakNodeVector observed_nodes_;
diff --git a/src/cobalt/dom/mutation_observer.idl b/src/cobalt/dom/mutation_observer.idl
index 7312a56..a90e18c 100644
--- a/src/cobalt/dom/mutation_observer.idl
+++ b/src/cobalt/dom/mutation_observer.idl
@@ -21,7 +21,7 @@
ConstructorCallWith=EnvironmentSettings,
]
interface MutationObserver {
- void observe(Node target, MutationObserverInit options);
+ [RaisesException] void observe(Node target, MutationObserverInit options);
void disconnect();
sequence<MutationRecord> takeRecords();
};
diff --git a/src/cobalt/dom/mutation_observer_test.cc b/src/cobalt/dom/mutation_observer_test.cc
index a8fccb1..d56eb76 100644
--- a/src/cobalt/dom/mutation_observer_test.cc
+++ b/src/cobalt/dom/mutation_observer_test.cc
@@ -23,6 +23,7 @@
#include "cobalt/dom/node_list.h"
#include "cobalt/dom/text.h"
#include "cobalt/script/sequence.h"
+#include "cobalt/script/testing/mock_exception_state.h"
#include "cobalt/test/empty_document.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -397,6 +398,16 @@
EXPECT_EQ(0, observer_list.registered_observers().size());
}
+TEST_F(MutationObserverTest, InvalidOptionsRaisesException) {
+ scoped_refptr<dom::Element> target = CreateDiv();
+ scoped_refptr<MutationObserver> observer = CreateObserver();
+ MutationObserverInit invalid_options;
+
+ script::testing::MockExceptionState exception_state;
+ EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kTypeError, _, _));
+ observer->Observe(target, invalid_options, &exception_state);
+}
+
TEST_F(MutationObserverTest, AddChildNodes) {
scoped_refptr<Element> root = CreateDiv();
scoped_refptr<MutationObserver> observer = CreateObserver();
diff --git a/src/cobalt/dom/named_node_map.cc b/src/cobalt/dom/named_node_map.cc
index d6c9aa8..a0ef093 100644
--- a/src/cobalt/dom/named_node_map.cc
+++ b/src/cobalt/dom/named_node_map.cc
@@ -15,7 +15,9 @@
#include "cobalt/dom/named_node_map.h"
#include <algorithm>
+#include <iterator>
+#include "base/optional.h"
#include "cobalt/dom/attr.h"
#include "cobalt/dom/element.h"
#include "cobalt/dom/global_stats.h"
@@ -26,86 +28,114 @@
NamedNodeMap::NamedNodeMap(const scoped_refptr<Element>& element)
: element_(element) {
- ConstructProxyAttributes();
+ DCHECK(element_);
GlobalStats::GetInstance()->Add(this);
}
+// The length attribute's getter must return the attribute list's size.
+// https://dom.spec.whatwg.org/#dom-namednodemap-length
unsigned int NamedNodeMap::length() const {
- return static_cast<unsigned int>(attribute_names_.size());
+ return static_cast<unsigned int>(element_->attribute_map().size());
}
+// Algorithm for Item:
+// https://dom.spec.whatwg.org/#dom-namednodemap-item
scoped_refptr<Attr> NamedNodeMap::Item(unsigned int item) {
- if (item < attribute_names_.size()) {
- return GetNamedItem(attribute_names_[item]);
+ // 1. If index is equal to or greater than context object's attribute list's
+ // size, then return null.
+ if (item >= element_->attribute_map().size()) {
+ return NULL;
}
- return NULL;
+
+ // 2. Otherwise, return context object's attribute list[index].
+ Element::AttributeMap::const_iterator iter =
+ element_->attribute_map().begin();
+ std::advance(iter, item);
+ return GetOrCreateAttr(iter->first);
}
+// The getNamedItem(qualifiedName) method, when invoked, must return the result
+// of getting an attribute given qualifiedName and element.
+// https://dom.spec.whatwg.org/#dom-namednodemap-getnameditem
scoped_refptr<Attr> NamedNodeMap::GetNamedItem(const std::string& name) const {
+ // 1. if element is in the HTML namespace and its node document is an HTML
+ // document, then set qualifiedName to qualifiedName in ASCII lowercase.
+ // 2. Return the first attribute in element's attribute list whose qualified
+ // name is qualifiedName, and null otherwise.
if (!element_->HasAttribute(name)) {
- // Not need to throw an exception, fail gracefully.
return NULL;
}
return GetOrCreateAttr(name);
}
+// The setNamedItem(attr) and setNamedItemNS(attr) methods, when invoked, must
+// return the result of setting an attribute given attr and element.
+// https://dom.spec.whatwg.org/#dom-namednodemap-setnameditem
scoped_refptr<Attr> NamedNodeMap::SetNamedItem(
const scoped_refptr<Attr>& attribute) {
+ // Custom, not in any spec.
if (!attribute) {
// TODO: Throw JS NotFoundError.
return NULL;
}
- if (attribute->container()) {
- if (attribute->container() == this) {
- // Already attached to this node map, don't do anything.
- return attribute;
- }
- // Attribute is already attached to a different NamedNodeMap.
+ // To set an attribute given an attr and element, run these steps:
+ // https://dom.spec.whatwg.org/#concept-element-attributes-set
+
+ // 1. If attr's element is neither null nor element, throw an
+ // InUseAttributeError.
+ if (attribute->container() && attribute->container() != this) {
// TODO: Throw JS InUseAttributeError.
return NULL;
}
+ // 2. Let oldAttr be the result of getting an attribute given attr's
+ // namespace, attr's local name, and element.
+ // 3. If oldAttr is attr, return attr.
+ // 4. If oldAttr is non-null, replace it by attr in element.
+ // 5. Otherwise, append attr to element.
const std::string& name = attribute->name();
- scoped_refptr<Attr> previous_attribute;
-
+ scoped_refptr<Attr> old_attr;
if (element_->HasAttribute(name)) {
- // Get the previous attribute that was associated with 'name' and detach it
- // from NamedNodeMap.
- previous_attribute = GetOrCreateAttr(name);
- previous_attribute->set_container(NULL);
- }
+ old_attr = GetOrCreateAttr(name);
- // Insert the new attribute and attach it to the NamedNodeMap.
+ if (attribute->value() == old_attr->value()) {
+ return old_attr;
+ }
+ old_attr->set_container(NULL);
+ }
attribute->set_container(this);
proxy_attributes_[name] = attribute->AsWeakPtr();
-
// Inform the element about the new attribute. This should trigger a call to
// NamedNodeMap::SetAttributeInternal and continue the update there.
element_->SetAttribute(name, attribute->value());
- return previous_attribute;
+
+ // 6. Return oldAttr.
+ return old_attr;
}
+// Algorithm for RemoveNamedItem:
+// https://dom.spec.whatwg.org/#dom-namednodemap-removenameditem
scoped_refptr<Attr> NamedNodeMap::RemoveNamedItem(const std::string& name) {
- ProxyAttributeMap::iterator iter = proxy_attributes_.find(name);
- if (iter == proxy_attributes_.end()) {
- // TODO: Throw JS NotFoundError.
- return NULL;
- }
-
- scoped_refptr<Attr> previous_attribute;
+ // 1. Let attr be the result of removing an attribute given qualifiedName and
+ // element.
+ // 2. If attr is null, then throw a NotFoundError.
+ scoped_refptr<Attr> attr;
if (element_->HasAttribute(name)) {
// Get the previous attribute that was associated with 'name' and detach it
// from NamedNodeMap.
- previous_attribute = GetOrCreateAttr(name);
- previous_attribute->set_container(NULL);
+ attr = GetOrCreateAttr(name);
+ attr->set_container(NULL);
+ } else {
+ // TODO: Throw JS NotFoundError.
+ return NULL;
}
-
// Inform the element about the removed attribute. This should trigger a call
// to NamedNodeMap::RemoveAttributeInternal and continue the update there.
element_->RemoveAttribute(name);
- return previous_attribute;
+ // 3. Return attr.
+ return attr;
}
bool NamedNodeMap::CanQueryNamedProperty(const std::string& name) const {
@@ -114,34 +144,25 @@
void NamedNodeMap::EnumerateNamedProperties(
script::PropertyEnumerator* enumerator) const {
- for (size_t i = 0; i < attribute_names_.size(); ++i) {
- enumerator->AddProperty(attribute_names_[i]);
+ const Element::AttributeMap& attribute_map = element_->attribute_map();
+ for (Element::AttributeMap::const_iterator iter = attribute_map.begin();
+ iter != attribute_map.end(); ++iter) {
+ enumerator->AddProperty(iter->first);
}
}
void NamedNodeMap::SetAttributeInternal(const std::string& name,
const std::string& value) {
- // If this a new name, add to it to the name vector.
- AttributeNameVector::const_iterator name_iter =
- std::find(attribute_names_.begin(), attribute_names_.end(), name);
- if (name_iter == attribute_names_.end()) {
- attribute_names_.push_back(name);
- }
-
// Update the associated Attr object.
- ProxyAttributeMap::iterator attribute_iter = proxy_attributes_.find(name);
+ NameToAttrMap::iterator attribute_iter = proxy_attributes_.find(name);
if (attribute_iter != proxy_attributes_.end() && attribute_iter->second) {
attribute_iter->second->SetValueInternal(value);
}
}
void NamedNodeMap::RemoveAttributeInternal(const std::string& name) {
- attribute_names_.erase(
- std::remove(attribute_names_.begin(), attribute_names_.end(), name),
- attribute_names_.end());
-
// Make sure to detach the proxy attribute from NamedNodeMap.
- ProxyAttributeMap::iterator iter = proxy_attributes_.find(name);
+ NameToAttrMap::iterator iter = proxy_attributes_.find(name);
if (iter != proxy_attributes_.end()) {
if (iter->second) {
iter->second->set_container(NULL);
@@ -154,23 +175,10 @@
NamedNodeMap::~NamedNodeMap() { GlobalStats::GetInstance()->Remove(this); }
-void NamedNodeMap::ConstructProxyAttributes() {
- // Construct the attribute name vector.
- attribute_names_.clear();
- const Element::AttributeMap& attribute_map = element_->attribute_map();
- for (Element::AttributeMap::const_iterator iter = attribute_map.begin();
- iter != attribute_map.end(); ++iter) {
- attribute_names_.push_back(iter->first);
- }
-
- // There's no need to create the ProxyAttributeMap since it'll be created
- // on demand when accessed by the user.
-}
-
scoped_refptr<Attr> NamedNodeMap::GetOrCreateAttr(
const std::string& name) const {
TRACK_MEMORY_SCOPE("DOM");
- ProxyAttributeMap::iterator iter = proxy_attributes_.find(name);
+ NameToAttrMap::iterator iter = proxy_attributes_.find(name);
if (iter != proxy_attributes_.end() && iter->second) {
return iter->second.get();
}
diff --git a/src/cobalt/dom/named_node_map.h b/src/cobalt/dom/named_node_map.h
index 0cd8dc0..57073d5 100644
--- a/src/cobalt/dom/named_node_map.h
+++ b/src/cobalt/dom/named_node_map.h
@@ -31,18 +31,19 @@
class Element;
// The NamedNodeMap interface represents a collection of Attr objects. Objects
-// inside a NamedNodeMap are not in any particular order.
-// https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1780488922
+// inside a NamedNodeMap are not in any particular order, unlike NodeList,
+// although they may be accessed by an index as in an array.
+// A NamedNodeMap object is live and will thus be auto-updated if changes are
+// made to its contents internally or elsewhere.
+// https://dom.spec.whatwg.org/#interface-namednodemap
//
-// Although called NamedNodeMap, this interface doesn't deal with Node objects
-// but with Attr objects, which were originally a specialized class of Node.
+// This interface is a legacy interface defined in DOM Level 3 Core, and is
+// referenced in DOM4. The whatwg live spec changes some definitions that is
+// adopted by major browsers. The whatwg version is followed here.
//
// Using Attr objects directly to represent Element attributes would incur
// a significant memory overhead. Instead, NamedNodeMap and Attr are created
// on demand and proxy the content of the actual attributes.
-//
-// Note that DOM4 deprecates this class and replaces it with a read-only
-// array of Attr objects.
class NamedNodeMap : public script::Wrappable,
public base::SupportsWeakPtr<NamedNodeMap> {
public:
@@ -57,10 +58,11 @@
scoped_refptr<Attr> SetNamedItem(const scoped_refptr<Attr>& attribute);
scoped_refptr<Attr> RemoveNamedItem(const std::string& name);
- // Custom, not in any spec.
- //
bool CanQueryNamedProperty(const std::string& name) const;
void EnumerateNamedProperties(script::PropertyEnumerator* enumerator) const;
+
+ // Custom, not in any spec.
+ //
void SetAttributeInternal(const std::string& name, const std::string& value);
void RemoveAttributeInternal(const std::string& name);
@@ -71,17 +73,14 @@
private:
~NamedNodeMap();
- void ConstructProxyAttributes();
scoped_refptr<Attr> GetOrCreateAttr(const std::string& name) const;
+ typedef base::hash_map<std::string, base::WeakPtr<Attr> > NameToAttrMap;
+
// The element that contains the actual attributes.
scoped_refptr<Element> element_;
- // This vector contains the names of the proxy attributes.
- typedef std::vector<std::string> AttributeNameVector;
- AttributeNameVector attribute_names_;
// Vector of weak pointers to Attr objects that proxies the actual attributes.
- typedef base::hash_map<std::string, base::WeakPtr<Attr> > ProxyAttributeMap;
- mutable ProxyAttributeMap proxy_attributes_;
+ mutable NameToAttrMap proxy_attributes_;
DISALLOW_COPY_AND_ASSIGN(NamedNodeMap);
};
diff --git a/src/cobalt/dom/named_node_map.idl b/src/cobalt/dom/named_node_map.idl
index 6bc6a42..7b5db82 100644
--- a/src/cobalt/dom/named_node_map.idl
+++ b/src/cobalt/dom/named_node_map.idl
@@ -12,10 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1780488922
+// https://dom.spec.whatwg.org/#interface-namednodemap
interface NamedNodeMap {
readonly attribute unsigned long length;
getter Attr? item(unsigned long index);
- getter Attr? getNamedItem(DOMString name);
+ getter Attr? getNamedItem(DOMString qualifiedName);
+ Attr? setNamedItem(Attr attr);
+ Attr removeNamedItem(DOMString qualifiedName);
};
diff --git a/src/cobalt/dom/named_node_map_test.cc b/src/cobalt/dom/named_node_map_test.cc
new file mode 100644
index 0000000..d99df3b
--- /dev/null
+++ b/src/cobalt/dom/named_node_map_test.cc
@@ -0,0 +1,98 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/dom/named_node_map.h"
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/dom/attr.h"
+#include "cobalt/dom/document.h"
+#include "cobalt/dom/element.h"
+#include "cobalt/dom/global_stats.h"
+#include "cobalt/dom/html_element_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace dom {
+
+class NamedNodeMapTest : public ::testing::Test {
+ protected:
+ NamedNodeMapTest();
+ ~NamedNodeMapTest() OVERRIDE;
+
+ HTMLElementContext html_element_context_;
+ scoped_refptr<Document> document_;
+ scoped_refptr<Element> element_;
+};
+
+NamedNodeMapTest::NamedNodeMapTest()
+ : html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
+ EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
+ document_ = new Document(&html_element_context_);
+ element_ = new Element(document_, base::Token("element"));
+}
+
+NamedNodeMapTest::~NamedNodeMapTest() {
+ element_ = NULL;
+ document_ = NULL;
+ EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
+}
+
+TEST_F(NamedNodeMapTest, EmptyNamedNodeMap) {
+ scoped_refptr<NamedNodeMap> named_node_map = new NamedNodeMap(element_);
+ EXPECT_EQ(0, named_node_map->length());
+ EXPECT_EQ(scoped_refptr<Attr>(NULL), named_node_map->Item(0));
+ EXPECT_EQ(scoped_refptr<Attr>(NULL), named_node_map->GetNamedItem("name"));
+}
+
+TEST_F(NamedNodeMapTest, ShouldBeAlive) {
+ scoped_refptr<NamedNodeMap> named_node_map = new NamedNodeMap(element_);
+ EXPECT_EQ(0, named_node_map->length());
+
+ element_->SetAttribute("name", "value");
+ ASSERT_EQ(1, named_node_map->length());
+ EXPECT_EQ("name", named_node_map->Item(0)->name());
+ EXPECT_EQ("value", named_node_map->Item(0)->value());
+ EXPECT_EQ("name", named_node_map->GetNamedItem("name")->name());
+ EXPECT_EQ("value", named_node_map->GetNamedItem("name")->value());
+ EXPECT_EQ(scoped_refptr<Attr>(NULL), named_node_map->Item(1));
+}
+
+TEST_F(NamedNodeMapTest, CanSetNamedItem) {
+ scoped_refptr<NamedNodeMap> named_node_map = new NamedNodeMap(element_);
+ EXPECT_EQ(0, named_node_map->length());
+
+ named_node_map->SetNamedItem(new Attr("name", "value", named_node_map));
+ ASSERT_EQ(1, named_node_map->length());
+ EXPECT_EQ("name", named_node_map->Item(0)->name());
+ EXPECT_EQ("value", named_node_map->Item(0)->value());
+ EXPECT_EQ("name", named_node_map->GetNamedItem("name")->name());
+ EXPECT_EQ("value", named_node_map->GetNamedItem("name")->value());
+ EXPECT_EQ(scoped_refptr<Attr>(NULL), named_node_map->Item(1));
+}
+
+TEST_F(NamedNodeMapTest, CanRemoveNamedItem) {
+ scoped_refptr<NamedNodeMap> named_node_map = new NamedNodeMap(element_);
+ element_->SetAttribute("name", "value");
+ ASSERT_EQ(1, named_node_map->length());
+
+ named_node_map->RemoveNamedItem("name");
+ EXPECT_EQ(0, named_node_map->length());
+}
+
+} // namespace dom
+} // namespace cobalt
diff --git a/src/cobalt/dom/navigator.cc b/src/cobalt/dom/navigator.cc
index 15ae59a..185385c 100644
--- a/src/cobalt/dom/navigator.cc
+++ b/src/cobalt/dom/navigator.cc
@@ -14,19 +14,22 @@
#include "cobalt/dom/navigator.h"
-#include "cobalt/media_session/media_session.h"
+#include "cobalt/media_session/media_session_client.h"
using cobalt::media_session::MediaSession;
namespace cobalt {
namespace dom {
-Navigator::Navigator(const std::string& user_agent, const std::string& language)
+Navigator::Navigator(const std::string& user_agent, const std::string& language,
+ scoped_refptr<MediaSession> media_session,
+ script::ScriptValueFactory* script_value_factory)
: user_agent_(user_agent),
language_(language),
mime_types_(new MimeTypeArray()),
plugins_(new PluginArray()),
- media_session_(new MediaSession()) {}
+ media_session_(media_session),
+ script_value_factory_(script_value_factory) {}
const std::string& Navigator::language() const { return language_; }
diff --git a/src/cobalt/dom/navigator.h b/src/cobalt/dom/navigator.h
index 8ee28cd..a962803 100644
--- a/src/cobalt/dom/navigator.h
+++ b/src/cobalt/dom/navigator.h
@@ -21,6 +21,7 @@
#include "cobalt/dom/mime_type_array.h"
#include "cobalt/dom/plugin_array.h"
#include "cobalt/media_session/media_session.h"
+#include "cobalt/script/script_value_factory.h"
#include "cobalt/script/wrappable.h"
namespace cobalt {
@@ -32,7 +33,9 @@
// https://www.w3.org/TR/html5/webappapis.html#navigator
class Navigator : public script::Wrappable {
public:
- Navigator(const std::string& user_agent, const std::string& language);
+ Navigator(const std::string& user_agent, const std::string& language,
+ scoped_refptr<cobalt::media_session::MediaSession> media_session,
+ script::ScriptValueFactory* script_value_factory);
// Web API: NavigatorID
const std::string& user_agent() const;
@@ -61,6 +64,7 @@
scoped_refptr<MimeTypeArray> mime_types_;
scoped_refptr<PluginArray> plugins_;
scoped_refptr<cobalt::media_session::MediaSession> media_session_;
+ script::ScriptValueFactory* script_value_factory_;
DISALLOW_COPY_AND_ASSIGN(Navigator);
};
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc
index 77834fe..fdc872e 100644
--- a/src/cobalt/dom/node.cc
+++ b/src/cobalt/dom/node.cc
@@ -482,34 +482,48 @@
}
}
-void Node::InvalidateComputedStylesRecursively() {
+void Node::PurgeCachedBackgroundImagesOfNodeAndDescendants() {
+ PurgeCachedBackgroundImagesOfDescendants();
+}
+
+void Node::InvalidateComputedStylesOfNodeAndDescendants() {
+ InvalidateComputedStylesOfDescendants();
+}
+
+void Node::InvalidateLayoutBoxesOfNodeAndAncestors() {
+ InvalidateLayoutBoxesOfAncestors();
+}
+
+void Node::InvalidateLayoutBoxesOfNodeAndDescendants() {
+ InvalidateLayoutBoxesOfDescendants();
+}
+
+void Node::PurgeCachedBackgroundImagesOfDescendants() {
Node* child = first_child_;
while (child) {
- child->InvalidateComputedStylesRecursively();
+ child->PurgeCachedBackgroundImagesOfNodeAndDescendants();
child = child->next_sibling_;
}
}
-void Node::PurgeCachedResourceReferencesRecursively() {
- ReleaseImagesAndInvalidateComputedStyleIfNecessary();
-
+void Node::InvalidateComputedStylesOfDescendants() {
Node* child = first_child_;
while (child) {
- child->PurgeCachedResourceReferencesRecursively();
+ child->InvalidateComputedStylesOfNodeAndDescendants();
child = child->next_sibling_;
}
}
-void Node::InvalidateLayoutBoxesFromNodeAndAncestors() {
+void Node::InvalidateLayoutBoxesOfAncestors() {
if (parent_) {
- parent_->InvalidateLayoutBoxesFromNodeAndAncestors();
+ parent_->InvalidateLayoutBoxesOfNodeAndAncestors();
}
}
-void Node::InvalidateLayoutBoxesFromNodeAndDescendants() {
+void Node::InvalidateLayoutBoxesOfDescendants() {
Node* child = first_child_;
while (child) {
- child->InvalidateLayoutBoxesFromNodeAndDescendants();
+ child->InvalidateLayoutBoxesOfNodeAndDescendants();
child = child->next_sibling_;
}
}
@@ -638,8 +652,7 @@
scoped_refptr<dom::NodeList> added_nodes = new dom::NodeList();
added_nodes->AppendNode(node);
mutation_reporter.ReportChildListMutation(
- added_nodes, NULL, child && child->previous_sibling_
- ? child->previous_sibling_
+ added_nodes, NULL, child ? child->previous_sibling_
: this->last_child_ /* previous_sibling */,
child /* next_sibling */);
}
@@ -674,7 +687,7 @@
// being changed.
// NOTE: The added node does not have any invalidations done, because they
// occur on the remove and are guaranteed to not be needed at this point.
- InvalidateLayoutBoxesFromNodeAndAncestors();
+ InvalidateLayoutBoxesOfNodeAndAncestors();
if (inserted_into_document_) {
node->OnInsertedIntoDocument();
@@ -711,12 +724,18 @@
// Invalidate the layout boxes of the previous parent as a result of its
// children being changed.
- InvalidateLayoutBoxesFromNodeAndAncestors();
+ InvalidateLayoutBoxesOfNodeAndAncestors();
+
+ // Purge any cached background images now that this node and its descendants
+ // are no longer in the tree, so that the images can be released from the
+ // resource cache.
+ node->PurgeCachedBackgroundImagesOfNodeAndDescendants();
+
// Invalidate the styles and layout boxes of the node being removed from
// the tree. These are no longer valid as a result of the child and its
// descendants losing their inherited styles.
- node->InvalidateComputedStylesRecursively();
- node->InvalidateLayoutBoxesFromNodeAndDescendants();
+ node->InvalidateComputedStylesOfNodeAndDescendants();
+ node->InvalidateLayoutBoxesOfNodeAndDescendants();
bool was_inserted_to_document = node->inserted_into_document_;
if (was_inserted_to_document) {
@@ -775,5 +794,58 @@
}
}
+// Algorithm for ReplaceAll:
+// https://www.w3.org/TR/dom/#concept-node-replace-all
+void Node::ReplaceAll(const scoped_refptr<Node>& node) {
+ // 1. If node is not null, adopt node into parent's node document.
+ if (node) {
+ node->AdoptIntoDocument(this->node_document());
+ }
+
+ // 2. Let removedNodes be parent's children.
+ scoped_refptr<NodeList> removed_nodes = new NodeList();
+ scoped_refptr<Node> next_child = first_child_;
+ while (next_child) {
+ removed_nodes->AppendNode(next_child);
+ next_child = next_child->next_sibling();
+ }
+
+ // 3. Let addedNodes be the empty list if node is null, node's children if
+ // node is a DocumentFragment node, and a list containing node otherwise.
+ scoped_refptr<NodeList> added_nodes = new NodeList();
+ if (node) {
+ added_nodes->AppendNode(node);
+ }
+
+ // 4. Remove all parent's children, in tree order, with the suppress observers
+ // flag set.
+ while (HasChildNodes()) {
+ Remove(first_child(), true);
+ }
+
+ // 5. If node is not null, insert node into parent before null with the
+ // suppress observers flag set.
+ if (node) {
+ Insert(node, NULL, true);
+ }
+
+ // 6. Queue a mutation record of "childList" for parent with addedNodes
+ // addedNodes and removedNodes removedNodes.
+ scoped_ptr<RegisteredObserverVector> observers =
+ GatherInclusiveAncestorsObservers();
+ if (!observers->empty()) {
+ MutationReporter mutation_reporter(this, observers.Pass());
+ scoped_refptr<dom::NodeList> added_nodes = new dom::NodeList();
+ if (node) {
+ added_nodes->AppendNode(node);
+ }
+ if (added_nodes->length() > 0 || removed_nodes->length() > 0) {
+ mutation_reporter.ReportChildListMutation(
+ added_nodes, removed_nodes, NULL /* previous_sibling */,
+ NULL /* next_sibling */);
+ }
+ }
+}
+
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/node.h b/src/cobalt/dom/node.h
index dfeec89..695ce4d 100644
--- a/src/cobalt/dom/node.h
+++ b/src/cobalt/dom/node.h
@@ -246,22 +246,19 @@
// removed from to its owner document.
virtual void OnRemovedFromDocument();
- // Invalidate computed styles from this node and all child nodes.
- virtual void InvalidateComputedStylesRecursively();
- // Invalidate layout boxes from this node and all parent nodes.
- virtual void InvalidateLayoutBoxesFromNodeAndAncestors();
- // Invalidate layout boxes from this node and all child nodes.
- virtual void InvalidateLayoutBoxesFromNodeAndDescendants();
- // Invalidate the sizes within the layout boxes of this node.
- virtual void InvalidateLayoutBoxSizesFromNode() {}
- // Invalidate the cross references within the layout boxes of this node.
- virtual void InvalidateLayoutBoxCrossReferencesFromNode() {}
- // Invalidate the render tree nodes within the layout boxes of this node.
- virtual void InvalidateRenderTreeNodesFromNode() {}
+ virtual void PurgeCachedBackgroundImagesOfNodeAndDescendants();
+ virtual void InvalidateComputedStylesOfNodeAndDescendants();
+ virtual void InvalidateLayoutBoxesOfNodeAndAncestors();
+ virtual void InvalidateLayoutBoxesOfNodeAndDescendants();
- // Releases image resources and invalidates computed style if there are images
- // associated with this html element in the image cache.
- virtual void ReleaseImagesAndInvalidateComputedStyleIfNecessary() {}
+ virtual void InvalidateLayoutBoxSizes() {}
+ virtual void InvalidateLayoutBoxCrossReferences() {}
+ virtual void InvalidateLayoutBoxRenderTreeNodes() {}
+
+ void PurgeCachedBackgroundImagesOfDescendants();
+ void InvalidateComputedStylesOfDescendants();
+ void InvalidateLayoutBoxesOfAncestors();
+ void InvalidateLayoutBoxesOfDescendants();
// Triggers a generation update in this node and all its ancestor nodes.
void UpdateGenerationForNodeAndAncestors();
@@ -270,6 +267,8 @@
typedef std::vector<RegisteredObserver> RegisteredObserverVector;
scoped_ptr<RegisteredObserverVector> GatherInclusiveAncestorsObservers();
+ void ReplaceAll(const scoped_refptr<Node>& node);
+
private:
// From EventTarget.
std::string GetDebugName() OVERRIDE { return node_name().c_str(); }
diff --git a/src/cobalt/dom/node_dispatch_event_test.cc b/src/cobalt/dom/node_dispatch_event_test.cc
index 8b9e300..5758126 100644
--- a/src/cobalt/dom/node_dispatch_event_test.cc
+++ b/src/cobalt/dom/node_dispatch_event_test.cc
@@ -19,6 +19,7 @@
#include "cobalt/dom/global_stats.h"
#include "cobalt/dom/html_element_context.h"
#include "cobalt/dom/testing/mock_event_listener.h"
+#include "cobalt/script/testing/fake_script_value.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::AllOf;
@@ -33,7 +34,7 @@
namespace cobalt {
namespace dom {
-using testing::FakeScriptValue;
+using ::cobalt::script::testing::FakeScriptValue;
using testing::MockEventListener;
//////////////////////////////////////////////////////////////////////////
@@ -57,7 +58,7 @@
NodeDispatchEventTest::NodeDispatchEventTest()
: html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, "") {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
document_ = new Document(&html_element_context_);
@@ -69,17 +70,23 @@
event_listener_bubbling_ = MockEventListener::Create();
grand_parent_->AddEventListener(
- "fired", FakeScriptValue(event_listener_bubbling_.get()), false);
+ "fired",
+ FakeScriptValue<EventListener>(event_listener_bubbling_.get()), false);
grand_parent_->AddEventListener(
- "fired", FakeScriptValue(event_listener_capture_.get()), true);
+ "fired",
+ FakeScriptValue<EventListener>(event_listener_capture_.get()), true);
parent_->AddEventListener(
- "fired", FakeScriptValue(event_listener_bubbling_.get()), false);
+ "fired",
+ FakeScriptValue<EventListener>(event_listener_bubbling_.get()), false);
parent_->AddEventListener(
- "fired", FakeScriptValue(event_listener_capture_.get()), true);
+ "fired",
+ FakeScriptValue<EventListener>(event_listener_capture_.get()), true);
child_->AddEventListener(
- "fired", FakeScriptValue(event_listener_bubbling_.get()), false);
+ "fired",
+ FakeScriptValue<EventListener>(event_listener_bubbling_.get()), false);
child_->AddEventListener(
- "fired", FakeScriptValue(event_listener_capture_.get()), true);
+ "fired",
+ FakeScriptValue<EventListener>(event_listener_capture_.get()), true);
}
NodeDispatchEventTest::~NodeDispatchEventTest() {
diff --git a/src/cobalt/dom/node_list_live_test.cc b/src/cobalt/dom/node_list_live_test.cc
index 3645485..a9762af 100644
--- a/src/cobalt/dom/node_list_live_test.cc
+++ b/src/cobalt/dom/node_list_live_test.cc
@@ -28,7 +28,8 @@
NodeListLiveTest()
: dom_stat_tracker_("NodeListLiveTest"),
html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, &dom_stat_tracker_, ""),
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ &dom_stat_tracker_, ""),
document_(new Document(&html_element_context_)) {}
~NodeListLiveTest() OVERRIDE {}
diff --git a/src/cobalt/dom/node_list_test.cc b/src/cobalt/dom/node_list_test.cc
index 7bea9d9..df03cba 100644
--- a/src/cobalt/dom/node_list_test.cc
+++ b/src/cobalt/dom/node_list_test.cc
@@ -28,8 +28,8 @@
NodeListTest()
: dom_stat_tracker_(new DomStatTracker("NodeListTest")),
html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, dom_stat_tracker_.get(),
- ""),
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ dom_stat_tracker_.get(), ""),
document_(new Document(&html_element_context_)) {}
~NodeListTest() OVERRIDE {}
diff --git a/src/cobalt/dom/node_test.cc b/src/cobalt/dom/node_test.cc
index 9056195..cd8e4f1 100644
--- a/src/cobalt/dom/node_test.cc
+++ b/src/cobalt/dom/node_test.cc
@@ -76,7 +76,7 @@
NodeTest::NodeTest()
: html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, "") {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
document_ = new Document(&html_element_context_);
diff --git a/src/cobalt/dom/rule_matching.cc b/src/cobalt/dom/rule_matching.cc
index 61ecb59..2e1348a 100644
--- a/src/cobalt/dom/rule_matching.cc
+++ b/src/cobalt/dom/rule_matching.cc
@@ -171,7 +171,7 @@
// tree.
// https://www.w3.org/TR/selectors4/#type-selector
void VisitTypeSelector(cssom::TypeSelector* type_selector) OVERRIDE {
- if (type_selector->element_name() != element_->tag_name()) {
+ if (type_selector->element_name() != element_->local_name()) {
element_ = NULL;
}
}
@@ -468,7 +468,7 @@
// Type selector.
if (node->HasSimpleSelector(cssom::kTypeSelector, combinator_type)) {
GatherCandidateNodesFromMap(cssom::kTypeSelector, combinator_type,
- selector_nodes_map, element->tag_name(),
+ selector_nodes_map, element->local_name(),
&candidate_nodes);
}
diff --git a/src/cobalt/dom/rule_matching_test.cc b/src/cobalt/dom/rule_matching_test.cc
index 5f1e58e..e7ef524 100644
--- a/src/cobalt/dom/rule_matching_test.cc
+++ b/src/cobalt/dom/rule_matching_test.cc
@@ -44,7 +44,7 @@
dom_stat_tracker_(new DomStatTracker("RuleMatchingTest")),
html_element_context_(NULL, css_parser_.get(), dom_parser_.get(), NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- dom_stat_tracker_.get(), ""),
+ NULL, NULL, dom_stat_tracker_.get(), ""),
document_(new Document(&html_element_context_)),
root_(document_->CreateElement("html")->AsHTMLElement()) {
document_->AppendChild(root_);
diff --git a/src/cobalt/dom/serializer.cc b/src/cobalt/dom/serializer.cc
index e2f641a..1f55741 100644
--- a/src/cobalt/dom/serializer.cc
+++ b/src/cobalt/dom/serializer.cc
@@ -113,11 +113,11 @@
void Serializer::Visit(const Element* element) {
if (entering_node_) {
- *out_stream_ << "<" << element->tag_name();
+ *out_stream_ << "<" << element->local_name();
WriteAtttributes(element, out_stream_);
*out_stream_ << ">";
} else {
- *out_stream_ << "</" << element->tag_name() << ">";
+ *out_stream_ << "</" << element->local_name() << ">";
}
}
diff --git a/src/cobalt/dom/serializer_test.cc b/src/cobalt/dom/serializer_test.cc
index ede650a..625e4cc 100644
--- a/src/cobalt/dom/serializer_test.cc
+++ b/src/cobalt/dom/serializer_test.cc
@@ -45,8 +45,8 @@
: dom_parser_(new dom_parser::Parser()),
dom_stat_tracker_(new DomStatTracker("SerializerTest")),
html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, dom_stat_tracker_.get(),
- ""),
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ dom_stat_tracker_.get(), ""),
document_(new Document(&html_element_context_)),
root_(new Element(document_, base::Token("root"))),
source_location_(base::SourceLocation("[object SerializerTest]", 1, 1)) {}
diff --git a/src/cobalt/dom/source_buffer.h b/src/cobalt/dom/source_buffer.h
index 0d72d64..c7f5a59 100644
--- a/src/cobalt/dom/source_buffer.h
+++ b/src/cobalt/dom/source_buffer.h
@@ -42,10 +42,6 @@
// shared between different versions of MSE.
class SourceBuffer : public dom::EventTarget {
public:
- // Web API: SourceBuffer
- //
- enum AppendMode { kSegments, kSequence };
-
// Custom, not in any spec.
//
SourceBuffer(const scoped_refptr<MediaSource>& media_source,
diff --git a/src/cobalt/dom/source_buffer.idl b/src/cobalt/dom/source_buffer.idl
index 914e6b2..e20aebf 100644
--- a/src/cobalt/dom/source_buffer.idl
+++ b/src/cobalt/dom/source_buffer.idl
@@ -15,11 +15,6 @@
// https://www.w3.org/TR/media-source/#idl-def-SourceBuffer
// https://www.w3.org/TR/2016/CR-media-source-20160705/#sourcebuffer
-enum AppendMode {
- "segments",
- "sequence"
-};
-
interface SourceBuffer : EventTarget {
[RaisesException] readonly attribute TimeRanges buffered;
[RaisesException] attribute double timestampOffset;
@@ -28,7 +23,7 @@
[RaisesException] void abort();
// MSE 2016 Interface
- [Conditional=COBALT_MEDIA_SOURCE_2016, RaisesException] attribute AppendMode mode;
+ [Conditional=COBALT_MEDIA_SOURCE_2016, RaisesException] attribute SourceBufferAppendMode mode;
[Conditional=COBALT_MEDIA_SOURCE_2016] readonly attribute boolean updating;
// [RaisesException] readonly attribute TimeRanges buffered;
// [RaisesException] attribute double timestampOffset;
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/source_buffer_append_mode.idl
similarity index 69%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/source_buffer_append_mode.idl
index d335495..5fa5586 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/source_buffer_append_mode.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/media-source/#idl-def-SourceBuffer
+// https://www.w3.org/TR/2016/CR-media-source-20160705/#sourcebuffer
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum SourceBufferAppendMode {
+ "segments",
+ "sequence"
+};
diff --git a/src/cobalt/dom/testing/fake_exception_state.h b/src/cobalt/dom/testing/fake_exception_state.h
new file mode 100644
index 0000000..85aafdb
--- /dev/null
+++ b/src/cobalt/dom/testing/fake_exception_state.h
@@ -0,0 +1,52 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_TESTING_FAKE_EXCEPTION_STATE_H_
+#define COBALT_DOM_TESTING_FAKE_EXCEPTION_STATE_H_
+
+#include "cobalt/script/exception_state.h"
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/dom/dom_exception.h"
+
+namespace cobalt {
+namespace dom {
+namespace testing {
+
+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 SetSimpleExceptionVA(script::SimpleExceptionType /*type*/,
+ const char* /*format*/, va_list /*args*/) 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 testing
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_TESTING_FAKE_EXCEPTION_STATE_H_
diff --git a/src/cobalt/dom/testing/html_collection_testing.h b/src/cobalt/dom/testing/html_collection_testing.h
index de31f0d..5aeebe3 100644
--- a/src/cobalt/dom/testing/html_collection_testing.h
+++ b/src/cobalt/dom/testing/html_collection_testing.h
@@ -168,9 +168,9 @@
scoped_refptr<Node> b1 = a1->AppendChild(
html_element_factory.CreateHTMLElement(document, base::Token("b1")));
scoped_refptr<Node> c1 = b1->AppendChild(
- html_element_factory.CreateHTMLElement(document, base::Token("tag")));
+ html_element_factory.CreateHTMLElement(document, base::Token("element")));
scoped_refptr<Node> d1 = a3->AppendChild(
- html_element_factory.CreateHTMLElement(document, base::Token("tag")));
+ html_element_factory.CreateHTMLElement(document, base::Token("element")));
// GetElementsByTagName should return all elements when provided with
// parameter "*".
@@ -183,9 +183,9 @@
EXPECT_EQ(d1, collection->Item(4));
EXPECT_EQ(kNullNode, collection->Item(5));
- // GetElementsByTagName should only return elements with the specific tag name
- // when that is provided.
- collection = node->GetElementsByTagName("tag");
+ // GetElementsByTagName should only return elements with the specific local
+ // name when that is provided.
+ collection = node->GetElementsByTagName("element");
EXPECT_EQ(2, collection->length());
EXPECT_EQ(c1, collection->Item(0));
EXPECT_EQ(d1, collection->Item(1));
@@ -202,7 +202,8 @@
// Add a new node with a matching tag.
scoped_refptr<Node> a2 = node->InsertBefore(
- html_element_factory.CreateHTMLElement(document, base::Token("tag")), a3);
+ html_element_factory.CreateHTMLElement(document, base::Token("element")),
+ a3);
EXPECT_EQ(3, collection->length());
EXPECT_EQ(c1, collection->Item(0));
EXPECT_EQ(a2, collection->Item(1));
diff --git a/src/cobalt/dom/testing/mock_event_listener.h b/src/cobalt/dom/testing/mock_event_listener.h
index 108f4ee..93445f5 100644
--- a/src/cobalt/dom/testing/mock_event_listener.h
+++ b/src/cobalt/dom/testing/mock_event_listener.h
@@ -139,33 +139,6 @@
DoAll(SetArgPointee<2>(false), Return(base::optional<bool>())));
}
};
-
-class FakeScriptValue : public script::ScriptValue<EventListener> {
- public:
- typedef script::ScriptValue<EventListener> BaseClass;
- explicit FakeScriptValue(const MockEventListener* listener)
- : mock_listener_(listener) {}
-
- void RegisterOwner(script::Wrappable*) OVERRIDE {}
- void DeregisterOwner(script::Wrappable*) OVERRIDE {}
- void PreventGarbageCollection() OVERRIDE {}
- void AllowGarbageCollection() OVERRIDE {}
- const EventListener* GetScriptValue(void) const OVERRIDE {
- return mock_listener_;
- }
- scoped_ptr<BaseClass> MakeCopy() const OVERRIDE {
- return make_scoped_ptr<BaseClass>(new FakeScriptValue(mock_listener_));
- }
-
- bool EqualTo(const BaseClass& other) const OVERRIDE {
- const FakeScriptValue* other_script_object =
- base::polymorphic_downcast<const FakeScriptValue*>(&other);
- return mock_listener_ == other_script_object->mock_listener_;
- }
-
- private:
- const MockEventListener* mock_listener_;
-};
} // namespace testing
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/testing/stub_script_runner.cc b/src/cobalt/dom/testing/stub_script_runner.cc
index b252908..148e67f 100644
--- a/src/cobalt/dom/testing/stub_script_runner.cc
+++ b/src/cobalt/dom/testing/stub_script_runner.cc
@@ -20,9 +20,13 @@
std::string StubScriptRunner::Execute(
const std::string& script_utf8,
- const base::SourceLocation& script_location) {
+ const base::SourceLocation& script_location,
+ bool* out_succeeded) {
UNREFERENCED_PARAMETER(script_utf8);
UNREFERENCED_PARAMETER(script_location);
+ if (out_succeeded) {
+ *out_succeeded = true;
+ }
return "";
}
diff --git a/src/cobalt/dom/testing/stub_script_runner.h b/src/cobalt/dom/testing/stub_script_runner.h
index 1b64e1a..9c68840 100644
--- a/src/cobalt/dom/testing/stub_script_runner.h
+++ b/src/cobalt/dom/testing/stub_script_runner.h
@@ -26,7 +26,8 @@
class StubScriptRunner : public script::ScriptRunner {
public:
std::string Execute(const std::string& script_utf8,
- const base::SourceLocation& script_location) OVERRIDE;
+ const base::SourceLocation& script_location,
+ bool* out_succeeded) OVERRIDE;
};
} // namespace testing
diff --git a/src/cobalt/dom/text_test.cc b/src/cobalt/dom/text_test.cc
index 54d7304..75ab826 100644
--- a/src/cobalt/dom/text_test.cc
+++ b/src/cobalt/dom/text_test.cc
@@ -39,7 +39,7 @@
TextTest::TextTest()
: html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, "") {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "") {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
document_ = new Document(&html_element_context_);
}
diff --git a/src/cobalt/dom/time_ranges_test.cc b/src/cobalt/dom/time_ranges_test.cc
index 8bf6fb4..be89db2 100644
--- a/src/cobalt/dom/time_ranges_test.cc
+++ b/src/cobalt/dom/time_ranges_test.cc
@@ -17,34 +17,13 @@
#include <limits>
#include "cobalt/base/polymorphic_downcast.h"
-#include "cobalt/dom/dom_exception.h"
+#include "cobalt/dom/testing/fake_exception_state.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
namespace dom {
namespace {
-class FakeExceptionState : public script::ExceptionState {
- public:
- void SetException(
- const scoped_refptr<script::ScriptException>& exception) OVERRIDE {
- dom_exception_ = make_scoped_refptr(
- base::polymorphic_downcast<DOMException*>(exception.get()));
- }
- void SetSimpleExceptionWithArgs(script::MessageType /*message_type*/,
- int /*dummy*/, ...) 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_;
-};
-
void CheckEqual(const scoped_refptr<TimeRanges>& time_ranges1,
const scoped_refptr<TimeRanges>& time_ranges2) {
ASSERT_EQ(time_ranges1->length(), time_ranges2->length());
@@ -56,6 +35,8 @@
} // namespace
+using testing::FakeExceptionState;
+
TEST(TimeRangesTest, Constructors) {
scoped_refptr<TimeRanges> time_ranges = new TimeRanges;
FakeExceptionState exception_state;
diff --git a/src/cobalt/dom/track_default.h b/src/cobalt/dom/track_default.h
index 7b459f6..89d0e4b 100644
--- a/src/cobalt/dom/track_default.h
+++ b/src/cobalt/dom/track_default.h
@@ -19,6 +19,7 @@
#include "base/logging.h"
#include "cobalt/dom/audio_track.h"
+#include "cobalt/dom/track_default_type.h"
#include "cobalt/dom/video_track.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/sequence.h"
@@ -35,12 +36,6 @@
public:
// Web API: TrackDefault
//
- enum TrackDefaultType {
- kAudio,
- kVideo,
- kText,
- };
-
TrackDefault(TrackDefaultType type, const std::string& language,
const std::string& label,
const script::Sequence<std::string>& kinds,
@@ -51,14 +46,14 @@
language_(language),
label_(label),
kinds_(kinds) {
- if (type == kAudio) {
+ if (type == kTrackDefaultTypeAudio) {
for (script::Sequence<std::string>::size_type i = 0; i < kinds.size();
++i) {
if (!AudioTrack::IsValidKind(kinds.at(i).c_str())) {
exception_state->SetSimpleException(script::kSimpleTypeError);
}
}
- } else if (type == kVideo) {
+ } else if (type == kTrackDefaultTypeVideo) {
for (script::Sequence<std::string>::size_type i = 0; i < kinds.size();
++i) {
if (!VideoTrack::IsValidKind(kinds.at(i).c_str())) {
diff --git a/src/cobalt/dom/track_default.idl b/src/cobalt/dom/track_default.idl
index e07a509..1049d49 100644
--- a/src/cobalt/dom/track_default.idl
+++ b/src/cobalt/dom/track_default.idl
@@ -14,12 +14,6 @@
// https://www.w3.org/TR/2016/CR-media-source-20160705/#trackdefault
-enum TrackDefaultType {
- "audio",
- "video",
- "text"
-};
-
[
Constructor(TrackDefaultType type,
DOMString language,
diff --git a/src/cobalt/dom/track_default_list.h b/src/cobalt/dom/track_default_list.h
index a0b82a4..e6e7904 100644
--- a/src/cobalt/dom/track_default_list.h
+++ b/src/cobalt/dom/track_default_list.h
@@ -43,7 +43,7 @@
script::ExceptionState* exception_state) {
// |track_defaults| shouldn't contain two or more TrackDefault objects with
// the same |type| and the same |byte_stream_track_id|.
- typedef std::pair<TrackDefault::TrackDefaultType, std::string> TypeAndId;
+ typedef std::pair<TrackDefaultType, std::string> TypeAndId;
std::set<TypeAndId> type_and_ids;
for (script::Sequence<scoped_refptr<TrackDefault> >::size_type i = 0;
i < track_defaults.size(); ++i) {
diff --git a/src/cobalt/base/math.cc b/src/cobalt/dom/track_default_type.idl
similarity index 71%
copy from src/cobalt/base/math.cc
copy to src/cobalt/dom/track_default_type.idl
index d335495..2207c92 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/dom/track_default_type.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// https://www.w3.org/TR/2016/CR-media-source-20160705/#trackdefault
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum TrackDefaultType {
+ "audio",
+ "video",
+ "text"
+};
diff --git a/src/cobalt/dom/typed_array.h b/src/cobalt/dom/typed_array.h
index 46cefec..b5ff828 100644
--- a/src/cobalt/dom/typed_array.h
+++ b/src/cobalt/dom/typed_array.h
@@ -72,8 +72,9 @@
script::ExceptionState* exception_state)
: ArrayBufferView(buffer) {
if (buffer->byte_length() % kBytesPerElement != 0) {
- exception_state->SetSimpleExceptionWithArgs(
- script::kWrongByteLengthMultiple, 0, kBytesPerElement);
+ exception_state->SetSimpleException(script::kRangeError,
+ kWrongByteLengthMultipleErrorFormat,
+ kBytesPerElement);
}
}
@@ -82,11 +83,13 @@
: ArrayBufferView(buffer, byte_offset,
buffer->byte_length() - byte_offset) {
if (this->byte_offset() % kBytesPerElement != 0) {
- exception_state->SetSimpleExceptionWithArgs(
- script::kWrongByteOffsetMultiple, 0, kBytesPerElement);
+ exception_state->SetSimpleException(script::kRangeError,
+ kWrongByteOffsetMultipleErrorFormat,
+ kBytesPerElement);
} else if (buffer->byte_length() % kBytesPerElement != 0) {
- exception_state->SetSimpleExceptionWithArgs(
- script::kWrongByteLengthMultiple, 0, kBytesPerElement);
+ exception_state->SetSimpleException(script::kRangeError,
+ kWrongByteLengthMultipleErrorFormat,
+ kBytesPerElement);
}
}
@@ -94,8 +97,9 @@
uint32 length, script::ExceptionState* exception_state)
: ArrayBufferView(buffer, byte_offset, length * kBytesPerElement) {
if (this->byte_offset() % kBytesPerElement != 0) {
- exception_state->SetSimpleExceptionWithArgs(
- script::kWrongByteOffsetMultiple, 0, kBytesPerElement);
+ exception_state->SetSimpleException(script::kRangeError,
+ kWrongByteOffsetMultipleErrorFormat,
+ kBytesPerElement);
} else if (byte_offset + length * kBytesPerElement >
buffer->byte_length()) {
exception_state->SetSimpleException(script::kInvalidLength);
diff --git a/src/cobalt/dom/typed_array_test.cc b/src/cobalt/dom/typed_array_test.cc
index 02332d8..8bd93b0 100644
--- a/src/cobalt/dom/typed_array_test.cc
+++ b/src/cobalt/dom/typed_array_test.cc
@@ -335,43 +335,34 @@
}
StrictMock<MockExceptionState> exception_state;
- script::MessageType message_type;
-
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
- .WillOnce(SaveArg<0>(&message_type));
// Create an ArrayBuffer whose size isn't a multiple of
// TypeParam::kBytesPerElement.
scoped_refptr<ArrayBuffer> array_buffer =
new ArrayBuffer(NULL, TypeParam::kBytesPerElement + 1);
+
// The size of the array_buffer isn't a multiple of
// TypeParam::kBytesPerElement.
+ EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
scoped_refptr<TypeParam> array =
new TypeParam(NULL, array_buffer, &exception_state);
- EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
- .WillOnce(SaveArg<0>(&message_type));
// Neither the size of the array_buffer nor the byte_offset is a multiple of
// TypeParam::kBytesPerElement, but SetSimpleException() should only be called
// once.
+ EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
array = new TypeParam(NULL, array_buffer, 1, &exception_state);
- EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
- .WillOnce(SaveArg<0>(&message_type));
// Now the size of the array_buffer is a multiple of
// TypeParam::kBytesPerElement but the byte_offset isn't.
+ EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
array_buffer = new ArrayBuffer(NULL, TypeParam::kBytesPerElement);
array = new TypeParam(NULL, array_buffer, 1, &exception_state);
- EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
- .WillOnce(SaveArg<0>(&message_type));
// Both the size of the array_buffer and the byte_offset are multiples of
// TypeParam::kBytesPerElement but array_buffer cannot hold 2 elements.
+ EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
array = new TypeParam(NULL, array_buffer, 0, 2, &exception_state);
- EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
// The size of the array_buffer isn't a multiple of
// TypeParam::kBytesPerElement but the byte_offset is. Also the whole typed
@@ -389,25 +380,19 @@
}
StrictMock<MockExceptionState> exception_state;
- script::MessageType message_type;
-
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
- .WillOnce(SaveArg<0>(&message_type));
static const uint32 kLength = 5;
scoped_refptr<TypeParam> source =
new TypeParam(NULL, kLength + 1, &exception_state);
scoped_refptr<TypeParam> dest =
new TypeParam(NULL, kLength, &exception_state);
+ EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
dest->Set(source, &exception_state);
- EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
- EXPECT_CALL(exception_state, SetSimpleExceptionVA(_, _))
- .WillOnce(SaveArg<0>(&message_type));
source = new TypeParam(NULL, kLength, &exception_state);
dest = new TypeParam(NULL, kLength + 1, &exception_state);
+ EXPECT_CALL(exception_state, SetSimpleExceptionVA(script::kRangeError, _, _));
dest->Set(source, 2, &exception_state);
- EXPECT_EQ(script::kRangeError, GetSimpleExceptionType(message_type));
dest->Set(source, 1, &exception_state);
}
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 7add6f2..16bdb99 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -39,7 +39,11 @@
#include "cobalt/dom/screen.h"
#include "cobalt/dom/storage.h"
#include "cobalt/dom/window_timers.h"
+#include "cobalt/media_session/media_session_client.h"
#include "cobalt/script/javascript_engine.h"
+#include "cobalt/speech/speech_synthesis.h"
+
+using cobalt::media_session::MediaSession;
namespace cobalt {
namespace dom {
@@ -65,6 +69,7 @@
Window::Window(int width, int height, cssom::CSSParser* css_parser,
Parser* dom_parser, loader::FetcherFactory* fetcher_factory,
render_tree::ResourceProvider** resource_provider,
+ loader::image::AnimatedImageTracker* animated_image_tracker,
loader::image::ImageCache* image_cache,
loader::image::ReducedCacheCapacityManager*
reduced_image_cache_capacity_manager,
@@ -75,6 +80,7 @@
media::WebMediaPlayerFactory* web_media_player_factory,
script::ExecutionState* execution_state,
script::ScriptRunner* script_runner,
+ script::ScriptValueFactory* script_value_factory,
MediaSource::Registry* media_source_registry,
DomStatTracker* dom_stat_tracker, const GURL& url,
const std::string& user_agent, const std::string& language,
@@ -87,15 +93,18 @@
const base::Closure& csp_policy_changed_callback,
const base::Closure& ran_animation_frame_callbacks_callback,
const base::Closure& window_close_callback,
+ const base::Closure& window_minimize_callback,
system_window::SystemWindow* system_window,
const scoped_refptr<input::InputPoller>& input_poller,
+ const scoped_refptr<MediaSession>& media_session,
int csp_insecure_allowed_token, int dom_max_element_depth)
: width_(width),
height_(height),
html_element_context_(new HTMLElementContext(
fetcher_factory, css_parser, dom_parser, can_play_type_handler,
- web_media_player_factory, script_runner, media_source_registry,
- resource_provider, image_cache, reduced_image_cache_capacity_manager,
+ web_media_player_factory, script_runner, script_value_factory,
+ media_source_registry, resource_provider, animated_image_tracker,
+ image_cache, reduced_image_cache_capacity_manager,
remote_typeface_cache, mesh_cache, dom_stat_tracker, language)),
performance_(new Performance(new base::SystemMonotonicClock())),
ALLOW_THIS_IN_INITIALIZER_LIST(document_(new Document(
@@ -111,7 +120,8 @@
dom_max_element_depth)))),
document_loader_(NULL),
history_(new History()),
- navigator_(new Navigator(user_agent, language)),
+ navigator_(new Navigator(user_agent, language, media_session,
+ script_value_factory)),
ALLOW_THIS_IN_INITIALIZER_LIST(
relay_on_load_event_(new RelayLoadEvent(this))),
console_(new Console(execution_state)),
@@ -129,6 +139,7 @@
ran_animation_frame_callbacks_callback_(
ran_animation_frame_callbacks_callback),
window_close_callback_(window_close_callback),
+ window_minimize_callback_(window_minimize_callback),
system_window_(system_window) {
#if defined(ENABLE_TEST_RUNNER)
test_runner_ = new TestRunner();
@@ -182,6 +193,12 @@
}
}
+void Window::Minimize() {
+ if (!window_minimize_callback_.is_null()) {
+ window_minimize_callback_.Run();
+ }
+}
+
const scoped_refptr<Navigator>& Window::navigator() const { return navigator_; }
scoped_refptr<cssom::CSSStyleDeclaration> Window::GetComputedStyle(
@@ -319,6 +336,10 @@
return performance_;
}
+scoped_refptr<speech::SpeechSynthesis> Window::speech_synthesis() const {
+ return speech_synthesis_;
+}
+
const scoped_refptr<Console>& Window::console() const { return console_; }
const scoped_refptr<Camera3D>& Window::camera_3d() const { return camera_3d_; }
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 77c67c3..d162f0a 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -42,6 +42,7 @@
#include "cobalt/loader/decoder.h"
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/loader/font/remote_typeface_cache.h"
+#include "cobalt/loader/image/animated_image_tracker.h"
#include "cobalt/loader/image/image_cache.h"
#include "cobalt/loader/loader.h"
#include "cobalt/loader/mesh/mesh_cache.h"
@@ -51,12 +52,21 @@
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/execution_state.h"
#include "cobalt/script/script_runner.h"
-#include "cobalt/speech/speech_synthesis.h"
+#include "cobalt/script/script_value_factory.h"
#include "cobalt/system_window/system_window.h"
#include "googleurl/src/gurl.h"
#include "starboard/window.h"
namespace cobalt {
+namespace media_session {
+class MediaSession;
+} // namespace media_session
+namespace speech {
+class SpeechSynthesis;
+} // namespace speech
+} // namespace cobalt
+
+namespace cobalt {
namespace dom {
class Camera3D;
@@ -91,34 +101,39 @@
HTMLDecoderCreatorCallback;
typedef UrlRegistry<MediaSource> MediaSourceRegistry;
- Window(int width, int height, cssom::CSSParser* css_parser,
- Parser* dom_parser, loader::FetcherFactory* fetcher_factory,
- render_tree::ResourceProvider** resource_provider,
- loader::image::ImageCache* image_cache,
- loader::image::ReducedCacheCapacityManager*
- reduced_image_cache_capacity_manager,
- loader::font::RemoteTypefaceCache* remote_typeface_cache,
- loader::mesh::MeshCache* mesh_cache,
- LocalStorageDatabase* local_storage_database,
- media::CanPlayTypeHandler* can_play_type_handler,
- media::WebMediaPlayerFactory* web_media_player_factory,
- script::ExecutionState* execution_state,
- script::ScriptRunner* script_runner,
- MediaSourceRegistry* media_source_registry,
- DomStatTracker* dom_stat_tracker, const GURL& url,
- const std::string& user_agent, const std::string& language,
- const base::Callback<void(const GURL&)> navigation_callback,
- const base::Callback<void(const std::string&)>& error_callback,
- network_bridge::CookieJar* cookie_jar,
- const network_bridge::PostSender& post_sender,
- const std::string& default_security_policy,
- dom::CspEnforcementType csp_enforcement_mode,
- const base::Closure& csp_policy_changed_callback,
- const base::Closure& ran_animation_frame_callbacks_callback,
- const base::Closure& window_close_callback,
- system_window::SystemWindow* system_window,
- const scoped_refptr<input::InputPoller>& input_poller,
- int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0);
+ Window(
+ int width, int height, cssom::CSSParser* css_parser, Parser* dom_parser,
+ loader::FetcherFactory* fetcher_factory,
+ render_tree::ResourceProvider** resource_provider,
+ loader::image::AnimatedImageTracker* animated_image_tracker,
+ loader::image::ImageCache* image_cache,
+ loader::image::ReducedCacheCapacityManager*
+ reduced_image_cache_capacity_manager,
+ loader::font::RemoteTypefaceCache* remote_typeface_cache,
+ loader::mesh::MeshCache* mesh_cache,
+ LocalStorageDatabase* local_storage_database,
+ media::CanPlayTypeHandler* can_play_type_handler,
+ media::WebMediaPlayerFactory* web_media_player_factory,
+ script::ExecutionState* execution_state,
+ script::ScriptRunner* script_runner,
+ script::ScriptValueFactory* script_value_factory,
+ MediaSourceRegistry* media_source_registry,
+ DomStatTracker* dom_stat_tracker, const GURL& url,
+ const std::string& user_agent, const std::string& language,
+ const base::Callback<void(const GURL&)> navigation_callback,
+ const base::Callback<void(const std::string&)>& error_callback,
+ network_bridge::CookieJar* cookie_jar,
+ const network_bridge::PostSender& post_sender,
+ const std::string& default_security_policy,
+ dom::CspEnforcementType csp_enforcement_mode,
+ const base::Closure& csp_policy_changed_callback,
+ const base::Closure& ran_animation_frame_callbacks_callback,
+ const base::Closure& window_close_callback,
+ const base::Closure& window_minimize_callback,
+ system_window::SystemWindow* system_window,
+ const scoped_refptr<input::InputPoller>& input_poller,
+ const scoped_refptr<cobalt::media_session::MediaSession>& media_session,
+ int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0);
// Web API: Window
//
@@ -128,6 +143,7 @@
const scoped_refptr<Location>& location() const;
const scoped_refptr<History>& history() const;
void Close();
+ void Minimize();
scoped_refptr<Window> frames() { return this; }
unsigned int length() { return 0; }
@@ -230,9 +246,7 @@
// Web API: SpeechSynthesisGetter (implements)
// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
- scoped_refptr<speech::SpeechSynthesis> speech_synthesis() const {
- return speech_synthesis_;
- }
+ scoped_refptr<speech::SpeechSynthesis> speech_synthesis() const;
// Custom, not in any spec.
//
@@ -307,6 +321,7 @@
const base::Closure ran_animation_frame_callbacks_callback_;
const base::Closure window_close_callback_;
+ const base::Closure window_minimize_callback_;
system_window::SystemWindow* system_window_;
diff --git a/src/cobalt/dom/window.idl b/src/cobalt/dom/window.idl
index bfb6fd8..d04fb7a 100644
--- a/src/cobalt/dom/window.idl
+++ b/src/cobalt/dom/window.idl
@@ -42,6 +42,9 @@
readonly attribute Camera3D camera3D;
[Conditional=ENABLE_TEST_RUNNER] readonly attribute TestRunner testRunner;
[CallWith=EnvironmentSettings] void gc();
+ // Mozilla's non-standard minimize() function.
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/minimize
+ void minimize();
};
Window implements GlobalEventHandlers;
Window implements WindowEventHandlers;
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc
index efc84d8..f087c28 100644
--- a/src/cobalt/dom/window_test.cc
+++ b/src/cobalt/dom/window_test.cc
@@ -25,6 +25,7 @@
#include "cobalt/dom_parser/parser.h"
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/media/media_module_stub.h"
+#include "cobalt/media_session/media_session.h"
#include "cobalt/network/network_module.h"
#include "cobalt/network_bridge/net_poster.h"
#include "googleurl/src/gurl.h"
@@ -51,10 +52,10 @@
url_("about:blank"),
window_(new Window(
1920, 1080, css_parser_.get(), dom_parser_.get(),
- fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL,
+ fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL, NULL,
&local_storage_database_, stub_media_module_.get(),
- stub_media_module_.get(), NULL, NULL, NULL, NULL, url_, "", "en-US",
- base::Callback<void(const GURL &)>(),
+ stub_media_module_.get(), NULL, NULL, NULL, NULL, NULL, url_, "",
+ "en-US", base::Callback<void(const GURL &)>(),
base::Bind(&MockErrorCallback::Run,
base::Unretained(&mock_error_callback_)),
NULL, network_bridge::PostSender(),
@@ -62,7 +63,8 @@
base::Closure() /* csp_policy_changed */,
base::Closure() /* ran_animation_frame_callbacks */,
base::Closure() /* window_close */,
- stub_media_module_->system_window(), NULL)) {}
+ base::Closure() /* window_minimize */,
+ stub_media_module_->system_window(), NULL, NULL)) {}
~WindowTest() OVERRIDE {}
diff --git a/src/cobalt/dom/xml_document_test.cc b/src/cobalt/dom/xml_document_test.cc
index 6509968..d4294eb 100644
--- a/src/cobalt/dom/xml_document_test.cc
+++ b/src/cobalt/dom/xml_document_test.cc
@@ -25,7 +25,7 @@
TEST(XMLDocumentTest, IsXMLDocument) {
HTMLElementContext html_element_context(NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, "");
+ NULL, NULL, NULL, "");
scoped_refptr<Document> document = new XMLDocument(&html_element_context);
EXPECT_TRUE(document->IsXMLDocument());
}
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc
index 082a006..367607d 100644
--- a/src/cobalt/dom_parser/html_decoder_test.cc
+++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -64,11 +64,11 @@
: fetcher_factory_(NULL /* network_module */),
dom_parser_(new Parser()),
dom_stat_tracker_(new dom::DomStatTracker("HTMLDecoderTest")),
- html_element_context_(&fetcher_factory_, &stub_css_parser_,
- dom_parser_.get(), NULL /* can_play_type_handler */,
- NULL /* web_media_player_factory */,
- &stub_script_runner_, NULL, NULL, NULL, NULL, NULL,
- NULL, dom_stat_tracker_.get(), ""),
+ html_element_context_(
+ &fetcher_factory_, &stub_css_parser_, dom_parser_.get(),
+ NULL /* can_play_type_handler */, NULL /* web_media_player_factory */,
+ &stub_script_runner_, NULL /* script_value_factory */, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, dom_stat_tracker_.get(), ""),
document_(
new dom::Document(&html_element_context_, dom::Document::Options())),
root_(new dom::Element(document_, base::Token("element"))),
@@ -87,11 +87,11 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("html", element->tag_name());
+ EXPECT_EQ("html", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("body", element->tag_name());
+ EXPECT_EQ("body", element->local_name());
}
// TODO: Currently HTMLDecoder is using libxml2 SAX parser. It doesn't
@@ -110,11 +110,11 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("html", element->tag_name());
+ EXPECT_EQ("html", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("body", element->tag_name());
+ EXPECT_EQ("body", element->local_name());
}
TEST_F(HTMLDecoderTest, DISABLED_CanParseDocumentWithOnlySpaces) {
@@ -129,11 +129,11 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("html", element->tag_name());
+ EXPECT_EQ("html", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("body", element->tag_name());
+ EXPECT_EQ("body", element->local_name());
}
TEST_F(HTMLDecoderTest, DISABLED_CanParseDocumentWithOnlyHTML) {
@@ -148,11 +148,11 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("html", element->tag_name());
+ EXPECT_EQ("html", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("body", element->tag_name());
+ EXPECT_EQ("body", element->local_name());
}
TEST_F(HTMLDecoderTest, CanParseDocumentWithOnlyBody) {
@@ -167,11 +167,11 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("html", element->tag_name());
+ EXPECT_EQ("html", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("body", element->tag_name());
+ EXPECT_EQ("body", element->local_name());
}
TEST_F(HTMLDecoderTest, DecodingWholeDocumentShouldAddImpliedTags) {
@@ -186,15 +186,15 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("html", element->tag_name());
+ EXPECT_EQ("html", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("body", element->tag_name());
+ EXPECT_EQ("body", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
EXPECT_FALSE(element->HasChildNodes());
}
@@ -210,7 +210,7 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
EXPECT_FALSE(element->HasChildNodes());
}
@@ -226,7 +226,7 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("div", element->tag_name());
+ EXPECT_EQ("div", element->local_name());
EXPECT_TRUE(element->HasAttributes());
EXPECT_EQ(4, element->attributes()->length());
EXPECT_EQ("a", element->attributes()->Item(0)->name());
@@ -251,7 +251,7 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("div", element->tag_name());
+ EXPECT_EQ("div", element->local_name());
EXPECT_TRUE(element->HasAttributes());
EXPECT_EQ(2, element->attributes()->length());
EXPECT_EQ("a", element->attributes()->Item(0)->name());
@@ -272,11 +272,11 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
element = element->next_element_sibling();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
}
TEST_F(HTMLDecoderTest, CanParseNormalCharacters) {
@@ -291,7 +291,7 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
dom::Text* text = element->first_child()->AsText();
ASSERT_TRUE(text);
@@ -325,7 +325,7 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
dom::Text* text = element->first_child()->AsText();
ASSERT_TRUE(text);
@@ -333,7 +333,7 @@
element = element->next_element_sibling();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
text = element->first_child()->AsText();
ASSERT_TRUE(text);
@@ -353,7 +353,7 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
dom::Text* text = element->first_child()->AsText();
ASSERT_TRUE(text);
@@ -373,7 +373,7 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
dom::Text* text = element->first_child()->AsText();
ASSERT_TRUE(text);
@@ -400,7 +400,7 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
dom::Text* text = element->first_child()->AsText();
ASSERT_TRUE(text);
@@ -424,15 +424,15 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("b", element->tag_name());
+ EXPECT_EQ("b", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("i", element->tag_name());
+ EXPECT_EQ("i", element->local_name());
}
// Misnested tags: <b><p></b></p>
@@ -451,11 +451,11 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("b", element->tag_name());
+ EXPECT_EQ("b", element->local_name());
element = element->next_element_sibling();
ASSERT_TRUE(element);
- EXPECT_EQ("p", element->tag_name());
+ EXPECT_EQ("p", element->local_name());
}
TEST_F(HTMLDecoderTest, TagNamesShouldBeCaseInsensitive) {
@@ -470,7 +470,7 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("div", element->tag_name());
+ EXPECT_EQ("div", element->local_name());
}
TEST_F(HTMLDecoderTest, AttributesShouldBeCaseInsensitive) {
@@ -485,7 +485,7 @@
dom::Element* element = root_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("div", element->tag_name());
+ EXPECT_EQ("div", element->local_name());
EXPECT_TRUE(element->HasAttributes());
EXPECT_EQ(1, element->attributes()->length());
EXPECT_EQ("a", element->attributes()->Item(0)->name());
@@ -509,11 +509,11 @@
root_ = document_->first_element_child();
ASSERT_TRUE(root_);
- EXPECT_EQ("html", root_->tag_name());
+ EXPECT_EQ("html", root_->local_name());
dom::Element* head = root_->first_element_child();
ASSERT_TRUE(head);
- EXPECT_EQ("head", head->tag_name());
+ EXPECT_EQ("head", head->local_name());
}
} // namespace dom_parser
diff --git a/src/cobalt/dom_parser/libxml_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
index 324f8fd..b85ba7a 100644
--- a/src/cobalt/dom_parser/libxml_parser_wrapper.cc
+++ b/src/cobalt/dom_parser/libxml_parser_wrapper.cc
@@ -32,6 +32,7 @@
#include "starboard/ps4/core_dump_handler.h"
#endif // SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
#endif // defined(OS_STARBOARD)
+#include "third_party/libxml/src/include/libxml/xmlerror.h"
namespace cobalt {
namespace dom_parser {
@@ -257,7 +258,7 @@
element->OnParserEndTag();
}
- if (element->node_name() == name) {
+ if (element->local_name() == name) {
return;
}
}
@@ -290,6 +291,12 @@
void LibxmlParserWrapper::OnParsingIssue(IssueSeverity severity,
const std::string& message) {
DCHECK(severity >= kWarning && severity <= kFatal);
+
+ xmlErrorPtr error = xmlGetLastError();
+ if (error->code == XML_HTML_UNKNOWN_TAG) {
+ return;
+ }
+
if (severity > max_severity_) {
max_severity_ = severity;
}
diff --git a/src/cobalt/dom_parser/xml_decoder_test.cc b/src/cobalt/dom_parser/xml_decoder_test.cc
index 59b2ee4..3fb2732 100644
--- a/src/cobalt/dom_parser/xml_decoder_test.cc
+++ b/src/cobalt/dom_parser/xml_decoder_test.cc
@@ -49,7 +49,7 @@
XMLDecoderTest::XMLDecoderTest()
: html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, ""),
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, ""),
document_(new dom::XMLDocument(&html_element_context_)),
source_location_(base::SourceLocation("[object XMLDecoderTest]", 1, 1)) {}
@@ -64,7 +64,7 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("ELEMENT", element->tag_name());
+ EXPECT_EQ("ELEMENT", element->local_name());
EXPECT_FALSE(element->HasChildNodes());
}
@@ -88,7 +88,7 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("ELEMENT", element->tag_name());
+ EXPECT_EQ("ELEMENT", element->local_name());
dom::CDATASection* cdata_section1 = element->first_child()->AsCDATASection();
ASSERT_TRUE(cdata_section1);
@@ -111,7 +111,7 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("ELEMENT", element->tag_name());
+ EXPECT_EQ("ELEMENT", element->local_name());
EXPECT_TRUE(element->HasAttributes());
EXPECT_EQ(2, element->attributes()->length());
EXPECT_EQ("a", element->attributes()->Item(0)->name());
@@ -131,11 +131,11 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("ELEMENT", element->tag_name());
+ EXPECT_EQ("ELEMENT", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("element", element->tag_name());
+ EXPECT_EQ("element", element->local_name());
}
TEST_F(XMLDecoderTest, AttributesShouldBeCaseSensitive) {
@@ -149,7 +149,7 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("ELEMENT", element->tag_name());
+ EXPECT_EQ("ELEMENT", element->local_name());
EXPECT_TRUE(element->HasAttributes());
EXPECT_EQ(2, element->attributes()->length());
EXPECT_EQ("A", element->attributes()->Item(0)->name());
@@ -171,7 +171,7 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("element", element->tag_name());
+ EXPECT_EQ("element", element->local_name());
}
TEST_F(XMLDecoderTest, CanDealWithHTMLAttack) {
@@ -187,7 +187,7 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("element", element->tag_name());
+ EXPECT_EQ("element", element->local_name());
}
TEST_F(XMLDecoderTest, CanDealWithAttack) {
@@ -203,7 +203,7 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("element", element->tag_name());
+ EXPECT_EQ("element", element->local_name());
}
TEST_F(XMLDecoderTest, CanDealWithPEAttack) {
@@ -219,7 +219,7 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("element", element->tag_name());
+ EXPECT_EQ("element", element->local_name());
}
TEST_F(XMLDecoderTest, CanDealWithDTDAttack) {
@@ -235,7 +235,7 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("element", element->tag_name());
+ EXPECT_EQ("element", element->local_name());
}
TEST_F(XMLDecoderTest, CanDealWithLaughsAttack) {
@@ -267,7 +267,7 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("element", element->tag_name());
+ EXPECT_EQ("element", element->local_name());
}
TEST_F(XMLDecoderTest, CanDealWithXIncludeAttack) {
@@ -287,19 +287,19 @@
dom::Element* element = document_->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("root", element->tag_name());
+ EXPECT_EQ("root", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("child", element->tag_name());
+ EXPECT_EQ("child", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("name", element->tag_name());
+ EXPECT_EQ("name", element->local_name());
element = element->first_element_child();
ASSERT_TRUE(element);
- EXPECT_EQ("xi:include", element->tag_name());
+ EXPECT_EQ("xi:include", element->local_name());
}
} // namespace dom_parser
diff --git a/src/cobalt/h5vcc/h5vcc.cc b/src/cobalt/h5vcc/h5vcc.cc
index 6f29c36..f3617cf 100644
--- a/src/cobalt/h5vcc/h5vcc.cc
+++ b/src/cobalt/h5vcc/h5vcc.cc
@@ -17,8 +17,10 @@
namespace cobalt {
namespace h5vcc {
-H5vcc::H5vcc(const Settings& settings) {
- accessibility_ = new H5vccAccessibility();
+H5vcc::H5vcc(const Settings& settings, const scoped_refptr<dom::Window>& window,
+ dom::MutationObserverTaskManager* mutation_observer_task_manager) {
+ accessibility_ = new H5vccAccessibility(settings.event_dispatcher, window,
+ mutation_observer_task_manager);
account_info_ = new H5vccAccountInfo(settings.account_manager);
audio_config_array_ = new H5vccAudioConfigArray();
c_val_ = new H5vccCVal();
diff --git a/src/cobalt/h5vcc/h5vcc.h b/src/cobalt/h5vcc/h5vcc.h
index 71996eb..b316784 100644
--- a/src/cobalt/h5vcc/h5vcc.h
+++ b/src/cobalt/h5vcc/h5vcc.h
@@ -18,6 +18,8 @@
#include <string>
#include "cobalt/base/event_dispatcher.h"
+#include "cobalt/dom/mutation_observer_task_manager.h"
+#include "cobalt/dom/window.h"
#include "cobalt/h5vcc/h5vcc_accessibility.h"
#include "cobalt/h5vcc/h5vcc_account_info.h"
#include "cobalt/h5vcc/h5vcc_audio_config_array.h"
@@ -48,7 +50,8 @@
std::string initial_deep_link;
};
- explicit H5vcc(const Settings& config);
+ H5vcc(const Settings& config, const scoped_refptr<dom::Window>& window,
+ dom::MutationObserverTaskManager* mutation_observer_task_manager);
const scoped_refptr<H5vccAccessibility>& accessibility() const {
return accessibility_;
}
diff --git a/src/cobalt/h5vcc/h5vcc_accessibility.cc b/src/cobalt/h5vcc/h5vcc_accessibility.cc
index 61edda0..d6f9aec 100644
--- a/src/cobalt/h5vcc/h5vcc_accessibility.cc
+++ b/src/cobalt/h5vcc/h5vcc_accessibility.cc
@@ -14,14 +14,84 @@
#include "cobalt/h5vcc/h5vcc_accessibility.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "cobalt/accessibility/starboard_tts_engine.h"
+#include "cobalt/accessibility/tts_engine.h"
+#include "cobalt/accessibility/tts_logger.h"
+#include "cobalt/base/accessibility_settings_changed_event.h"
+#include "cobalt/browser/switches.h"
#include "starboard/accessibility.h"
#include "starboard/memory.h"
namespace cobalt {
namespace h5vcc {
+namespace {
+#if SB_HAS(SPEECH_SYNTHESIS)
+bool IsTextToSpeechEnabled() {
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+ // Check for a command-line override to enable TTS.
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(browser::switches::kUseTTS)) {
+ return true;
+ }
+#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+#if SB_API_VERSION >= 4
+ // Check if the tts feature is enabled in Starboard.
+ SbAccessibilityTextToSpeechSettings tts_settings = {0};
+ // Check platform settings.
+ if (SbAccessibilityGetTextToSpeechSettings(&tts_settings)) {
+ return tts_settings.has_text_to_speech_setting &&
+ tts_settings.is_text_to_speech_enabled;
+ }
+#endif // SB_API_VERSION >= 4
+ return false;
+}
+#endif // SB_HAS(SPEECH_SYNTHESIS)
+} // namespace
+
+H5vccAccessibility::H5vccAccessibility(
+ base::EventDispatcher* event_dispatcher,
+ const scoped_refptr<dom::Window>& window,
+ dom::MutationObserverTaskManager* mutation_observer_task_manager)
+ : event_dispatcher_(event_dispatcher) {
+ message_loop_proxy_ = base::MessageLoopProxy::current();
+ event_dispatcher_->AddEventCallback(
+ base::AccessibilitySettingsChangedEvent::TypeId(),
+ base::Bind(&H5vccAccessibility::OnApplicationEvent,
+ base::Unretained(this)));
+#if SB_HAS(SPEECH_SYNTHESIS)
+ if (IsTextToSpeechEnabled()) {
+ // Create a StarboardTTSEngine if TTS is enabled.
+ tts_engine_.reset(new accessibility::StarboardTTSEngine());
+ }
+#endif // SB_HAS(SPEECH_SYNTHESIS)
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+ if (!tts_engine_) {
+ tts_engine_.reset(new accessibility::TTSLogger());
+ }
+#endif // !defined(COBALT_BUILD_TYPE_GOLD)
+ if (tts_engine_) {
+ screen_reader_.reset(new accessibility::ScreenReader(
+ window->document(), tts_engine_.get(), mutation_observer_task_manager));
+ }
+}
+
+bool H5vccAccessibility::built_in_screen_reader() const {
+ return screen_reader_ && screen_reader_->enabled();
+}
+
+void H5vccAccessibility::set_built_in_screen_reader(bool value) {
+ if (!screen_reader_) {
+ LOG(WARNING) << "h5vcc.accessibility.builtInScreenReader: not available";
+ return;
+ }
+ screen_reader_->set_enabled(value);
+}
+
bool H5vccAccessibility::high_contrast_text() const {
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
SbAccessibilityDisplaySettings settings;
SbMemorySet(&settings, 0, sizeof(settings));
@@ -30,13 +100,13 @@
}
return settings.is_high_contrast_text_enabled;
-#else // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#else // SB_API_VERSION >= 4
return false;
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif // SB_API_VERSION >= 4
}
bool H5vccAccessibility::text_to_speech() const {
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
SbAccessibilityTextToSpeechSettings settings;
SbMemorySet(&settings, 0, sizeof(settings));
@@ -46,9 +116,32 @@
return settings.has_text_to_speech_setting &&
settings.is_text_to_speech_enabled;
-#else // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#else // SB_API_VERSION >= 4
return false;
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif // SB_API_VERSION >= 4
+}
+
+void H5vccAccessibility::AddHighContrastTextListener(
+ const H5vccAccessibilityCallbackHolder& holder) {
+ DCHECK_EQ(base::MessageLoopProxy::current(), message_loop_proxy_);
+ high_contrast_text_listener_.reset(
+ new H5vccAccessibilityCallbackReference(this, holder));
+}
+
+void H5vccAccessibility::OnApplicationEvent(const base::Event* event) {
+ UNREFERENCED_PARAMETER(event);
+ // This method should be called from the application event thread.
+ DCHECK_NE(base::MessageLoopProxy::current(), message_loop_proxy_);
+ message_loop_proxy_->PostTask(
+ FROM_HERE, base::Bind(&H5vccAccessibility::InternalOnApplicationEvent,
+ base::Unretained(this)));
+}
+
+void H5vccAccessibility::InternalOnApplicationEvent() {
+ DCHECK_EQ(base::MessageLoopProxy::current(), message_loop_proxy_);
+ if (high_contrast_text_listener_) {
+ high_contrast_text_listener_->value().Run();
+ }
}
} // namespace h5vcc
diff --git a/src/cobalt/h5vcc/h5vcc_accessibility.h b/src/cobalt/h5vcc/h5vcc_accessibility.h
index e93c1ab..6d6721f 100644
--- a/src/cobalt/h5vcc/h5vcc_accessibility.h
+++ b/src/cobalt/h5vcc/h5vcc_accessibility.h
@@ -15,6 +15,11 @@
#ifndef COBALT_H5VCC_H5VCC_ACCESSIBILITY_H_
#define COBALT_H5VCC_H5VCC_ACCESSIBILITY_H_
+#include "base/message_loop_proxy.h"
+#include "cobalt/accessibility/screen_reader.h"
+#include "cobalt/accessibility/tts_engine.h"
+#include "cobalt/base/event_dispatcher.h"
+#include "cobalt/dom/window.h"
#include "cobalt/script/callback_function.h"
#include "cobalt/script/script_value.h"
#include "cobalt/script/wrappable.h"
@@ -28,19 +33,36 @@
typedef script::CallbackFunction<void()> H5vccAccessibilityCallback;
typedef script::ScriptValue<H5vccAccessibilityCallback>
H5vccAccessibilityCallbackHolder;
- H5vccAccessibility() {}
+ H5vccAccessibility(
+ base::EventDispatcher* event_dispatcher,
+ const scoped_refptr<dom::Window>& window,
+ dom::MutationObserverTaskManager* mutation_observer_task_manager);
bool high_contrast_text() const;
void AddHighContrastTextListener(
- const H5vccAccessibilityCallbackHolder& holder) {
- UNREFERENCED_PARAMETER(holder);
- }
+ const H5vccAccessibilityCallbackHolder& holder);
+ void OnApplicationEvent(const base::Event* event);
bool text_to_speech() const;
+ bool built_in_screen_reader() const;
+ void set_built_in_screen_reader(bool value);
+
DEFINE_WRAPPABLE_TYPE(H5vccAccessibility);
private:
+ typedef H5vccAccessibilityCallbackHolder::Reference
+ H5vccAccessibilityCallbackReference;
+
+ void InternalOnApplicationEvent();
+
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
+
+ base::EventDispatcher* event_dispatcher_;
+ scoped_ptr<H5vccAccessibilityCallbackReference> high_contrast_text_listener_;
+ scoped_ptr<accessibility::TTSEngine> tts_engine_;
+ scoped_ptr<accessibility::ScreenReader> screen_reader_;
+
DISALLOW_COPY_AND_ASSIGN(H5vccAccessibility);
};
diff --git a/src/cobalt/h5vcc/h5vcc_accessibility.idl b/src/cobalt/h5vcc/h5vcc_accessibility.idl
index 7f62abf..14b5b90 100644
--- a/src/cobalt/h5vcc/h5vcc_accessibility.idl
+++ b/src/cobalt/h5vcc/h5vcc_accessibility.idl
@@ -21,6 +21,10 @@
// True if the host operating system's "text to speech" accessibility
// option is enabled.
readonly attribute boolean textToSpeech;
+
+ // True if the built-in screen reader is enabled.
+ // Setting it to false disables the built-in screen reader.
+ attribute boolean builtInScreenReader;
};
callback H5vccAccessibilityCallback = void();
diff --git a/src/cobalt/input/input.gyp b/src/cobalt/input/input.gyp
index 56ca050..27a850e 100644
--- a/src/cobalt/input/input.gyp
+++ b/src/cobalt/input/input.gyp
@@ -45,14 +45,6 @@
'input_device_manager_starboard.cc',
],
}],
- ['OS!="starboard" and actual_target_arch=="ps3"', {
- 'sources': [
- 'input_device_manager_ps3.cc',
- 'input_device_manager_ps3.h',
- 'keycode_conversion_ps3.cc',
- 'keycode_conversion_ps3.h',
- ],
- }],
['OS!="starboard" and actual_target_arch=="win"', {
'sources': [
'input_device_manager_<(actual_target_arch).cc',
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 3e71639..269f2d1 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -624,22 +624,22 @@
Border CreateBorderFromStyle(
const scoped_refptr<const cssom::CSSComputedStyleData>& style) {
render_tree::BorderSide left(
- GetUsedLength(style->border_left_width()).toFloat(),
+ GetUsedNonNegativeLength(style->border_left_width()).toFloat(),
GetRenderTreeBorderStyle(style->border_left_style()),
GetUsedColor(style->border_left_color()));
render_tree::BorderSide right(
- GetUsedLength(style->border_right_width()).toFloat(),
+ GetUsedNonNegativeLength(style->border_right_width()).toFloat(),
GetRenderTreeBorderStyle(style->border_right_style()),
GetUsedColor(style->border_right_color()));
render_tree::BorderSide top(
- GetUsedLength(style->border_top_width()).toFloat(),
+ GetUsedNonNegativeLength(style->border_top_width()).toFloat(),
GetRenderTreeBorderStyle(style->border_top_style()),
GetUsedColor(style->border_top_color()));
render_tree::BorderSide bottom(
- GetUsedLength(style->border_bottom_width()).toFloat(),
+ GetUsedNonNegativeLength(style->border_bottom_width()).toFloat(),
GetRenderTreeBorderStyle(style->border_bottom_style()),
GetUsedColor(style->border_bottom_color()));
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index 80fff36..e1f5288 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -64,7 +64,7 @@
scoped_refptr<render_tree::Image> GetVideoFrame(
const scoped_refptr<ShellVideoFrameProvider>& frame_provider,
render_tree::ResourceProvider* resource_provider) {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget decode_target = frame_provider->GetCurrentSbDecodeTarget();
if (SbDecodeTargetIsValid(decode_target)) {
#if SB_HAS(GRAPHICS)
@@ -74,7 +74,7 @@
return NULL;
#endif
} else {
-#else // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // SB_API_VERSION >= 4
UNREFERENCED_PARAMETER(resource_provider);
{
#endif
diff --git a/src/cobalt/layout/layout.cc b/src/cobalt/layout/layout.cc
index db09515..2331e2e 100644
--- a/src/cobalt/layout/layout.cc
+++ b/src/cobalt/layout/layout.cc
@@ -152,6 +152,11 @@
->RenderAndAnimate(&render_tree_root_builder, math::Vector2dF(0, 0));
}
+ // During computed style update and RenderAndAnimate, we get the actual images
+ // that are linked to their URLs. Now go through them and update the playing
+ // status for animated images.
+ used_style_provider->UpdateAnimatedImages();
+
render_tree::CompositionNode* static_root_node =
new render_tree::CompositionNode(render_tree_root_builder.Pass());
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc
index b9d6c75..88f5a41 100644
--- a/src/cobalt/layout/layout_manager.cc
+++ b/src/cobalt/layout/layout_manager.cc
@@ -105,16 +105,19 @@
void UpdateCamera(
float width_to_height_aspect_ratio, scoped_refptr<dom::Camera3DImpl> camera,
+ float max_horizontal_fov_rad, float max_vertical_fov_rad,
render_tree::MatrixTransform3DNode::Builder* transform_node_builder,
base::TimeDelta time) {
UNREFERENCED_PARAMETER(time);
- transform_node_builder->transform =
- camera->QueryViewPerspectiveMatrix(width_to_height_aspect_ratio);
+ transform_node_builder->transform = camera->QueryViewPerspectiveMatrix(
+ width_to_height_aspect_ratio, max_horizontal_fov_rad,
+ max_vertical_fov_rad);
}
scoped_refptr<render_tree::Node> AttachCameraNodes(
const scoped_refptr<dom::Window> window,
- const scoped_refptr<render_tree::Node>& source) {
+ const scoped_refptr<render_tree::Node>& source,
+ float max_horizontal_fov_rad, float max_vertical_fov_rad) {
// Attach a 3D transform node that applies the current camera matrix transform
// to the rest of the render tree.
scoped_refptr<render_tree::MatrixTransform3DNode> transform_node =
@@ -127,7 +130,8 @@
animate_node_builder.Add(
transform_node,
base::Bind(&UpdateCamera, window->inner_width() / window->inner_height(),
- window->camera_3d()->impl()));
+ window->camera_3d()->impl(), max_horizontal_fov_rad,
+ max_vertical_fov_rad));
return new render_tree::animations::AnimateNode(animate_node_builder,
transform_node);
}
@@ -142,12 +146,9 @@
LayoutStatTracker* layout_stat_tracker)
: window_(window),
locale_(icu::Locale::createCanonical(language.c_str())),
- used_style_provider_(
- new UsedStyleProvider(window->html_element_context(),
- window->html_element_context()->image_cache(),
- window->document()->font_cache(),
- base::Bind(&AttachCameraNodes, window),
- window->html_element_context()->mesh_cache())),
+ used_style_provider_(new UsedStyleProvider(
+ window->html_element_context(), window->document()->font_cache(),
+ base::Bind(&AttachCameraNodes, window))),
on_render_tree_produced_callback_(on_render_tree_produced),
layout_trigger_(layout_trigger),
layout_dirty_(StringPrintf("%s.Layout.IsDirty", name.c_str()), true,
@@ -230,13 +231,15 @@
// Mark that we are suspended so that we don't try to perform any layouts.
suspended_ = true;
+ // Invalidate any cached layout boxes from the document prior to clearing
+ // the initial containing block. That'll ensure that the full box tree is
+ // destroyed when the containing block is destroyed and that no children of
+ // |initial_containing_block_| are holding on to stale parent pointers.
+ window_->document()->InvalidateLayoutBoxes();
+
// Clear our reference to the initial containing block to allow any resources
// like images that were referenced by it to be released.
initial_containing_block_ = NULL;
-
- // Invalidate the document's layout so that all references to any resources
- // such as images will be released.
- window_->document()->InvalidateLayout();
}
void LayoutManager::Impl::Resume() {
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc
index 755f400..4811118 100644
--- a/src/cobalt/layout/replaced_box.cc
+++ b/src/cobalt/layout/replaced_box.cc
@@ -17,6 +17,7 @@
#include <algorithm>
#include "base/bind.h"
+#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/cssom/filter_function_list_value.h"
@@ -64,6 +65,13 @@
const char* kEquirectangularMeshURL =
"h5vcc-embedded://equirectangular_40_40.msh";
+const char* kWarningInvalidMeshUrl =
+ "Mesh specification invalid in map-to-mesh filter: "
+ "must be either a valid URL or 'equirectangular'.";
+
+const char* kWarningLoadingMeshFailed =
+ "Could not load mesh specified by map-to-mesh filter.";
+
// Convert the parsed keyword value for a stereo mode into a stereo mode enum
// value.
render_tree::StereoMode ReadStereoMode(
@@ -572,38 +580,88 @@
// Fetch either the embedded equirectangular mesh or a custom one depending
// on the spec.
- GURL url;
+ MapToMeshFilter::Builder builder;
if (spec.mesh_type() == cssom::MapToMeshFunction::kUrls) {
// Custom mesh URLs.
+ // Set a default mesh (in case no resolution-specific mesh matches).
cssom::URLValue* url_value =
base::polymorphic_downcast<cssom::URLValue*>(spec.mesh_url().get());
- url = GURL(url_value->value());
+ GURL default_url(url_value->value());
+
+ if (!default_url.is_valid()) {
+ DLOG(WARNING) << kWarningInvalidMeshUrl;
+ return;
+ }
+
+ scoped_refptr<loader::mesh::MeshProjection> default_mesh_projection(
+ used_style_provider()->ResolveURLToMeshProjection(default_url));
+
+ if (!default_mesh_projection) {
+ DLOG(WARNING) << kWarningLoadingMeshFailed;
+ return;
+ }
+
+ builder.SetDefaultMeshes(
+ default_mesh_projection->GetMesh(
+ loader::mesh::MeshProjection::kLeftEyeOrMonoCollection),
+ default_mesh_projection->GetMesh(
+ loader::mesh::MeshProjection::kRightEyeCollection));
+
+ // Lookup among the list of resolutions for a match and use that mesh URL.
+ const cssom::MapToMeshFunction::ResolutionMatchedMeshListBuilder& meshes =
+ spec.resolution_matched_meshes();
+ for (size_t i = 0; i < meshes.size(); i++) {
+ cssom::URLValue* url_value = base::polymorphic_downcast<cssom::URLValue*>(
+ meshes[i]->mesh_url().get());
+ GURL url(url_value->value());
+
+ if (!url.is_valid()) {
+ DLOG(WARNING) << kWarningInvalidMeshUrl;
+ return;
+ }
+ scoped_refptr<loader::mesh::MeshProjection> mesh_projection(
+ used_style_provider()->ResolveURLToMeshProjection(url));
+
+ if (!mesh_projection) {
+ DLOG(WARNING) << kWarningLoadingMeshFailed;
+ }
+
+ TRACE_EVENT2("cobalt::layout",
+ "ReplacedBox::RenderAndAnimateContentWithMapToMesh()",
+ "height", meshes[i]->height_match(),
+ "crc", mesh_projection->crc().value_or(-1));
+
+ builder.AddResolutionMatchedMeshes(
+ math::Size(meshes[i]->width_match(), meshes[i]->height_match()),
+ mesh_projection->GetMesh(
+ loader::mesh::MeshProjection::kLeftEyeOrMonoCollection),
+ mesh_projection->GetMesh(
+ loader::mesh::MeshProjection::kRightEyeCollection));
+ }
} else if (spec.mesh_type() == cssom::MapToMeshFunction::kEquirectangular) {
- url = GURL(kEquirectangularMeshURL);
+ GURL url(kEquirectangularMeshURL);
+ scoped_refptr<loader::mesh::MeshProjection> mesh_projection(
+ used_style_provider()->ResolveURLToMeshProjection(url));
+
+ if (!mesh_projection) {
+ DLOG(WARNING) << kWarningLoadingMeshFailed;
+ }
+
+ builder.SetDefaultMeshes(
+ mesh_projection->GetMesh(
+ loader::mesh::MeshProjection::kLeftEyeOrMonoCollection),
+ mesh_projection->GetMesh(
+ loader::mesh::MeshProjection::kRightEyeCollection));
}
- DCHECK(url.is_valid())
- << "Mesh specification invalid in map-to-mesh filter: must be either a"
- " valid URL or 'equirectangular'";
-
- scoped_refptr<loader::mesh::MeshProjection> mesh_projection(
- used_style_provider()->ResolveURLToMeshProjection(url));
-
- DCHECK(mesh_projection)
- << "Could not load mesh specified by map-to-mesh filter.";
-
- scoped_refptr<render_tree::Mesh> left_eye_mesh = mesh_projection->GetMesh(
- loader::mesh::MeshProjection::kLeftEyeOrMonoCollection);
- scoped_refptr<render_tree::Mesh> right_eye_mesh = mesh_projection->GetMesh(
- loader::mesh::MeshProjection::kRightEyeCollection);
-
- scoped_refptr<render_tree::Node> filter_node = new FilterNode(
- MapToMeshFilter(stereo_mode, left_eye_mesh, right_eye_mesh),
- animate_node);
+ scoped_refptr<render_tree::Node> filter_node =
+ new FilterNode(MapToMeshFilter(stereo_mode, builder), animate_node);
// Attach a 3D camera to the map-to-mesh node.
border_node_builder->AddChild(
- used_style_provider()->attach_camera_node_function().Run(filter_node));
+ used_style_provider()->attach_camera_node_function().Run(
+ filter_node, mtm_function->horizontal_fov_in_radians(),
+ mtm_function->vertical_fov_in_radians()));
}
void ReplacedBox::RenderAndAnimateContentWithLetterboxing(
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index d7d0b88..91e64d4 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -407,15 +407,13 @@
} // namespace
UsedStyleProvider::UsedStyleProvider(
- dom::HTMLElementContext* html_element_context,
- loader::image::ImageCache* image_cache, dom::FontCache* font_cache,
- const AttachCameraNodeFunction& attach_camera_node_function,
- loader::mesh::MeshCache* mesh_cache)
- : html_element_context_(html_element_context),
- image_cache_(image_cache),
- font_cache_(font_cache),
- attach_camera_node_function_(attach_camera_node_function),
- mesh_cache_(mesh_cache) {}
+ dom::HTMLElementContext* html_element_context, dom::FontCache* font_cache,
+ const AttachCameraNodeFunction& attach_camera_node_function)
+ : font_cache_(font_cache),
+ animated_image_tracker_(html_element_context->animated_image_tracker()),
+ image_cache_(html_element_context->image_cache()),
+ mesh_cache_(html_element_context->mesh_cache()),
+ attach_camera_node_function_(attach_camera_node_function) {}
scoped_refptr<dom::FontList> UsedStyleProvider::GetUsedFontList(
const scoped_refptr<cssom::PropertyValue>& font_family_refptr,
@@ -469,8 +467,16 @@
scoped_refptr<loader::image::Image> UsedStyleProvider::ResolveURLToImage(
const GURL& url) {
+ DCHECK(animated_image_tracker_);
DCHECK(image_cache_);
- return image_cache_->CreateCachedResource(url)->TryGetResource();
+ scoped_refptr<loader::image::Image> image =
+ image_cache_->CreateCachedResource(url)->TryGetResource();
+ if (image && image->IsAnimated()) {
+ loader::image::AnimatedImage* animated_image =
+ base::polymorphic_downcast<loader::image::AnimatedImage*>(image.get());
+ animated_image_tracker_->RecordImage(url, animated_image);
+ }
+ return image;
}
scoped_refptr<loader::mesh::MeshProjection>
@@ -479,6 +485,10 @@
return mesh_cache_->CreateCachedResource(url)->TryGetResource();
}
+void UsedStyleProvider::UpdateAnimatedImages() {
+ animated_image_tracker_->ProcessRecordedImages();
+}
+
void UsedStyleProvider::CleanupAfterLayout() {
// Clear out the last font properties prior to requesting that the font cache
// process inactive font lists. The reason for this is that the font cache
@@ -516,6 +526,21 @@
return LayoutUnit(length->value());
}
+LayoutUnit GetUsedNonNegativeLength(
+ const scoped_refptr<cssom::PropertyValue>& length_refptr) {
+ cssom::LengthValue* length =
+ base::polymorphic_downcast<cssom::LengthValue*>(length_refptr.get());
+ DCHECK_EQ(length->unit(), cssom::kPixelsUnit);
+ LayoutUnit layout_unit(length->value());
+ if (layout_unit < LayoutUnit(0)) {
+ DLOG(WARNING) << "Invalid non-negative layout length "
+ << layout_unit.toFloat() << ", original length was "
+ << length->value();
+ layout_unit = LayoutUnit(0);
+ }
+ return layout_unit;
+}
+
class UsedLengthValueProvider : public cssom::NotReachedPropertyValueVisitor {
public:
explicit UsedLengthValueProvider(LayoutUnit percentage_base,
diff --git a/src/cobalt/layout/used_style.h b/src/cobalt/layout/used_style.h
index bfcdfa1..3e670df 100644
--- a/src/cobalt/layout/used_style.h
+++ b/src/cobalt/layout/used_style.h
@@ -28,6 +28,7 @@
#include "cobalt/dom/html_element_context.h"
#include "cobalt/layout/layout_unit.h"
#include "cobalt/layout/size_layout_unit.h"
+#include "cobalt/loader/image/animated_image_tracker.h"
#include "cobalt/loader/image/image.h"
#include "cobalt/loader/image/image_cache.h"
#include "cobalt/loader/mesh/mesh_projection.h"
@@ -57,14 +58,19 @@
class UsedStyleProvider {
public:
+ // A function that will attach a camera matrix projection transform to a given
+ // sub render tree. |max_horizontal_fov_rad| and |max_vertical_fov_rad|
+ // dictate the minimum field of view to use in that direction, in radians.
+ // These minimum FOV values are compared against the values derived from
+ // applying the aspect ratio to the other FOV value, and using whichever one
+ // is most minimum.
typedef base::Callback<scoped_refptr<render_tree::Node>(
- const scoped_refptr<render_tree::Node>&)> AttachCameraNodeFunction;
+ const scoped_refptr<render_tree::Node>&, float max_horizontal_fov_rad,
+ float max_vertical_fov_rad)> AttachCameraNodeFunction;
- UsedStyleProvider(dom::HTMLElementContext* html_element_context,
- loader::image::ImageCache* image_cache,
- dom::FontCache* font_cache,
- const AttachCameraNodeFunction& attach_camera_node_function,
- loader::mesh::MeshCache* mesh_cache = NULL);
+ UsedStyleProvider(
+ dom::HTMLElementContext* html_element_context, dom::FontCache* font_cache,
+ const AttachCameraNodeFunction& attach_camera_node_function);
scoped_refptr<dom::FontList> GetUsedFontList(
const scoped_refptr<cssom::PropertyValue>& font_family_refptr,
@@ -75,29 +81,26 @@
scoped_refptr<loader::image::Image> ResolveURLToImage(const GURL& url);
scoped_refptr<loader::mesh::MeshProjection> ResolveURLToMeshProjection(
const GURL& url);
- bool has_image_cache(const loader::image::ImageCache* image_cache) const {
- return image_cache == image_cache_;
- }
-
- dom::HTMLElementContext* html_element_context() const {
- return html_element_context_;
- }
const AttachCameraNodeFunction attach_camera_node_function() const {
return attach_camera_node_function_;
}
+ // Notifies animated image tracker to update the playing status of animated
+ // images.
+ void UpdateAnimatedImages();
+
private:
// Called after layout is completed so that it can perform any necessary
// cleanup. Its primary current purpose is making a request to the font cache
// to remove any font lists that are no longer being referenced by boxes.
void CleanupAfterLayout();
- dom::HTMLElementContext* html_element_context_;
- loader::image::ImageCache* const image_cache_;
dom::FontCache* const font_cache_;
- AttachCameraNodeFunction attach_camera_node_function_;
+ loader::image::AnimatedImageTracker* const animated_image_tracker_;
+ loader::image::ImageCache* const image_cache_;
loader::mesh::MeshCache* const mesh_cache_;
+ AttachCameraNodeFunction attach_camera_node_function_;
// |font_list_key_| is retained in between lookups so that the font names
// vector will not need to allocate elements each time that it is populated.
@@ -133,6 +136,9 @@
LayoutUnit GetUsedLength(
const scoped_refptr<cssom::PropertyValue>& length_refptr);
+LayoutUnit GetUsedNonNegativeLength(
+ const scoped_refptr<cssom::PropertyValue>& length_refptr);
+
class UsedBackgroundNodeProvider
: public cssom::NotReachedPropertyValueVisitor {
public:
diff --git a/src/cobalt/layout_tests/layout_snapshot.cc b/src/cobalt/layout_tests/layout_snapshot.cc
index 756d84f..cb375a7 100644
--- a/src/cobalt/layout_tests/layout_snapshot.cc
+++ b/src/cobalt/layout_tests/layout_snapshot.cc
@@ -68,12 +68,14 @@
scoped_ptr<media::MediaModule> stub_media_module(
new media::MediaModuleStub());
+ // Use 128M of image cache to minimize the effect of image loading.
+ const size_t kImageCacheCapacity = 128 * 1024 * 1024;
+
// Use test runner mode to allow the content itself to dictate when it is
// ready for layout should be performed. See cobalt/dom/test_runner.h.
- browser::WebModule::Options web_module_options(viewport_size);
+ browser::WebModule::Options web_module_options;
web_module_options.layout_trigger = layout::LayoutManager::kTestRunnerMode;
- // Use 128M of image cache to minimize the effect of image loading.
- web_module_options.image_cache_capacity = 128 * 1024 * 1024;
+ web_module_options.image_cache_capacity = kImageCacheCapacity;
// Prepare a slot for our results to be placed when ready.
base::optional<browser::WebModule::LayoutResults> results;
@@ -83,9 +85,11 @@
url, base::Bind(&WebModuleOnRenderTreeProducedCallback, &results,
&run_loop, MessageLoop::current()),
base::Bind(&WebModuleErrorCallback, &run_loop, MessageLoop::current()),
- base::Closure() /* window_close_callback */, stub_media_module.get(),
- &network_module, viewport_size, resource_provider,
- stub_media_module->system_window(), 60.0f, web_module_options);
+ base::Closure() /* window_close_callback */,
+ base::Closure() /* window_minimize_callback */,
+ stub_media_module.get(), &network_module, viewport_size,
+ resource_provider, stub_media_module->system_window(), 60.0f,
+ web_module_options);
run_loop.Run();
diff --git a/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html b/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html
index aa33681..d7da758 100644
--- a/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html
+++ b/src/cobalt/layout_tests/testdata/cobalt/image-from-blob.html
@@ -36,10 +36,7 @@
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_blob = new Blob([image_array.buffer]);
var image_url = URL.createObjectURL(image_blob);
var image = new Image();
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name-expected.png
new file mode 100644
index 0000000..b265af2
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name.html
new file mode 100644
index 0000000..01a1ebd
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-font-postscript-name.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<!--
+ | The "local" string is used to match the Postscript name or the full font name
+ | in the name table of locally available fonts. While not explicitly specified
+ | this is done in a case insensitive manner to mimic the other browsers.
+ | https://www.w3.org/TR/css-fonts-3/#descdef-src
+ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ margin: 0;
+ font-family: Roboto;
+ font-size: 30px;
+ font-weight: normal;
+ color: #fff;
+ }
+ .containing-block {
+ background-color: #03a9f4;
+ width: 700px;
+ }
+ .roboto-regular {
+ font-family: font-face-roboto-regular;
+ }
+ .roboto-bold {
+ font-family: font-face-roboto-bold;
+ }
+ .coming-soon-regular {
+ font-family: font-face-coming-soon-regular;
+ }
+ .dancing-script-regular {
+ font-family: font-face-dancing-script-regular;
+ }
+ .dancing-script-bold {
+ font-family: font-face-dancing-script-bold;
+ }
+ @font-face {
+ font-family: font-face-roboto-regular;
+ src: local(Roboto-Regular);
+ }
+ @font-face {
+ font-family: font-face-roboto-bold;
+ src: local(RoBoTo-BoLd);
+ }
+ @font-face {
+ font-family: font-face-coming-soon-regular;
+ src: local(comingsoon);
+ }
+ @font-face {
+ font-family: font-face-dancing-script-regular;
+ src: local(DANCINGscript);
+ }
+ @font-face {
+ font-family: font-face-dancing-script-bold;
+ src: local(dancingSCRIPT-bold);
+ }
+ </style>
+</head>
+<body>
+ <div class="containing-block">
+ <div class="roboto-regulary">The Hegemony Consul</div>
+ <div class="roboto-bold">The Hegemony Consul</div>
+ <div class="coming-soon-regular">The Hegemony Consul</div>
+ <div class="dancing-script-regular">The Hegemony Consul</div>
+ <div class="dancing-script-bold">The Hegemony Consul</div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name-expected.png
new file mode 100644
index 0000000..b265af2
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name.html
new file mode 100644
index 0000000..f6072d6
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-3-src-local-can-match-full-font-name.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<!--
+ | The "local" string is used to match the Postscript name or the full font name
+ | in the name table of locally available fonts. While not explicitly specified,
+ | this is done in a case insensitive manner to mimic the other browsers.
+ | https://www.w3.org/TR/css-fonts-3/#descdef-src
+ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ margin: 0;
+ font-family: Roboto;
+ font-size: 30px;
+ font-weight: normal;
+ color: #fff;
+ }
+ .containing-block {
+ background-color: #03a9f4;
+ width: 700px;
+ }
+ .roboto-regular {
+ font-family: font-face-roboto-regular;
+ }
+ .roboto-bold {
+ font-family: font-face-roboto-bold;
+ }
+ .coming-soon-regular {
+ font-family: font-face-coming-soon-regular;
+ }
+ .dancing-script-regular {
+ font-family: font-face-dancing-script-regular;
+ }
+ .dancing-script-bold {
+ font-family: font-face-dancing-script-bold;
+ }
+ @font-face {
+ font-family: font-face-roboto-regular;
+ src: local(Roboto Regular);
+ }
+ @font-face {
+ font-family: font-face-roboto-bold;
+ src: local(RoBoTo BoLd);
+ }
+ @font-face {
+ font-family: font-face-coming-soon-regular;
+ src: local(coming soon);
+ }
+ @font-face {
+ font-family: font-face-dancing-script-regular;
+ src: local(DANCING script);
+ }
+ @font-face {
+ font-family: font-face-dancing-script-bold;
+ src: local(dancing SCRIPT bold);
+ }
+ </style>
+</head>
+<body>
+ <div class="containing-block">
+ <div class="roboto-regulary">The Hegemony Consul</div>
+ <div class="roboto-bold">The Hegemony Consul</div>
+ <div class="coming-soon-regular">The Hegemony Consul</div>
+ <div class="dancing-script-regular">The Hegemony Consul</div>
+ <div class="dancing-script-bold">The Hegemony Consul</div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png
index e894c76..018a47c 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html
index b9bd6d1..419b372 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-4-use-correct-style-in-font-face-set.html
@@ -40,11 +40,6 @@
font-weight: bold;
font-style: italic;
}
- .font-face-1-normal-italic {
- font-family: font-face-1;
- font-weight: normal;
- font-style: italic;
- }
.font-face-2-normal-normal {
font-family: font-face-2;
font-weight: normal;
@@ -99,7 +94,8 @@
<div class="containing-block">
<div class="font-face-1-normal-normal">The Hegemony Consul</div>
<!-- Note: font-face-1 selects a local font file, which only has an regular
- style. So, for the next three cases, a normal style is rendered.
+ style. So, for the next three cases, a normal style is rendered,
+ although synthetic bolding is used for the bold weights.
-->
<div class="font-face-1-normal-italic">The Hegemony Consul</div>
<div class="font-face-1-bold-normal">The Hegemony Consul</div>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
index 76ce4dd..eb5beaa 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
@@ -1,4 +1,6 @@
4-2-font-face-font-family-hides-system-font-family
+4-3-src-local-can-match-font-postscript-name
+4-3-src-local-can-match-full-font-name
4-3-use-first-available-local-font-face
4-3-use-next-font-family-if-font-face-sources-unavailable
4-4-use-correct-style-in-font-face-set
@@ -7,4 +9,6 @@
5-2-use-numerical-font-weights-in-family-face-matching
5-2-use-specified-font-family-if-available
5-2-use-system-fallback-if-no-matching-family-is-found
-text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs
\ No newline at end of file
+synthetic-bolding-should-not-occur-on-bold-font
+synthetic-bolding-should-occur-on-non-bold-font
+text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font-expected.png
new file mode 100644
index 0000000..5c923af
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font.html b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font.html
new file mode 100644
index 0000000..aa176c3
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-not-occur-on-bold-font.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<!--
+ | In Cobalt, bold fonts are never synthetically boldable.
+ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ margin: 0;
+ font-family: Roboto;
+ font-size: 30px;
+ font-weight: normal;
+ color: #fff;
+ }
+ .containing-block {
+ background-color: #03a9f4;
+ width: 700px;
+ }
+ .roboto-normal {
+ font-family: roboto-face;
+ font-weight: normal;
+ }
+ .roboto-bold {
+ font-family: roboto-face;
+ font-weight: bold;
+ }
+ .noto-serif-normal {
+ font-family: serif-face;
+ font-weight: normal;
+ }
+ .noto-serif-bold {
+ font-family: serif-face;
+ font-weight: bold;
+ }
+ .dancing-script-normal {
+ font-family: dancing-script-face;
+ font-weight: normal;
+ }
+ .dancing-script-bold {
+ font-family: dancing-script-face;
+ font-weight: bold;
+ }
+ @font-face {
+ font-family: roboto-face;
+ src: local(Roboto-Bold);
+ }
+ @font-face {
+ font-family: serif-face;
+ src: local(NotoSerif-Bold);
+ }
+ @font-face {
+ font-family: dancing-script-face;
+ src: local(DancingScript-Bold);
+ }
+ </style>
+</head>
+<body>
+ <div class="containing-block">
+ <div class="roboto-normal">The Hegemony Consul</div>
+ <div class="roboto-bold">The Hegemony Consul</div>
+ <div class="noto-serif-normal">The Hegemony Consul</div>
+ <div class="noto-serif-bold">The Hegemony Consul</div>
+ <div class="dancing-script-normal">The Hegemony Consul</div>
+ <div class="dancing-script-bold">The Hegemony Consul</div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font-expected.png
new file mode 100644
index 0000000..324f60e
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font.html b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font.html
new file mode 100644
index 0000000..504805b
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/synthetic-bolding-should-occur-on-non-bold-font.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<!--
+ | In Cobalt, non-bold fonts are synthetically boldable if the desired weight is
+ | bold.
+ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ margin: 0;
+ font-family: Roboto;
+ font-size: 30px;
+ font-weight: normal;
+ color: #fff;
+ }
+ .containing-block {
+ background-color: #03a9f4;
+ width: 700px;
+ }
+ .roboto-normal {
+ font-family: roboto-face;
+ font-weight: normal;
+ }
+ .roboto-bold {
+ font-family: roboto-face;
+ font-weight: bold;
+ }
+ .noto-serif-normal {
+ font-family: serif-face;
+ font-weight: normal;
+ }
+ .noto-serif-bold {
+ font-family: serif-face;
+ font-weight: bold;
+ }
+ .dancing-script-normal {
+ font-family: dancing-script-face;
+ font-weight: normal;
+ }
+ .dancing-script-bold {
+ font-family: dancing-script-face;
+ font-weight: bold;
+ }
+ @font-face {
+ font-family: roboto-face;
+ src: local(Roboto-Regular);
+ }
+ @font-face {
+ font-family: serif-face;
+ src: local(NotoSerif);
+ }
+ @font-face {
+ font-family: dancing-script-face;
+ src: local(DancingScript);
+ }
+ </style>
+</head>
+<body>
+ <div class="containing-block">
+ <div class="roboto-normal">The Hegemony Consul</div>
+ <div class="roboto-bold">The Hegemony Consul</div>
+ <div class="noto-serif-normal">The Hegemony Consul</div>
+ <div class="noto-serif-bold">The Hegemony Consul</div>
+ <div class="dancing-script-normal">The Hegemony Consul</div>
+ <div class="dancing-script-bold">The Hegemony Consul</div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs-expected.png
index 3a2da46..2afbcce 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs.html b/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs.html
index e17019c..ba5bc69 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs.html
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs.html
@@ -10,7 +10,7 @@
margin: 0;
font-family: Roboto;
font-size: 75px;
- font-weight: bold;
+ font-weight: normal;
color: #fff;
}
.absolute-block1 {
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt
index b54cb76..8363eef 100644
--- a/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt
@@ -114,7 +114,7 @@
# Synchronous
responsetype.html,DISABLE
responsexml-basic.htm,FAIL
-responsexml-document-properties.htm,FAIL
+# js_error: responsexml-document-properties.htm,FAIL
responsexml-media-type.htm,FAIL
responsexml-non-document-types.htm,PASS
# Synchronous
@@ -135,7 +135,7 @@
send-authentication-basic-setrequestheader.htm,FAIL
send-authentication-competing-names-passwords.htm,FAIL
send-authentication-cors-basic-setrequestheader.htm,FAIL
-send-authentication-existing-session-manual.htm,FAIL
+# js_error: send-authentication-existing-session-manual.htm,FAIL
# Synchronous
send-authentication-prompt-2-manual.htm,FAIL
send-authentication-prompt-manual.htm,FAIL
@@ -150,7 +150,7 @@
send-entity-body-basic.htm,FAIL
send-entity-body-document-bogus.htm,FAIL
# SyntaxError: Unexpected EOF
-send-entity-body-document.htm,FAIL
+# js_error: send-entity-body-document.htm,FAIL
send-entity-body-empty.htm,FAIL
send-entity-body-get-head-async.htm,PASS
send-entity-body-get-head.htm,FAIL
@@ -211,14 +211,14 @@
xmlhttprequest-network-error.htm,PASS
xmlhttprequest-network-error-sync.htm,FAIL
# Not sure why these fail. Seems to break the harness.
-xmlhttprequest-timeout-aborted.html,FAIL
-xmlhttprequest-timeout-abortedonmain.html,FAIL
-xmlhttprequest-timeout-overridesexpires.html,FAIL
-xmlhttprequest-timeout-overrides.html,FAIL
-xmlhttprequest-timeout-simple.html,FAIL
+# js_error: xmlhttprequest-timeout-aborted.html,FAIL
+# js_error: xmlhttprequest-timeout-abortedonmain.html,FAIL
+# js_error: xmlhttprequest-timeout-overridesexpires.html,FAIL
+# js_error: xmlhttprequest-timeout-overrides.html,FAIL
+# js_error: xmlhttprequest-timeout-simple.html,FAIL
# Synchronous
-xmlhttprequest-timeout-synconmain.html,FAIL
-xmlhttprequest-timeout-twice.html,FAIL
+# js_error: xmlhttprequest-timeout-synconmain.html,FAIL
+# js_error: xmlhttprequest-timeout-twice.html,FAIL
# Workers are disabled.
xmlhttprequest-timeout-worker-aborted.html,DISABLE
xmlhttprequest-timeout-worker-overridesexpires.html,DISABLE
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/dom/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/dom/web_platform_tests.txt
index 52e488f..4351f85 100644
--- a/src/cobalt/layout_tests/testdata/web-platform-tests/dom/web_platform_tests.txt
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/dom/web_platform_tests.txt
@@ -5,19 +5,16 @@
nodes/MutationObserver-attributes.html,FAIL,attributes Element.setAttributeNS: creation mutation
nodes/MutationObserver-attributes.html,FAIL,attributes Element.setAttributeNS: prefixed attribute creation mutation
nodes/MutationObserver-attributes.html,FAIL,attributes Element.setAttributeNS: removal mutation
-# TODO: Triage these failures.
+# Sets Element.localName, which isn't implemented in Cobalt.
nodes/MutationObserver-attributes.html,FAIL,attributes Element.attributes.value: update mutation
+# Input element is not supported in Cobalt
nodes/MutationObserver-attributes.html,FAIL,attributes HTMLInputElement.type: type update mutation
-nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.add: same value mutation
-nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.remove: missing token removal mutation
+# Cobalt doesn't implement DomTokenList.toggle
nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: token removal mutation
nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: token addition mutation
nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced token removal mutation
-nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced token removal no mutation
nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced missing token removal no mutation
nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced existing token addition no mutation
-nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced existing token removal no mutation
-nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced token addition no mutation
nodes/MutationObserver-attributes.html,FAIL,attributes Element.classList.toggle: forced token addition mutation
# Most of these tests use operations such as insertData, replaceData, etc.,
@@ -28,21 +25,15 @@
nodes/MutationObserver-characterData.html,PASS,characterData/characterDataOldValue alone Text.data: simple mutation
nodes/MutationObserver-childList.html,PASS
-# Node.textContent tests fail because set_text_content doesn't run the
-# replace-all algorithm exactly.
-# TODO: Fix this.
-nodes/MutationObserver-childList.html,FAIL,childList Node.textContent: replace content mutation
# Cobalt doesn't implement Node.normalize.
nodes/MutationObserver-childList.html,FAIL,childList Node.normalize mutation
nodes/MutationObserver-childList.html,FAIL,childList Node.normalize mutations
-# TODO: Investigate failures.
-nodes/MutationObserver-childList.html,FAIL,childList Node.insertBefore: addition mutation
-nodes/MutationObserver-childList.html,FAIL,childList Node.insertBefore: removal and addition mutations
nodes/MutationObserver-disconnect.html,PASS
nodes/MutationObserver-document.html,PASS
-# TODO: Investigate failures.
+# These fail because Cobalt doesn't support Event Loop, and MutationObserver spec relies on the concept of
+# queuing a microtask to ensure that queued events get dispatched at the correct time.
nodes/MutationObserver-document.html,FAIL,parser insertion mutations
nodes/MutationObserver-document.html,FAIL,parser script insertion mutation
nodes/MutationObserver-document.html,FAIL,removal of parent during parsing
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/mediasession/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/mediasession/web_platform_tests.txt
new file mode 100644
index 0000000..ab64abb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/mediasession/web_platform_tests.txt
@@ -0,0 +1,6 @@
+# MediaSession tests
+# TODO
+# idlharness.html
+# mediametadata.html
+# js_error: playbackstate.html,PASS
+setactionhandler.html,PASS
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc
index b491caf..5794fa7 100644
--- a/src/cobalt/layout_tests/web_platform_tests.cc
+++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -23,7 +23,6 @@
#include "base/run_loop.h"
#include "base/string_util.h"
#include "base/values.h"
-#include "cobalt/browser/memory_settings/memory_settings.h"
#include "cobalt/browser/web_module.h"
#include "cobalt/dom/csp_delegate_factory.h"
#include "cobalt/layout_tests/test_utils.h"
@@ -134,7 +133,7 @@
message_loop->PostTask(FROM_HERE, base::Bind(Quit, run_loop));
}
-std::string RunWebPlatformTest(const GURL& url) {
+std::string RunWebPlatformTest(const GURL& url, bool* got_results) {
LogFilter log_filter;
for (size_t i = 0; i < arraysize(kLogSuppressions); ++i) {
log_filter.Add(kLogSuppressions[i]);
@@ -164,7 +163,7 @@
dom::kCspEnforcementEnable, CspDelegatePermissive::Create);
// Use test runner mode to allow the content itself to dictate when it is
// ready for layout should be performed. See cobalt/dom/test_runner.h.
- browser::WebModule::Options web_module_options(kDefaultViewportSize);
+ browser::WebModule::Options web_module_options;
web_module_options.layout_trigger = layout::LayoutManager::kTestRunnerMode;
// Prepare a slot for our results to be placed when ready.
@@ -176,14 +175,17 @@
url, base::Bind(&WebModuleOnRenderTreeProducedCallback, &results,
&run_loop, MessageLoop::current()),
base::Bind(&WebModuleErrorCallback, &run_loop, MessageLoop::current()),
- base::Closure() /* window_close_callback */, media_module.get(),
- &network_module, kDefaultViewportSize, &resource_provider,
- media_module->system_window(), 60.0f, web_module_options);
+ base::Closure() /* window_close_callback */,
+ base::Closure() /* window_minimize_callback */,
+ media_module.get(), &network_module, kDefaultViewportSize,
+ &resource_provider, media_module->system_window(), 60.0f,
+ web_module_options);
run_loop.Run();
const std::string extract_results =
"document.getElementById(\"__testharness__results__\").textContent;";
std::string output = web_module.ExecuteJavascript(
- extract_results, base::SourceLocation(__FILE__, __LINE__, 1));
+ extract_results, base::SourceLocation(__FILE__, __LINE__, 1),
+ got_results);
return output;
}
@@ -274,7 +276,9 @@
std::cout << "(" << test_url << ")" << std::endl;
- std::string json_results = RunWebPlatformTest(test_url);
+ bool got_results = false;
+ std::string json_results = RunWebPlatformTest(test_url, &got_results);
+ EXPECT_TRUE(got_results);
std::vector<TestResult> results = ParseResults(json_results);
for (size_t i = 0; i < results.size(); ++i) {
const WebPlatformTestInfo& test_info = GetParam();
@@ -302,6 +306,10 @@
INSTANTIATE_TEST_CASE_P(dom, WebPlatformTest,
::testing::ValuesIn(EnumerateWebPlatformTests("dom")));
+
+INSTANTIATE_TEST_CASE_P(
+ mediasession, WebPlatformTest,
+ ::testing::ValuesIn(EnumerateWebPlatformTests("mediasession")));
#endif // !defined(COBALT_WIN)
} // namespace layout_tests
diff --git a/src/cobalt/loader/font/typeface_decoder.cc b/src/cobalt/loader/font/typeface_decoder.cc
index cd41c09..8736541 100644
--- a/src/cobalt/loader/font/typeface_decoder.cc
+++ b/src/cobalt/loader/font/typeface_decoder.cc
@@ -26,14 +26,13 @@
success_callback_(success_callback),
error_callback_(error_callback),
is_raw_data_too_large_(false),
- suspended_(false) {
- DCHECK(resource_provider_);
+ is_suspended_(!resource_provider_) {
DCHECK(!success_callback_.is_null());
DCHECK(!error_callback_.is_null());
}
void TypefaceDecoder::DecodeChunk(const char* data, size_t size) {
- if (suspended_) {
+ if (is_suspended_) {
DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
return;
}
@@ -65,7 +64,7 @@
}
void TypefaceDecoder::Finish() {
- if (suspended_) {
+ if (is_suspended_) {
DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
return;
}
@@ -75,12 +74,6 @@
return;
}
- if (!resource_provider_) {
- error_callback_.Run(
- "No resource provider was passed to the TypefaceDecoder.");
- return;
- }
-
std::string error_string;
scoped_refptr<render_tree::Typeface> decoded_typeface =
resource_provider_->CreateTypefaceFromRawData(raw_data_.Pass(),
@@ -96,21 +89,24 @@
void TypefaceDecoder::ReleaseRawData() { raw_data_.reset(); }
bool TypefaceDecoder::Suspend() {
- DCHECK(!suspended_);
- suspended_ = true;
- is_raw_data_too_large_ = false;
+ DCHECK(!is_suspended_);
+ DCHECK(resource_provider_);
+
+ is_suspended_ = true;
resource_provider_ = NULL;
+
+ is_raw_data_too_large_ = false;
ReleaseRawData();
+
return true;
}
void TypefaceDecoder::Resume(render_tree::ResourceProvider* resource_provider) {
- if (!suspended_) {
- DCHECK_EQ(resource_provider_, resource_provider);
- return;
- }
+ DCHECK(is_suspended_);
+ DCHECK(!resource_provider_);
+ DCHECK(resource_provider);
- suspended_ = false;
+ is_suspended_ = false;
resource_provider_ = resource_provider;
}
diff --git a/src/cobalt/loader/font/typeface_decoder.h b/src/cobalt/loader/font/typeface_decoder.h
index 700e1dd..0824691 100644
--- a/src/cobalt/loader/font/typeface_decoder.h
+++ b/src/cobalt/loader/font/typeface_decoder.h
@@ -56,7 +56,7 @@
scoped_ptr<render_tree::ResourceProvider::RawTypefaceDataVector> raw_data_;
bool is_raw_data_too_large_;
- bool suspended_;
+ bool is_suspended_;
};
} // namespace font
diff --git a/src/cobalt/loader/image/animated_image_tracker.cc b/src/cobalt/loader/image/animated_image_tracker.cc
new file mode 100644
index 0000000..25280aa
--- /dev/null
+++ b/src/cobalt/loader/image/animated_image_tracker.cc
@@ -0,0 +1,98 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/loader/image/animated_image_tracker.h"
+
+#include <algorithm>
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+AnimatedImageTracker::AnimatedImageTracker(
+ base::ThreadPriority animated_image_decode_thread_priority)
+ : animated_image_decode_thread_("AnimatedImage") {
+ base::Thread::Options options(MessageLoop::TYPE_DEFAULT,
+ 0 /* default stack size */,
+ animated_image_decode_thread_priority);
+ animated_image_decode_thread_.StartWithOptions(options);
+}
+
+AnimatedImageTracker::~AnimatedImageTracker() {
+ animated_image_decode_thread_.Stop();
+}
+
+void AnimatedImageTracker::IncreaseURLCount(const GURL& url) {
+ current_url_counts_[url]++;
+}
+
+void AnimatedImageTracker::DecreaseURLCount(const GURL& url) {
+ DCHECK_GT(current_url_counts_[url], 0);
+ current_url_counts_[url]--;
+}
+
+void AnimatedImageTracker::RecordImage(
+ const GURL& url, loader::image::AnimatedImage* animated_image) {
+ DCHECK(animated_image);
+ image_map_[url] = animated_image;
+}
+
+void AnimatedImageTracker::ProcessRecordedImages() {
+ for (URLToIntMap::iterator it = current_url_counts_.begin();
+ it != current_url_counts_.end();) {
+ const GURL& url = it->first;
+ URLSet::iterator playing_url = playing_urls_.find(url);
+ if (it->second == 0) {
+ // Remove the images that are no longer displayed, and stop the
+ // animations.
+ if (playing_url != playing_urls_.end()) {
+ playing_urls_.erase(playing_url);
+ OnDisplayEnd(image_map_[url]);
+ }
+ image_map_.erase(url);
+ previous_url_counts_.erase(url);
+ current_url_counts_.erase(it++);
+ } else {
+ // Record the images that start to be displayed, and start the
+ // animations.
+ if (playing_url == playing_urls_.end()) {
+ URLToImageMap::iterator playing_image = image_map_.find(url);
+ if (playing_image != image_map_.end()) {
+ playing_urls_.insert(std::make_pair(url, base::Unused()));
+ OnDisplayStart(playing_image->second);
+ }
+ }
+ previous_url_counts_[url] = it->second;
+ ++it;
+ }
+ }
+}
+
+void AnimatedImageTracker::OnDisplayStart(
+ loader::image::AnimatedImage* animated_image) {
+ DCHECK(animated_image);
+ animated_image->Play(animated_image_decode_thread_.message_loop_proxy());
+}
+
+void AnimatedImageTracker::OnDisplayEnd(
+ loader::image::AnimatedImage* animated_image) {
+ DCHECK(animated_image);
+ animated_image->Stop();
+}
+
+} // namespace image
+} // namespace loader
+} // namespace cobalt
diff --git a/src/cobalt/loader/image/animated_image_tracker.h b/src/cobalt/loader/image/animated_image_tracker.h
new file mode 100644
index 0000000..6fdb115
--- /dev/null
+++ b/src/cobalt/loader/image/animated_image_tracker.h
@@ -0,0 +1,72 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_LOADER_IMAGE_ANIMATED_IMAGE_TRACKER_H_
+#define COBALT_LOADER_IMAGE_ANIMATED_IMAGE_TRACKER_H_
+
+#include <map>
+#include <string>
+
+#include "base/containers/small_map.h"
+#include "base/threading/thread.h"
+#include "cobalt/base/unused.h"
+#include "cobalt/loader/image/image.h"
+#include "googleurl/src/gurl.h"
+
+namespace cobalt {
+namespace loader {
+namespace image {
+
+// This class maintains the list of all images that are being displayed
+// at the current time. Whenever an animated image enters / exits the list, the
+// playing status is updated hence decoding is turned on / off for it.
+class AnimatedImageTracker {
+ public:
+ explicit AnimatedImageTracker(
+ base::ThreadPriority animated_image_decode_thread_priority);
+ ~AnimatedImageTracker();
+
+ // Updates the count of an image URL that is being displayed.
+ void IncreaseURLCount(const GURL& url);
+ void DecreaseURLCount(const GURL& url);
+
+ // Record an actual animated image that is linked to its URL.
+ void RecordImage(const GURL& url,
+ loader::image::AnimatedImage* animated_image);
+
+ // This indicates the update calls for the current frame have finished. All
+ // changes during two ProcessRecordedImages calls will be processed. It should
+ // be called at the end of a layout.
+ void ProcessRecordedImages();
+
+ private:
+ void OnDisplayStart(loader::image::AnimatedImage* image);
+ void OnDisplayEnd(loader::image::AnimatedImage* image);
+
+ typedef std::map<GURL, loader::image::AnimatedImage*> URLToImageMap;
+ typedef std::map<GURL, int> URLToIntMap;
+ typedef base::SmallMap<std::map<GURL, base::Unused>, 1> URLSet;
+
+ URLToImageMap image_map_;
+ URLToIntMap previous_url_counts_;
+ URLToIntMap current_url_counts_;
+ URLSet playing_urls_;
+ base::Thread animated_image_decode_thread_;
+};
+
+} // namespace image
+} // namespace loader
+} // namespace cobalt
+
+#endif // COBALT_LOADER_IMAGE_ANIMATED_IMAGE_TRACKER_H_
diff --git a/src/cobalt/loader/image/animated_webp_image.cc b/src/cobalt/loader/image/animated_webp_image.cc
index 25da0b1..a6a9373 100644
--- a/src/cobalt/loader/image/animated_webp_image.cc
+++ b/src/cobalt/loader/image/animated_webp_image.cc
@@ -17,6 +17,12 @@
#include "cobalt/loader/image/animated_webp_image.h"
#include "cobalt/loader/image/webp_image_decoder.h"
+#include "cobalt/render_tree/brush.h"
+#include "cobalt/render_tree/composition_node.h"
+#include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/node.h"
+#include "cobalt/render_tree/rect_node.h"
+#include "nb/memory_scope.h"
#include "starboard/memory.h"
namespace cobalt {
@@ -24,37 +30,59 @@
namespace image {
namespace {
-const int kPixelSize = 4;
const int kLoopInfinite = 0;
-
-inline uint32_t DivideBy255(uint32_t value) {
- return (value + 1 + (value >> 8)) >> 8;
-}
+const int kMinimumDelayInMilliseconds = 10;
} // namespace
AnimatedWebPImage::AnimatedWebPImage(
const math::Size& size, bool is_opaque,
+ render_tree::PixelFormat pixel_format,
render_tree::ResourceProvider* resource_provider)
- : thread_("AnimatedWebPImage Decoding"),
- size_(size),
+ : size_(size),
is_opaque_(is_opaque),
+ pixel_format_(pixel_format),
demux_(NULL),
demux_state_(WEBP_DEMUX_PARSING_HEADER),
+ received_first_frame_(false),
+ is_playing_(false),
frame_count_(0),
loop_count_(kLoopInfinite),
- background_color_(0),
- should_dispose_previous_frame_(false),
current_frame_index_(0),
next_frame_index_(0),
+ should_dispose_previous_frame_to_background_(false),
resource_provider_(resource_provider) {}
scoped_refptr<render_tree::Image> AnimatedWebPImage::GetFrame() {
base::AutoLock lock(lock_);
- return current_frame_image_;
+ return current_canvas_;
+}
+
+void AnimatedWebPImage::Play(
+ const scoped_refptr<base::MessageLoopProxy>& message_loop) {
+ base::AutoLock lock(lock_);
+
+ if (is_playing_) {
+ return;
+ }
+ is_playing_ = true;
+
+ message_loop_ = message_loop;
+ if (received_first_frame_) {
+ PlayInternal();
+ }
+}
+
+void AnimatedWebPImage::Stop() {
+ if (!decode_closure_.callback().is_null()) {
+ message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&AnimatedWebPImage::StopInternal, base::Unretained(this)));
+ }
}
void AnimatedWebPImage::AppendChunk(const uint8* data, size_t input_byte) {
+ TRACK_MEMORY_SCOPE("Rendering");
base::AutoLock lock(lock_);
data_buffer_.insert(data_buffer_.end(), data, data + input_byte);
@@ -66,131 +94,168 @@
// Update frame count.
int new_frame_count = WebPDemuxGetI(demux_, WEBP_FF_FRAME_COUNT);
if (new_frame_count > 0 && frame_count_ == 0) {
- // We've just received the first frame, start playing animation now.
+ // We've just received the first frame.
+ received_first_frame_ = true;
loop_count_ = WebPDemuxGetI(demux_, WEBP_FF_LOOP_COUNT);
- background_color_ = WebPDemuxGetI(demux_, WEBP_FF_BACKGROUND_COLOR);
- if (size_.width() > 0 && size_.height() > 0) {
- image_buffer_.resize(size_.width() * size_.height() * kPixelSize);
- FillImageBufferWithBackgroundColor();
- current_frame_time_ = base::TimeTicks::Now();
- current_frame_index_ = 0;
- thread_.Start();
- thread_.message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
+ // The default background color of the canvas in [Blue, Green, Red, Alpha]
+ // byte order. It is read in little endian order as an 32bit int.
+ uint32_t background_color = WebPDemuxGetI(demux_, WEBP_FF_BACKGROUND_COLOR);
+ background_color_ =
+ render_tree::ColorRGBA((background_color >> 16 & 0xff) / 255.0f,
+ (background_color >> 8 & 0xff) / 255.0f,
+ (background_color & 0xff) / 255.0f,
+ (background_color >> 24 & 0xff) / 255.0f);
+
+ if (is_playing_) {
+ PlayInternal();
}
}
frame_count_ = new_frame_count;
}
-void AnimatedWebPImage::DecodeFrames() {
- TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::DecodeFrames()");
+AnimatedWebPImage::~AnimatedWebPImage() {
+ Stop();
base::AutoLock lock(lock_);
+ WebPDemuxDelete(demux_);
+}
+
+void AnimatedWebPImage::StopInternal() {
+ is_playing_ = false;
+ decode_closure_.Cancel();
+}
+
+void AnimatedWebPImage::PlayInternal() {
+ current_frame_time_ = base::TimeTicks::Now();
+ DCHECK(message_loop_);
+ message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
+}
+
+void AnimatedWebPImage::DecodeFrames() {
+ TRACK_MEMORY_SCOPE("Rendering");
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::DecodeFrames()");
+ DCHECK(is_playing_ && received_first_frame_);
+ DCHECK(message_loop_->BelongsToCurrentThread());
+
+ if (decode_closure_.callback().is_null()) {
+ decode_closure_.Reset(
+ base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
+ }
UpdateTimelineInfo();
// Decode the frames from current frame to next frame and blend the results.
for (int frame_index = current_frame_index_ + 1;
frame_index <= next_frame_index_; ++frame_index) {
- // Dispose previous frame if necessary.
- if (should_dispose_previous_frame_) {
- FillImageBufferWithBackgroundColor();
- }
-
- // Decode the current frame.
- WebPIterator webp_iterator;
- WebPDemuxGetFrame(demux_, frame_index, &webp_iterator);
- if (!webp_iterator.complete) {
+ if (!DecodeOneFrame(frame_index)) {
break;
}
- WEBPImageDecoder webp_image_decoder(resource_provider_);
+ }
+ current_frame_index_ = next_frame_index_;
+
+ // Set up the next time to call the decode callback.
+ base::AutoLock lock(lock_);
+ if (is_playing_) {
+ base::TimeDelta delay = next_frame_time_ - base::TimeTicks::Now();
+ const base::TimeDelta min_delay =
+ base::TimeDelta::FromMilliseconds(kMinimumDelayInMilliseconds);
+ if (delay < min_delay) {
+ delay = min_delay;
+ }
+ message_loop_->PostDelayedTask(FROM_HERE, decode_closure_.callback(),
+ delay);
+ }
+}
+
+bool AnimatedWebPImage::DecodeOneFrame(int frame_index) {
+ TRACK_MEMORY_SCOPE("Rendering");
+ base::AutoLock lock(lock_);
+ WebPIterator webp_iterator;
+ WEBPImageDecoder webp_image_decoder(resource_provider_);
+ scoped_refptr<render_tree::Image> next_frame_image;
+
+ // Decode the current frame.
+ {
+ TRACE_EVENT0("cobalt::loader::image", "Decoding");
+
+ WebPDemuxGetFrame(demux_, frame_index, &webp_iterator);
+ if (!webp_iterator.complete) {
+ return false;
+ }
webp_image_decoder.DecodeChunk(webp_iterator.fragment.bytes,
webp_iterator.fragment.size);
if (!webp_image_decoder.FinishWithSuccess()) {
LOG(ERROR) << "Failed to decode WebP image frame.";
- break;
+ return false;
}
- // Alpha blend the current frame on top of previous frame.
- {
- TRACE_EVENT0("cobalt::loader::image", "Blending");
- DCHECK(!image_buffer_.empty());
- uint8_t* image_buffer_memory = &image_buffer_[0];
- uint8_t* next_frame_memory = webp_image_decoder.GetOriginalMemory();
- // Alpha-blending: Given that each of the R, G, B and A channels is 8-bit,
- // and the RGB channels are not premultiplied by alpha, the formula for
- // blending 'dst' onto 'src' is:
- // blend.A = src.A + dst.A * (1 - src.A / 255)
- // if blend.A = 0 then
- // blend.RGB = 0
- // else
- // blend.RGB = (src.RGB * src.A +
- // dst.RGB * dst.A * (1 - src.A / 255)) / blend.A
- // https://developers.google.com/speed/webp/docs/riff_container#animation
- for (int y = 0; y < webp_iterator.height; ++y) {
- uint8_t* src =
- next_frame_memory + (y * webp_iterator.width) * kPixelSize;
- uint8_t* dst = image_buffer_memory +
- ((y + webp_iterator.y_offset) * size_.width() +
- webp_iterator.x_offset) *
- kPixelSize;
- for (int x = 0; x < webp_iterator.width; ++x) {
- uint32_t dst_factor = dst[3] * (255 - src[3]);
- dst[3] = static_cast<uint8_t>(src[3] + DivideBy255(dst_factor));
- if (dst[3] == 0) {
- dst[0] = dst[1] = dst[2] = 0;
- } else {
- for (int i = 0; i < 3; ++i) {
- dst[i] = static_cast<uint8_t>(
- (src[i] * src[3] + DivideBy255(dst[i] * dst_factor)) /
- dst[3]);
- }
- }
-
- src += kPixelSize;
- dst += kPixelSize;
- }
- }
- }
-
- // Record the dispose method for current frame.
- should_dispose_previous_frame_ =
- webp_iterator.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND;
- WebPDemuxReleaseIterator(&webp_iterator);
- }
-
- // Generate the next frame render tree image if necessary.
- if (current_frame_index_ != next_frame_index_) {
- current_frame_index_ = next_frame_index_;
- scoped_ptr<render_tree::ImageData> next_frame_data = AllocateImageData();
+ scoped_ptr<render_tree::ImageData> next_frame_data = AllocateImageData(
+ math::Size(webp_iterator.width, webp_iterator.height));
DCHECK(next_frame_data);
- DCHECK(!image_buffer_.empty());
- uint8_t* image_buffer_memory = &image_buffer_[0];
- uint8_t* next_frame_memory = next_frame_data->GetMemory();
- SbMemoryCopy(next_frame_memory, image_buffer_memory,
- size_.width() * size_.height() * kPixelSize);
- current_frame_image_ =
- resource_provider_->CreateImage(next_frame_data.Pass());
+ {
+ TRACE_EVENT0("cobalt::loader::image", "SbMemoryCopy");
+ SbMemoryCopy(next_frame_data->GetMemory(),
+ webp_image_decoder.GetOriginalMemory(),
+ webp_iterator.width * webp_iterator.height *
+ render_tree::BytesPerPixel(pixel_format_));
+ }
+ next_frame_image = resource_provider_->CreateImage(next_frame_data.Pass());
}
- // Set up the next time to call the decode callback.
- base::TimeDelta delay = next_frame_time_ - base::TimeTicks::Now();
- if (delay < base::TimeDelta()) {
- delay = base::TimeDelta();
- }
- thread_.message_loop()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)),
- delay);
+ // Alpha blend the current frame on top of the buffer.
+ {
+ TRACE_EVENT0("cobalt::loader::image", "Blending");
- if (!decoded_callback_.is_null()) {
- decoded_callback_.Run();
+ render_tree::CompositionNode::Builder builder;
+ // Add the current canvas or, if there is not one, a background color
+ // rectangle;
+ if (current_canvas_) {
+ builder.AddChild(new render_tree::ImageNode(current_canvas_));
+ } else {
+ scoped_ptr<render_tree::Brush> brush(
+ new render_tree::SolidColorBrush(background_color_));
+ builder.AddChild(
+ new render_tree::RectNode(math::RectF(size_), brush.Pass()));
+ }
+ // Dispose previous frame by adding a solid rectangle.
+ if (should_dispose_previous_frame_to_background_) {
+ scoped_ptr<render_tree::Brush> brush(
+ new render_tree::SolidColorBrush(background_color_));
+ builder.AddChild(
+ new render_tree::RectNode(previous_frame_rect_, brush.Pass()));
+ }
+ // Add the current frame.
+ builder.AddChild(new render_tree::ImageNode(
+ next_frame_image,
+ math::Vector2dF(webp_iterator.x_offset, webp_iterator.y_offset)));
+
+ scoped_refptr<render_tree::Node> root =
+ new render_tree::CompositionNode(builder);
+
+ current_canvas_ = resource_provider_->DrawOffscreenImage(root);
}
+
+ if (webp_iterator.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
+ should_dispose_previous_frame_to_background_ = true;
+ previous_frame_rect_ =
+ math::RectF(webp_iterator.x_offset, webp_iterator.y_offset,
+ webp_iterator.width, webp_iterator.height);
+ } else if (webp_iterator.dispose_method == WEBP_MUX_DISPOSE_NONE) {
+ should_dispose_previous_frame_to_background_ = false;
+ } else {
+ NOTREACHED();
+ }
+
+ WebPDemuxReleaseIterator(&webp_iterator);
+ return true;
}
void AnimatedWebPImage::UpdateTimelineInfo() {
+ TRACK_MEMORY_SCOPE("Rendering");
+ base::AutoLock lock(lock_);
base::TimeTicks current_time = base::TimeTicks::Now();
next_frame_index_ = current_frame_index_ ? current_frame_index_ : 1;
while (true) {
@@ -224,29 +289,12 @@
}
}
-void AnimatedWebPImage::FillImageBufferWithBackgroundColor() {
- for (int y = 0; y < size_.height(); ++y) {
- for (int x = 0; x < size_.width(); ++x) {
- *reinterpret_cast<uint32_t*>(
- &image_buffer_[(y * size_.width() + x) * kPixelSize]) =
- background_color_;
- }
- }
-}
-
-AnimatedWebPImage::~AnimatedWebPImage() {
- WebPDemuxDelete(demux_);
- thread_.Stop();
-}
-
-scoped_ptr<render_tree::ImageData> AnimatedWebPImage::AllocateImageData() {
- render_tree::PixelFormat pixel_format = render_tree::kPixelFormatRGBA8;
- if (!resource_provider_->PixelFormatSupported(pixel_format)) {
- pixel_format = render_tree::kPixelFormatBGRA8;
- }
+scoped_ptr<render_tree::ImageData> AnimatedWebPImage::AllocateImageData(
+ const math::Size& size) {
+ TRACK_MEMORY_SCOPE("Rendering");
scoped_ptr<render_tree::ImageData> image_data =
resource_provider_->AllocateImageData(
- size_, pixel_format, render_tree::kAlphaFormatPremultiplied);
+ size, pixel_format_, render_tree::kAlphaFormatPremultiplied);
DCHECK(image_data) << "Failed to allocate image.";
return image_data.Pass();
}
diff --git a/src/cobalt/loader/image/animated_webp_image.h b/src/cobalt/loader/image/animated_webp_image.h
index 3f93216..e988519 100644
--- a/src/cobalt/loader/image/animated_webp_image.h
+++ b/src/cobalt/loader/image/animated_webp_image.h
@@ -20,13 +20,15 @@
#include <vector>
#include "base/basictypes.h"
-#include "base/callback.h"
+#include "base/cancelable_callback.h"
#include "base/debug/trace_event.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
#include "base/time.h"
#include "cobalt/loader/image/image.h"
+#include "cobalt/render_tree/color_rgba.h"
#include "cobalt/render_tree/resource_provider.h"
#include "third_party/libwebp/webp/demux.h"
@@ -37,6 +39,7 @@
class AnimatedWebPImage : public AnimatedImage {
public:
AnimatedWebPImage(const math::Size& size, bool is_opaque,
+ render_tree::PixelFormat pixel_format,
render_tree::ResourceProvider* resource_provider);
const math::Size& GetSize() const OVERRIDE { return size_; }
@@ -49,47 +52,56 @@
scoped_refptr<render_tree::Image> GetFrame() OVERRIDE;
- void AppendChunk(const uint8* data, size_t input_byte);
+ void Play(const scoped_refptr<base::MessageLoopProxy>& message_loop) OVERRIDE;
- void SetDecodedFramesCallback(const base::Closure& callback) {
- decoded_callback_ = callback;
- }
+ void Stop() OVERRIDE;
+
+ void AppendChunk(const uint8* data, size_t input_byte);
private:
~AnimatedWebPImage() OVERRIDE;
+ // To be called the decoding thread, to cancel future decodings.
+ void StopInternal();
+
+ void PlayInternal();
+
+ // Decodes all frames until current time.
void DecodeFrames();
+ // Decodes the frame with the given index, returns if it succeeded.
+ bool DecodeOneFrame(int frame_index);
+
// Updates the index and time info of the current and next frames.
void UpdateTimelineInfo();
- void FillImageBufferWithBackgroundColor();
+ scoped_ptr<render_tree::ImageData> AllocateImageData(const math::Size& size);
- scoped_ptr<render_tree::ImageData> AllocateImageData();
-
- base::Thread thread_;
const math::Size size_;
const bool is_opaque_;
+ const render_tree::PixelFormat pixel_format_;
WebPDemuxer* demux_;
WebPDemuxState demux_state_;
+ bool received_first_frame_;
+ bool is_playing_;
int frame_count_;
// The remaining number of times to loop the animation. kLoopInfinite means
// looping infinitely.
int loop_count_;
- uint32_t background_color_;
- bool should_dispose_previous_frame_;
int current_frame_index_;
int next_frame_index_;
+ bool should_dispose_previous_frame_to_background_;
render_tree::ResourceProvider* resource_provider_;
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+ render_tree::ColorRGBA background_color_;
+ math::RectF previous_frame_rect_;
+ base::CancelableClosure decode_closure_;
base::TimeTicks current_frame_time_;
base::TimeTicks next_frame_time_;
// The original encoded data.
std::vector<uint8> data_buffer_;
- // The alpha-blended frame.
- std::vector<uint8> image_buffer_;
- scoped_refptr<render_tree::Image> current_frame_image_;
- base::Closure decoded_callback_;
+ scoped_refptr<render_tree::Image> current_canvas_;
base::Lock lock_;
};
diff --git a/src/cobalt/loader/image/image.h b/src/cobalt/loader/image/image.h
index 2330a45..feb1bd3 100644
--- a/src/cobalt/loader/image/image.h
+++ b/src/cobalt/loader/image/image.h
@@ -18,6 +18,7 @@
#define COBALT_LOADER_IMAGE_IMAGE_H_
#include "base/memory/ref_counted.h"
+#include "base/message_loop_proxy.h"
#include "base/time.h"
#include "cobalt/math/size_f.h"
#include "cobalt/math/transform_2d.h"
@@ -83,6 +84,14 @@
// Get the current frame. Implementation should be thread safe.
virtual scoped_refptr<render_tree::Image> GetFrame() = 0;
+ // Start playing the animation, decoding on the given message loop.
+ // Implementation should be thread safe.
+ virtual void Play(
+ const scoped_refptr<base::MessageLoopProxy>& message_loop) = 0;
+
+ // Stop playing the animation.
+ virtual void Stop() = 0;
+
// This callback is intended to be used in a render_tree::AnimateNode.
void AnimateCallback(const math::RectF& destination_rect,
const math::Matrix3F& local_transform,
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc
index 9c2b351..831a574 100644
--- a/src/cobalt/loader/image/image_decoder.cc
+++ b/src/cobalt/loader/image/image_decoder.cc
@@ -19,11 +19,11 @@
#include "base/debug/trace_event.h"
#include "cobalt/loader/image/dummy_gif_image_decoder.h"
#include "cobalt/loader/image/image_decoder_starboard.h"
-#if defined(__LB_PS3__)
+#if defined(USE_PS3_JPEG_IMAGE_DECODER)
#include "cobalt/loader/image/jpeg_image_decoder_ps3.h"
-#else // defined(__LB_PS3__)
+#else // defined(USE_PS3_JPEG_IMAGE_DECODER)
#include "cobalt/loader/image/jpeg_image_decoder.h"
-#endif // defined(__LB_PS3__)
+#endif // defined(USE_PS3_JPEG_IMAGE_DECODER)
#include "cobalt/loader/image/png_image_decoder.h"
#include "cobalt/loader/image/stub_image_decoder.h"
#include "cobalt/loader/image/webp_image_decoder.h"
@@ -83,14 +83,10 @@
: resource_provider_(resource_provider),
success_callback_(success_callback),
error_callback_(error_callback),
- state_(kWaitingForHeader),
+ state_(resource_provider_ ? kWaitingForHeader : kSuspended),
is_deletion_pending_(false) {
TRACE_EVENT0("cobalt::loader::image", "ImageDecoder::ImageDecoder()");
signature_cache_.position = 0;
-
- if (!resource_provider_) {
- state_ = kNoResourceProvider;
- }
}
LoadResponseType ImageDecoder::OnResponseStarted(
@@ -197,9 +193,6 @@
case kUnsupportedImageFormat:
error_callback_.Run("Unsupported image format.");
break;
- case kNoResourceProvider:
- error_callback_.Run("No resource provider was passed to the decoder.");
- break;
case kSuspended:
DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
break;
@@ -212,6 +205,9 @@
bool ImageDecoder::Suspend() {
TRACE_EVENT0("cobalt::loader::image", "ImageDecoder::Suspend()");
+ DCHECK_NE(state_, kSuspended);
+ DCHECK(resource_provider_);
+
if (state_ == kDecoding) {
DCHECK(decoder_ || base::subtle::Acquire_Load(&is_deletion_pending_));
decoder_.reset();
@@ -224,17 +220,12 @@
void ImageDecoder::Resume(render_tree::ResourceProvider* resource_provider) {
TRACE_EVENT0("cobalt::loader::image", "ImageDecoder::Resume()");
- if (state_ != kSuspended) {
- DCHECK_EQ(resource_provider_, resource_provider);
- return;
- }
+ DCHECK_EQ(state_, kSuspended);
+ DCHECK(!resource_provider_);
+ DCHECK(resource_provider);
state_ = kWaitingForHeader;
resource_provider_ = resource_provider;
-
- if (!resource_provider_) {
- state_ = kNoResourceProvider;
- }
}
void ImageDecoder::SetDeletionPending() {
@@ -268,7 +259,6 @@
case kNotApplicable:
case kUnsupportedImageFormat:
case kSuspended:
- case kNoResourceProvider:
default: {
// Do not attempt to continue processing data.
DCHECK(!decoder_);
@@ -347,13 +337,13 @@
return make_scoped_ptr<ImageDataDecoder>(
new StubImageDecoder(resource_provider));
} else if (image_type == kImageTypeJPEG) {
-#if defined(__LB_PS3__)
+#if defined(USE_PS3_JPEG_IMAGE_DECODER)
return make_scoped_ptr<ImageDataDecoder>(
new JPEGImageDecoderPS3(resource_provider));
-#else // defined(__LB_PS3__)
+#else // defined(USE_PS3_JPEG_IMAGE_DECODER)
return make_scoped_ptr<ImageDataDecoder>(
new JPEGImageDecoder(resource_provider));
-#endif // defined(__LB_PS3__)
+#endif // defined(USE_PS3_JPEG_IMAGE_DECODER)
} else if (image_type == kImageTypePNG) {
return make_scoped_ptr<ImageDataDecoder>(
new PNGImageDecoder(resource_provider));
diff --git a/src/cobalt/loader/image/image_decoder.h b/src/cobalt/loader/image/image_decoder.h
index c4b03a3..2127a07 100644
--- a/src/cobalt/loader/image/image_decoder.h
+++ b/src/cobalt/loader/image/image_decoder.h
@@ -66,7 +66,6 @@
kDecoding,
kNotApplicable,
kUnsupportedImageFormat,
- kNoResourceProvider,
kSuspended,
};
@@ -90,6 +89,7 @@
State state_;
std::string error_message_;
std::string mime_type_;
+
// Whether or not there is a pending task deleting this decoder on a message
// loop.
base::subtle::Atomic32 is_deletion_pending_;
diff --git a/src/cobalt/loader/image/image_decoder_starboard.cc b/src/cobalt/loader/image/image_decoder_starboard.cc
index 34b5e58..a0518c0 100644
--- a/src/cobalt/loader/image/image_decoder_starboard.cc
+++ b/src/cobalt/loader/image/image_decoder_starboard.cc
@@ -23,7 +23,7 @@
#include "starboard/decode_target.h"
#include "starboard/image.h"
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
namespace cobalt {
namespace loader {
@@ -35,7 +35,11 @@
: ImageDataDecoder(resource_provider),
mime_type_(mime_type),
format_(format),
+#if SB_API_VERSION >= 4
+ provider_(resource_provider->GetSbDecodeTargetGraphicsContextProvider()),
+#else // #if SB_API_VERSION >= 4
provider_(resource_provider->GetSbDecodeTargetProvider()),
+#endif // #if SB_API_VERSION >= 4
target_(kSbDecodeTargetInvalid) {
TRACE_EVENT0("cobalt::loader::image",
"ImageDecoderStarboard::ImageDecoderStarboard()");
@@ -71,6 +75,6 @@
} // namespace loader
} // namespace cobalt
-#endif // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
#endif // #if defined(STARBOARD)
diff --git a/src/cobalt/loader/image/image_decoder_starboard.h b/src/cobalt/loader/image/image_decoder_starboard.h
index 0132a1b..2cc98ef 100644
--- a/src/cobalt/loader/image/image_decoder_starboard.h
+++ b/src/cobalt/loader/image/image_decoder_starboard.h
@@ -51,7 +51,11 @@
const char* mime_type_;
SbDecodeTargetFormat format_;
std::vector<uint8> buffer_;
+#if SB_API_VERSION >= 4
+ SbDecodeTargetGraphicsContextProvider* provider_;
+#else // #if SB_API_VERSION >= 4
SbDecodeTargetProvider* provider_;
+#endif // #if SB_API_VERSION >= 4
SbDecodeTarget target_;
};
diff --git a/src/cobalt/loader/image/image_decoder_test.cc b/src/cobalt/loader/image/image_decoder_test.cc
index 7466799b..7f995fc 100644
--- a/src/cobalt/loader/image/image_decoder_test.cc
+++ b/src/cobalt/loader/image/image_decoder_test.cc
@@ -21,7 +21,7 @@
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
-#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
#include "cobalt/base/cobalt_paths.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/loader/image/animated_webp_image.h"
@@ -652,8 +652,6 @@
CheckSameColor(pixels, size.width(), size.height(), expected_color));
}
-void SignalWaitable(base::WaitableEvent* event) { event->Signal(); }
-
// Test that we can properly decode animated WEBP image.
TEST(ImageDecoderTest, DecodeAnimatedWEBPImage) {
MockImageDecoder image_decoder;
@@ -669,10 +667,11 @@
image_decoder.image().get());
ASSERT_TRUE(animated_webp_image);
- base::WaitableEvent decoded_frames_event(true, false);
- animated_webp_image->SetDecodedFramesCallback(
- base::Bind(&SignalWaitable, &decoded_frames_event));
- decoded_frames_event.Wait();
+ base::Thread thread("AnimatedWebP test");
+ thread.Start();
+ animated_webp_image->Play(thread.message_loop_proxy());
+ animated_webp_image->Stop();
+ thread.Stop();
// The image should contain the whole undecoded data from the file.
EXPECT_EQ(3224674u, animated_webp_image->GetEstimatedSizeInBytes());
@@ -699,10 +698,11 @@
image_decoder.image().get());
ASSERT_TRUE(animated_webp_image);
- base::WaitableEvent decoded_frames_event(true, false);
- animated_webp_image->SetDecodedFramesCallback(
- base::Bind(&SignalWaitable, &decoded_frames_event));
- decoded_frames_event.Wait();
+ base::Thread thread("AnimatedWebP test");
+ thread.Start();
+ animated_webp_image->Play(thread.message_loop_proxy());
+ animated_webp_image->Stop();
+ thread.Stop();
// The image should contain the whole undecoded data from the file.
EXPECT_EQ(3224674u, animated_webp_image->GetEstimatedSizeInBytes());
diff --git a/src/cobalt/loader/image/jpeg_image_decoder.cc b/src/cobalt/loader/image/jpeg_image_decoder.cc
index 274a133..10ca062 100644
--- a/src/cobalt/loader/image/jpeg_image_decoder.cc
+++ b/src/cobalt/loader/image/jpeg_image_decoder.cc
@@ -18,6 +18,7 @@
#include "base/debug/trace_event.h"
#include "base/logging.h"
+#include "nb/memory_scope.h"
#include "third_party/libjpeg/jpegint.h"
namespace cobalt {
@@ -83,6 +84,7 @@
render_tree::ResourceProvider* resource_provider)
: ImageDataDecoder(resource_provider) {
TRACE_EVENT0("cobalt::loader::image", "JPEGImageDecoder::JPEGImageDecoder()");
+ TRACK_MEMORY_SCOPE("Rendering");
memset(&info_, 0, sizeof(info_));
memset(&source_manager_, 0, sizeof(source_manager_));
@@ -110,6 +112,7 @@
size_t JPEGImageDecoder::DecodeChunkInternal(const uint8* data,
size_t input_byte) {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image",
"JPEGImageDecoder::DecodeChunkInternal()");
// |client_data| is available for use by application.
@@ -176,6 +179,7 @@
}
bool JPEGImageDecoder::ReadHeader() {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image", "JPEGImageDecoder::ReadHeader()");
if (jpeg_read_header(&info_, true) == JPEG_SUSPENDED) {
// Since |jpeg_read_header| doesn't have enough data, go back to the state
@@ -194,6 +198,7 @@
}
bool JPEGImageDecoder::StartDecompress() {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image", "JPEGImageDecoder::StartDecompress()");
// jpeg_has_multiple_scans() returns TRUE if the incoming image file has more
// than one scan.
@@ -221,6 +226,7 @@
// TODO: support displaying the low resolution image while decoding
// the progressive JPEG.
bool JPEGImageDecoder::DecodeProgressiveJPEG() {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image",
"JPEGImageDecoder::DecodeProgressiveJPEG()");
int status;
@@ -299,6 +305,8 @@
}
bool JPEGImageDecoder::ReadLines() {
+ TRACK_MEMORY_SCOPE("Rendering");
+
TRACE_EVENT0("cobalt::loader::image", "JPEGImageDecoder::ReadLines()");
// Creation of 2-D sample arrays which is for one row.
diff --git a/src/cobalt/loader/image/png_image_decoder.cc b/src/cobalt/loader/image/png_image_decoder.cc
index 75921cc..9e9f602 100644
--- a/src/cobalt/loader/image/png_image_decoder.cc
+++ b/src/cobalt/loader/image/png_image_decoder.cc
@@ -16,6 +16,7 @@
#include "base/debug/trace_event.h"
#include "base/logging.h"
+#include "nb/memory_scope.h"
namespace cobalt {
namespace loader {
@@ -71,6 +72,7 @@
info_(NULL),
has_alpha_(false),
interlace_buffer_(0) {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image", "PNGImageDecoder::PNGImageDecoder()");
png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, DecodingFailed,
@@ -81,6 +83,7 @@
}
size_t PNGImageDecoder::DecodeChunkInternal(const uint8* data, size_t size) {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image",
"PNGImageDecoder::DecodeChunkInternal()");
// int setjmp(jmp_buf env) saves the current environment (ths program state),
@@ -130,6 +133,7 @@
// Called when we have obtained the header information (including the size).
// static
void PNGImageDecoder::HeaderAvailable(png_structp png, png_infop info) {
+ TRACK_MEMORY_SCOPE("Rendering");
UNREFERENCED_PARAMETER(info);
TRACE_EVENT0("cobalt::loader::image", "PNGImageDecoder::~PNGImageDecoder()");
PNGImageDecoder* decoder =
@@ -150,6 +154,7 @@
// Called when decoding is done.
// static
void PNGImageDecoder::DecodeDone(png_structp png, png_infop info) {
+ TRACK_MEMORY_SCOPE("Rendering");
UNREFERENCED_PARAMETER(info);
TRACE_EVENT0("cobalt::loader::image", "PNGImageDecoder::DecodeDone()");
@@ -159,6 +164,7 @@
}
void PNGImageDecoder::HeaderAvailableCallback() {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image",
"PNGImageDecoder::HeaderAvailableCallback()");
DCHECK_EQ(state(), kWaitingForHeader);
@@ -278,6 +284,7 @@
// from the previous pass.
void PNGImageDecoder::RowAvailableCallback(png_bytep row_buffer,
png_uint_32 row_index) {
+ TRACK_MEMORY_SCOPE("Rendering");
DCHECK_EQ(state(), kReadLines);
// Nothing to do if the row is unchanged, or the row is outside
diff --git a/src/cobalt/loader/image/webp_image_decoder.cc b/src/cobalt/loader/image/webp_image_decoder.cc
index f2ae873..74afb79 100644
--- a/src/cobalt/loader/image/webp_image_decoder.cc
+++ b/src/cobalt/loader/image/webp_image_decoder.cc
@@ -17,6 +17,7 @@
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "cobalt/loader/image/animated_webp_image.h"
+#include "nb/memory_scope.h"
#include "starboard/memory.h"
namespace cobalt {
@@ -28,6 +29,7 @@
: ImageDataDecoder(resource_provider),
internal_decoder_(NULL),
has_animation_(false) {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image", "WEBPImageDecoder::WEBPImageDecoder()");
// Initialize the configuration as empty.
WebPInitDecoderConfig(&config_);
@@ -53,6 +55,7 @@
size_t WEBPImageDecoder::DecodeChunkInternal(const uint8* data,
size_t input_byte) {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image",
"WEBPImageDecoder::DecodeChunkInternal()");
if (state() == kWaitingForHeader) {
@@ -73,7 +76,7 @@
has_animation_ = true;
animated_webp_image_ = new AnimatedWebPImage(
math::Size(config_.input.width, config_.input.height),
- !!config_.input.has_alpha, resource_provider());
+ !!config_.input.has_alpha, pixel_format(), resource_provider());
}
set_state(kReadLines);
}
@@ -110,6 +113,7 @@
}
bool WEBPImageDecoder::ReadHeader(const uint8* data, size_t size) {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image", "WEBPImageDecoder::ReadHeader()");
// Retrieve features from the bitstream. The *features structure is filled
// with information gathered from the bitstream.
@@ -130,9 +134,12 @@
}
bool WEBPImageDecoder::CreateInternalDecoder(bool has_alpha) {
+ TRACK_MEMORY_SCOPE("Rendering");
TRACE_EVENT0("cobalt::loader::image",
"WEBPImageDecoder::CreateInternalDecoder()");
- config_.output.colorspace = has_alpha ? MODE_rgbA : MODE_RGBA;
+ config_.output.colorspace = pixel_format() == render_tree::kPixelFormatRGBA8
+ ? (has_alpha ? MODE_rgbA : MODE_RGBA)
+ : (has_alpha ? MODE_bgrA : MODE_BGRA);
config_.output.u.RGBA.stride = image_data()->GetDescriptor().pitch_in_bytes;
config_.output.u.RGBA.size =
static_cast<size_t>(config_.output.u.RGBA.stride *
diff --git a/src/cobalt/loader/loader.cc b/src/cobalt/loader/loader.cc
index 514f48e..d5e453d 100644
--- a/src/cobalt/loader/loader.cc
+++ b/src/cobalt/loader/loader.cc
@@ -74,41 +74,36 @@
Loader::Loader(const FetcherCreator& fetcher_creator,
scoped_ptr<Decoder> decoder, const OnErrorFunction& on_error,
- const OnDestructionFunction& on_destruction)
+ const OnDestructionFunction& on_destruction, bool is_suspended)
: fetcher_creator_(fetcher_creator),
decoder_(decoder.Pass()),
on_error_(on_error),
on_destruction_(on_destruction),
- suspended_(false) {
+ is_suspended_(is_suspended) {
+ DCHECK(!fetcher_creator_.is_null());
DCHECK(decoder_);
DCHECK(!on_error.is_null());
- Start();
-
- // Post the error callback on the current message loop in case loader is
- // destroyed in the callback.
- if (!fetcher_) {
- fetcher_creator_error_closure_.Reset(
- base::Bind(on_error, "Fetcher was not created."));
- MessageLoop::current()->PostTask(FROM_HERE,
- fetcher_creator_error_closure_.callback());
+ if (!is_suspended_) {
+ Start();
}
}
Loader::~Loader() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
if (!on_destruction_.is_null()) {
on_destruction_.Run(this);
}
- DCHECK(thread_checker_.CalledOnValidThread());
fetcher_creator_error_closure_.Cancel();
}
void Loader::Suspend() {
DCHECK(thread_checker_.CalledOnValidThread());
- if (suspended_) {
- return;
- }
+ DCHECK(!is_suspended_);
+
+ is_suspended_ = true;
bool suspendable = decoder_->Suspend();
if (fetcher_) {
@@ -116,9 +111,7 @@
}
fetcher_to_decoder_adaptor_.reset();
-
fetcher_creator_error_closure_.Cancel();
- suspended_ = true;
if (!suspendable) {
on_error_.Run("Aborted.");
@@ -127,21 +120,30 @@
void Loader::Resume(render_tree::ResourceProvider* resource_provider) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (!suspended_) {
- return;
- }
- suspended_ = false;
+ DCHECK(is_suspended_);
+
+ is_suspended_ = false;
+
decoder_->Resume(resource_provider);
Start();
}
void Loader::Start() {
DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(decoder_);
- DCHECK(!fetcher_creator_.is_null());
+ DCHECK(!is_suspended_);
+
fetcher_to_decoder_adaptor_.reset(
new FetcherToDecoderAdapter(decoder_.get(), on_error_));
fetcher_ = fetcher_creator_.Run(fetcher_to_decoder_adaptor_.get());
+
+ // Post the error callback on the current message loop in case the loader is
+ // destroyed in the callback.
+ if (!fetcher_) {
+ fetcher_creator_error_closure_.Reset(
+ base::Bind(on_error_, "Fetcher was not created."));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ fetcher_creator_error_closure_.callback());
+ }
}
} // namespace loader
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index 5b029f9..0c9edff 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -47,6 +47,8 @@
'image/image_decoder.h',
'image/image_decoder_starboard.cc',
'image/image_decoder_starboard.h',
+ 'image/animated_image_tracker.cc',
+ 'image/animated_image_tracker.h',
'image/jpeg_image_decoder.cc',
'image/jpeg_image_decoder.h',
'image/png_image_decoder.cc',
diff --git a/src/cobalt/loader/loader.h b/src/cobalt/loader/loader.h
index 60afa5f..60c8b3f 100644
--- a/src/cobalt/loader/loader.h
+++ b/src/cobalt/loader/loader.h
@@ -46,7 +46,8 @@
// It is allowed to destroy the loader in the error callback.
Loader(const FetcherCreator& fetcher_creator, scoped_ptr<Decoder> decoder,
const OnErrorFunction& on_error,
- const OnDestructionFunction& on_destruction = OnDestructionFunction());
+ const OnDestructionFunction& on_destruction = OnDestructionFunction(),
+ bool is_suspended = false);
~Loader();
@@ -75,7 +76,7 @@
OnErrorFunction on_error_;
OnDestructionFunction on_destruction_;
- bool suspended_;
+ bool is_suspended_;
DISALLOW_COPY_AND_ASSIGN(Loader);
};
diff --git a/src/cobalt/loader/loader_factory.cc b/src/cobalt/loader/loader_factory.cc
index 0561dac..b2794a5 100644
--- a/src/cobalt/loader/loader_factory.cc
+++ b/src/cobalt/loader/loader_factory.cc
@@ -34,7 +34,8 @@
base::ThreadPriority loader_thread_priority)
: fetcher_factory_(fetcher_factory),
resource_provider_(resource_provider),
- load_thread_("ResourceLoader") {
+ load_thread_("ResourceLoader"),
+ is_suspended_(false) {
base::Thread::Options options(MessageLoop::TYPE_DEFAULT, kLoadThreadStackSize,
loader_thread_priority);
load_thread_.StartWithOptions(options);
@@ -52,7 +53,8 @@
resource_provider_, success_callback, error_callback,
load_thread_.message_loop())),
error_callback,
- base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this))));
+ base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this)),
+ is_suspended_));
OnLoaderCreated(loader.get());
return loader.Pass();
}
@@ -68,7 +70,8 @@
scoped_ptr<Decoder>(new font::TypefaceDecoder(
resource_provider_, success_callback, error_callback)),
error_callback,
- base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this))));
+ base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this)),
+ is_suspended_));
OnLoaderCreated(loader.get());
return loader.Pass();
}
@@ -85,7 +88,8 @@
scoped_ptr<Decoder>(new mesh::MeshDecoder(
resource_provider_, success_callback, error_callback)),
error_callback,
- base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this))));
+ base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this)),
+ is_suspended_));
OnLoaderCreated(loader.get());
return loader.Pass();
}
@@ -102,8 +106,11 @@
void LoaderFactory::Suspend() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(resource_provider_);
+ DCHECK(!is_suspended_);
+ is_suspended_ = true;
resource_provider_ = NULL;
+
for (LoaderSet::const_iterator iter = active_loaders_.begin();
iter != active_loaders_.end(); ++iter) {
(*iter)->Suspend();
@@ -121,8 +128,11 @@
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(resource_provider);
DCHECK(!resource_provider_);
+ DCHECK(is_suspended_);
+ is_suspended_ = false;
resource_provider_ = resource_provider;
+
for (LoaderSet::const_iterator iter = active_loaders_.begin();
iter != active_loaders_.end(); ++iter) {
(*iter)->Resume(resource_provider);
diff --git a/src/cobalt/loader/loader_factory.h b/src/cobalt/loader/loader_factory.h
index 571a87c..4cd8b25 100644
--- a/src/cobalt/loader/loader_factory.h
+++ b/src/cobalt/loader/loader_factory.h
@@ -90,6 +90,10 @@
// 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_;
+
+ // Whether or not the LoaderFactory is currently suspended. While it is, all
+ // loaders created by it begin in a suspended state.
+ bool is_suspended_;
};
} // namespace loader
diff --git a/src/cobalt/loader/mesh/mesh_decoder.cc b/src/cobalt/loader/mesh/mesh_decoder.cc
index 9902879..2964508 100644
--- a/src/cobalt/loader/mesh/mesh_decoder.cc
+++ b/src/cobalt/loader/mesh/mesh_decoder.cc
@@ -199,13 +199,17 @@
: resource_provider_(resource_provider),
success_callback_(success_callback),
error_callback_(error_callback),
- suspended_(false) {
- DCHECK(resource_provider_);
+ is_suspended_(!resource_provider_) {
DCHECK(!success_callback_.is_null());
DCHECK(!error_callback_.is_null());
}
void MeshDecoder::DecodeChunk(const char* data, size_t size) {
+ if (is_suspended_) {
+ DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
+ return;
+ }
+
if (!raw_data_) {
raw_data_ = make_scoped_ptr(new std::vector<uint8>());
}
@@ -216,10 +220,8 @@
memcpy(&((*raw_data_)[start_size]), data, size);
}
-void MeshDecoder::ReleaseRawData() { raw_data_.reset(); }
-
void MeshDecoder::Finish() {
- if (suspended_) {
+ if (is_suspended_) {
DLOG(WARNING) << __FUNCTION__ << "[" << this << "] while suspended.";
return;
}
@@ -249,23 +251,27 @@
}
bool MeshDecoder::Suspend() {
- DCHECK(!suspended_);
- suspended_ = true;
+ DCHECK(!is_suspended_);
+ DCHECK(resource_provider_);
+
+ is_suspended_ = true;
resource_provider_ = NULL;
+
ReleaseRawData();
return true;
}
void MeshDecoder::Resume(render_tree::ResourceProvider* resource_provider) {
- if (!suspended_) {
- DCHECK_EQ(resource_provider_, resource_provider);
- return;
- }
+ DCHECK(is_suspended_);
+ DCHECK(!resource_provider_);
+ DCHECK(resource_provider);
- suspended_ = false;
+ is_suspended_ = false;
resource_provider_ = resource_provider;
}
+void MeshDecoder::ReleaseRawData() { raw_data_.reset(); }
+
} // namespace mesh
} // namespace loader
} // namespace cobalt
diff --git a/src/cobalt/loader/mesh/mesh_decoder.h b/src/cobalt/loader/mesh/mesh_decoder.h
index 6d81cc3..5c528b0 100644
--- a/src/cobalt/loader/mesh/mesh_decoder.h
+++ b/src/cobalt/loader/mesh/mesh_decoder.h
@@ -63,7 +63,7 @@
scoped_ptr<std::vector<uint8> > raw_data_;
- bool suspended_;
+ bool is_suspended_;
};
} // namespace mesh
diff --git a/src/cobalt/media/base/decryptor.cc b/src/cobalt/media/base/decryptor.cc
deleted file mode 100644
index 91496ec..0000000
--- a/src/cobalt/media/base/decryptor.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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 "cobalt/media/base/decryptor.h"
-
-namespace cobalt {
-namespace media {
-
-Decryptor::Decryptor() {}
-
-Decryptor::~Decryptor() {}
-
-} // namespace media
-} // namespace cobalt
diff --git a/src/cobalt/media/base/decryptor.h b/src/cobalt/media/base/decryptor.h
deleted file mode 100644
index c7f0c6d..0000000
--- a/src/cobalt/media/base/decryptor.h
+++ /dev/null
@@ -1,167 +0,0 @@
-// 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 COBALT_MEDIA_BASE_DECRYPTOR_H_
-#define COBALT_MEDIA_BASE_DECRYPTOR_H_
-
-#include <list>
-
-#include "base/basictypes.h"
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "cobalt/media/base/audio_buffer.h"
-#include "cobalt/media/base/media_export.h"
-
-namespace cobalt {
-namespace media {
-
-class AudioDecoderConfig;
-class DecoderBuffer;
-class VideoDecoderConfig;
-class VideoFrame;
-
-// Decrypts (and decodes) encrypted buffer.
-//
-// All methods are called on the (video/audio) decoder thread. Decryptor
-// implementations must be thread safe when methods are called this way.
-// Depending on the implementation callbacks may be fired synchronously or
-// asynchronously.
-class MEDIA_EXPORT Decryptor {
- public:
- // TODO(xhwang): Replace kError with kDecryptError and kDecodeError.
- // TODO(xhwang): Replace kNeedMoreData with kNotEnoughData.
- enum Status {
- kSuccess, // Decryption successfully completed. Decrypted buffer ready.
- kNoKey, // No key is available to decrypt.
- kNeedMoreData, // Decoder needs more data to produce a frame.
- kError // Key is available but an error occurred during decryption.
- };
-
- // TODO(xhwang): Unify this with DemuxerStream::Type.
- enum StreamType { kAudio, kVideo };
-
- Decryptor();
- virtual ~Decryptor();
-
- // Indicates that a new key has been added to the MediaKeys object associated
- // with the Decryptor.
- typedef base::Callback<void()> NewKeyCB;
-
- // Registers a NewKeyCB which should be called when a new key is added to the
- // decryptor. Only one NewKeyCB can be registered for one |stream_type|.
- // If this function is called multiple times for the same |stream_type|, the
- // previously registered callback will be replaced. In other words,
- // registering a null callback cancels the originally registered callback.
- virtual void RegisterNewKeyCB(StreamType stream_type,
- const NewKeyCB& key_added_cb) = 0;
-
- // Indicates completion of a decryption operation.
- //
- // First parameter: The status of the decryption operation.
- // - Set to kSuccess if the encrypted buffer is successfully decrypted and
- // the decrypted buffer is ready to be read.
- // - Set to kNoKey if no decryption key is available to decrypt the encrypted
- // buffer. In this case the decrypted buffer must be NULL.
- // - Set to kError if unexpected error has occurred. In this case the
- // decrypted buffer must be NULL.
- // - This parameter should not be set to kNeedMoreData.
- // Second parameter: The decrypted buffer.
- // - Only |data|, |data_size| and |timestamp| are set in the returned
- // DecoderBuffer. The callback handler is responsible for setting other
- // fields as appropriate.
- typedef base::Callback<void(Status, const scoped_refptr<DecoderBuffer>&)>
- DecryptCB;
-
- // Decrypts the |encrypted| buffer. The decrypt status and decrypted buffer
- // are returned via the provided callback |decrypt_cb|. The |encrypted| buffer
- // must not be NULL.
- // Decrypt() should not be called until any previous DecryptCB of the same
- // |stream_type| has completed. Thus, only one DecryptCB may be pending at
- // a time for a given |stream_type|.
- virtual void Decrypt(StreamType stream_type,
- const scoped_refptr<DecoderBuffer>& encrypted,
- const DecryptCB& decrypt_cb) = 0;
-
- // Cancels the scheduled decryption operation for |stream_type| and fires the
- // pending DecryptCB immediately with kSuccess and NULL.
- // Decrypt() should not be called again before the pending DecryptCB for the
- // same |stream_type| is fired.
- virtual void CancelDecrypt(StreamType stream_type) = 0;
-
- // Indicates completion of audio/video decoder initialization.
- //
- // First Parameter: Indicates initialization success.
- // - Set to true if initialization was successful. False if an error occurred.
- typedef base::Callback<void(bool)> DecoderInitCB;
-
- // Initializes a decoder with the given |config|, executing the |init_cb|
- // upon completion.
- virtual void InitializeAudioDecoder(const AudioDecoderConfig& config,
- const DecoderInitCB& init_cb) = 0;
- virtual void InitializeVideoDecoder(const VideoDecoderConfig& config,
- const DecoderInitCB& init_cb) = 0;
-
- // Helper structure for managing multiple decoded audio buffers per input.
- typedef std::list<scoped_refptr<AudioBuffer> > AudioFrames;
-
- // Indicates completion of audio/video decrypt-and-decode operation.
- //
- // First parameter: The status of the decrypt-and-decode operation.
- // - Set to kSuccess if the encrypted buffer is successfully decrypted and
- // decoded. In this case, the decoded frame/buffers can be/contain:
- // 1) NULL, which means the operation has been aborted.
- // 2) End-of-stream (EOS) frame, which means that the decoder has hit EOS,
- // flushed all internal buffers and cannot produce more video frames.
- // 3) Decrypted and decoded video frame or audio buffer.
- // - Set to kNoKey if no decryption key is available to decrypt the encrypted
- // buffer. In this case the returned frame(s) must be NULL/empty.
- // - Set to kNeedMoreData if more data is needed to produce a video frame. In
- // this case the returned frame(s) must be NULL/empty.
- // - Set to kError if unexpected error has occurred. In this case the
- // returned frame(s) must be NULL/empty.
- // Second parameter: The decoded video frame or audio buffers.
- typedef base::Callback<void(Status, const AudioFrames&)> AudioDecodeCB;
- typedef base::Callback<void(Status, const scoped_refptr<VideoFrame>&)>
- VideoDecodeCB;
-
- // Decrypts and decodes the |encrypted| buffer. The status and the decrypted
- // buffer are returned via the provided callback.
- // The |encrypted| buffer must not be NULL.
- // At end-of-stream, this method should be called repeatedly with
- // end-of-stream DecoderBuffer until no frame/buffer can be produced.
- // These methods can only be called after the corresponding decoder has
- // been successfully initialized.
- virtual void DecryptAndDecodeAudio(
- const scoped_refptr<DecoderBuffer>& encrypted,
- const AudioDecodeCB& audio_decode_cb) = 0;
- virtual void DecryptAndDecodeVideo(
- const scoped_refptr<DecoderBuffer>& encrypted,
- const VideoDecodeCB& video_decode_cb) = 0;
-
- // Resets the decoder to an initialized clean state, cancels any scheduled
- // decrypt-and-decode operations, and fires any pending
- // AudioDecodeCB/VideoDecodeCB immediately with kError and NULL.
- // This method can only be called after the corresponding decoder has been
- // successfully initialized.
- virtual void ResetDecoder(StreamType stream_type) = 0;
-
- // Releases decoder resources, deinitializes the decoder, cancels any
- // scheduled initialization or decrypt-and-decode operations, and fires
- // any pending DecoderInitCB/AudioDecodeCB/VideoDecodeCB immediately.
- // DecoderInitCB should be fired with false. AudioDecodeCB/VideoDecodeCB
- // should be fired with kError.
- // This method can be called any time after Initialize{Audio|Video}Decoder()
- // has been called (with the correct stream type).
- // After this operation, the decoder is set to an uninitialized state.
- // The decoder can be reinitialized after it is uninitialized.
- virtual void DeinitializeDecoder(StreamType stream_type) = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Decryptor);
-};
-
-} // namespace media
-} // namespace cobalt
-
-#endif // COBALT_MEDIA_BASE_DECRYPTOR_H_
diff --git a/src/cobalt/media/base/drm_system.cc b/src/cobalt/media/base/drm_system.cc
new file mode 100644
index 0000000..38a0be8
--- /dev/null
+++ b/src/cobalt/media/base/drm_system.cc
@@ -0,0 +1,117 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/media/base/drm_system.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+
+namespace cobalt {
+namespace media {
+
+DrmSystem::DrmSystem(const char* key_system, DrmSystemClient* client)
+ : client_(client),
+ wrapped_drm_system_(SbDrmCreateSystem(key_system, this,
+ OnSessionUpdateRequestGeneratedFunc,
+ OnSessionUpdatedFunc)),
+ message_loop_(MessageLoop::current()) {
+ DCHECK_NE(kSbDrmSystemInvalid, wrapped_drm_system_);
+}
+
+DrmSystem::~DrmSystem() { SbDrmDestroySystem(wrapped_drm_system_); }
+
+void DrmSystem::GenerateSessionUpdateRequest(int ticket,
+ const std::string& type,
+ const uint8_t* init_data,
+ int init_data_length) {
+ SbDrmGenerateSessionUpdateRequest(wrapped_drm_system_, ticket, type.c_str(),
+ init_data, init_data_length);
+}
+
+void DrmSystem::UpdateSession(int ticket, const std::string& session_id,
+ const uint8_t* key, int key_length) {
+ SbDrmUpdateSession(wrapped_drm_system_, ticket, key, key_length,
+ session_id.c_str(), session_id.size());
+}
+
+void DrmSystem::CloseSession(const std::string& session_id) {
+ SbDrmCloseSession(wrapped_drm_system_, session_id.c_str(), session_id.size());
+}
+
+void DrmSystem::OnSessionUpdateRequestGenerated(int ticket,
+ const void* session_id,
+ int session_id_size,
+ const void* content,
+ int content_size) {
+ if (session_id) {
+ std::string session_id_copy(
+ static_cast<const char*>(session_id),
+ static_cast<const char*>(session_id) + session_id_size);
+
+ scoped_array<uint8> content_copy(new uint8[content_size]);
+ SbMemoryCopy(content_copy.get(), content, content_size);
+
+ message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&DrmSystemClient::OnSessionUpdateRequestGenerated,
+ base::Unretained(client_), ticket, session_id_copy,
+ base::Passed(&content_copy), content_size));
+ } else {
+ message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&DrmSystemClient::OnSessionUpdateRequestDidNotGenerate,
+ base::Unretained(client_), ticket));
+ }
+}
+
+void DrmSystem::OnSessionUpdated(int ticket, const void* /*session_id*/,
+ int /*session_id_size*/, bool succeeded) {
+ if (succeeded) {
+ message_loop_->PostTask(FROM_HERE,
+ base::Bind(&DrmSystemClient::OnSessionUpdated,
+ base::Unretained(client_), ticket));
+ } else {
+ message_loop_->PostTask(FROM_HERE,
+ base::Bind(&DrmSystemClient::OnSessionDidNotUpdate,
+ base::Unretained(client_), ticket));
+ }
+}
+
+// static
+void DrmSystem::OnSessionUpdateRequestGeneratedFunc(
+ SbDrmSystem wrapped_drm_system, void* context, int ticket,
+ const void* session_id, int session_id_size, const void* content,
+ int content_size, const char* url) {
+ DCHECK(context);
+ DrmSystem* drm_system = static_cast<DrmSystem*>(context);
+ DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
+
+ drm_system->OnSessionUpdateRequestGenerated(
+ ticket, session_id, session_id_size, content, content_size);
+}
+
+// static
+void DrmSystem::OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
+ void* context, int ticket,
+ const void* session_id,
+ int session_id_size, bool succeeded) {
+ DCHECK(context);
+ DrmSystem* drm_system = static_cast<DrmSystem*>(context);
+ DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
+
+ drm_system->OnSessionUpdated(ticket, session_id, session_id_size, succeeded);
+}
+
+} // namespace media
+} // namespace cobalt
diff --git a/src/cobalt/media/base/drm_system.h b/src/cobalt/media/base/drm_system.h
new file mode 100644
index 0000000..018c428
--- /dev/null
+++ b/src/cobalt/media/base/drm_system.h
@@ -0,0 +1,78 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_MEDIA_BASE_DRM_SYSTEM_H_
+#define COBALT_MEDIA_BASE_DRM_SYSTEM_H_
+
+#include <string>
+
+#include "base/message_loop.h"
+#include "cobalt/media/base/drm_system_client.h"
+#include "starboard/drm.h"
+
+#if SB_API_VERSION < 4
+#error "Cobalt media stack requires Starboard 4 or above."
+#endif // SB_API_VERSION < 4
+
+namespace cobalt {
+namespace media {
+
+// A C++ wrapper around |SbDrmSystem|.
+//
+// Ensures that calls to |DrmSystemClient| are always asynchronous and performed
+// from the same thread where |DrmSystem| was instantiated.
+class DrmSystem {
+ public:
+ DrmSystem(const char* key_system, DrmSystemClient* client);
+ ~DrmSystem();
+
+ SbDrmSystem wrapped_drm_system() { return wrapped_drm_system_; }
+
+ // Wraps |SbDrmGenerateSessionUpdateRequest|.
+ void GenerateSessionUpdateRequest(int ticket, const std::string& type,
+ const uint8* init_data,
+ int init_data_length);
+ // Wraps |SbDrmUpdateSession|.
+ void UpdateSession(int ticket, const std::string& session_id,
+ const uint8* key, int key_length);
+ // Wraps |SbDrmCloseSession|.
+ void CloseSession(const std::string& session_id);
+
+ private:
+ void OnSessionUpdateRequestGenerated(int ticket, const void* session_id,
+ int session_id_size, const void* content,
+ int content_size);
+ void OnSessionUpdated(int ticket, const void* session_id, int session_id_size,
+ bool succeeded);
+
+ static void OnSessionUpdateRequestGeneratedFunc(
+ SbDrmSystem wrapped_drm_system, void* context, int ticket,
+ const void* session_id, int session_id_size, const void* content,
+ int content_size, const char* url);
+ static void OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
+ void* context, int ticket,
+ const void* session_id,
+ int session_id_length, bool succeeded);
+
+ DrmSystemClient* const client_;
+ const SbDrmSystem wrapped_drm_system_;
+ MessageLoop* const message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(DrmSystem);
+};
+
+} // namespace media
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_BASE_DRM_SYSTEM_H_
diff --git a/src/cobalt/media/base/drm_system_client.h b/src/cobalt/media/base/drm_system_client.h
new file mode 100644
index 0000000..a0fddfe
--- /dev/null
+++ b/src/cobalt/media/base/drm_system_client.h
@@ -0,0 +1,62 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_MEDIA_BASE_DRM_SYSTEM_CLIENT_H_
+#define COBALT_MEDIA_BASE_DRM_SYSTEM_CLIENT_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace cobalt {
+namespace media {
+
+// An interface to be implemented by clients of |DrmSystem|.
+//
+// Calls to |DrmSystemClient| are always performed from the same thread
+// where |DrmSystem| was instantiated.
+class DrmSystemClient {
+ public:
+ // When called as a result of |DrmSystem::GenerateSessionUpdateRequest|, this
+ // method denotes a successful request generation.
+ //
+ // This method may be called multiple times after a single call to
+ // |GenerateSessionUpdateRequest|, for example when the underlying DRM system
+ // needs to update a license. In this case |ticket| will be
+ // |kSbDrmTicketInvalid|.
+ virtual void OnSessionUpdateRequestGenerated(int ticket,
+ const std::string& session_id,
+ scoped_array<uint8> message,
+ int message_size) = 0;
+ // Called as a result of |GenerateSessionUpdateRequest| and denotes a failure
+ // during request generation.
+ //
+ // Unlike its successful counterpart, never called spontaneously.
+ virtual void OnSessionUpdateRequestDidNotGenerate(int ticket) = 0;
+
+ // Called as a result of |UpdateSession| and denotes a successful session
+ // update.
+ virtual void OnSessionUpdated(int ticket) = 0;
+ // Called as a result of |UpdateSession| and denotes a failure during session
+ // update.
+ virtual void OnSessionDidNotUpdate(int ticket) = 0;
+
+ protected:
+ virtual ~DrmSystemClient() {}
+};
+
+} // namespace media
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_BASE_DRM_SYSTEM_CLIENT_H_
diff --git a/src/cobalt/media/base/pipeline.h b/src/cobalt/media/base/pipeline.h
index a9d3234..82863ce 100644
--- a/src/cobalt/media/base/pipeline.h
+++ b/src/cobalt/media/base/pipeline.h
@@ -19,21 +19,17 @@
#include "base/memory/ref_counted.h"
#include "base/message_loop_proxy.h"
#include "base/time.h"
-#include "cobalt/media/base/decryptor.h"
#include "cobalt/media/base/demuxer.h"
#include "cobalt/media/base/media_export.h"
#include "cobalt/media/base/pipeline_status.h"
#include "cobalt/media/base/ranges.h"
+#include "starboard/drm.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
-#if defined(OS_STARBOARD)
#if SB_HAS(PLAYER)
-
#define COBALT_USE_SBPLAYER_PIPELINE
-
#endif // SB_HAS(PLAYER)
-#endif // defined(OS_STARBOARD)
#if defined(COBALT_USE_SBPLAYER_PIPELINE)
#include "starboard/window.h"
@@ -47,6 +43,12 @@
class MediaLog;
+// Callback to notify that a DRM system is ready.
+typedef base::Callback<void(SbDrmSystem)> DrmSystemReadyCB;
+
+// Callback to set a DrmSystemReadyCB.
+typedef base::Callback<void(const DrmSystemReadyCB&)> SetDrmSystemReadyCB;
+
// Pipeline contains the common interface for media pipelines. It provides
// functions to perform asynchronous initialization, pausing, seeking and
// playing.
@@ -90,8 +92,8 @@
//
// The following permanent callbacks will be executed as follows up until
// Stop() has completed:
- // |decryptor_ready_cb| can be used if Pipeline needs to be notified when
- // the Decryptor is ready.
+ // |set_drm_system_ready_cb| can be used if Pipeline needs to be notified
+ // when the |SbDrmSystem| is ready.
// |ended_cb| will be executed whenever the media reaches the end.
// |error_cb| will be executed whenever an error occurs but hasn't
// been reported already through another callback.
@@ -100,12 +102,13 @@
// |duration_change_cb| optional callback that will be executed whenever the
// presentation duration changes.
// It is an error to call this method after the pipeline has already started.
- virtual void Start(Demuxer* demuxer, const PipelineStatusCB& ended_cb,
+ virtual void Start(Demuxer* demuxer,
+ const SetDrmSystemReadyCB& set_drm_system_ready_cb,
+ const PipelineStatusCB& ended_cb,
const PipelineStatusCB& error_cb,
const PipelineStatusCB& seek_cb,
const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb,
- bool prefer_decode_to_texture) = 0;
+ const base::Closure& duration_change_cb) = 0;
// Asynchronously stops the pipeline, executing |stop_cb| when the pipeline
// teardown has completed.
@@ -176,8 +179,8 @@
// Get the SetBoundsCB used to set the bounds of the video frame.
virtual SetBoundsCB GetSetBoundsCB() { return SetBoundsCB(); }
- // Returns whether the player is configured for outputting in punch out mode.
- virtual bool IsPunchOutMode() { return false; }
+ // Updates the player's preference for decode-to-texture versus punch through.
+ virtual void SetDecodeToTextureOutputMode(bool /*enabled*/) {}
};
} // namespace media
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 98cdfab..40763c6 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -17,9 +17,11 @@
#include "base/basictypes.h" // For COMPILE_ASSERT
#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
+#include "base/optional.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/time.h"
@@ -53,12 +55,12 @@
// parameters exceed what base::Bind() can support.
struct StartTaskParameters {
Demuxer* demuxer;
+ SetDrmSystemReadyCB set_drm_system_ready_cb;
PipelineStatusCB ended_cb;
PipelineStatusCB error_cb;
PipelineStatusCB seek_cb;
Pipeline::BufferingStateCB buffering_state_cb;
base::Closure duration_change_cb;
- bool prefer_decode_to_texture;
};
// SbPlayerPipeline is a PipelineBase implementation that uses the SbPlayer
@@ -75,11 +77,12 @@
void Suspend() OVERRIDE;
void Resume() OVERRIDE;
- void Start(Demuxer* demuxer, const PipelineStatusCB& ended_cb,
- const PipelineStatusCB& error_cb, const PipelineStatusCB& seek_cb,
+ void Start(Demuxer* demuxer,
+ const SetDrmSystemReadyCB& set_drm_system_ready_cb,
+ const PipelineStatusCB& ended_cb, const PipelineStatusCB& error_cb,
+ const PipelineStatusCB& seek_cb,
const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb,
- bool prefer_decode_to_texture) OVERRIDE;
+ const base::Closure& duration_change_cb) OVERRIDE;
void Stop(const base::Closure& stop_cb) OVERRIDE;
void Seek(TimeDelta time, const PipelineStatusCB& seek_cb);
@@ -99,8 +102,7 @@
bool DidLoadingProgress() const OVERRIDE;
PipelineStatistics GetStatistics() const OVERRIDE;
SetBoundsCB GetSetBoundsCB() OVERRIDE;
-
- bool IsPunchOutMode() OVERRIDE;
+ void SetDecodeToTextureOutputMode(bool enabled) OVERRIDE;
private:
void StartTask(const StartTaskParameters& parameters);
@@ -118,7 +120,6 @@
void RemoveTextStream(DemuxerStream* text_stream) OVERRIDE;
void CreatePlayer(SbDrmSystem drm_system);
- void SetDecryptor(Decryptor* decryptor);
void OnDemuxerInitialized(PipelineStatus status);
void OnDemuxerSeeked(PipelineStatus status);
void OnDemuxerStopped();
@@ -166,12 +167,11 @@
// the filters.
float playback_rate_;
- // Whether the media contains rendered audio and video streams.
- // TODO(fischman,scherkus): replace these with checks for
- // {audio,video}_decoder_ once extraction of {Audio,Video}Decoder from the
- // Filter heirarchy is done.
- bool has_audio_;
- bool has_video_;
+ // The saved audio and video demuxer streams. Note that it is safe to store
+ // raw pointers of the demuxer streams, as the Demuxer guarantees that its
+ // |DemuxerStream|s live as long as the Demuxer itself.
+ DemuxerStream* audio_stream_;
+ DemuxerStream* video_stream_;
mutable PipelineStatistics statistics_;
@@ -182,11 +182,12 @@
base::Closure stop_cb_;
// Permanent callbacks passed in via Start().
+ SetDrmSystemReadyCB set_drm_system_ready_cb_;
PipelineStatusCB ended_cb_;
PipelineStatusCB error_cb_;
BufferingStateCB buffering_state_cb_;
base::Closure duration_change_cb_;
- bool prefer_decode_to_texture_;
+ base::optional<bool> decode_to_texture_output_mode_;
// Demuxer reference used for setting the preload value.
Demuxer* demuxer_;
@@ -207,6 +208,7 @@
base::TimeDelta seek_time_;
scoped_ptr<StarboardPlayer> player_;
bool suspended_;
+ bool stopped_;
DISALLOW_COPY_AND_ASSIGN(SbPlayerPipeline);
};
@@ -220,14 +222,14 @@
natural_size_(0, 0),
volume_(1.f),
playback_rate_(0.f),
- has_audio_(false),
- has_video_(false),
+ audio_stream_(NULL),
+ video_stream_(NULL),
demuxer_(NULL),
audio_read_in_progress_(false),
video_read_in_progress_(false),
set_bounds_helper_(new SbPlayerSetBoundsHelper),
suspended_(false),
- prefer_decode_to_texture_(false) {}
+ stopped_(false) {}
SbPlayerPipeline::~SbPlayerPipeline() { DCHECK(!player_); }
@@ -253,12 +255,15 @@
waitable_event.Wait();
}
-void SbPlayerPipeline::Start(Demuxer* demuxer, const PipelineStatusCB& ended_cb,
+void SbPlayerPipeline::Start(Demuxer* demuxer,
+ const SetDrmSystemReadyCB& set_drm_system_ready_cb,
+ const PipelineStatusCB& ended_cb,
const PipelineStatusCB& error_cb,
const PipelineStatusCB& seek_cb,
const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb,
- bool prefer_decode_to_texture) {
+ const base::Closure& duration_change_cb) {
+ TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::Start");
+
DCHECK(demuxer);
DCHECK(!ended_cb.is_null());
DCHECK(!error_cb.is_null());
@@ -268,18 +273,20 @@
StartTaskParameters parameters;
parameters.demuxer = demuxer;
+ parameters.set_drm_system_ready_cb = set_drm_system_ready_cb;
parameters.ended_cb = ended_cb;
parameters.error_cb = error_cb;
parameters.seek_cb = seek_cb;
parameters.buffering_state_cb = buffering_state_cb;
parameters.duration_change_cb = duration_change_cb;
- parameters.prefer_decode_to_texture = prefer_decode_to_texture;
message_loop_->PostTask(
FROM_HERE, base::Bind(&SbPlayerPipeline::StartTask, this, parameters));
}
void SbPlayerPipeline::Stop(const base::Closure& stop_cb) {
+ TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::Stop");
+
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(FROM_HERE,
base::Bind(&SbPlayerPipeline::Stop, this, stop_cb));
@@ -289,6 +296,8 @@
DCHECK(stop_cb_.is_null());
DCHECK(!stop_cb.is_null());
+ stopped_ = true;
+
if (player_) {
scoped_ptr<StarboardPlayer> player;
{
@@ -345,12 +354,12 @@
bool SbPlayerPipeline::HasAudio() const {
base::AutoLock auto_lock(lock_);
- return has_audio_;
+ return audio_stream_ != NULL;
}
bool SbPlayerPipeline::HasVideo() const {
base::AutoLock auto_lock(lock_);
- return has_video_;
+ return video_stream_ != NULL;
}
float SbPlayerPipeline::GetPlaybackRate() const {
@@ -443,25 +452,34 @@
return base::Bind(&SbPlayerSetBoundsHelper::SetBounds, set_bounds_helper_);
}
-bool SbPlayerPipeline::IsPunchOutMode() {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
- base::AutoLock auto_lock(lock_);
- if (player_) {
- return player_->GetSbPlayerOutputMode() == kSbPlayerOutputModePunchOut;
- } else {
- return true;
+void SbPlayerPipeline::SetDecodeToTextureOutputMode(bool enabled) {
+ TRACE_EVENT1("cobalt::media",
+ "SbPlayerPipeline::SetDecodeToTextureOutputMode", "mode",
+ enabled);
+
+ if (!message_loop_->BelongsToCurrentThread()) {
+ message_loop_->PostTask(
+ FROM_HERE, base::Bind(&SbPlayerPipeline::SetDecodeToTextureOutputMode,
+ this, enabled));
+ return;
}
-#else // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
- return true;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+
+ // The player can't be created yet, if it is, then we're updating the output
+ // mode too late.
+ DCHECK(!player_);
+
+ decode_to_texture_output_mode_ = enabled;
}
void SbPlayerPipeline::StartTask(const StartTaskParameters& parameters) {
+ TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::StartTask");
+
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(!demuxer_);
demuxer_ = parameters.demuxer;
+ set_drm_system_ready_cb_ = parameters.set_drm_system_ready_cb;
ended_cb_ = parameters.ended_cb;
error_cb_ = parameters.error_cb;
{
@@ -470,7 +488,6 @@
}
buffering_state_cb_ = parameters.buffering_state_cb;
duration_change_cb_ = parameters.duration_change_cb;
- prefer_decode_to_texture_ = parameters.prefer_decode_to_texture;
const bool kEnableTextTracks = false;
demuxer_->Initialize(this,
@@ -539,7 +556,11 @@
}
void SbPlayerPipeline::CreatePlayer(SbDrmSystem drm_system) {
+ TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::CreatePlayer");
+
DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(audio_stream_);
+ DCHECK(video_stream_);
if (suspended_) {
message_loop_->PostTask(
@@ -552,15 +573,15 @@
// player is created. In this case we should delay creating the player as the
// creation of player may fail.
const AudioDecoderConfig& audio_config =
- demuxer_->GetStream(DemuxerStream::AUDIO)->audio_decoder_config();
+ audio_stream_->audio_decoder_config();
const VideoDecoderConfig& video_config =
- demuxer_->GetStream(DemuxerStream::VIDEO)->video_decoder_config();
+ video_stream_->video_decoder_config();
{
base::AutoLock auto_lock(lock_);
player_.reset(new StarboardPlayer(
message_loop_, audio_config, video_config, window_, drm_system, this,
- set_bounds_helper_.get(), prefer_decode_to_texture_));
+ set_bounds_helper_.get(), *decode_to_texture_output_mode_));
SetPlaybackRateTask(playback_rate_);
SetVolumeTask(volume_);
}
@@ -580,55 +601,47 @@
seek_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
}
-void SbPlayerPipeline::SetDecryptor(Decryptor* decryptor) {
+void SbPlayerPipeline::OnDemuxerInitialized(PipelineStatus status) {
+ TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::OnDemuxerInitialized");
+
DCHECK(message_loop_->BelongsToCurrentThread());
- if (!decryptor) {
+ if (stopped_) {
return;
}
- NOTREACHED();
- // TODO: Migrate this
- // StarboardDecryptor* sb_decryptor =
- // reinterpret_cast<StarboardDecryptor*>(decryptor);
- // CreatePlayer(sb_decryptor->drm_system());
-}
-
-void SbPlayerPipeline::OnDemuxerInitialized(PipelineStatus status) {
- DCHECK(message_loop_->BelongsToCurrentThread());
if (status != PIPELINE_OK) {
ResetAndRunIfNotNull(&error_cb_, status);
return;
}
- if (demuxer_->GetStream(DemuxerStream::AUDIO) == NULL ||
- demuxer_->GetStream(DemuxerStream::VIDEO) == NULL) {
+ if (suspended_) {
+ message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&SbPlayerPipeline::OnDemuxerInitialized, this, status));
+ return;
+ }
+
+ DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO);
+ DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO);
+ if (audio_stream == NULL || video_stream == NULL) {
ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
return;
}
{
base::AutoLock auto_lock(lock_);
- has_audio_ = demuxer_->GetStream(DemuxerStream::AUDIO) != NULL;
- DCHECK(has_audio_);
- has_video_ = demuxer_->GetStream(DemuxerStream::VIDEO) != NULL;
+ audio_stream_ = audio_stream;
+ video_stream_ = video_stream;
buffering_state_cb_.Run(kHaveMetadata);
- const AudioDecoderConfig& audio_config =
- demuxer_->GetStream(DemuxerStream::AUDIO)->audio_decoder_config();
- bool is_encrypted = audio_config.is_encrypted();
- if (has_video_) {
- const VideoDecoderConfig& video_config =
- demuxer_->GetStream(DemuxerStream::VIDEO)->video_decoder_config();
- natural_size_ = video_config.natural_size();
- is_encrypted |= video_config.is_encrypted();
- }
+ bool is_encrypted = audio_stream_->audio_decoder_config().is_encrypted();
+ natural_size_ = video_stream_->video_decoder_config().natural_size();
+ is_encrypted |= video_stream_->video_decoder_config().is_encrypted();
if (is_encrypted) {
- // TODO: Migrate this
- // decryptor_ready_cb_.Run(
- // BindToCurrentLoop(base::Bind(&SbPlayerPipeline::SetDecryptor,
- // this)));
+ set_drm_system_ready_cb_.Run(
+ BindToCurrentLoop(base::Bind(&SbPlayerPipeline::CreatePlayer, this)));
return;
}
}
@@ -645,6 +658,8 @@
}
void SbPlayerPipeline::OnDemuxerStopped() {
+ TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::OnDemuxerStopped");
+
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(
FROM_HERE, base::Bind(&SbPlayerPipeline::OnDemuxerStopped, this));
@@ -667,7 +682,9 @@
return;
}
- DemuxerStream* stream = demuxer_->GetStream(type);
+ DemuxerStream* stream =
+ type == DemuxerStream::AUDIO ? audio_stream_ : video_stream_;
+ DCHECK(stream);
// In case if Stop() has been called.
if (!player_) {
@@ -729,7 +746,9 @@
}
video_read_in_progress_ = true;
}
- DemuxerStream* stream = demuxer_->GetStream(type);
+ DemuxerStream* stream =
+ type == DemuxerStream::AUDIO ? audio_stream_ : video_stream_;
+ DCHECK(stream);
stream->Read(base::Bind(&SbPlayerPipeline::OnDemuxerStreamRead, this, type));
}
diff --git a/src/cobalt/media/base/shell_data_source_reader.cc b/src/cobalt/media/base/shell_data_source_reader.cc
index b901b79..ecf501f 100644
--- a/src/cobalt/media/base/shell_data_source_reader.cc
+++ b/src/cobalt/media/base/shell_data_source_reader.cc
@@ -49,9 +49,15 @@
int total_bytes_read = 0;
while (size > 0 && !read_has_failed_) {
- data_source_->Read(
- position, size, data,
- base::Bind(&ShellDataSourceReader::BlockingReadCompleted, this));
+ {
+ base::AutoLock auto_lock(lock_);
+ if (!data_source_) {
+ break;
+ }
+ data_source_->Read(
+ position, size, data,
+ base::Bind(&ShellDataSourceReader::BlockingReadCompleted, this));
+ }
// wait for callback on read completion
blocking_read_event_.Wait();
@@ -87,6 +93,8 @@
void ShellDataSourceReader::Stop() {
if (data_source_) {
data_source_->Stop();
+
+ base::AutoLock auto_lock(lock_);
data_source_ = NULL;
}
}
@@ -99,7 +107,8 @@
int64 ShellDataSourceReader::FileSize() {
if (file_size_ == -1) {
- if (!data_source_->GetSize(&file_size_)) {
+ base::AutoLock auto_lock(lock_);
+ if (data_source_ && !data_source_->GetSize(&file_size_)) {
file_size_ = -1;
}
}
diff --git a/src/cobalt/media/base/shell_data_source_reader.h b/src/cobalt/media/base/shell_data_source_reader.h
index b6eb9a7..06aef83 100644
--- a/src/cobalt/media/base/shell_data_source_reader.h
+++ b/src/cobalt/media/base/shell_data_source_reader.h
@@ -20,6 +20,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
+#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "cobalt/media/base/data_source.h"
@@ -58,6 +59,7 @@
// blocking read callback
virtual void BlockingReadCompleted(int bytes_read);
+ base::Lock lock_;
DataSource* data_source_;
base::WaitableEvent blocking_read_event_;
int64 file_size_;
diff --git a/src/cobalt/media/base/shell_media_platform.h b/src/cobalt/media/base/shell_media_platform.h
index 5a05594..73510fd 100644
--- a/src/cobalt/media/base/shell_media_platform.h
+++ b/src/cobalt/media/base/shell_media_platform.h
@@ -22,6 +22,7 @@
#include "cobalt/media/base/limits.h"
#include "cobalt/media/base/media_export.h"
#include "cobalt/media/base/shell_video_frame_provider.h"
+#include "cobalt/render_tree/resource_provider.h"
#include "starboard/decode_target.h"
namespace cobalt {
@@ -46,7 +47,7 @@
// The following functions will be called when the application enters or
// leaves suspending status.
virtual void Suspend() {}
- virtual void Resume() {}
+ virtual void Resume(render_tree::ResourceProvider* /*resource_provider*/) {}
// Media stack buffer allocate/free functions currently only used by
// ShellBufferFactory.
@@ -62,7 +63,12 @@
return NULL;
}
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+ virtual SbDecodeTargetGraphicsContextProvider*
+ GetSbDecodeTargetGraphicsContextProvider() {
+ return NULL;
+ }
+#elif SB_API_VERSION >= 3
virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; }
#endif // SB_API_VERSION >= 3
diff --git a/src/cobalt/media/base/shell_video_frame_provider.h b/src/cobalt/media/base/shell_video_frame_provider.h
index ba92fb6..eb98efb 100644
--- a/src/cobalt/media/base/shell_video_frame_provider.h
+++ b/src/cobalt/media/base/shell_video_frame_provider.h
@@ -17,6 +17,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
#include "base/time.h"
#include "starboard/decode_target.h"
@@ -46,18 +47,25 @@
kOutputModeInvalid,
};
- ShellVideoFrameProvider() : output_mode_(kOutputModePunchOut) {}
+ ShellVideoFrameProvider() : output_mode_(kOutputModeInvalid) {}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
typedef base::Callback<SbDecodeTarget()> GetCurrentSbDecodeTargetFunction;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
scoped_refptr<VideoFrame> GetCurrentFrame() { return NULL; }
- void SetOutputMode(OutputMode output_mode) { output_mode_ = output_mode; }
- OutputMode GetOutputMode() const { return output_mode_; }
+ void SetOutputMode(OutputMode output_mode) {
+ base::AutoLock auto_lock(lock_);
+ output_mode_ = output_mode;
+ }
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+ ShellVideoFrameProvider::OutputMode GetOutputMode() const {
+ base::AutoLock auto_lock(lock_);
+ return output_mode_;
+ }
+
+#if SB_API_VERSION >= 4
// For Starboard platforms that have a decode-to-texture player, we enable
// this ShellVideoFrameProvider to act as a bridge for Cobalt code to query
// for the current SbDecodeTarget. In effect, we bypass all of
@@ -66,27 +74,32 @@
// needed.
void SetGetCurrentSbDecodeTargetFunction(
GetCurrentSbDecodeTargetFunction function) {
+ base::AutoLock auto_lock(lock_);
get_current_sb_decode_target_function_ = function;
}
void ResetGetCurrentSbDecodeTargetFunction() {
+ base::AutoLock auto_lock(lock_);
get_current_sb_decode_target_function_.Reset();
}
SbDecodeTarget GetCurrentSbDecodeTarget() const {
+ base::AutoLock auto_lock(lock_);
if (get_current_sb_decode_target_function_.is_null()) {
return kSbDecodeTargetInvalid;
} else {
return get_current_sb_decode_target_function_.Run();
}
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
private:
+ mutable base::Lock lock_;
+
OutputMode output_mode_;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
GetCurrentSbDecodeTargetFunction get_current_sb_decode_target_function_;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
DISALLOW_COPY_AND_ASSIGN(ShellVideoFrameProvider);
};
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index cd17293..01d4a3c 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -17,6 +17,8 @@
#include <algorithm>
#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/debug/trace_event.h"
#include "base/location.h"
#include "base/logging.h"
#include "cobalt/media/base/shell_media_platform.h"
@@ -27,6 +29,46 @@
namespace cobalt {
namespace media {
+StarboardPlayer::CallbackHelper::CallbackHelper(StarboardPlayer* player)
+ : player_(player) {}
+
+void StarboardPlayer::CallbackHelper::ClearDecoderBufferCache() {
+ base::AutoLock auto_lock(lock_);
+ if (player_) {
+ player_->ClearDecoderBufferCache();
+ }
+}
+
+void StarboardPlayer::CallbackHelper::OnDecoderStatus(
+ SbPlayer player, SbMediaType type, SbPlayerDecoderState state, int ticket) {
+ base::AutoLock auto_lock(lock_);
+ if (player_) {
+ player_->OnDecoderStatus(player, type, state, ticket);
+ }
+}
+
+void StarboardPlayer::CallbackHelper::OnPlayerStatus(SbPlayer player,
+ SbPlayerState state,
+ int ticket) {
+ base::AutoLock auto_lock(lock_);
+ if (player_) {
+ player_->OnPlayerStatus(player, state, ticket);
+ }
+}
+
+void StarboardPlayer::CallbackHelper::OnDeallocateSample(
+ const void* sample_buffer) {
+ base::AutoLock auto_lock(lock_);
+ if (player_) {
+ player_->OnDeallocateSample(sample_buffer);
+ }
+}
+
+void StarboardPlayer::CallbackHelper::ResetPlayer() {
+ base::AutoLock auto_lock(lock_);
+ player_ = NULL;
+}
+
StarboardPlayer::StarboardPlayer(
const scoped_refptr<base::MessageLoopProxy>& message_loop,
const AudioDecoderConfig& audio_config,
@@ -34,13 +76,14 @@
SbDrmSystem drm_system, Host* host,
SbPlayerSetBoundsHelper* set_bounds_helper, bool prefer_decode_to_texture)
: message_loop_(message_loop),
+ callback_helper_(
+ new CallbackHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this))),
audio_config_(audio_config),
video_config_(video_config),
window_(window),
drm_system_(drm_system),
host_(host),
set_bounds_helper_(set_bounds_helper),
- weak_this_(AsWeakPtr()),
frame_width_(1),
frame_height_(1),
ticket_(SB_PLAYER_INITIAL_TICKET),
@@ -53,29 +96,33 @@
DCHECK(host_);
DCHECK(set_bounds_helper_);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
output_mode_ = ComputeSbPlayerOutputMode(
MediaVideoCodecToSbMediaVideoCodec(video_config.codec()), drm_system,
prefer_decode_to_texture);
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
CreatePlayer();
message_loop->PostTask(
FROM_HERE,
- base::Bind(&StarboardPlayer::ClearDecoderBufferCache, weak_this_));
+ base::Bind(&StarboardPlayer::CallbackHelper::ClearDecoderBufferCache,
+ callback_helper_));
}
StarboardPlayer::~StarboardPlayer() {
DCHECK(message_loop_->BelongsToCurrentThread());
+ callback_helper_->ResetPlayer();
set_bounds_helper_->SetPlayer(NULL);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+ ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+ ShellVideoFrameProvider::kOutputModeInvalid);
+#if SB_API_VERSION >= 4
ShellMediaPlatform::Instance()
->GetVideoFrameProvider()
->ResetGetCurrentSbDecodeTargetFunction();
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
SbPlayerDestroy(player_);
}
@@ -129,7 +176,7 @@
video_info.frame_width = frame_width_;
video_info.frame_height = frame_height_;
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
SbMediaColorMetadata sb_media_color_metadata =
MediaToSbMediaColorMetadata(video_config_.webm_color_metadata());
video_info.color_metadata = &sb_media_color_metadata;
@@ -137,27 +184,44 @@
if (is_encrypted) {
FillDrmSampleInfo(buffer, &drm_info, &subsample_mapping);
}
+
+#if SB_API_VERSION >= 4
+ const void* sample_buffers[] = {buffer->data()};
+ int sample_buffer_sizes[] = {buffer->data_size()};
+ SbPlayerWriteSample(player_, DemuxerStreamTypeToSbMediaType(type),
+ sample_buffers, sample_buffer_sizes, 1,
+ TimeDeltaToSbMediaTime(buffer->timestamp()),
+ type == DemuxerStream::VIDEO ? &video_info : NULL,
+ drm_info.subsample_count > 0 ? &drm_info : NULL);
+#else // SB_API_VERSION >= 4
SbPlayerWriteSample(player_, DemuxerStreamTypeToSbMediaType(type),
buffer->data(), buffer->data_size(),
TimeDeltaToSbMediaTime(buffer->timestamp()),
type == DemuxerStream::VIDEO ? &video_info : NULL,
drm_info.subsample_count > 0 ? &drm_info : NULL);
+#endif // SB_API_VERSION >= 4
}
void StarboardPlayer::SetBounds(const gfx::Rect& rect) {
DCHECK(SbPlayerIsValid(player_));
+#if SB_API_VERSION >= 4
+ const int kZIndex = 0;
+ SbPlayerSetBounds(player_, kZIndex, rect.x(), rect.y(), rect.width(),
+ rect.height());
+#else // SB_API_VERSION >= 4
SbPlayerSetBounds(player_, rect.x(), rect.y(), rect.width(), rect.height());
+#endif // SB_API_VERSION >= 4
}
void StarboardPlayer::PrepareForSeek() {
DCHECK(message_loop_->BelongsToCurrentThread());
++ticket_;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
SbPlayerSetPause(player_, true);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, 0.f);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
seek_pending_ = true;
}
@@ -183,11 +247,11 @@
++ticket_;
SbPlayerSeek(player_, TimeDeltaToSbMediaTime(time), ticket_);
seek_pending_ = false;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
}
void StarboardPlayer::SetVolume(float volume) {
@@ -209,11 +273,11 @@
DCHECK(SbPlayerIsValid(player_));
playback_rate_ = playback_rate;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
SbPlayerSetPause(player_, playback_rate == 0.0);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, playback_rate);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
}
void StarboardPlayer::GetInfo(uint32* video_frames_decoded,
@@ -263,11 +327,11 @@
DCHECK(SbPlayerIsValid(player_));
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
SbPlayerSetPause(player_, true);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, 0.0);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
base::AutoLock auto_lock(lock_);
@@ -304,7 +368,27 @@
state_ = kResuming;
}
+namespace {
+#if SB_API_VERSION >= 4
+ShellVideoFrameProvider::OutputMode ToVideoFrameProviderOutputMode(
+ SbPlayerOutputMode output_mode) {
+ switch (output_mode) {
+ case kSbPlayerOutputModeDecodeToTexture:
+ return ShellVideoFrameProvider::kOutputModeDecodeToTexture;
+ case kSbPlayerOutputModePunchOut:
+ return ShellVideoFrameProvider::kOutputModePunchOut;
+ case kSbPlayerOutputModeInvalid:
+ return ShellVideoFrameProvider::kOutputModeInvalid;
+ }
+
+ NOTREACHED();
+ return ShellVideoFrameProvider::kOutputModeInvalid;
+}
+#endif // #if SB_API_VERSION >= 4
+} // namespace
+
void StarboardPlayer::CreatePlayer() {
+ TRACE_EVENT0("cobalt::media", "StarboardPlayer::CreatePlayer");
DCHECK(message_loop_->BelongsToCurrentThread());
SbMediaAudioHeader audio_header;
@@ -330,26 +414,26 @@
SbMediaVideoCodec video_codec =
MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
player_ = SbPlayerCreate(
window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
&audio_header, &StarboardPlayer::DeallocateSampleCB,
&StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, this
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
,
- output_mode_
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+ output_mode_,
+ ShellMediaPlatform::Instance()->GetSbDecodeTargetGraphicsContextProvider()
+#elif SB_API_VERSION >= 3
,
ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider() // provider
#endif // SB_API_VERSION >= 3
- );
+ );
DCHECK(SbPlayerIsValid(player_));
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
// If the player is setup to decode to texture, then provide Cobalt with
// a method of querying that texture.
@@ -359,19 +443,24 @@
base::Bind(&StarboardPlayer::GetCurrentSbDecodeTarget,
base::Unretained(this)));
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+ ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+ ToVideoFrameProviderOutputMode(output_mode_));
+#else // SB_API_VERSION >= 4
+ ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+ ShellVideoFrameProvider::kOutputModePunchOut);
+#endif // SB_API_VERSION >= 4
set_bounds_helper_->SetPlayer(this);
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget StarboardPlayer::GetCurrentSbDecodeTarget() {
return SbPlayerGetCurrentFrame(player_);
}
SbPlayerOutputMode StarboardPlayer::GetSbPlayerOutputMode() {
return output_mode_;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
void StarboardPlayer::ClearDecoderBufferCache() {
DCHECK(message_loop_->BelongsToCurrentThread());
@@ -382,7 +471,8 @@
message_loop_->PostDelayedTask(
FROM_HERE,
- base::Bind(&StarboardPlayer::ClearDecoderBufferCache, weak_this_),
+ base::Bind(&StarboardPlayer::CallbackHelper::ClearDecoderBufferCache,
+ callback_helper_),
base::TimeDelta::FromMilliseconds(
kClearDecoderCacheIntervalInMilliseconds));
}
@@ -443,11 +533,11 @@
}
SbPlayerSeek(player_, TimeDeltaToSbMediaTime(preroll_timestamp_), ticket_);
SetVolume(volume_);
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
return;
}
host_->OnPlayerStatus(state);
@@ -475,8 +565,9 @@
SbPlayerDecoderState state, int ticket) {
StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context);
helper->message_loop_->PostTask(
- FROM_HERE, base::Bind(&StarboardPlayer::OnDecoderStatus,
- helper->weak_this_, player, type, state, ticket));
+ FROM_HERE,
+ base::Bind(&StarboardPlayer::CallbackHelper::OnDecoderStatus,
+ helper->callback_helper_, player, type, state, ticket));
}
// static
@@ -484,8 +575,8 @@
SbPlayerState state, int ticket) {
StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context);
helper->message_loop_->PostTask(
- FROM_HERE, base::Bind(&StarboardPlayer::OnPlayerStatus,
- helper->weak_this_, player, state, ticket));
+ FROM_HERE, base::Bind(&StarboardPlayer::CallbackHelper::OnPlayerStatus,
+ helper->callback_helper_, player, state, ticket));
}
// static
@@ -493,11 +584,12 @@
const void* sample_buffer) {
StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context);
helper->message_loop_->PostTask(
- FROM_HERE, base::Bind(&StarboardPlayer::OnDeallocateSample,
- helper->weak_this_, sample_buffer));
+ FROM_HERE,
+ base::Bind(&StarboardPlayer::CallbackHelper::OnDeallocateSample,
+ helper->callback_helper_, sample_buffer));
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// static
SbPlayerOutputMode StarboardPlayer::ComputeSbPlayerOutputMode(
SbMediaVideoCodec codec, SbDrmSystem drm_system,
@@ -511,7 +603,7 @@
output_mode = kSbPlayerOutputModePunchOut;
}
if ((prefer_decode_to_texture || output_mode == kSbPlayerOutputModeInvalid) &&
- SbPlayerOutputModeSupported(kSbPlayerOutputModePunchOut, codec,
+ SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture, codec,
drm_system)) {
output_mode = kSbPlayerOutputModeDecodeToTexture;
}
@@ -519,7 +611,7 @@
return output_mode;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
} // namespace media
} // namespace cobalt
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h
index 548014e..5acfecd 100644
--- a/src/cobalt/media/base/starboard_player.h
+++ b/src/cobalt/media/base/starboard_player.h
@@ -19,7 +19,6 @@
#include <utility>
#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
#include "base/message_loop_proxy.h"
#include "base/synchronization/lock.h"
#include "base/time.h"
@@ -36,7 +35,7 @@
namespace media {
// TODO: Add switch to disable caching
-class StarboardPlayer : public base::SupportsWeakPtr<StarboardPlayer> {
+class StarboardPlayer {
public:
class Host {
public:
@@ -74,10 +73,10 @@
void Suspend();
void Resume();
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget GetCurrentSbDecodeTarget();
SbPlayerOutputMode GetSbPlayerOutputMode();
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
private:
enum State {
@@ -86,6 +85,24 @@
kResuming,
};
+ // This class ensures that the callbacks posted to |message_loop_| are ignored
+ // automatically once StarboardPlayer is destroyed.
+ class CallbackHelper : public base::RefCountedThreadSafe<CallbackHelper> {
+ public:
+ explicit CallbackHelper(StarboardPlayer* player);
+
+ void ClearDecoderBufferCache();
+ void OnDecoderStatus(SbPlayer player, SbMediaType type,
+ SbPlayerDecoderState state, int ticket);
+ void OnPlayerStatus(SbPlayer player, SbPlayerState state, int ticket);
+ void OnDeallocateSample(const void* sample_buffer);
+ void ResetPlayer();
+
+ private:
+ base::Lock lock_;
+ StarboardPlayer* player_;
+ };
+
static const int64 kClearDecoderCacheIntervalInMilliseconds = 1000;
// A map from raw data pointer returned by DecoderBuffer::GetData() to the
@@ -110,23 +127,24 @@
static void DeallocateSampleCB(SbPlayer player, void* context,
const void* sample_buffer);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// Returns the output mode that should be used for a video with the given
// specifications.
static SbPlayerOutputMode ComputeSbPlayerOutputMode(
SbMediaVideoCodec codec, SbDrmSystem drm_system,
bool prefer_decode_to_texture);
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
// The following variables are initialized in the ctor and never changed.
const scoped_refptr<base::MessageLoopProxy> message_loop_;
+ scoped_refptr<CallbackHelper> callback_helper_;
AudioDecoderConfig audio_config_;
VideoDecoderConfig video_config_;
const SbWindow window_;
const SbDrmSystem drm_system_;
Host* const host_;
+ // Consider merge |SbPlayerSetBoundsHelper| into CallbackHelper.
SbPlayerSetBoundsHelper* const set_bounds_helper_;
- const base::WeakPtr<StarboardPlayer> weak_this_;
// The following variables are only changed or accessed from the
// |message_loop_|.
@@ -149,10 +167,10 @@
uint32 cached_video_frames_dropped_;
base::TimeDelta preroll_timestamp_;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// Keep track of the output mode we are supposed to output to.
SbPlayerOutputMode output_mode_;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
};
} // namespace media
diff --git a/src/cobalt/media/base/starboard_utils.cc b/src/cobalt/media/base/starboard_utils.cc
index 4344006..72c4c72 100644
--- a/src/cobalt/media/base/starboard_utils.cc
+++ b/src/cobalt/media/base/starboard_utils.cc
@@ -128,7 +128,7 @@
}
}
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
// Ensure that the enums in starboard/media.h match enums in gfx::ColorSpace.
#define ENUM_EQ(a, b) \
COMPILE_ASSERT(static_cast<int>(a) == static_cast<int>(b), mismatching_enums)
@@ -279,7 +279,7 @@
return sb_media_color_metadata;
}
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif // SB_API_VERSION >= 3
} // namespace media
} // namespace cobalt
diff --git a/src/cobalt/media/base/starboard_utils.h b/src/cobalt/media/base/starboard_utils.h
index 5f965a6..0cb43c1 100644
--- a/src/cobalt/media/base/starboard_utils.h
+++ b/src/cobalt/media/base/starboard_utils.h
@@ -39,7 +39,7 @@
SbDrmSampleInfo* drm_info,
SbDrmSubSampleMapping* subsample_mapping);
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
SbMediaColorMetadata MediaToSbMediaColorMetadata(
const WebMColorMetadata& webm_color_metadata);
#endif
diff --git a/src/cobalt/media/base/video_resolution.h b/src/cobalt/media/base/video_resolution.h
new file mode 100644
index 0000000..fb4c3fb
--- /dev/null
+++ b/src/cobalt/media/base/video_resolution.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_MEDIA_BASE_VIDEO_RESOLUTION_H_
+#define COBALT_MEDIA_BASE_VIDEO_RESOLUTION_H_
+
+#include "ui/gfx/size.h"
+
+namespace cobalt {
+namespace media {
+
+// Enumerates the various representations of the resolution of videos. Note
+// that except |kVideoResolutionInvalid|, all other values are guaranteed to be
+// in the same order as its (width, height) pair.
+enum VideoResolution {
+ kVideoResolution1080p, // 1920 x 1080
+ kVideoResolution2k, // 2560 x 1440
+ kVideoResolution4k, // 3840 x 2160
+ kVideoResolutionInvalid
+};
+
+inline VideoResolution GetVideoResolution(int width, int height) {
+ if (width <= 1920 && height <= 1080) {
+ return kVideoResolution1080p;
+ }
+ if (width <= 2560 && height <= 1440) {
+ return kVideoResolution2k;
+ }
+ if (width <= 3840 && height <= 2160) {
+ return kVideoResolution4k;
+ }
+ return kVideoResolutionInvalid;
+}
+
+inline VideoResolution GetVideoResolution(const gfx::Size& size) {
+ return GetVideoResolution(size.width(), size.height());
+}
+
+} // namespace media
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_BASE_VIDEO_RESOLUTION_H_
diff --git a/src/cobalt/media/decoder_buffer_allocator.cc b/src/cobalt/media/decoder_buffer_allocator.cc
index bf1f4ac..f60a1c7 100644
--- a/src/cobalt/media/decoder_buffer_allocator.cc
+++ b/src/cobalt/media/decoder_buffer_allocator.cc
@@ -14,6 +14,7 @@
#include "cobalt/media/decoder_buffer_allocator.h"
+#include "starboard/common/scoped_ptr.h"
#include "starboard/configuration.h"
#include "starboard/memory.h"
@@ -21,30 +22,32 @@
namespace media {
namespace {
-bool kThreadSafe = true;
bool kPreAllocateAllMemory = true;
} // namespace
DecoderBufferAllocator::DecoderBufferAllocator()
- : memory_block_(SbMemoryAllocateAligned(DecoderBuffer::kAlignmentSize,
- SB_MEDIA_MAIN_BUFFER_BUDGET)),
- memory_pool_(memory_block_, SB_MEDIA_MAIN_BUFFER_BUDGET, kThreadSafe,
- kPreAllocateAllMemory) {}
+ : memory_block_(
+ SbMemoryAllocateAligned(DecoderBuffer::kAlignmentSize,
+ COBALT_MEDIA_BUFFER_INITIAL_CAPACITY)) {
+ memory_pool_.set(starboard::make_scoped_ptr(
+ new nb::MemoryPool(memory_block_, COBALT_MEDIA_BUFFER_INITIAL_CAPACITY,
+ kPreAllocateAllMemory)));
+}
DecoderBufferAllocator::~DecoderBufferAllocator() {
- DCHECK_EQ(memory_pool_.GetAllocated(), 0);
+ DCHECK_EQ(memory_pool_->GetAllocated(), 0);
SbMemoryDeallocateAligned(memory_block_);
}
void* DecoderBufferAllocator::Allocate(Type type, size_t size,
size_t alignment) {
UNREFERENCED_PARAMETER(type);
- return memory_pool_.Allocate(size, alignment);
+ return memory_pool_->Allocate(size, alignment);
}
void DecoderBufferAllocator::Free(Type type, void* ptr) {
UNREFERENCED_PARAMETER(type);
- memory_pool_.Free(ptr);
+ memory_pool_->Free(ptr);
}
} // namespace media
diff --git a/src/cobalt/media/decoder_buffer_allocator.h b/src/cobalt/media/decoder_buffer_allocator.h
index d7a9a00..b83426b 100644
--- a/src/cobalt/media/decoder_buffer_allocator.h
+++ b/src/cobalt/media/decoder_buffer_allocator.h
@@ -18,6 +18,7 @@
#include "base/compiler_specific.h"
#include "cobalt/media/base/decoder_buffer.h"
#include "nb/memory_pool.h"
+#include "starboard/common/locked_ptr.h"
namespace cobalt {
namespace media {
@@ -32,7 +33,7 @@
private:
void* memory_block_;
- nb::MemoryPool memory_pool_;
+ starboard::LockedPtr<nb::MemoryPool> memory_pool_;
};
} // namespace media
diff --git a/src/cobalt/media/fetcher_buffered_data_source.cc b/src/cobalt/media/fetcher_buffered_data_source.cc
index 0e20745..4ae7605 100644
--- a/src/cobalt/media/fetcher_buffered_data_source.cc
+++ b/src/cobalt/media/fetcher_buffered_data_source.cc
@@ -175,7 +175,6 @@
void FetcherBufferedDataSource::OnURLFetchDownloadData(
const net::URLFetcher* source, scoped_ptr<std::string> download_data) {
- UNREFERENCED_PARAMETER(source);
DCHECK(message_loop_->BelongsToCurrentThread());
size_t size = download_data->size();
if (size == 0) {
@@ -252,7 +251,7 @@
const net::URLRequestStatus& status = source->GetStatus();
if (status.is_success()) {
- if (total_size_of_resource_ && last_request_size_ != 0) {
+ if (!total_size_of_resource_ && last_request_size_ != 0) {
total_size_of_resource_ = buffer_offset_ + buffer_.GetLength();
}
} else {
@@ -273,7 +272,8 @@
base::AutoLock auto_lock(lock_);
- fetcher_.reset();
+ DCHECK(!fetcher_);
+ fetcher_to_be_destroyed_.reset();
DCHECK_GE(static_cast<int64>(last_request_offset_), 0);
DCHECK_GE(static_cast<int64>(last_request_size_), 0);
@@ -352,8 +352,8 @@
pending_read_size_ = size;
pending_read_data_ = data;
- // Combine the range of the buffer and any ongoing fetch to see if the read
- // is overlapped with it.
+ // Combine the range of the buffer and any ongoing fetch to see if the read is
+ // overlapped with it.
if (fetcher_) {
uint64 begin = last_request_offset_;
uint64 end = last_request_offset_ + last_request_size_;
@@ -367,9 +367,9 @@
}
}
- // Now we have to issue a new fetch and we no longer care about the range
- // of the current fetch in progress if there is any. Ideally the request
- // range starts at |last_read_position_ - kBackwardBytes| with length of
+ // Now we have to issue a new fetch and we no longer care about the range of
+ // the current fetch in progress if there is any. Ideally the request range
+ // starts at |last_read_position_ - kBackwardBytes| with length of
// buffer_.GetMaxCapacity().
if (last_read_position_ > kBackwardBytes) {
last_request_offset_ = last_read_position_ - kBackwardBytes;
@@ -406,6 +406,7 @@
&FetcherBufferedDataSource::CreateNewFetcher, base::Unretained(this));
cancelable_create_fetcher_closure_ =
new CancelableClosure(create_fetcher_closure);
+ fetcher_to_be_destroyed_.reset(fetcher_.release());
message_loop_->PostTask(FROM_HERE,
cancelable_create_fetcher_closure_->AsClosure());
}
diff --git a/src/cobalt/media/fetcher_buffered_data_source.h b/src/cobalt/media/fetcher_buffered_data_source.h
index 8cccda9..1afea8c 100644
--- a/src/cobalt/media/fetcher_buffered_data_source.h
+++ b/src/cobalt/media/fetcher_buffered_data_source.h
@@ -115,6 +115,12 @@
GURL url_;
network::NetworkModule* network_module_;
scoped_ptr<net::URLFetcher> fetcher_;
+ // |fetcher_| has to be destroyed on the thread it's created. So it cannot be
+ // safely destroyed inside Read_Locked(). Save |fetcher_| into
+ // |fetcher_to_be_destroyed_| to ensure that it is properly destroyed either
+ // inside CreateNewFetcher() or in the dtor while still allow |fetcher_| to be
+ // set to NULL to invalidate outstanding read.
+ scoped_ptr<net::URLFetcher> fetcher_to_be_destroyed_;
// |buffer_| stores a continuous block of data of target resource starts from
// |buffer_offset_|. When the target resource can be fit into |buffer_|,
diff --git a/src/cobalt/media/filters/chunk_demuxer.cc b/src/cobalt/media/filters/chunk_demuxer.cc
index aae74ed..66cb0c0 100644
--- a/src/cobalt/media/filters/chunk_demuxer.cc
+++ b/src/cobalt/media/filters/chunk_demuxer.cc
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "base/debug/trace_event.h"
#include "base/location.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
@@ -422,6 +423,7 @@
void ChunkDemuxer::Initialize(DemuxerHost* host,
const PipelineStatusCB& init_cb,
bool enable_text_tracks) {
+ TRACE_EVENT0("cobalt::media", "ChunkDemuxer::Initialize");
DLOG(INFO) << "This is an ASYNC MEDIA SOURCE playback.";
DVLOG(1) << "Init()";
@@ -1111,6 +1113,7 @@
void ChunkDemuxer::OnSourceInitDone(
const std::string& source_id, const StreamParser::InitParameters& params) {
+ TRACE_EVENT0("cobalt::media", "ChunkDemuxer::OnSourceInitDone");
DVLOG(1) << "OnSourceInitDone source_id=" << source_id
<< " duration=" << params.duration.InSecondsF();
lock_.AssertAcquired();
diff --git a/src/cobalt/media/filters/source_buffer_platform.cc b/src/cobalt/media/filters/source_buffer_platform.cc
deleted file mode 100644
index c87febf..0000000
--- a/src/cobalt/media/filters/source_buffer_platform.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cobalt/media/filters/source_buffer_platform.h"
-
-#include "starboard/configuration.h"
-
-namespace cobalt {
-namespace media {
-
-// TODO: These limits should be determinated during runtime for individual video
-// when multiple video tags are supported.
-const size_t kSourceBufferAudioMemoryLimit =
- SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT;
-const size_t kSourceBufferVideoMemoryLimit =
- SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT;
-
-} // namespace media
-} // namespace cobalt
diff --git a/src/cobalt/media/filters/source_buffer_platform.h b/src/cobalt/media/filters/source_buffer_platform.h
deleted file mode 100644
index da89b57..0000000
--- a/src/cobalt/media/filters/source_buffer_platform.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COBALT_MEDIA_FILTERS_SOURCE_BUFFER_PLATFORM_H_
-#define COBALT_MEDIA_FILTERS_SOURCE_BUFFER_PLATFORM_H_
-
-#include "cobalt/media/base/media_export.h"
-#include "starboard/types.h"
-
-namespace cobalt {
-namespace media {
-
-// The maximum amount of data in bytes the stream will keep in memory.
-MEDIA_EXPORT extern const size_t kSourceBufferAudioMemoryLimit;
-MEDIA_EXPORT extern const size_t kSourceBufferVideoMemoryLimit;
-
-} // namespace media
-} // namespace cobalt
-
-#endif // COBALT_MEDIA_FILTERS_SOURCE_BUFFER_PLATFORM_H_
diff --git a/src/cobalt/media/filters/source_buffer_platform_lowmem.cc b/src/cobalt/media/filters/source_buffer_platform_lowmem.cc
deleted file mode 100644
index f07feca..0000000
--- a/src/cobalt/media/filters/source_buffer_platform_lowmem.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cobalt/media/filters/source_buffer_platform.h"
-#include "starboard/types.h"
-
-namespace cobalt {
-namespace media {
-
-// 2MB: approximately 1 minute of 256Kbps content.
-// 30MB: approximately 1 minute of 4Mbps content.
-const size_t kSourceBufferAudioMemoryLimit = 2 * 1024 * 1024;
-const size_t kSourceBufferVideoMemoryLimit = 30 * 1024 * 1024;
-
-} // namespace media
-} // namespace cobalt
diff --git a/src/cobalt/media/filters/source_buffer_stream.cc b/src/cobalt/media/filters/source_buffer_stream.cc
index 7014234..2602fd0 100644
--- a/src/cobalt/media/filters/source_buffer_stream.cc
+++ b/src/cobalt/media/filters/source_buffer_stream.cc
@@ -12,7 +12,7 @@
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "cobalt/media/base/timestamp_constants.h"
-#include "cobalt/media/filters/source_buffer_platform.h"
+#include "cobalt/media/base/video_resolution.h"
#include "cobalt/media/filters/source_buffer_range.h"
namespace cobalt {
@@ -157,7 +157,7 @@
range_for_next_append_(ranges_.end()),
last_output_buffer_timestamp_(kNoDecodeTimestamp()),
max_interbuffer_distance_(kNoTimestamp),
- memory_limit_(kSourceBufferAudioMemoryLimit) {
+ memory_limit_(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) {
DCHECK(audio_config.IsValidConfig());
audio_configs_.push_back(audio_config);
}
@@ -171,10 +171,14 @@
coded_frame_group_start_time_(kNoDecodeTimestamp()),
range_for_next_append_(ranges_.end()),
last_output_buffer_timestamp_(kNoDecodeTimestamp()),
- max_interbuffer_distance_(kNoTimestamp),
- memory_limit_(kSourceBufferVideoMemoryLimit) {
+ max_interbuffer_distance_(kNoTimestamp) {
DCHECK(video_config.IsValidConfig());
video_configs_.push_back(video_config);
+ VideoResolution resolution =
+ GetVideoResolution(video_config.visible_rect().size());
+ memory_limit_ = resolution <= kVideoResolution1080p
+ ? COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
+ : COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K;
}
SourceBufferStream::SourceBufferStream(const TextTrackConfig& text_config,
@@ -188,7 +192,7 @@
range_for_next_append_(ranges_.end()),
last_output_buffer_timestamp_(kNoDecodeTimestamp()),
max_interbuffer_distance_(kNoTimestamp),
- memory_limit_(kSourceBufferAudioMemoryLimit) {}
+ memory_limit_(COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) {}
SourceBufferStream::~SourceBufferStream() {
while (!ranges_.empty()) {
@@ -1547,6 +1551,14 @@
DVLOG(2) << "New video config - index: " << append_config_index_;
video_configs_.resize(video_configs_.size() + 1);
video_configs_[append_config_index_] = config;
+
+ VideoResolution resolution = GetVideoResolution(config.visible_rect().size());
+
+ // TODO: Reduce the memory limit when there is no more 4k samples cached.
+ if (resolution > kVideoResolution1080p) {
+ memory_limit_ = COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K;
+ }
+
return true;
}
diff --git a/src/cobalt/media/media.gyp b/src/cobalt/media/media.gyp
index 8b72dc1..383f25f 100644
--- a/src/cobalt/media/media.gyp
+++ b/src/cobalt/media/media.gyp
@@ -52,8 +52,6 @@
'sources': [
'decoder_working_memory_allocator_impl_ps4.cc',
'decoder_working_memory_allocator_impl_ps4.h',
- 'shell_video_data_allocator_stub.cc',
- 'shell_video_data_allocator_stub.h',
],
}],
['OS=="lb_shell"', {
diff --git a/src/cobalt/media/media2.gyp b/src/cobalt/media/media2.gyp
index 79bd59e..18c1683 100644
--- a/src/cobalt/media/media2.gyp
+++ b/src/cobalt/media/media2.gyp
@@ -63,6 +63,9 @@
'base/demuxer_stream.h',
'base/demuxer_stream_provider.cc',
'base/demuxer_stream_provider.h',
+ 'base/drm_system.cc',
+ 'base/drm_system.h',
+ 'base/drm_system_client.h',
'base/encryption_scheme.cc',
'base/encryption_scheme.h',
'base/hdr_metadata.cc',
@@ -116,10 +119,10 @@
'filters/h264_bit_reader.h',
'filters/h264_bitstream_buffer.cc',
'filters/h264_bitstream_buffer.h',
- 'filters/h264_to_annex_b_bitstream_converter.cc',
- 'filters/h264_to_annex_b_bitstream_converter.h',
'filters/h264_parser.cc',
'filters/h264_parser.h',
+ 'filters/h264_to_annex_b_bitstream_converter.cc',
+ 'filters/h264_to_annex_b_bitstream_converter.h',
'filters/h265_parser.cc',
'filters/h265_parser.h',
'filters/shell_au.cc',
@@ -136,8 +139,6 @@
'filters/shell_parser.h',
'filters/shell_rbsp_stream.cc',
'filters/shell_rbsp_stream.h',
- 'filters/source_buffer_platform.cc',
- 'filters/source_buffer_platform.h',
'filters/source_buffer_range.cc',
'filters/source_buffer_range.h',
'filters/source_buffer_state.cc',
@@ -193,9 +194,9 @@
'formats/webm/webm_constants.cc',
'formats/webm/webm_constants.h',
'formats/webm/webm_content_encodings.cc',
+ 'formats/webm/webm_content_encodings.h',
'formats/webm/webm_content_encodings_client.cc',
'formats/webm/webm_content_encodings_client.h',
- 'formats/webm/webm_content_encodings.h',
'formats/webm/webm_crypto_helpers.cc',
'formats/webm/webm_crypto_helpers.h',
'formats/webm/webm_info_parser.cc',
diff --git a/src/cobalt/media/media_buffer_allocator.h b/src/cobalt/media/media_buffer_allocator.h
index 0247605..b14616e 100644
--- a/src/cobalt/media/media_buffer_allocator.h
+++ b/src/cobalt/media/media_buffer_allocator.h
@@ -15,8 +15,8 @@
#ifndef COBALT_MEDIA_MEDIA_BUFFER_ALLOCATOR_H_
#define COBALT_MEDIA_MEDIA_BUFFER_ALLOCATOR_H_
-#include "base/optional.h"
#include "nb/memory_pool.h"
+#include "starboard/common/scoped_ptr.h"
namespace cobalt {
namespace media {
@@ -30,14 +30,13 @@
main_pool_size_(main_pool_size),
small_allocation_pool_size_(small_allocation_pool_size),
small_allocation_threshold_(small_allocation_threshold),
- main_pool_(pool_, main_pool_size_, true, /* thread_safe */
- true /* verify_full_capacity */) {
+ main_pool_(starboard::make_scoped_ptr(new nb::MemoryPool(
+ pool_, main_pool_size_, true /* verify_full_capacity */))) {
if (small_allocation_pool_size_ > 0u) {
DCHECK_GT(small_allocation_threshold_, 0u);
- small_allocation_pool_.emplace(pool_ + main_pool_size_,
- small_allocation_pool_size_,
- true, /* thread_safe */
- true /* verify_full_capacity */);
+ small_allocation_pool_.set(starboard::make_scoped_ptr(new nb::MemoryPool(
+ pool_ + main_pool_size_, small_allocation_pool_size_,
+ true /* verify_full_capacity */)));
} else {
DCHECK_EQ(small_allocation_pool_size_, 0u);
DCHECK_EQ(small_allocation_threshold_, 0u);
@@ -46,11 +45,12 @@
void* Allocate(size_t size, size_t alignment) {
void* p = NULL;
- if (size < small_allocation_threshold_ && small_allocation_pool_) {
+ if (size < small_allocation_threshold_ &&
+ small_allocation_pool_->is_valid()) {
p = small_allocation_pool_->Allocate(size, alignment);
}
if (!p) {
- p = main_pool_.Allocate(size, alignment);
+ p = main_pool_->Allocate(size, alignment);
}
if (!p && small_allocation_pool_) {
p = small_allocation_pool_->Allocate(size, alignment);
@@ -59,14 +59,14 @@
}
void Free(void* p) {
- if (p >= pool_ + main_pool_size_ && small_allocation_pool_) {
+ if (p >= pool_ + main_pool_size_ && small_allocation_pool_->is_valid()) {
DCHECK_LT(p, pool_ + main_pool_size_ + small_allocation_pool_size_);
small_allocation_pool_->Free(p);
return;
}
DCHECK_GE(p, pool_);
DCHECK_LT(p, pool_ + main_pool_size_);
- main_pool_.Free(p);
+ main_pool_->Free(p);
}
private:
@@ -75,8 +75,8 @@
size_t small_allocation_pool_size_;
size_t small_allocation_threshold_;
- nb::MemoryPool main_pool_;
- base::optional<nb::MemoryPool> small_allocation_pool_;
+ starboard::LockedPtr<nb::MemoryPool> main_pool_;
+ starboard::LockedPtr<nb::MemoryPool> small_allocation_pool_;
DISALLOW_COPY_AND_ASSIGN(MediaBufferAllocator);
};
diff --git a/src/cobalt/media/media_module.cc b/src/cobalt/media/media_module.cc
index 0c68cbf..d92a105 100644
--- a/src/cobalt/media/media_module.cc
+++ b/src/cobalt/media/media_module.cc
@@ -49,8 +49,8 @@
OnSuspend();
}
-void MediaModule::Resume() {
- OnResume();
+void MediaModule::Resume(render_tree::ResourceProvider* resource_provider) {
+ OnResume(resource_provider);
RunClosureOnMessageLoopAndWait(
message_loop_,
base::Bind(&MediaModule::ResumeTask, base::Unretained(this)));
diff --git a/src/cobalt/media/media_module.h b/src/cobalt/media/media_module.h
index d5599c0..f45ef44 100644
--- a/src/cobalt/media/media_module.h
+++ b/src/cobalt/media/media_module.h
@@ -84,12 +84,14 @@
// from the main thread. Sub-classes can override these functions for
// platform specific tasks.
virtual void OnSuspend() {}
- virtual void OnResume() {}
+ virtual void OnResume(render_tree::ResourceProvider* resource_provider) {
+ UNREFERENCED_PARAMETER(resource_provider);
+ }
virtual system_window::SystemWindow* system_window() const { return NULL; }
void Suspend();
- void Resume();
+ void Resume(render_tree::ResourceProvider* resource_provider);
#if !defined(COBALT_MEDIA_SOURCE_2016)
#if !defined(COBALT_BUILD_TYPE_GOLD)
diff --git a/src/cobalt/media/media_module_starboard.cc b/src/cobalt/media/media_module_starboard.cc
index b8cd20a..cbfd9d2 100644
--- a/src/cobalt/media/media_module_starboard.cc
+++ b/src/cobalt/media/media_module_starboard.cc
@@ -99,6 +99,12 @@
return system_window_;
}
+ void OnSuspend() OVERRIDE { media_platform_.Suspend(); }
+
+ void OnResume(render_tree::ResourceProvider* resource_provider) OVERRIDE {
+ media_platform_.Resume(resource_provider);
+ }
+
private:
const Options options_;
system_window::SystemWindow* system_window_;
diff --git a/src/cobalt/media/media_module_stub.cc b/src/cobalt/media/media_module_stub.cc
index 174078a..f857978 100644
--- a/src/cobalt/media/media_module_stub.cc
+++ b/src/cobalt/media/media_module_stub.cc
@@ -26,7 +26,7 @@
typedef ::media::WebMediaPlayer WebMediaPlayer;
typedef ::media::WebMediaPlayerClient WebMediaPlayerClient;
using ::media::Ranges;
-#endif // !defined(WebMediaPlayerDelegate)
+#endif // !defined(COBALT_MEDIA_SOURCE_2016)
namespace {
@@ -82,7 +82,9 @@
ReadyState GetReadyState() const OVERRIDE { return kReadyStateHaveNothing; }
bool DidLoadingProgress() const OVERRIDE { return false; }
+#if !defined(COBALT_MEDIA_SOURCE_2016)
unsigned long long GetTotalBytes() const OVERRIDE { return 0; }
+#endif // !defined(COBALT_MEDIA_SOURCE_2016)
bool HasSingleSecurityOrigin() const OVERRIDE { return false; }
bool DidPassCORSAccessCheck() const OVERRIDE { return false; }
@@ -97,6 +99,10 @@
unsigned GetAudioDecodedByteCount() const OVERRIDE { return 0; }
unsigned GetVideoDecodedByteCount() const OVERRIDE { return 0; }
+#if defined(COBALT_MEDIA_SOURCE_2016)
+ void SetDrmSystem(DrmSystem* drm_system) OVERRIDE {}
+#endif // defined(COBALT_MEDIA_SOURCE_2016)
+
Ranges<base::TimeDelta> buffer_;
};
diff --git a/src/cobalt/media/player/web_media_player.h b/src/cobalt/media/player/web_media_player.h
index 70ecf2d..6cd7800 100644
--- a/src/cobalt/media/player/web_media_player.h
+++ b/src/cobalt/media/player/web_media_player.h
@@ -26,13 +26,11 @@
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
-// Disable `unreferenced formal parameter` as we have many stub functions in
-// this file and we want to keep their parameters.
-MSVC_PUSH_DISABLE_WARNING(4100)
-
namespace cobalt {
namespace media {
+class DrmSystem;
+
class WebMediaPlayer {
public:
// Return true if the punch through box should be rendered. Return false if
@@ -203,8 +201,16 @@
size_t* /*out_size*/) {
return false;
}
+
+ // Sets the DRM system which must be initialized with the data passed
+ // in |WebMediaPlayerClient::EncryptedMediaInitData|.
+ //
+ // |drm_system| must not be NULL. The method can only be called once.
+ virtual void SetDrmSystem(DrmSystem* drm_system) = 0;
};
+// TODO: Add prefix "On" to all methods that handle events, such
+// as |NetworkStateChanged|, |SourceOpened|, |EncryptedMediaInitData|.
class WebMediaPlayerClient {
public:
enum MediaKeyErrorCode {
@@ -239,6 +245,12 @@
// one that is. This can be used to indicate that, say, for spherical video
// playback, we would like a decode-to-texture output mode.
virtual bool PreferDecodeToTexture() const { return false; }
+ // Notifies the client that a video is encrypted. Client is supposed to call
+ // |WebMediaPlayer::SetDrmSystem| as soon as possible to avoid stalling
+ // playback.
+ virtual void EncryptedMediaInitData(EmeInitDataType init_data_type,
+ const unsigned char* init_data,
+ unsigned init_data_length) = 0;
// TODO: Make the EME related functions pure virtual again once
// we have proper EME implementation. Currently empty implementation are
// provided to make media temporarily work.
@@ -275,6 +287,4 @@
} // namespace media
} // namespace cobalt
-MSVC_POP_WARNING()
-
#endif // COBALT_MEDIA_PLAYER_WEB_MEDIA_PLAYER_H_
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index 238e585..7bff85f 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -12,12 +12,14 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
+#include "base/debug/trace_event.h"
#include "base/float_util.h"
#include "base/message_loop_proxy.h"
#include "base/metrics/histogram.h"
#include "base/string_number_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "cobalt/media/base/bind_to_current_loop.h"
+#include "cobalt/media/base/drm_system.h"
#include "cobalt/media/base/limits.h"
#include "cobalt/media/base/media_log.h"
#include "cobalt/media/filters/chunk_demuxer.h"
@@ -118,7 +120,10 @@
incremented_externally_allocated_memory_(false),
is_local_source_(false),
supports_save_(true),
- suppress_destruction_errors_(false) {
+ suppress_destruction_errors_(false),
+ drm_system_(NULL) {
+ TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::WebMediaPlayerImpl");
+
DCHECK(buffer_allocator_);
media_log_->AddEvent(
media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED));
@@ -136,12 +141,10 @@
}
WebMediaPlayerImpl::~WebMediaPlayerImpl() {
+ TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::~WebMediaPlayerImpl");
+
DCHECK(!main_loop_ || main_loop_ == MessageLoop::current());
- if (video_frame_provider_) {
- video_frame_provider_->SetOutputMode(
- ShellVideoFrameProvider::kOutputModeInvalid);
- }
if (delegate_) {
delegate_->UnregisterPlayer(this);
}
@@ -196,6 +199,8 @@
} // anonymous namespace
void WebMediaPlayerImpl::LoadMediaSource() {
+ TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::LoadMediaSource");
+
DCHECK_EQ(main_loop_, MessageLoop::current());
// Handle any volume changes that occured before load().
@@ -219,6 +224,8 @@
void WebMediaPlayerImpl::LoadProgressive(
const GURL& url, scoped_ptr<BufferedDataSource> data_source,
CORSMode cors_mode) {
+ TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::LoadProgressive");
+
DCHECK_EQ(main_loop_, MessageLoop::current());
UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(url), kMaxURLScheme);
@@ -247,6 +254,8 @@
}
void WebMediaPlayerImpl::Play() {
+ TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::Play");
+
DCHECK_EQ(main_loop_, MessageLoop::current());
#if defined(__LB_ANDROID__)
audio_focus_bridge_.RequestAudioFocus();
@@ -299,9 +308,6 @@
state_.pending_seek_seconds = seconds;
if (chunk_demuxer_) {
chunk_demuxer_->CancelPendingSeek(ConvertSecondsToTimestamp(seconds));
- // TODO: Migrate the following
- // decryptor_->CancelDecrypt(Decryptor::kAudio);
- // decryptor_->CancelDecrypt(Decryptor::kVideo);
}
return;
}
@@ -317,9 +323,6 @@
if (chunk_demuxer_) {
chunk_demuxer_->StartWaitingForSeek(seek_time);
- // TODO: Migrate the following
- // decryptor_->CancelDecrypt(Decryptor::kAudio);
- // decryptor_->CancelDecrypt(Decryptor::kVideo);
}
// Kick off the asynchronous seek!
@@ -529,6 +532,24 @@
return true;
}
+void WebMediaPlayerImpl::SetDrmSystem(DrmSystem* drm_system) {
+ DCHECK_EQ(static_cast<DrmSystem*>(NULL), drm_system_);
+ DCHECK_NE(static_cast<DrmSystem*>(NULL), drm_system);
+
+ drm_system_ = drm_system;
+ if (!drm_system_ready_cb_.is_null()) {
+ drm_system_ready_cb_.Run(drm_system_->wrapped_drm_system());
+ }
+}
+
+void WebMediaPlayerImpl::SetDrmSystemReadyCB(
+ const DrmSystemReadyCB& drm_system_ready_cb) {
+ drm_system_ready_cb_ = drm_system_ready_cb;
+ if (drm_system_) {
+ drm_system_ready_cb_.Run(drm_system_->wrapped_drm_system());
+ }
+}
+
void WebMediaPlayerImpl::OnPipelineSeek(PipelineStatus status) {
DCHECK_EQ(main_loop_, MessageLoop::current());
state_.starting = false;
@@ -615,10 +636,6 @@
switch (buffering_state) {
case Pipeline::kHaveMetadata:
- video_frame_provider_->SetOutputMode(
- (pipeline_->IsPunchOutMode()
- ? ShellVideoFrameProvider::kOutputModePunchOut
- : ShellVideoFrameProvider::kOutputModeDecodeToTexture));
SetReadyState(WebMediaPlayer::kReadyStateHaveMetadata);
break;
case Pipeline::kPrerollCompleted:
@@ -628,8 +645,10 @@
}
void WebMediaPlayerImpl::OnDemuxerOpened() {
+ TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::OnDemuxerOpened");
DCHECK_EQ(main_loop_, MessageLoop::current());
DCHECK(chunk_demuxer_);
+
GetClient()->SourceOpened(chunk_demuxer_.get());
}
@@ -651,15 +670,18 @@
}
void WebMediaPlayerImpl::StartPipeline(Demuxer* demuxer) {
+ TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::StartPipeline");
+
state_.starting = true;
+ pipeline_->SetDecodeToTextureOutputMode(client_->PreferDecodeToTexture());
pipeline_->Start(
- demuxer, BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
+ demuxer, BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetDrmSystemReadyCB),
+ BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError),
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek),
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingState),
- BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged),
- client_->PreferDecodeToTexture());
+ BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged));
}
void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) {
@@ -690,6 +712,8 @@
}
void WebMediaPlayerImpl::Destroy() {
+ TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::Destroy");
+
DCHECK(!main_loop_ || main_loop_ == MessageLoop::current());
// If |main_loop_| has already stopped, do nothing here.
@@ -735,8 +759,10 @@
void WebMediaPlayerImpl::OnEncryptedMediaInitData(
EmeInitDataType init_data_type, const std::vector<uint8_t>& init_data) {
- // TODO: Implement EME.
- NOTREACHED();
+ DCHECK_EQ(main_loop_, MessageLoop::current());
+
+ GetClient()->EncryptedMediaInitData(init_data_type, &init_data[0],
+ init_data.size());
}
WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() {
diff --git a/src/cobalt/media/player/web_media_player_impl.h b/src/cobalt/media/player/web_media_player_impl.h
index 77f4b32..9df3e68 100644
--- a/src/cobalt/media/player/web_media_player_impl.h
+++ b/src/cobalt/media/player/web_media_player_impl.h
@@ -182,6 +182,9 @@
bool GetDebugReportDataAddress(void** out_address, size_t* out_size) OVERRIDE;
+ void SetDrmSystem(DrmSystem* drm_system) OVERRIDE;
+ void SetDrmSystemReadyCB(const DrmSystemReadyCB& drm_system_ready_cb);
+
void OnPipelineSeek(PipelineStatus status);
void OnPipelineEnded(PipelineStatus status);
void OnPipelineError(PipelineStatus error);
@@ -212,6 +215,7 @@
// Getter method to |client_|.
WebMediaPlayerClient* GetClient();
+ private:
// Callbacks that forward duration change from |pipeline_| to |client_|.
void OnDurationChanged();
@@ -304,6 +308,9 @@
base::Callback<void(base::TimeDelta*, bool*)>
media_time_and_seeking_state_cb_;
+ DrmSystemReadyCB drm_system_ready_cb_;
+ DrmSystem* drm_system_;
+
DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl);
};
diff --git a/src/cobalt/media/sandbox/media_source_sandbox.cc b/src/cobalt/media/sandbox/media_source_sandbox.cc
index 68560ef..02b16ca 100644
--- a/src/cobalt/media/sandbox/media_source_sandbox.cc
+++ b/src/cobalt/media/sandbox/media_source_sandbox.cc
@@ -32,7 +32,7 @@
#else // defined(COBALT_MEDIA_SOURCE_2016)
#include "media/base/video_frame.h"
#endif // defined(COBALT_MEDIA_SOURCE_2016)
-#include "net/base/net_util.h"
+#include "starboard/file.h"
namespace cobalt {
namespace media {
@@ -50,56 +50,19 @@
using base::TimeDelta;
using render_tree::Image;
+using starboard::ScopedFile;
-GURL ResolveUrl(const char* arg) {
- GURL video_url(arg);
- if (!video_url.is_valid()) {
- // Assume the input is a path.
- // Try to figure out the path to this file and convert it to a URL.
- FilePath result(arg);
- if (!result.IsAbsolute()) {
- FilePath content_path;
- PathService::Get(base::DIR_EXE, &content_path);
- DCHECK(content_path.IsAbsolute());
- // TODO: Get the "real" exe path.
- result = content_path.DirName().DirName().Append(result);
- }
- video_url = net::FilePathToFileURL(result);
+FilePath ResolvePath(const char* path) {
+ FilePath result(path);
+ if (!result.IsAbsolute()) {
+ FilePath content_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &content_path);
+ DCHECK(content_path.IsAbsolute());
+ result = content_path.Append(result);
}
- return video_url;
+ return result;
}
-// Note that this class loads the content of the url into memory, so it won't
-// work with large media files.
-class Loader : loader::Fetcher::Handler {
- public:
- Loader(const GURL& url, loader::FetcherFactory* fetcher_factory)
- : done_(false), error_(false) {
- fetcher_ = fetcher_factory->CreateFetcher(url, this);
- }
-
- bool done() const { return done_; }
- bool error() const { return error_; }
- const std::vector<uint8_t>& buffer() const { return buffer_; }
-
- private:
- void OnReceived(loader::Fetcher* fetcher, const char* data,
- size_t size) OVERRIDE {
- buffer_.insert(buffer_.end(), reinterpret_cast<const uint8_t*>(data),
- reinterpret_cast<const uint8_t*>(data) + size);
- }
- void OnDone(loader::Fetcher* fetcher) OVERRIDE { done_ = true; }
- void OnError(loader::Fetcher* fetcher, const std::string& error) OVERRIDE {
- buffer_.clear();
- error_ = true;
- }
-
- scoped_ptr<loader::Fetcher> fetcher_;
- std::vector<uint8_t> buffer_;
- bool done_;
- bool error_;
-};
-
std::vector<std::string> MakeStringVector(const char* string) {
std::vector<std::string> result;
result.push_back(string);
@@ -124,18 +87,19 @@
CHECK_EQ(status, WebMediaPlayer::kAddIdStatusOk);
}
-bool IsWebMURL(const GURL& url) {
- std::string filename = url.ExtractFileName();
+bool IsWebM(const FilePath& path) {
+ std::string filename = path.value();
return filename.size() >= 5 &&
filename.substr(filename.size() - 5) == ".webm";
}
-void AppendData(const std::string& id, const std::vector<uint8_t>& data,
- size_t* offset, WebMediaPlayer* player) {
+void AppendData(WebMediaPlayer* player, const std::string& id, ScopedFile* file,
+ int64* offset) {
const float kLowWaterMarkInSeconds = 5.f;
- const size_t kMaxBytesToAppend = 1024 * 1024;
+ const int64 kMaxBytesToAppend = 1024 * 1024;
+ char buffer[kMaxBytesToAppend];
- while (*offset < data.size()) {
+ while (*offset < file->GetSize()) {
Ranges<TimeDelta> ranges = player->SourceBuffered(id);
float end_of_buffer =
ranges.size() == 0 ? 0.f : ranges.end(ranges.size() - 1).InSecondsF();
@@ -143,8 +107,10 @@
if (end_of_buffer - media_time > kLowWaterMarkInSeconds) {
break;
}
- size_t bytes_to_append = std::min(kMaxBytesToAppend, data.size() - *offset);
- player->SourceAppend(id, &data[0] + *offset, bytes_to_append);
+ int64 bytes_to_append =
+ std::min(kMaxBytesToAppend, file->GetSize() - *offset);
+ file->Read(buffer, bytes_to_append);
+ player->SourceAppend(id, reinterpret_cast<uint8*>(buffer), bytes_to_append);
*offset += bytes_to_append;
}
}
@@ -160,7 +126,8 @@
int SandboxMain(int argc, char** argv) {
if (argc != 3 && argc != 4) {
LOG(ERROR) << "Usage: " << argv[0]
- << " [--null_audio_streamer] <audio url|path> <video url|path>";
+ << " [--null_audio_streamer] <audio file path> "
+ << "<video file path>";
return 1;
}
MediaSandbox media_sandbox(
@@ -169,51 +136,36 @@
WebMediaPlayerHelper player_helper(media_sandbox.GetMediaModule());
// Note that we can't access PathService until MediaSandbox is initialized.
- GURL audio_url = ResolveUrl(argv[argc - 2]);
- GURL video_url = ResolveUrl(argv[argc - 1]);
+ FilePath audio_path = ResolvePath(argv[argc - 2]);
+ FilePath video_path = ResolvePath(argv[argc - 1]);
- if (!audio_url.is_valid()) {
- LOG(ERROR) << "Invalid Audio URL: " << audio_url;
+ ScopedFile audio_file(audio_path.value().c_str(),
+ kSbFileOpenOnly | kSbFileRead);
+ ScopedFile video_file(video_path.value().c_str(),
+ kSbFileOpenOnly | kSbFileRead);
+
+ if (!audio_file.IsValid()) {
+ LOG(ERROR) << "Failed to open audio file: " << audio_path.value();
return 1;
}
- if (!video_url.is_valid()) {
- LOG(ERROR) << "Invalid Video URL: " << video_url;
+ if (!video_file.IsValid()) {
+ LOG(ERROR) << "Failed to open video file: " << video_path.value();
return 1;
}
- LOG(INFO) << "Loading " << audio_url << " and " << video_url;
-
- Loader audio_loader(audio_url, media_sandbox.GetFetcherFactory());
- Loader video_loader(video_url, media_sandbox.GetFetcherFactory());
-
- bool audio_finished = false;
- bool video_finished = false;
- while (!audio_finished || !video_finished) {
- MessageLoop::current()->RunUntilIdle();
- audio_finished = audio_loader.done() || audio_loader.error();
- video_finished = video_loader.done() || video_loader.error();
- }
-
- if (audio_loader.error()) {
- LOG(ERROR) << "Failed to load audio: " << audio_url;
- return 1;
- }
-
- if (video_loader.error()) {
- LOG(ERROR) << "Failed to load video: " << video_url;
- return 1;
- }
+ LOG(INFO) << "Playing " << audio_path.value() << " and "
+ << video_path.value();
WebMediaPlayer* player = player_helper.player();
const std::string kAudioId = "audio";
const std::string kVideoId = "video";
- AddSourceBuffers(kAudioId, kVideoId, IsWebMURL(video_url), player);
+ AddSourceBuffers(kAudioId, kVideoId, IsWebM(video_path), player);
- size_t audio_offset = 0;
- size_t video_offset = 0;
+ int64 audio_offset = 0;
+ int64 video_offset = 0;
bool eos_appended = false;
scoped_refptr<VideoFrame> last_frame;
@@ -222,12 +174,14 @@
base::Bind(FrameCB, base::Unretained(&player_helper)));
for (;;) {
- AppendData(kAudioId, audio_loader.buffer(), &audio_offset, player);
- AppendData(kVideoId, video_loader.buffer(), &video_offset, player);
- if (!eos_appended && audio_offset == audio_loader.buffer().size() &&
- video_offset == video_loader.buffer().size()) {
- player->SourceEndOfStream(WebMediaPlayer::kEndOfStreamStatusNoError);
- eos_appended = true;
+ if (!eos_appended) {
+ AppendData(player, kAudioId, &audio_file, &audio_offset);
+ AppendData(player, kVideoId, &video_file, &video_offset);
+ if (video_offset == video_file.GetSize()) {
+ player->SourceAbort(kAudioId);
+ player->SourceEndOfStream(WebMediaPlayer::kEndOfStreamStatusNoError);
+ eos_appended = true;
+ }
}
if (player_helper.IsPlaybackFinished()) {
diff --git a/src/cobalt/media/sandbox/sandbox.gyp b/src/cobalt/media/sandbox/sandbox.gyp
index 73a3b49..1a2a762 100644
--- a/src/cobalt/media/sandbox/sandbox.gyp
+++ b/src/cobalt/media/sandbox/sandbox.gyp
@@ -116,7 +116,7 @@
},
],
'conditions': [
- ['sb_media_platform == "starboard"', {
+ ['sb_media_platform == "starboard" and cobalt_media_source_2016==1', {
'targets': [
{
'target_name': 'media2_sandbox',
diff --git a/src/cobalt/media/sandbox/web_media_player_helper.cc b/src/cobalt/media/sandbox/web_media_player_helper.cc
index 62de834..2adef29 100644
--- a/src/cobalt/media/sandbox/web_media_player_helper.cc
+++ b/src/cobalt/media/sandbox/web_media_player_helper.cc
@@ -46,6 +46,11 @@
void SourceOpened() OVERRIDE {}
#endif // defined(COBALT_MEDIA_SOURCE_2016)
std::string SourceURL() const OVERRIDE { return ""; }
+#if defined(COBALT_MEDIA_SOURCE_2016)
+ void EncryptedMediaInitData(EmeInitDataType init_data_type,
+ const unsigned char* init_data,
+ unsigned init_data_length) OVERRIDE {}
+#endif // defined(COBALT_MEDIA_SOURCE_2016)
};
WebMediaPlayerHelper::WebMediaPlayerHelper(MediaModule* media_module)
diff --git a/src/cobalt/media/shell_media_platform_starboard.cc b/src/cobalt/media/shell_media_platform_starboard.cc
index c1975b1..92c9194 100644
--- a/src/cobalt/media/shell_media_platform_starboard.cc
+++ b/src/cobalt/media/shell_media_platform_starboard.cc
@@ -22,6 +22,7 @@
#include "media/audio/shell_audio_streamer.h"
#include "media/base/shell_buffer_factory.h"
#include "media/base/shell_cached_decoder_buffer.h"
+#include "starboard/common/scoped_ptr.h"
#include "starboard/configuration.h"
#include "starboard/media.h"
@@ -56,21 +57,20 @@
DCHECK(gpu_memory_buffer_space_->GetMemory());
DCHECK_GE(gpu_memory_buffer_space_->GetSizeInBytes(),
kGPUMemoryBufferBudget);
- gpu_memory_pool_.reset(new nb::MemoryPool(
+ gpu_memory_pool_.set(starboard::make_scoped_ptr(new nb::MemoryPool(
gpu_memory_buffer_space_->GetMemory(),
- gpu_memory_buffer_space_->GetSizeInBytes(), true, /* thread_safe */
- true /* verify_full_capacity */, kSmallAllocationThreshold));
+ gpu_memory_buffer_space_->GetSizeInBytes(),
+ true /* verify_full_capacity */, kSmallAllocationThreshold)));
}
DCHECK_LE(0, kMainMemoryBufferBudget > 0);
main_memory_buffer_space_.reset(static_cast<uint8*>(
base::AlignedAlloc(kMainMemoryBufferBudget, kMediaBufferAlignment)));
DCHECK(main_memory_buffer_space_);
- main_memory_pool_.reset(new nb::MemoryPool(main_memory_buffer_space_.get(),
- kMainMemoryBufferBudget,
- true, /* thread_safe */
- true, /* verify_full_capacity */
- kSmallAllocationThreshold));
+ main_memory_pool_.set(starboard::make_scoped_ptr(new nb::MemoryPool(
+ main_memory_buffer_space_.get(), kMainMemoryBufferBudget,
+ true, /* verify_full_capacity */
+ kSmallAllocationThreshold)));
ShellBufferFactory::Initialize();
ShellAudioStreamer::Initialize();
@@ -105,16 +105,9 @@
scoped_refptr<DecoderBuffer>
ShellMediaPlatformStarboard::ProcessBeforeLeavingDemuxer(
const scoped_refptr<DecoderBuffer>& buffer) {
- if (!buffer || buffer->IsEndOfStream() || buffer->GetDataSize() == 0 ||
- !kGPUMemoryBufferBudget)
- return buffer;
- void* cached_buffer =
- main_memory_pool_->Allocate(buffer->GetDataSize(), kMediaBufferAlignment);
- if (!cached_buffer) return buffer;
- return new ShellCachedDecoderBuffer(
- buffer, cached_buffer,
- base::Bind(&nb::MemoryPool::Free,
- base::Unretained(main_memory_pool_.get())));
+ // TODO: Completely remove GPU buffer for the new DecoderBuffer
+ // implementation.
+ return buffer;
}
bool ShellMediaPlatformStarboard::IsOutputProtected() {
diff --git a/src/cobalt/media/shell_media_platform_starboard.h b/src/cobalt/media/shell_media_platform_starboard.h
index 7c7fd3d..0119293 100644
--- a/src/cobalt/media/shell_media_platform_starboard.h
+++ b/src/cobalt/media/shell_media_platform_starboard.h
@@ -44,7 +44,16 @@
return video_frame_provider_;
}
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+ SbDecodeTargetGraphicsContextProvider*
+ GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+#if SB_HAS(GRAPHICS)
+ return resource_provider_->GetSbDecodeTargetGraphicsContextProvider();
+#else // SB_HAS(GRAPHICS)
+ return NULL;
+#endif // SB_HAS(GRAPHICS)
+ }
+#elif SB_API_VERSION >= 3
SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE {
#if SB_HAS(GRAPHICS)
return resource_provider_->GetSbDecodeTargetProvider();
@@ -52,7 +61,12 @@
return NULL;
#endif // SB_HAS(GRAPHICS)
}
-#endif // SB_API_VERSION >= 3
+#endif // SB_API_VERSION >= 4
+
+ void Suspend() OVERRIDE { resource_provider_ = NULL; }
+ void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE {
+ resource_provider_ = resource_provider;
+ }
private:
void* AllocateBuffer(size_t size) OVERRIDE {
@@ -89,6 +103,7 @@
#include "cobalt/media/shell_video_data_allocator_common.h"
#include "media/base/shell_video_frame_provider.h"
#include "nb/memory_pool.h"
+#include "starboard/common/locked_ptr.h"
namespace media {
@@ -119,7 +134,16 @@
const scoped_refptr<DecoderBuffer>& buffer) OVERRIDE;
bool IsOutputProtected() OVERRIDE;
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+ SbDecodeTargetGraphicsContextProvider*
+ GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+#if SB_HAS(GRAPHICS)
+ return resource_provider_->GetSbDecodeTargetGraphicsContextProvider();
+#else // SB_HAS(GRAPHICS)
+ return NULL;
+#endif // SB_HAS(GRAPHICS)
+ }
+#elif SB_API_VERSION >= 3
virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() {
#if SB_HAS(GRAPHICS)
return resource_provider_->GetSbDecodeTargetProvider();
@@ -127,19 +151,25 @@
return NULL;
#endif // SB_HAS(GRAPHICS)
}
-#endif // SB_API_VERSION >= 3
+#endif // SB_API_VERSION >= 4
+
+ void Suspend() OVERRIDE { resource_provider_ = NULL; }
+ void Resume(
+ cobalt::render_tree::ResourceProvider* resource_provider) OVERRIDE {
+ resource_provider_ = resource_provider;
+ }
private:
cobalt::render_tree::ResourceProvider* resource_provider_;
// Optional GPU Memory buffer pool, for buffer offloading.
scoped_ptr<cobalt::render_tree::RawImageMemory> gpu_memory_buffer_space_;
- scoped_ptr<nb::MemoryPool> gpu_memory_pool_;
+ starboard::LockedPtr<nb::MemoryPool> gpu_memory_pool_;
// Main Memory buffer pool.
scoped_ptr_malloc<uint8, base::ScopedPtrAlignedFree>
main_memory_buffer_space_;
- scoped_ptr<nb::MemoryPool> main_memory_pool_;
+ starboard::LockedPtr<nb::MemoryPool> main_memory_pool_;
ShellVideoDataAllocatorCommon video_data_allocator_;
scoped_refptr<ShellVideoFrameProvider> video_frame_provider_;
diff --git a/src/cobalt/media/shell_video_data_allocator_stub.cc b/src/cobalt/media/shell_video_data_allocator_stub.cc
deleted file mode 100644
index 96fad8f..0000000
--- a/src/cobalt/media/shell_video_data_allocator_stub.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cobalt/media/shell_video_data_allocator_stub.h"
-
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-
-namespace media {
-
-ShellVideoDataAllocatorStub::ShellVideoDataAllocatorStub(
- scoped_ptr<nb::MemoryPool> memory_pool)
- : memory_pool_(memory_pool.Pass()) {
- DCHECK(memory_pool_);
-}
-
-scoped_refptr<ShellVideoDataAllocator::FrameBuffer>
-ShellVideoDataAllocatorStub::AllocateFrameBuffer(size_t size,
- size_t alignment) {
- return new FrameBufferStub(memory_pool_.get(), size, alignment);
-}
-
-scoped_refptr<VideoFrame> ShellVideoDataAllocatorStub::CreateYV12Frame(
- const scoped_refptr<FrameBuffer>& frame_buffer, const YV12Param& param,
- const base::TimeDelta& timestamp) {
- UNREFERENCED_PARAMETER(frame_buffer);
- scoped_refptr<VideoFrame> frame =
- VideoFrame::CreateBlackFrame(param.visible_rect().size());
- frame->SetTimestamp(timestamp);
- return frame;
-}
-
-scoped_refptr<VideoFrame> ShellVideoDataAllocatorStub::CreateNV12Frame(
- const scoped_refptr<FrameBuffer>& frame_buffer, const NV12Param& param,
- const base::TimeDelta& timestamp) {
- UNREFERENCED_PARAMETER(frame_buffer);
- scoped_refptr<VideoFrame> frame =
- VideoFrame::CreateBlackFrame(param.visible_rect().size());
- frame->SetTimestamp(timestamp);
- return frame;
-}
-
-ShellVideoDataAllocatorStub::FrameBufferStub::FrameBufferStub(
- nb::MemoryPool* memory_pool, size_t size, size_t alignment)
- : memory_pool_(memory_pool), size_(size) {
- data_ = reinterpret_cast<uint8*>(memory_pool_->Allocate(size, alignment));
- DCHECK(data_);
-}
-
-ShellVideoDataAllocatorStub::FrameBufferStub::~FrameBufferStub() {
- memory_pool_->Free(data_);
-}
-
-} // namespace media
diff --git a/src/cobalt/media/shell_video_data_allocator_stub.h b/src/cobalt/media/shell_video_data_allocator_stub.h
deleted file mode 100644
index 1539999..0000000
--- a/src/cobalt/media/shell_video_data_allocator_stub.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef COBALT_MEDIA_SHELL_VIDEO_DATA_ALLOCATOR_STUB_H_
-#define COBALT_MEDIA_SHELL_VIDEO_DATA_ALLOCATOR_STUB_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "media/base/shell_video_data_allocator.h"
-#include "nb/memory_pool.h"
-
-namespace media {
-
-class ShellVideoDataAllocatorStub : public ShellVideoDataAllocator {
- public:
- explicit ShellVideoDataAllocatorStub(scoped_ptr<nb::MemoryPool> memory_pool);
-
- scoped_refptr<FrameBuffer> AllocateFrameBuffer(size_t size,
- size_t alignment) OVERRIDE;
- scoped_refptr<VideoFrame> CreateYV12Frame(
- const scoped_refptr<FrameBuffer>& frame_buffer, const YV12Param& param,
- const base::TimeDelta& timestamp) OVERRIDE;
-
- scoped_refptr<VideoFrame> CreateNV12Frame(
- const scoped_refptr<FrameBuffer>& frame_buffer, const NV12Param& param,
- const base::TimeDelta& timestamp) OVERRIDE;
-
- private:
- class FrameBufferStub : public FrameBuffer {
- public:
- FrameBufferStub(nb::MemoryPool* memory_pool, size_t size, size_t alignment);
- ~FrameBufferStub();
-
- uint8* data() const OVERRIDE { return data_; }
- size_t size() const OVERRIDE { return size_; }
-
- private:
- nb::MemoryPool* memory_pool_;
- uint8* data_;
- size_t size_;
- };
-
- scoped_ptr<nb::MemoryPool> memory_pool_;
-};
-
-} // namespace media
-
-#endif // COBALT_MEDIA_SHELL_VIDEO_DATA_ALLOCATOR_STUB_H_
diff --git a/src/cobalt/media_session/default_media_session_client.cc b/src/cobalt/media_session/default_media_session_client.cc
new file mode 100644
index 0000000..c616091
--- /dev/null
+++ b/src/cobalt/media_session/default_media_session_client.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_MEDIA_SESSION_DEFAULT_MEDIA_SESSION_CLIENT_H_
+#define COBALT_MEDIA_SESSION_DEFAULT_MEDIA_SESSION_CLIENT_H_
+
+#include "cobalt/media_session/media_session_client.h"
+
+namespace cobalt {
+namespace media_session {
+
+class DefaultMediaSessionClient : public MediaSessionClient {
+ public:
+ DefaultMediaSessionClient() {}
+ virtual ~DefaultMediaSessionClient() {}
+
+ private:
+ virtual void OnMediaSessionChanged() {}
+};
+
+// static
+scoped_ptr<MediaSessionClient> MediaSessionClient::Create() {
+ return make_scoped_ptr<MediaSessionClient>(new DefaultMediaSessionClient());
+}
+
+} // namespace media_session
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_SESSION_DEFAULT_MEDIA_SESSION_CLIENT_H_
diff --git a/src/cobalt/media_session/media_metadata.h b/src/cobalt/media_session/media_metadata.h
index 184c569..4c03eb3 100644
--- a/src/cobalt/media_session/media_metadata.h
+++ b/src/cobalt/media_session/media_metadata.h
@@ -16,6 +16,7 @@
#define COBALT_MEDIA_SESSION_MEDIA_METADATA_H_
#include <string>
+#include <vector>
#include "cobalt/media_session/media_image.h"
#include "cobalt/media_session/media_metadata_init.h"
@@ -39,6 +40,10 @@
if (init.has_album()) {
album_ = init.album();
}
+
+ if (init.has_artwork()) {
+ set_artwork(init.artwork());
+ }
}
scoped_refptr<MediaMetadata> metadata() {
@@ -57,10 +62,22 @@
void set_album(const std::string& value) { album_ = value; }
- MediaImage const artwork() { return image_; }
+ script::Sequence<MediaImage> const artwork() {
+ script::Sequence<MediaImage> result;
+
+ for (std::vector<MediaImage>::iterator it = artwork_.begin();
+ it != artwork_.end();
+ ++it) {
+ result.push_back(*it);
+ }
+ return result;
+ }
void set_artwork(script::Sequence<MediaImage> value) {
- UNREFERENCED_PARAMETER(value);
+ artwork_.clear();
+ for (size_t i = 0; i < value.size(); ++i) {
+ artwork_.push_back(value.at(i));
+ }
}
DEFINE_WRAPPABLE_TYPE(MediaMetadata);
@@ -69,7 +86,7 @@
std::string title_;
std::string artist_;
std::string album_;
- MediaImage image_;
+ std::vector<MediaImage> artwork_;
DISALLOW_COPY_AND_ASSIGN(MediaMetadata);
};
diff --git a/src/cobalt/media_session/media_metadata_init.idl b/src/cobalt/media_session/media_metadata_init.idl
index 1f02514..1bad3e7 100644
--- a/src/cobalt/media_session/media_metadata_init.idl
+++ b/src/cobalt/media_session/media_metadata_init.idl
@@ -18,6 +18,5 @@
DOMString title = "";
DOMString artist = "";
DOMString album = "";
- // TODO FrozenArray not implemented in current IDL parser.
- // attribute FrozenArray<MediaImage> artwork;
+ sequence<MediaImage> artwork;
};
diff --git a/src/cobalt/media_session/media_session.cc b/src/cobalt/media_session/media_session.cc
new file mode 100644
index 0000000..5dc7180
--- /dev/null
+++ b/src/cobalt/media_session/media_session.cc
@@ -0,0 +1,79 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/media_session/media_session.h"
+
+#include "cobalt/media_session/media_session_client.h"
+
+namespace cobalt {
+namespace media_session {
+
+MediaSession::MediaSession(MediaSessionClient* client)
+ : media_session_client_(client),
+ state_(kMediaSessionPlaybackStateNone),
+ message_loop_(base::MessageLoopProxy::current()),
+ is_change_task_queued_(false) {}
+
+MediaSession::~MediaSession() {
+ ActionMap::iterator it;
+ for (it = action_map_.begin(); it != action_map_.end(); ++it) {
+ delete it->second;
+ }
+ action_map_.clear();
+}
+
+void MediaSession::set_metadata(scoped_refptr<MediaMetadata> value) {
+ metadata_ = value;
+ MaybeQueueChangeTask();
+}
+
+void MediaSession::set_playback_state(MediaSessionPlaybackState state) {
+ state_ = state;
+ MaybeQueueChangeTask();
+}
+
+void MediaSession::SetActionHandler(
+ MediaSessionAction action, const MediaSessionActionHandlerHolder& handler) {
+ // See algorithm https://wicg.github.io/mediasession/#actions-model
+ DCHECK_EQ(base::MessageLoopProxy::current(), message_loop_.get());
+ ActionMap::iterator it = action_map_.find(action);
+
+ if (it != action_map_.end()) {
+ delete it->second;
+ action_map_.erase(it);
+ }
+ if (!handler.IsNull()) {
+ action_map_[action] = new MediaSessionActionHandlerReference(this, handler);
+ }
+
+ MaybeQueueChangeTask();
+}
+
+void MediaSession::MaybeQueueChangeTask() {
+ DCHECK_EQ(base::MessageLoopProxy::current(), message_loop_.get());
+ if (is_change_task_queued_) {
+ return;
+ }
+ is_change_task_queued_ = true;
+ message_loop_->PostTask(
+ FROM_HERE, base::Bind(&MediaSession::OnChanged, base::Unretained(this)));
+}
+
+void MediaSession::OnChanged() {
+ is_change_task_queued_ = false;
+ media_session_client_->OnMediaSessionChanged();
+}
+
+} // namespace media_session
+} // namespace cobalt
diff --git a/src/cobalt/media_session/media_session.gyp b/src/cobalt/media_session/media_session.gyp
index e57aaf3..4de3b9d 100644
--- a/src/cobalt/media_session/media_session.gyp
+++ b/src/cobalt/media_session/media_session.gyp
@@ -21,12 +21,35 @@
'target_name': 'media_session',
'type': 'static_library',
'sources': [
- 'media_session.h'
- 'media_metadata.h'
+ 'media_session.h',
+ 'media_session.cc',
+ 'media_metadata.h',
+ 'media_session_client.h',
+ 'media_session_client.cc',
+ ],
+ 'conditions': [
+ ['custom_media_session_client == 0', {
+ 'sources': [
+ 'default_media_session_client.cc',
+ ]
+ }]
],
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types'
],
+ # This target doesn't generate any headers, but it exposes generated
+ # header files (for idl dictionaries) through this module's public header
+ # files. So mark this target as a hard dependency to ensure that any
+ # dependent targets wait until this target (and its hard dependencies) are
+ # built.
+ 'hard_dependency': 1,
+ 'export_dependent_settings': [
+ # Additionally, ensure that the include directories for generated
+ # headers are put on the include directories for targets that depend
+ # on this one.
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+ ]
},
],
}
diff --git a/src/cobalt/media_session/media_session.h b/src/cobalt/media_session/media_session.h
index 405c04e..3fb6ae7ea 100644
--- a/src/cobalt/media_session/media_session.h
+++ b/src/cobalt/media_session/media_session.h
@@ -15,53 +15,66 @@
#ifndef COBALT_MEDIA_SESSION_MEDIA_SESSION_H_
#define COBALT_MEDIA_SESSION_MEDIA_SESSION_H_
+#include <map>
+
#include "cobalt/media_session/media_metadata.h"
+#include "base/containers/small_map.h"
+#include "base/location.h"
#include "base/logging.h"
+#include "base/message_loop_proxy.h"
+#include "cobalt/media_session/media_session_action.h"
+#include "cobalt/media_session/media_session_playback_state.h"
#include "cobalt/script/callback_function.h"
#include "cobalt/script/script_value.h"
namespace cobalt {
namespace media_session {
+class MediaSessionClient;
+
class MediaSession : public script::Wrappable {
+ friend class MediaSessionClient;
+
public:
typedef script::CallbackFunction<void()> MediaSessionActionHandler;
typedef script::ScriptValue<MediaSessionActionHandler>
MediaSessionActionHandlerHolder;
- enum MediaSessionPlaybackState { kNone, kPaused, kPlaying };
+ typedef script::ScriptValue<MediaSessionActionHandler>::Reference
+ MediaSessionActionHandlerReference;
- enum MediaSessionAction {
- kPlay,
- kPause,
- kSeekbackward,
- kSeekforward,
- kPrevioustrack,
- kNexttrack
- };
+ private:
+ typedef base::SmallMap<
+ std::map<MediaSessionAction, MediaSessionActionHandlerReference*>,
+ kMediaSessionActionNumActions> ActionMap;
- MediaSession() : state_(kNone) {}
+ public:
+ explicit MediaSession(MediaSessionClient* client);
+ ~MediaSession() OVERRIDE;
scoped_refptr<MediaMetadata> metadata() const { return metadata_; }
- void set_metadata(scoped_refptr<MediaMetadata> value) { metadata_ = value; }
+ void set_metadata(scoped_refptr<MediaMetadata> value);
MediaSessionPlaybackState playback_state() const { return state_; }
- void set_playback_state(MediaSessionPlaybackState state) { state_ = state; }
+ void set_playback_state(MediaSessionPlaybackState state);
void SetActionHandler(MediaSessionAction action,
- const MediaSessionActionHandlerHolder& handler) {
- UNREFERENCED_PARAMETER(action);
- UNREFERENCED_PARAMETER(handler);
- NOTIMPLEMENTED();
- }
+ const MediaSessionActionHandlerHolder& handler);
DEFINE_WRAPPABLE_TYPE(MediaSession);
private:
+ void MaybeQueueChangeTask();
+ void OnChanged();
+
+ ActionMap action_map_;
+ MediaSessionClient* media_session_client_;
scoped_refptr<MediaMetadata> metadata_;
MediaSessionPlaybackState state_;
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+ bool is_change_task_queued_;
DISALLOW_COPY_AND_ASSIGN(MediaSession);
};
diff --git a/src/cobalt/media_session/media_session.idl b/src/cobalt/media_session/media_session.idl
index 8248d3d..ffb06e2 100644
--- a/src/cobalt/media_session/media_session.idl
+++ b/src/cobalt/media_session/media_session.idl
@@ -15,21 +15,6 @@
// MediaSession interface
// https://wicg.github.io/mediasession
-enum MediaSessionPlaybackState {
- "none",
- "paused",
- "playing"
-};
-
-enum MediaSessionAction {
- "play",
- "pause",
- "seekbackward",
- "seekforward",
- "previoustrack",
- "nexttrack",
-};
-
callback MediaSessionActionHandler = void();
interface MediaSession {
diff --git a/src/cobalt/base/math.cc b/src/cobalt/media_session/media_session_action.idl
similarity index 63%
copy from src/cobalt/base/math.cc
copy to src/cobalt/media_session/media_session_action.idl
index d335495..c57a01b 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/media_session/media_session_action.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// MediaSession interface
+// https://wicg.github.io/mediasession
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum MediaSessionAction {
+ "play",
+ "pause",
+ "seekbackward",
+ "seekforward",
+ "previoustrack",
+ "nexttrack",
+
+ // Not part of spec, but used in Cobalt implementation.
+ "num-actions"
+};
diff --git a/src/cobalt/media_session/media_session_client.cc b/src/cobalt/media_session/media_session_client.cc
new file mode 100644
index 0000000..f6a635b
--- /dev/null
+++ b/src/cobalt/media_session/media_session_client.cc
@@ -0,0 +1,119 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/media_session/media_session_client.h"
+
+namespace cobalt {
+namespace media_session {
+
+MediaSessionPlaybackState MediaSessionClient::GetActualPlaybackState() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Per https://wicg.github.io/mediasession/#guessed-playback-state
+ // - If the "declared playback state" is "playing", then return "playing"
+ // - Otherwise, return the guessed playback state
+ MediaSessionPlaybackState declared_state;
+ declared_state = media_session_->playback_state();
+ if (declared_state == kMediaSessionPlaybackStatePlaying) {
+ return kMediaSessionPlaybackStatePlaying;
+ }
+
+ if (platform_playback_state_ == kMediaSessionPlaybackStatePlaying) {
+ // "...guessed playback state is playing if any of them is
+ // potentially playing and not muted..."
+ return kMediaSessionPlaybackStatePlaying;
+ }
+
+ // It's not super clear what to do when the declared state or the
+ // active media session state is kPaused or kNone
+
+ if (declared_state == kMediaSessionPlaybackStatePaused) {
+ return kMediaSessionPlaybackStatePaused;
+ }
+
+ return kMediaSessionPlaybackStateNone;
+}
+
+MediaSessionClient::AvailableActionsSet
+MediaSessionClient::GetAvailableActions() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // "Available actions" are determined based on active media session
+ // and supported media session actions.
+ // Note for cobalt, there's only one window/tab so there's only one
+ // "active media session"
+ // https://wicg.github.io/mediasession/#actions-model
+ //
+ // Note that this is essentially the "media session actions update algorithm"
+ // inverted.
+ AvailableActionsSet result = AvailableActionsSet();
+
+ for (MediaSession::ActionMap::iterator it =
+ media_session_->action_map_.begin();
+ it != media_session_->action_map_.end();
+ ++it) {
+ result[it->first] = true;
+ }
+
+ switch (GetActualPlaybackState()) {
+ case kMediaSessionPlaybackStatePlaying:
+ // "If the active media session’s actual playback state is playing, remove
+ // play from available actions."
+ result[kMediaSessionActionPlay] = false;
+ break;
+ default:
+ // "Otherwise, remove pause from available actions."
+ result[kMediaSessionActionPause] = false;
+ break;
+ }
+
+ return result;
+}
+
+void MediaSessionClient::UpdatePlatformPlaybackState(
+ MediaSessionPlaybackState state) {
+ if (base::MessageLoopProxy::current() != media_session_->message_loop_) {
+ media_session_->message_loop_->PostTask(
+ FROM_HERE, base::Bind(&MediaSessionClient::UpdatePlatformPlaybackState,
+ base::Unretained(this), state));
+ return;
+ }
+
+ MediaSessionPlaybackState prev_actual_state = GetActualPlaybackState();
+ platform_playback_state_ = state;
+
+ if (prev_actual_state != GetActualPlaybackState()) {
+ OnMediaSessionChanged();
+ }
+}
+
+void MediaSessionClient::InvokeAction(MediaSessionAction action) {
+ if (base::MessageLoopProxy::current() != media_session_->message_loop_) {
+ media_session_->message_loop_->PostTask(
+ FROM_HERE, base::Bind(&MediaSessionClient::InvokeAction,
+ base::Unretained(this), action));
+ return;
+ }
+
+ MediaSession::ActionMap::iterator it =
+ media_session_->action_map_.find(action);
+
+ if (it == media_session_->action_map_.end()) {
+ return;
+ }
+
+ it->second->value().Run();
+}
+
+} // namespace media_session
+} // namespace cobalt
diff --git a/src/cobalt/media_session/media_session_client.h b/src/cobalt/media_session/media_session_client.h
new file mode 100644
index 0000000..d838785
--- /dev/null
+++ b/src/cobalt/media_session/media_session_client.h
@@ -0,0 +1,80 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_MEDIA_SESSION_MEDIA_SESSION_CLIENT_H_
+#define COBALT_MEDIA_SESSION_MEDIA_SESSION_CLIENT_H_
+
+#include <bitset>
+
+#include "base/threading/thread_checker.h"
+
+#include "cobalt/media_session/media_session.h"
+
+namespace cobalt {
+namespace media_session {
+
+// Base class for a platform-level implementation of MediaSession.
+// Platforms should subclass this to connect MediaSession to their platform.
+class MediaSessionClient {
+ public:
+ typedef std::bitset<kMediaSessionActionNumActions> AvailableActionsSet;
+ MediaSessionClient()
+ : media_session_(new MediaSession(this)),
+ platform_playback_state_(kMediaSessionPlaybackStateNone) {}
+
+ virtual ~MediaSessionClient() {}
+
+ // Creates platform-specific instance.
+ static scoped_ptr<MediaSessionClient> Create();
+
+ // Retrieves the singleton MediaSession associated with this client.
+ scoped_refptr<MediaSession>& GetMediaSession() { return media_session_; }
+
+ // Retrieves the current actual playback state.
+ // https://wicg.github.io/mediasession/#actual-playback-state
+ // Must be called on the browser thread.
+ MediaSessionPlaybackState GetActualPlaybackState();
+
+ // Retrieves the set of currently available mediasession actions
+ // per "media session actions update algorithm"
+ // https://wicg.github.io/mediasession/#actions-model
+ AvailableActionsSet GetAvailableActions();
+
+ // Sets the platform's current playback state. This is used to compute
+ // the "guessed playback state"
+ // https://wicg.github.io/mediasession/#guessed-playback-state
+ // Can be invoked from any thread.
+ void UpdatePlatformPlaybackState(MediaSessionPlaybackState state);
+
+ // Invokes a given media session action
+ // https://wicg.github.io/mediasession/#actions-model
+ // Can be invoked from any thread.
+ void InvokeAction(MediaSessionAction action);
+
+ // Invoked on the browser thread when any metadata, playback state,
+ // or supported session actions change.
+ virtual void OnMediaSessionChanged() = 0;
+
+ private:
+ base::ThreadChecker thread_checker_;
+ scoped_refptr<MediaSession> media_session_;
+ MediaSessionPlaybackState platform_playback_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaSessionClient);
+};
+
+} // namespace media_session
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_SESSION_MEDIA_SESSION_CLIENT_H_
diff --git a/src/cobalt/base/math.cc b/src/cobalt/media_session/media_session_playback_state.idl
similarity index 71%
copy from src/cobalt/base/math.cc
copy to src/cobalt/media_session/media_session_playback_state.idl
index d335495..435ffe3 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/media_session/media_session_playback_state.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// MediaSession interface
+// https://wicg.github.io/mediasession
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum MediaSessionPlaybackState {
+ "none",
+ "paused",
+ "playing"
+};
diff --git a/src/cobalt/media_session/media_session_test.cc b/src/cobalt/media_session/media_session_test.cc
new file mode 100644
index 0000000..345dbd9
--- /dev/null
+++ b/src/cobalt/media_session/media_session_test.cc
@@ -0,0 +1,225 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/media_session/media_session.h"
+
+#include "cobalt/bindings/testing/script_object_owner.h"
+#include "cobalt/media_session/media_session_client.h"
+#include "cobalt/script/callback_function.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/testing/fake_script_value.h"
+#include "cobalt/script/wrappable.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/message_loop.h"
+#include "base/run_loop.h"
+
+using ::cobalt::script::testing::FakeScriptValue;
+using ::cobalt::script::CallbackResult;
+using ::cobalt::script::ScriptValue;
+using ::cobalt::script::Wrappable;
+
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+using ::testing::_;
+
+namespace cobalt {
+namespace media_session {
+namespace {
+
+class MockCallbackFunction : public MediaSession::MediaSessionActionHandler {
+ public:
+ MOCK_CONST_METHOD0(Run, ReturnValue());
+};
+
+class MockMediaSessionClient : public MediaSessionClient {
+ public:
+ MOCK_METHOD0(OnMediaSessionChanged, void());
+};
+
+TEST(MediaSessionTest, MediaSessionTest) {
+ MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ base::RunLoop run_loop;
+
+ MockMediaSessionClient client;
+
+ ON_CALL(client, OnMediaSessionChanged())
+ .WillByDefault(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ EXPECT_CALL(client, OnMediaSessionChanged()).Times(1);
+
+ scoped_refptr<MediaSession> session = client.GetMediaSession();
+
+ EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+
+ session->set_playback_state(kMediaSessionPlaybackStatePlaying);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+
+ run_loop.Run();
+}
+
+TEST(MediaSessionTest, GetActualPlaybackState) {
+ MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ base::RunLoop run_loop;
+
+ MockMediaSessionClient client;
+
+ ON_CALL(client, OnMediaSessionChanged())
+ .WillByDefault(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ EXPECT_CALL(client, OnMediaSessionChanged()).Times(AtLeast(2));
+
+ scoped_refptr<MediaSession> session = client.GetMediaSession();
+
+ EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+
+ client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePlaying);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+
+ session->set_playback_state(kMediaSessionPlaybackStatePlaying);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+
+ session->set_playback_state(kMediaSessionPlaybackStatePaused);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+
+ client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePaused);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePaused, client.GetActualPlaybackState());
+
+ session->set_playback_state(kMediaSessionPlaybackStateNone);
+
+ EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+
+ client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStateNone);
+
+ EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+
+ run_loop.Run();
+}
+
+TEST(MediaSessionTest, NullActionClears) {
+ MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ base::RunLoop run_loop;
+
+ MockMediaSessionClient client;
+
+ ON_CALL(client, OnMediaSessionChanged())
+ .WillByDefault(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ EXPECT_CALL(client, OnMediaSessionChanged()).Times(AtLeast(0));
+
+ scoped_refptr<MediaSession> session = client.GetMediaSession();
+
+ EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+ EXPECT_EQ(0, client.GetAvailableActions().to_ulong());
+
+ MockCallbackFunction cf;
+ EXPECT_CALL(cf, Run())
+ .Times(1)
+ .WillRepeatedly(Return(CallbackResult<void>()));
+ FakeScriptValue<MediaSession::MediaSessionActionHandler> holder(&cf);
+
+ FakeScriptValue<MediaSession::MediaSessionActionHandler> null_holder(NULL);
+
+ session->SetActionHandler(kMediaSessionActionPlay, holder);
+ EXPECT_EQ(1, client.GetAvailableActions().to_ulong());
+ client.InvokeAction(kMediaSessionActionPlay);
+
+ session->SetActionHandler(kMediaSessionActionPlay, null_holder);
+ EXPECT_EQ(0, client.GetAvailableActions().to_ulong());
+ client.InvokeAction(kMediaSessionActionPlay);
+}
+
+TEST(MediaSessionTest, GetAvailableActions) {
+ MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ base::RunLoop run_loop;
+
+ MockMediaSessionClient client;
+
+ ON_CALL(client, OnMediaSessionChanged())
+ .WillByDefault(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ EXPECT_CALL(client, OnMediaSessionChanged()).Times(AtLeast(0));
+
+ scoped_refptr<MediaSession> session = client.GetMediaSession();
+
+ EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+ EXPECT_EQ(0, client.GetAvailableActions().to_ulong());
+
+ MockCallbackFunction cf;
+ EXPECT_CALL(cf, Run()).Times(0);
+ FakeScriptValue<MediaSession::MediaSessionActionHandler> holder(&cf);
+
+ session->SetActionHandler(kMediaSessionActionPlay, holder);
+
+ EXPECT_EQ(1, client.GetAvailableActions().to_ulong());
+
+ session->SetActionHandler(kMediaSessionActionPause, holder);
+
+ EXPECT_EQ(1, client.GetAvailableActions().to_ulong());
+
+ session->SetActionHandler(kMediaSessionActionSeekbackward, holder);
+
+ EXPECT_EQ(5, client.GetAvailableActions().to_ulong());
+
+ client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePlaying);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+ EXPECT_EQ(6, client.GetAvailableActions().to_ulong());
+
+ session->set_playback_state(kMediaSessionPlaybackStatePlaying);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+ EXPECT_EQ(6, client.GetAvailableActions().to_ulong());
+
+ session->set_playback_state(kMediaSessionPlaybackStatePaused);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+ EXPECT_EQ(6, client.GetAvailableActions().to_ulong());
+
+ session->set_playback_state(kMediaSessionPlaybackStatePlaying);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+ EXPECT_EQ(6, client.GetAvailableActions().to_ulong());
+
+ client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStatePaused);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePlaying, client.GetActualPlaybackState());
+ EXPECT_EQ(6, client.GetAvailableActions().to_ulong());
+
+ session->set_playback_state(kMediaSessionPlaybackStateNone);
+
+ EXPECT_EQ(kMediaSessionPlaybackStateNone, client.GetActualPlaybackState());
+ EXPECT_EQ(5, client.GetAvailableActions().to_ulong());
+
+ session->set_playback_state(kMediaSessionPlaybackStatePaused);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePaused, client.GetActualPlaybackState());
+ EXPECT_EQ(5, client.GetAvailableActions().to_ulong());
+
+ client.UpdatePlatformPlaybackState(kMediaSessionPlaybackStateNone);
+
+ EXPECT_EQ(kMediaSessionPlaybackStatePaused, client.GetActualPlaybackState());
+ EXPECT_EQ(5, client.GetAvailableActions().to_ulong());
+
+ run_loop.Run();
+}
+
+} // namespace
+} // namespace media_session
+} // namespace cobalt
diff --git a/src/cobalt/media_session/media_session_test.gyp b/src/cobalt/media_session/media_session_test.gyp
new file mode 100644
index 0000000..e4787af
--- /dev/null
+++ b/src/cobalt/media_session/media_session_test.gyp
@@ -0,0 +1,46 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'media_session_test',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'media_session_test.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/media_session/media_session.gyp:media_session',
+ '<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/browser/browser.gyp:browser',
+ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ },
+ {
+ 'target_name': 'media_session_test_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'media_session_test',
+ ],
+ 'variables': {
+ 'executable_name': 'media_session_test',
+ },
+ 'includes': [ '../../starboard/build/deploy.gypi' ],
+ },
+ ]
+}
+
+
diff --git a/src/cobalt/network/local_network.cc b/src/cobalt/network/local_network.cc
new file mode 100644
index 0000000..725f3e4
--- /dev/null
+++ b/src/cobalt/network/local_network.cc
@@ -0,0 +1,108 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/network/local_network.h"
+
+#include "base/logging.h"
+#include "base/string_piece.h"
+#include "net/base/net_util.h"
+
+namespace cobalt {
+namespace network {
+
+namespace {
+
+#if SB_API_VERSION >= 4
+bool CompareNBytesOfAddress(const SbSocketAddress& ip,
+ const SbSocketAddress& source_address,
+ const SbSocketAddress& netmask,
+ size_t number_bytes_to_compare) {
+ for (size_t i = 0; i != number_bytes_to_compare; ++i) {
+ if ((ip.address[i] & netmask.address[i]) !=
+ (source_address.address[i] & netmask.address[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool IsLocalIP(const SbSocketAddress& ip, const SbSocketAddress& source_address,
+ const SbSocketAddress& netmask) {
+ DCHECK(source_address.type == ip.type);
+ DCHECK(netmask.type == ip.type);
+
+ switch (ip.type) {
+ case kSbSocketAddressTypeIpv4:
+ return CompareNBytesOfAddress(ip, source_address, netmask,
+ net::kIPv4AddressSize);
+#if SB_HAS(IPV6)
+ case kSbSocketAddressTypeIpv6:
+ return CompareNBytesOfAddress(ip, source_address, netmask,
+ net::kIPv6AddressSize);
+#endif
+ default:
+ NOTREACHED() << "Invalid IP type " << ip.type;
+ return false;
+ }
+}
+
+#endif // SB_API_VERSION >= 4
+
+} // namespace
+
+bool IsIPInLocalNetwork(const SbSocketAddress& destination) {
+#if SB_API_VERSION >= 4
+ SbSocketAddress source_address;
+ SbSocketAddress netmask;
+ if (!(SbSocketGetInterfaceAddress(&destination, &source_address, &netmask))) {
+ return false;
+ }
+ return IsLocalIP(destination, source_address, netmask);
+#else
+ UNREFERENCED_PARAMETER(destination);
+ return false;
+#endif
+}
+
+bool IsIPInPrivateRange(const SbSocketAddress& ip) {
+ // For IPv4, the private address range is defined by RFC 1918
+ // avaiable at https://tools.ietf.org/html/rfc1918#section-3.
+ if (ip.type == kSbSocketAddressTypeIpv4) {
+ if (ip.address[0] == 10) {
+ // IP is in range 10.0.0.0 - 10.255.255.255 (10/8 prefix).
+ return true;
+ }
+ if ((ip.address[0] == 192) && (ip.address[1] == 168)) {
+ // IP is in range 192.168.0.0 - 192.168.255.255 (192.168/16 prefix).
+ return true;
+ }
+ if ((ip.address[0] == 172) &&
+ ((ip.address[1] >= 16) || (ip.address[1] <= 31))) {
+ // IP is in range 172.16.0.0 - 172.31.255.255 (172.16/12 prefix).
+ return true;
+ }
+ }
+#if SB_HAS(IPV6)
+ if (ip.type == kSbSocketAddressTypeIpv6) {
+ // Unique Local Addresses for IPv6 are _effectively_ fd00::/8.
+ // See https://tools.ietf.org/html/rfc4193#section-3 for details.
+ return ip.address[0] == 0xfd && ip.address[1] == 0;
+ }
+#endif
+
+ return false;
+}
+
+} // namespace network
+} // namespace cobalt
diff --git a/src/cobalt/network/local_network.h b/src/cobalt/network/local_network.h
new file mode 100644
index 0000000..9cbb6be
--- /dev/null
+++ b/src/cobalt/network/local_network.h
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_NETWORK_LOCAL_NETWORK_H_
+#define COBALT_NETWORK_LOCAL_NETWORK_H_
+
+#include "starboard/socket.h"
+
+namespace cobalt {
+namespace network {
+
+bool IsIPInLocalNetwork(const SbSocketAddress& destination);
+bool IsIPInPrivateRange(const SbSocketAddress& destination);
+
+} // namespace network
+} // namespace cobalt
+
+#endif // COBALT_NETWORK_LOCAL_NETWORK_H_
diff --git a/src/cobalt/network/local_network_test.cc b/src/cobalt/network/local_network_test.cc
new file mode 100644
index 0000000..025e04d
--- /dev/null
+++ b/src/cobalt/network/local_network_test.cc
@@ -0,0 +1,59 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/network/local_network.h"
+
+#include "base/logging.h"
+#include "cobalt/network/socket_address_parser.h"
+#include "googleurl/src/url_parse.h"
+#include "starboard/socket.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace network {
+
+template <size_t N>
+SbSocketAddress ParseSocketAddress(const char(&address)[N]) {
+ const char* spec = address;
+ url_parse::Component component = url_parse::MakeRange(0, N - 1);
+ SbSocketAddress out_socket_address;
+ SbMemorySet(&out_socket_address, 0, sizeof(SbSocketAddress));
+ CHECK(ParseSocketAddress(spec, component, &out_socket_address));
+ return out_socket_address;
+}
+
+TEST(IsPrivateRange, v4) {
+ EXPECT_TRUE(IsIPInPrivateRange(ParseSocketAddress("10.0.0.1")));
+ EXPECT_TRUE(IsIPInPrivateRange(ParseSocketAddress("172.16.1.1")));
+ EXPECT_TRUE(IsIPInPrivateRange(ParseSocketAddress("192.168.1.1")));
+ EXPECT_FALSE(IsIPInPrivateRange(ParseSocketAddress("127.0.0.1")));
+ EXPECT_FALSE(IsIPInPrivateRange(ParseSocketAddress("143.195.170.1")));
+ EXPECT_FALSE(IsIPInPrivateRange(ParseSocketAddress("0.0.0.0")));
+ EXPECT_FALSE(IsIPInPrivateRange(ParseSocketAddress("239.255.255.255")));
+}
+
+#if SB_HAS(IPV6)
+
+TEST(IsPrivateRange, v6) {
+ EXPECT_TRUE(IsIPInPrivateRange(ParseSocketAddress("[fd00::]")));
+ EXPECT_TRUE(IsIPInPrivateRange(ParseSocketAddress("[fd00:1:2:3:4:5::]")));
+ EXPECT_FALSE(IsIPInPrivateRange(ParseSocketAddress("[fe80::]")));
+ EXPECT_FALSE(IsIPInPrivateRange(
+ ParseSocketAddress("[2606:2800:220:1:248:1893:25c8:1946]")));
+}
+
+#endif // SB_HAS(IPV6)
+
+} // namespace network
+} // namespace cobalt
diff --git a/src/cobalt/network/network.gyp b/src/cobalt/network/network.gyp
index e350e3b..d6296e3 100644
--- a/src/cobalt/network/network.gyp
+++ b/src/cobalt/network/network.gyp
@@ -23,10 +23,14 @@
'target_name': 'network',
'type': 'static_library',
'sources': [
+ 'socket_address_parser.cc',
+ 'socket_address_parser.h',
'cookie_jar_impl.cc',
'cookie_jar_impl.h',
'net_poster.cc',
'net_poster.h',
+ 'local_network.cc',
+ 'local_network.h',
'network_delegate.cc',
'network_delegate.h',
'network_event.h',
@@ -103,6 +107,7 @@
'target_name': 'network_test',
'type': '<(gtest_target_type)',
'sources': [
+ 'local_network_test.cc',
'persistent_cookie_store_test.cc',
'user_agent_string_factory_test.cc',
],
diff --git a/src/cobalt/network/network_delegate.cc b/src/cobalt/network/network_delegate.cc
index 39ff273..2755569 100644
--- a/src/cobalt/network/network_delegate.cc
+++ b/src/cobalt/network/network_delegate.cc
@@ -14,7 +14,10 @@
#include "cobalt/network/network_delegate.h"
+#include "cobalt/network/local_network.h"
+#include "cobalt/network/socket_address_parser.h"
#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
namespace cobalt {
namespace network {
@@ -40,14 +43,50 @@
bool require_https = require_https_;
#endif
+ const GURL& url = request->url();
if (!require_https) {
return net::OK;
- } else if (request->url().SchemeIsSecure() ||
- request->url().SchemeIs("data")) {
+ } else if (url.SchemeIsSecure() || url.SchemeIs("data")) {
return net::OK;
- } else {
- return net::ERR_DISALLOWED_URL_SCHEME;
}
+
+ if (!url.is_valid() || url.is_empty()) {
+ return net::ERR_INVALID_ARGUMENT;
+ }
+
+ const url_parse::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
+
+ if (!url.has_host() || !parsed.host.is_valid() ||
+ !parsed.host.is_nonempty()) {
+ return net::ERR_INVALID_ARGUMENT;
+ }
+
+ const std::string& valid_spec = url.possibly_invalid_spec();
+ const char* valid_spec_cstr = valid_spec.c_str();
+
+ std::string host;
+#if SB_HAS(IPV6)
+ host = url.HostNoBrackets();
+#else
+ host.append(valid_spec_cstr + parsed.host.begin,
+ valid_spec_cstr + parsed.host.begin + parsed.host.len);
+#endif
+ if (net::IsLocalhost(host)) {
+ return net::OK;
+ }
+
+ SbSocketAddress destination;
+ // Note that ParseSocketAddress will only pass if host is a numeric IP.
+ if (!cobalt::network::ParseSocketAddress(valid_spec_cstr, parsed.host,
+ &destination)) {
+ return net::ERR_INVALID_ARGUMENT;
+ }
+
+ if (IsIPInPrivateRange(destination) || IsIPInLocalNetwork(destination)) {
+ return net::OK;
+ }
+
+ return net::ERR_DISALLOWED_URL_SCHEME;
}
int NetworkDelegate::OnBeforeSendHeaders(
diff --git a/src/cobalt/network/socket_address_parser.cc b/src/cobalt/network/socket_address_parser.cc
new file mode 100644
index 0000000..6241682
--- /dev/null
+++ b/src/cobalt/network/socket_address_parser.cc
@@ -0,0 +1,85 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/network/socket_address_parser.h"
+
+#include "base/logging.h"
+#include "googleurl/src/url_canon.h"
+#include "googleurl/src/url_canon_ip.h"
+#include "net/base/net_util.h"
+#include "starboard/memory.h"
+
+namespace cobalt {
+namespace network {
+
+bool ParseSocketAddress(const char* spec,
+ const url_parse::Component& host_component,
+ SbSocketAddress* out_socket_address) {
+ DCHECK(out_socket_address);
+
+ unsigned char address_v4[net::kIPv4AddressSize];
+ int num_ipv4_components = 0;
+
+ // IPv4AddressToNumber will return either IPV4, BROKEN, or NEUTRAL. If the
+ // input IP address is IPv6, then NEUTRAL will be returned, and a different
+ // function will be used to covert the hostname to IPv6 address.
+ // If the return value is IPV4, then address will be in network byte order.
+ url_canon::CanonHostInfo::Family family = url_canon::IPv4AddressToNumber(
+ spec, host_component, address_v4, &num_ipv4_components);
+
+ switch (family) {
+ case url_canon::CanonHostInfo::IPV4:
+ if (num_ipv4_components != net::kIPv4AddressSize) {
+ return false;
+ }
+
+ SbMemorySet(out_socket_address, 0, sizeof(SbSocketAddress));
+ out_socket_address->type = kSbSocketAddressTypeIpv4;
+ DCHECK_GE(sizeof(address_v4),
+ static_cast<std::size_t>(num_ipv4_components));
+ SbMemoryCopy(out_socket_address->address, address_v4,
+ num_ipv4_components);
+
+ return true;
+ case url_canon::CanonHostInfo::NEUTRAL:
+#if SB_HAS(IPV6)
+ unsigned char address_v6[net::kIPv6AddressSize];
+ if (!url_canon::IPv6AddressToNumber(spec, host_component, address_v6)) {
+ break;
+ }
+
+ SbMemorySet(out_socket_address, 0, sizeof(SbSocketAddress));
+ out_socket_address->type = kSbSocketAddressTypeIpv6;
+ COMPILE_ASSERT(sizeof(address_v6), kIPv6AddressLength);
+ SbMemoryCopy(out_socket_address->address, address_v6, sizeof(address_v6));
+ return true;
+#else
+ return false;
+#endif
+ case url_canon::CanonHostInfo::BROKEN:
+ break;
+ case url_canon::CanonHostInfo::IPV6:
+ NOTREACHED() << "Invalid return value from IPv4AddressToNumber.";
+ break;
+ default:
+ NOTREACHED() << "Unexpected return value from IPv4AddressToNumber: "
+ << family;
+ break;
+ }
+
+ return false;
+}
+
+} // namespace network
+} // namespace cobalt
diff --git a/src/cobalt/network/socket_address_parser.h b/src/cobalt/network/socket_address_parser.h
new file mode 100644
index 0000000..091c450
--- /dev/null
+++ b/src/cobalt/network/socket_address_parser.h
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_NETWORK_SOCKET_ADDRESS_PARSER_H_
+#define COBALT_NETWORK_SOCKET_ADDRESS_PARSER_H_
+
+#include "googleurl/src/url_parse.h"
+#include "starboard/socket.h"
+
+namespace cobalt {
+namespace network {
+
+bool ParseSocketAddress(const char* spec,
+ const url_parse::Component& host_component,
+ SbSocketAddress* out_socket_address);
+} // namespace network
+} // namespace cobalt
+
+#endif // COBALT_NETWORK_SOCKET_ADDRESS_PARSER_H_
diff --git a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
index f37e90e..867e7ab 100644
--- a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
+++ b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
@@ -32,6 +32,9 @@
case kSbSystemDeviceTypeOverTheTopBox:
case kSbSystemDeviceTypeSetTopBox:
case kSbSystemDeviceTypeTV:
+#if SB_API_VERSION >= 4
+ case kSbSystemDeviceTypeAndroidTV:
+#endif // SB_API_VERSION >= 4
return true;
case kSbSystemDeviceTypeDesktopPC:
case kSbSystemDeviceTypeUnknown:
@@ -69,6 +72,14 @@
youtube_tv_info_->network_operator = value;
}
+#if SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
+ result = SbSystemGetProperty(kSbSystemPropertyUserAgentAuxField, value,
+ kSystemPropertyMaxLength);
+ if (result) {
+ aux_field_ = value;
+ }
+#endif // SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
+
// Device Type
switch (device_type) {
case kSbSystemDeviceTypeBlueRayDiskPlayer:
@@ -86,11 +97,11 @@
case kSbSystemDeviceTypeTV:
youtube_tv_info_->device_type = YouTubeTVInfo::kTV;
break;
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
case kSbSystemDeviceTypeAndroidTV:
youtube_tv_info_->device_type = YouTubeTVInfo::kAndroidTV;
break;
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif // SB_API_VERSION >= 4
case kSbSystemDeviceTypeDesktopPC:
default:
youtube_tv_info_->device_type = YouTubeTVInfo::kInvalidDeviceType;
diff --git a/src/cobalt/network/user_agent_string_factory.cc b/src/cobalt/network/user_agent_string_factory.cc
index b0c0639..7ad310b 100644
--- a/src/cobalt/network/user_agent_string_factory.cc
+++ b/src/cobalt/network/user_agent_string_factory.cc
@@ -73,6 +73,11 @@
youtube_tv_info_->brand.c_str(), youtube_tv_info_->model.c_str(),
CreateConnectionTypeString().c_str());
}
+
+ if (!aux_field_.empty()) {
+ user_agent.append(" ");
+ user_agent.append(aux_field_);
+ }
return user_agent;
}
@@ -107,10 +112,10 @@
return "STB";
case YouTubeTVInfo::kTV:
return "TV";
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
case YouTubeTVInfo::kAndroidTV:
return "ATV";
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif // SB_API_VERSION >= 4
case YouTubeTVInfo::kInvalidDeviceType:
default:
NOTREACHED();
diff --git a/src/cobalt/network/user_agent_string_factory.h b/src/cobalt/network/user_agent_string_factory.h
index d9e1b6e..5869855 100644
--- a/src/cobalt/network/user_agent_string_factory.h
+++ b/src/cobalt/network/user_agent_string_factory.h
@@ -42,13 +42,14 @@
std::string os_name_and_version_;
std::string architecture_tokens_;
std::string starboard_version_;
+ std::string aux_field_;
struct YouTubeTVInfo {
enum DeviceType {
kInvalidDeviceType,
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
kAndroidTV,
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif // SB_API_VERSION >= 4
kBlueRayDiskPlayer,
kGameConsole,
kOverTheTopBox,
diff --git a/src/cobalt/render_tree/font_provider.h b/src/cobalt/render_tree/font_provider.h
index 9df5a9c..5dc1a48 100644
--- a/src/cobalt/render_tree/font_provider.h
+++ b/src/cobalt/render_tree/font_provider.h
@@ -22,10 +22,16 @@
namespace render_tree {
// The FontProvider class is an abstract base class representing a collection of
-// fonts with a matching size and style, which provides fonts for any given
+// fonts with a matching style and size, which provides fonts for any given
// character based upon what it considers to be the best match.
class FontProvider {
public:
+ // The style of the fonts contained within the collection.
+ virtual const render_tree::FontStyle& style() const = 0;
+
+ // The size of the fonts contained within the collection.
+ virtual float size() const = 0;
+
// Returns the font-glyph combination that the FontProvider considers to be
// the best match for the passed in character. The returned font is guaranteed
// to be non-NULL. However, the glyph index may be set to |kInvalidGlyphIndex|
diff --git a/src/cobalt/render_tree/map_to_mesh_filter.h b/src/cobalt/render_tree/map_to_mesh_filter.h
index 4e583f0..2c126e6 100644
--- a/src/cobalt/render_tree/map_to_mesh_filter.h
+++ b/src/cobalt/render_tree/map_to_mesh_filter.h
@@ -15,8 +15,13 @@
#ifndef COBALT_RENDER_TREE_MAP_TO_MESH_FILTER_H_
#define COBALT_RENDER_TREE_MAP_TO_MESH_FILTER_H_
+#include <map>
+#include <utility>
+
#include "base/logging.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/math/size.h"
#include "cobalt/render_tree/mesh.h"
namespace cobalt {
@@ -37,41 +42,94 @@
};
// A MapToMeshFilter can be used to map source content onto a 3D mesh, within a
-// specified well-defined viewport. A null mesh indicates the equirectangular
-// mesh should be used.
+// specified well-defined viewport.
class MapToMeshFilter {
public:
- MapToMeshFilter(StereoMode stereo_mode,
- scoped_refptr<render_tree::Mesh> left_eye_mesh,
- scoped_refptr<render_tree::Mesh> right_eye_mesh = NULL)
- : stereo_mode_(stereo_mode),
- left_eye_mesh_(left_eye_mesh),
- right_eye_mesh_(right_eye_mesh) {
- DCHECK(left_eye_mesh_);
+ struct Builder {
+ Builder() {}
+ void SetDefaultMeshes(
+ const scoped_refptr<render_tree::Mesh>& left_eye_mesh,
+ const scoped_refptr<render_tree::Mesh>& right_eye_mesh) {
+ left_eye_default_mesh_ = left_eye_mesh;
+ right_eye_default_mesh_ = right_eye_mesh;
+ }
+ void AddResolutionMatchedMeshes(
+ math::Size resolution,
+ const scoped_refptr<render_tree::Mesh>& left_eye_mesh,
+ const scoped_refptr<render_tree::Mesh>& right_eye_mesh) {
+ DCHECK_GT(resolution.width(), 0);
+ DCHECK_GT(resolution.height(), 0);
+ DCHECK(left_eye_mesh);
+ resolution_matched_meshes_[resolution] =
+ std::make_pair(left_eye_mesh, right_eye_mesh);
+ }
+ const scoped_refptr<render_tree::Mesh>& left_eye_mesh(
+ math::Size resolution) const {
+ MeshMap::const_iterator match =
+ resolution_matched_meshes_.find(resolution);
+ if (match != resolution_matched_meshes_.end()) {
+ return match->second.first;
+ }
+ return left_eye_default_mesh_;
+ }
+ const scoped_refptr<render_tree::Mesh>& right_eye_mesh(
+ math::Size resolution) const {
+ MeshMap::const_iterator match =
+ resolution_matched_meshes_.find(resolution);
+ if (match != resolution_matched_meshes_.end()) {
+ return match->second.second;
+ }
+ return right_eye_default_mesh_;
+ }
+
+ private:
+ struct SizeLessThan {
+ bool operator()(const math::Size& a, const math::Size& b) const {
+ return a.width() < b.width() ||
+ (a.width() == b.width() && a.height() < b.height());
+ }
+ };
+ typedef std::pair<scoped_refptr<render_tree::Mesh>,
+ scoped_refptr<render_tree::Mesh> > MeshPair;
+ typedef std::map<math::Size, MeshPair, SizeLessThan> MeshMap;
+ MeshMap resolution_matched_meshes_;
+ scoped_refptr<render_tree::Mesh> left_eye_default_mesh_;
+ scoped_refptr<render_tree::Mesh> right_eye_default_mesh_;
+ };
+
+ MapToMeshFilter(StereoMode stereo_mode, const Builder& builder)
+ : stereo_mode_(stereo_mode), data_(builder) {
+ DCHECK(left_eye_mesh());
if (stereo_mode == kLeftRightUnadjustedTextureCoords) {
// This stereo mode implies there are two meshes.
- DCHECK(left_eye_mesh_ && right_eye_mesh_);
+ DCHECK(right_eye_mesh());
}
}
StereoMode stereo_mode() const { return stereo_mode_; }
- const scoped_refptr<render_tree::Mesh>& mono_mesh() const {
- return left_eye_mesh_;
+ // The omission of the |resolution| parameter will yield the default
+ // meshes in each of the following functions (by failing to match the
+ // invalid resolution).
+ const scoped_refptr<render_tree::Mesh>& mono_mesh(
+ math::Size resolution = InvalidSize()) const {
+ return data_.left_eye_mesh(resolution);
}
- const scoped_refptr<render_tree::Mesh>& left_eye_mesh() const {
- return left_eye_mesh_;
+ const scoped_refptr<render_tree::Mesh>& left_eye_mesh(
+ math::Size resolution = InvalidSize()) const {
+ return data_.left_eye_mesh(resolution);
}
- const scoped_refptr<render_tree::Mesh>& right_eye_mesh() const {
- return right_eye_mesh_;
+ const scoped_refptr<render_tree::Mesh>& right_eye_mesh(
+ math::Size resolution = InvalidSize()) const {
+ return data_.right_eye_mesh(resolution);
}
private:
+ static math::Size InvalidSize() { return math::Size(-1, -1); }
StereoMode stereo_mode_;
- scoped_refptr<render_tree::Mesh> left_eye_mesh_;
- scoped_refptr<render_tree::Mesh> right_eye_mesh_;
+ Builder data_;
};
} // namespace render_tree
diff --git a/src/cobalt/render_tree/mock_resource_provider.h b/src/cobalt/render_tree/mock_resource_provider.h
index 76edce2..1d13b6c 100644
--- a/src/cobalt/render_tree/mock_resource_provider.h
+++ b/src/cobalt/render_tree/mock_resource_provider.h
@@ -24,6 +24,7 @@
#include "cobalt/render_tree/font_provider.h"
#include "cobalt/render_tree/glyph_buffer.h"
#include "cobalt/render_tree/image.h"
+#include "cobalt/render_tree/node.h"
#include "cobalt/render_tree/resource_provider.h"
#include "cobalt/render_tree/typeface.h"
@@ -77,6 +78,10 @@
render_tree::Mesh*(std::vector<render_tree::Mesh::Vertex>*,
render_tree::Mesh::DrawMode));
+ MOCK_METHOD1(DrawOffscreenImage,
+ scoped_refptr<render_tree::Image>(
+ const scoped_refptr<render_tree::Node>& root));
+
scoped_ptr<ImageData> AllocateImageData(const math::Size& size,
PixelFormat pixel_format,
AlphaFormat alpha_format) {
@@ -87,18 +92,27 @@
return scoped_refptr<Image>(CreateImageMock(pixel_data.get()));
}
-#if defined(STARBOARD)
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
+
+#if SB_API_VERSION >= 3
scoped_refptr<Image> CreateImageFromSbDecodeTarget(SbDecodeTarget target) {
UNREFERENCED_PARAMETER(target);
return NULL;
}
- SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; }
-
bool SupportsSbDecodeTarget() { return false; }
-#endif // SB_VERSION(3) && SB_HAS(GRAPHICS)
-#endif // defined(STARBOARD)
+#endif // SB_API_VERSION >= 3
+
+#if SB_API_VERSION >= 4
+ SbDecodeTargetGraphicsContextProvider*
+ GetSbDecodeTargetGraphicsContextProvider() {
+ return NULL;
+ }
+#elif SB_API_VERSION >= 3
+ SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; }
+#endif // SB_API_VERSION >= 4
+
+#endif // SB_HAS(GRAPHICS)
scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes,
size_t alignment) {
@@ -117,7 +131,7 @@
GetLocalTypefaceMock(font_family_name, font_style));
}
scoped_refptr<Typeface> GetLocalTypefaceByFaceNameIfAvailable(
- const std::string& font_face_name) {
+ const char* font_face_name) {
return scoped_refptr<Typeface>(
GetLocalTypefaceIfAvailableMock(font_face_name));
}
diff --git a/src/cobalt/render_tree/resource_provider.h b/src/cobalt/render_tree/resource_provider.h
index 817ccd3..aa716d7 100644
--- a/src/cobalt/render_tree/resource_provider.h
+++ b/src/cobalt/render_tree/resource_provider.h
@@ -25,6 +25,7 @@
#include "cobalt/render_tree/glyph_buffer.h"
#include "cobalt/render_tree/image.h"
#include "cobalt/render_tree/mesh.h"
+#include "cobalt/render_tree/node.h"
#include "cobalt/render_tree/typeface.h"
#if defined(STARBOARD)
#include "starboard/decode_target.h"
@@ -74,22 +75,30 @@
virtual scoped_refptr<Image> CreateImage(
scoped_ptr<ImageData> pixel_data) = 0;
-#if defined(STARBOARD)
-#if SB_VERSION(3) && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3
// This function will consume an SbDecodeTarget object produced by
// SbDecodeTargetCreate(), wrap it in a render_tree::Image that can be used
// in a render tree, and return it to the caller.
virtual scoped_refptr<Image> CreateImageFromSbDecodeTarget(
SbDecodeTarget target) = 0;
+ // Whether SbDecodeTargetIsSupported or not.
+ virtual bool SupportsSbDecodeTarget() = 0;
+#endif // SB_API_VERSION >= 3
+
+#if SB_API_VERSION >= 4
+ // Return the SbDecodeTargetGraphicsContextProvider associated with the
+ // ResourceProvider, if it exists. Returns NULL if SbDecodeTarget is not
+ // supported.
+ virtual SbDecodeTargetGraphicsContextProvider*
+ GetSbDecodeTargetGraphicsContextProvider() = 0;
+#elif SB_API_VERSION >= 3
// Return the associated SbDecodeTargetProvider with the ResourceProvider,
// if it exists. Returns NULL if SbDecodeTarget is not supported.
virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() = 0;
-
- // Whether SbDecodeTargetIsSupported or not.
- virtual bool SupportsSbDecodeTarget() = 0;
#endif // SB_VERSION(3) && SB_HAS(GRAPHICS)
-#endif // defined(STARBOARD)
+#endif // SB_HAS(GRAPHICS)
// Returns a raw chunk of memory that can later be passed into a function like
// CreateMultiPlaneImageFromRawMemory() in order to create a texture.
@@ -134,7 +143,7 @@
// Font's typeface (aka face name) is combination of a style and a font
// family. Font's style consists of weight, and a slant (but not size).
virtual scoped_refptr<Typeface> GetLocalTypefaceByFaceNameIfAvailable(
- const std::string& font_face_name) = 0;
+ const char* font_face_name) = 0;
// Given a UTF-32 character, a set of typeface information, and a language,
// this method returns the best-fit locally available fallback typeface that
@@ -206,6 +215,9 @@
virtual scoped_refptr<Mesh> CreateMesh(
scoped_ptr<std::vector<Mesh::Vertex> > vertices,
Mesh::DrawMode draw_mode) = 0;
+
+ virtual scoped_refptr<Image> DrawOffscreenImage(
+ const scoped_refptr<render_tree::Node>& root) = 0;
};
} // namespace render_tree
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h
index e1e85cf..84658d8 100644
--- a/src/cobalt/render_tree/resource_provider_stub.h
+++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -212,19 +212,26 @@
scoped_refptr<Image> CreateImageFromSbDecodeTarget(
SbDecodeTarget decode_target) OVERRIDE {
NOTREACHED();
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
SbDecodeTargetDestroy(decode_target);
-#else // SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // 4
SbDecodeTargetRelease(decode_target);
-#endif // SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // 4
return NULL;
}
- SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
-
bool SupportsSbDecodeTarget() OVERRIDE { return false; }
#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 4
+ SbDecodeTargetGraphicsContextProvider*
+ GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+ return NULL;
+ }
+#elif SB_API_VERSION >= 3
+ SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
+#endif // SB_API_VERSION >= 4
+
scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes,
size_t alignment) OVERRIDE {
return scoped_ptr<RawImageMemory>(
@@ -252,7 +259,7 @@
}
scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
- const std::string& font_face_name) OVERRIDE {
+ const char* font_face_name) OVERRIDE {
UNREFERENCED_PARAMETER(font_face_name);
return make_scoped_refptr(new TypefaceStub(NULL));
}
@@ -316,6 +323,12 @@
render_tree::Mesh::DrawMode draw_mode) OVERRIDE {
return new MeshStub(vertices.Pass(), draw_mode);
}
+
+ scoped_refptr<Image> DrawOffscreenImage(
+ const scoped_refptr<render_tree::Node>& root) OVERRIDE {
+ UNREFERENCED_PARAMETER(root);
+ return scoped_refptr<Image>(NULL);
+ }
};
} // namespace render_tree
diff --git a/src/cobalt/renderer/animations_test.cc b/src/cobalt/renderer/animations_test.cc
index 8a4e153..1cdda44 100644
--- a/src/cobalt/renderer/animations_test.cc
+++ b/src/cobalt/renderer/animations_test.cc
@@ -141,7 +141,7 @@
// in this case, but it really should be working, as the media engine
// relies on this mechanism to deliver responsive video with minimal frame
// drops.
-TEST(AnimationsTest, FreshlyCreatedImagesCanBeUsedInAnimations) {
+TEST(AnimationsTest, DISABLED_FreshlyCreatedImagesCanBeUsedInAnimations) {
scoped_ptr<backend::GraphicsSystem> graphics_system =
backend::CreateDefaultGraphicsSystem();
scoped_ptr<backend::GraphicsContext> graphics_context =
diff --git a/src/cobalt/renderer/backend/blitter/surface_render_target.cc b/src/cobalt/renderer/backend/blitter/surface_render_target.cc
index 19255d7..2bdc05c 100644
--- a/src/cobalt/renderer/backend/blitter/surface_render_target.cc
+++ b/src/cobalt/renderer/backend/blitter/surface_render_target.cc
@@ -36,7 +36,9 @@
}
SurfaceRenderTargetBlitter::~SurfaceRenderTargetBlitter() {
- SbBlitterDestroySurface(surface_);
+ if (surface_ != kSbBlitterInvalidSurface) {
+ SbBlitterDestroySurface(surface_);
+ }
}
const math::Size& SurfaceRenderTargetBlitter::GetSize() { return size_; }
diff --git a/src/cobalt/renderer/backend/blitter/surface_render_target.h b/src/cobalt/renderer/backend/blitter/surface_render_target.h
index b8237a1..1a19e4f 100644
--- a/src/cobalt/renderer/backend/blitter/surface_render_target.h
+++ b/src/cobalt/renderer/backend/blitter/surface_render_target.h
@@ -36,6 +36,15 @@
SbBlitterSurface GetSbSurface() const { return surface_; }
+ // Returns and gives up ownership of the surface. After this is called, the
+ // SurfaceRenderTargetBlitter's internal surface will be invalid and so the
+ // object itself becomes invalid.
+ SbBlitterSurface TakeSbSurface() {
+ SbBlitterSurface original_surface_ = surface_;
+ surface_ = kSbBlitterInvalidSurface;
+ return original_surface_;
+ }
+
void Flip() OVERRIDE {}
private:
diff --git a/src/cobalt/renderer/backend/egl/display.cc b/src/cobalt/renderer/backend/egl/display.cc
index 4111f71..c0603ca 100644
--- a/src/cobalt/renderer/backend/egl/display.cc
+++ b/src/cobalt/renderer/backend/egl/display.cc
@@ -52,6 +52,7 @@
surface_ = eglCreateWindowSurface(display_, config_, native_window_, NULL);
CHECK_EQ(EGL_SUCCESS, eglGetError());
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
// Configure the surface to preserve contents on swap.
EGLBoolean surface_attrib_set =
eglSurfaceAttrib(display_, surface_,
@@ -61,6 +62,7 @@
// set the error condition.
content_preserved_on_swap_ =
eglGetError() == EGL_SUCCESS && surface_attrib_set == EGL_TRUE;
+#endif // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
// Query and cache information about the surface now that we have created it.
EGLint egl_surface_width;
diff --git a/src/cobalt/renderer/backend/egl/egl_backend.gypi b/src/cobalt/renderer/backend/egl/egl_backend.gypi
index 3cb7d12..e0d840c 100644
--- a/src/cobalt/renderer/backend/egl/egl_backend.gypi
+++ b/src/cobalt/renderer/backend/egl/egl_backend.gypi
@@ -18,6 +18,7 @@
'display.h',
'framebuffer.h',
'framebuffer.cc',
+ 'framebuffer_render_target.h',
'graphics_context.cc',
'graphics_context.h',
'graphics_system.cc',
@@ -38,7 +39,9 @@
'utils.cc',
'utils.h',
],
-
+ 'defines': [
+ 'COBALT_EGL_SWAP_INTERVAL=<(cobalt_egl_swap_interval)',
+ ],
'dependencies': [
'<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
],
@@ -46,4 +49,11 @@
'<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
],
+ 'conditions': [
+ ['render_dirty_region_only==1', {
+ 'defines': [
+ 'COBALT_RENDER_DIRTY_REGION_ONLY',
+ ],
+ }],
+ ],
}
diff --git a/src/cobalt/renderer/backend/egl/framebuffer.cc b/src/cobalt/renderer/backend/egl/framebuffer.cc
index 16ce201..068da37 100644
--- a/src/cobalt/renderer/backend/egl/framebuffer.cc
+++ b/src/cobalt/renderer/backend/egl/framebuffer.cc
@@ -56,13 +56,7 @@
// Create and attach a depth buffer if requested.
depthbuffer_handle_ = 0;
if (depth_format != GL_NONE) {
- GL_CALL(glGenRenderbuffers(1, &depthbuffer_handle_));
- GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer_handle_));
- GL_CALL(glRenderbufferStorage(GL_RENDERBUFFER, depth_format, size_.width(),
- size_.height()));
- GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
- GL_RENDERBUFFER, depthbuffer_handle_));
- GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
+ CreateDepthAttachment(depth_format);
}
// Verify the framebuffer object is valid.
@@ -70,6 +64,21 @@
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
}
+void FramebufferEGL::EnsureDepthBufferAttached(GLenum depth_format) {
+ if (depthbuffer_handle_ == 0) {
+ GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
+ graphics_context_);
+
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_handle_));
+ CreateDepthAttachment(depth_format);
+
+ // Verify the framebuffer object is valid.
+ DCHECK_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
+ GL_FRAMEBUFFER_COMPLETE);
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+ }
+}
+
FramebufferEGL::~FramebufferEGL() {
GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(graphics_context_);
GL_CALL(glDeleteFramebuffers(1, &framebuffer_handle_));
@@ -78,6 +87,16 @@
}
}
+void FramebufferEGL::CreateDepthAttachment(GLenum depth_format) {
+ GL_CALL(glGenRenderbuffers(1, &depthbuffer_handle_));
+ GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer_handle_));
+ GL_CALL(glRenderbufferStorage(GL_RENDERBUFFER, depth_format, size_.width(),
+ size_.height()));
+ GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, depthbuffer_handle_));
+ GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
+}
+
} // namespace backend
} // namespace renderer
} // namespace cobalt
diff --git a/src/cobalt/renderer/backend/egl/framebuffer.h b/src/cobalt/renderer/backend/egl/framebuffer.h
index d0f6839..20ffd68 100644
--- a/src/cobalt/renderer/backend/egl/framebuffer.h
+++ b/src/cobalt/renderer/backend/egl/framebuffer.h
@@ -39,6 +39,10 @@
const math::Size& GetSize() const { return size_; }
+ // Ensure the framebuffer has a depth buffer. If not, then create and
+ // attach one with the specified format.
+ void EnsureDepthBufferAttached(GLenum depth_format);
+
// Return the color attachment for the framebuffer as a texture.
TextureEGL* GetColorTexture() const { return color_texture_.get(); }
@@ -46,6 +50,8 @@
GLuint gl_handle() const { return framebuffer_handle_; }
private:
+ void CreateDepthAttachment(GLenum depth_format);
+
GraphicsContextEGL* graphics_context_;
math::Size size_;
diff --git a/src/cobalt/renderer/backend/egl/framebuffer_render_target.h b/src/cobalt/renderer/backend/egl/framebuffer_render_target.h
new file mode 100644
index 0000000..bdd6a10
--- /dev/null
+++ b/src/cobalt/renderer/backend/egl/framebuffer_render_target.h
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_BACKEND_EGL_FRAMEBUFFER_RENDER_TARGET_H_
+#define COBALT_RENDERER_BACKEND_EGL_FRAMEBUFFER_RENDER_TARGET_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/math/size.h"
+#include "cobalt/renderer/backend/egl/framebuffer.h"
+#include "cobalt/renderer/backend/egl/render_target.h"
+
+namespace cobalt {
+namespace renderer {
+namespace backend {
+
+// A framebuffer object wrapped in a RenderTarget interface. This allows
+// an easier path to reading pixels from the render target.
+class FramebufferRenderTargetEGL : public RenderTargetEGL {
+ public:
+ FramebufferRenderTargetEGL(GraphicsContextEGL* graphics_context,
+ const math::Size& size)
+ : framebuffer_(
+ new FramebufferEGL(graphics_context, size, GL_RGBA, GL_NONE)) {}
+
+ const math::Size& GetSize() OVERRIDE { return framebuffer_->GetSize(); }
+
+ // This render target does not have an EGLSurface.
+ EGLSurface GetSurface() const OVERRIDE { return EGL_NO_SURFACE; }
+
+ // This handle is suitable for use with glBindFramebuffer.
+ intptr_t GetPlatformHandle() OVERRIDE { return framebuffer_->gl_handle(); }
+
+ // Create a depth buffer for the render target if it doesn't already have one.
+ void EnsureDepthBufferAttached(GLenum depth_format) {
+ framebuffer_->EnsureDepthBufferAttached(depth_format);
+ }
+
+ // Get the color texture attachment of the framebuffer.
+ TextureEGL* GetColorTexture() const {
+ return framebuffer_->GetColorTexture();
+ }
+
+ private:
+ ~FramebufferRenderTargetEGL() OVERRIDE {}
+
+ scoped_ptr<FramebufferEGL> framebuffer_;
+};
+
+} // namespace backend
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_BACKEND_EGL_FRAMEBUFFER_RENDER_TARGET_H_
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.cc b/src/cobalt/renderer/backend/egl/graphics_context.cc
index 574ddd4..7b8b9bd 100644
--- a/src/cobalt/renderer/backend/egl/graphics_context.cc
+++ b/src/cobalt/renderer/backend/egl/graphics_context.cc
@@ -17,9 +17,13 @@
#include "cobalt/renderer/backend/egl/graphics_context.h"
+#include "base/debug/leak_annotations.h"
#include "base/debug/trace_event.h"
#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
#include "cobalt/renderer/backend/egl/graphics_system.h"
+#include "cobalt/renderer/backend/egl/pbuffer_render_target.h"
+#include "cobalt/renderer/backend/egl/render_target.h"
#include "cobalt/renderer/backend/egl/texture.h"
#include "cobalt/renderer/backend/egl/texture_data.h"
#include "cobalt/renderer/backend/egl/utils.h"
@@ -69,6 +73,13 @@
bgra_format_supported_ = HasExtension("GL_EXT_texture_format_BGRA8888");
SetupBlitObjects();
+
+ {
+ // The current mesa egl drivers leak memory on the first call to glDraw*.
+ // Get that first draw out of the way, and do something useful with it.
+ ANNOTATE_SCOPED_MEMORY_LEAK;
+ ComputeReadPixelsNeedVerticalFlip();
+ }
}
GraphicsSystemEGL* GraphicsContextEGL::system_egl() {
@@ -94,13 +105,11 @@
ScopedMakeCurrent scoped_make_current(this);
- scoped_refptr<RenderTarget> render_target = CreateOffscreenRenderTarget(
- math::Size(kDummyTextureWidth, kDummyTextureHeight));
- scoped_refptr<PBufferRenderTargetEGL> render_target_egl = make_scoped_refptr(
- base::polymorphic_downcast<PBufferRenderTargetEGL*>(render_target.get()));
- {
- ScopedMakeCurrent scoped_make_current(this, render_target_egl);
+ scoped_ptr<FramebufferEGL> framebuffer(new FramebufferEGL(this,
+ math::Size(kDummyTextureWidth, kDummyTextureHeight), GL_RGBA, GL_NONE));
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_handle()));
+ {
// Create a 2-pixel texture and then immediately blit it to our 2-pixel
// framebuffer render target.
GLuint texture;
@@ -135,23 +144,11 @@
}
// Now read back the texture data using glReadPixels().
- scoped_ptr<TextureEGL> render_target_texture(
- new TextureEGL(this, render_target_egl));
-
- GLuint framebuffer;
- GL_CALL(glGenFramebuffers(1, &framebuffer));
- GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer));
-
- GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D,
- render_target_texture->gl_handle(), 0));
-
- uint32_t out_data[2];
+ uint32_t out_data[kDummyTextureWidth * kDummyTextureHeight];
GL_CALL(glReadPixels(0, 0, kDummyTextureWidth, kDummyTextureHeight, GL_RGBA,
GL_UNSIGNED_BYTE, out_data));
GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
- GL_CALL(glDeleteFramebuffers(1, &framebuffer));
// Ensure the data is in one of two possible states, flipped or not flipped.
DCHECK((out_data[0] == 0x00000000 && out_data[1] == 0xFFFFFFFF) ||
@@ -243,7 +240,29 @@
"Use ReleaseCurrentContext().";
EGLSurface egl_surface = surface->GetSurface();
- EGL_CALL(eglMakeCurrent(display_, egl_surface, egl_surface, context_));
+ if (egl_surface != EGL_NO_SURFACE) {
+ // This render target is not a frame buffer object. It has an EGLSurface
+ // that can be bound using eglMakeCurrent.
+ DCHECK_EQ(surface->GetPlatformHandle(), 0);
+
+ EGL_CALL(eglMakeCurrent(display_, egl_surface, egl_surface, context_));
+
+ // Minimize calls to glBindFramebuffer. Normally, nothing keeps their
+ // framebuffer object bound, so 0 is normally bound at this point --
+ // unless the previous MakeCurrentWithSurface bound a framebuffer object.
+ if (current_surface_ && current_surface_->GetPlatformHandle() != 0) {
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+ }
+ } else {
+ // This is a framebuffer object, and it does not have an EGL surface. It
+ // must be bound using glBindFramebuffer. Use the null surface's EGLSurface
+ // with eglMakeCurrent to avoid polluting the previous EGLSurface target.
+ DCHECK_NE(surface->GetPlatformHandle(), 0);
+
+ egl_surface = null_surface_->GetSurface();
+ EGL_CALL(eglMakeCurrent(display_, egl_surface, egl_surface, context_));
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, surface->GetPlatformHandle()));
+ }
if (surface->IsWindowRenderTarget() && !surface->has_been_made_current()) {
SecurityClear();
@@ -254,11 +273,26 @@
current_surface_ = surface;
}
+void GraphicsContextEGL::ResetCurrentSurface() {
+ if (is_current_ && current_surface_) {
+ EGLSurface egl_surface = current_surface_->GetSurface();
+ if (egl_surface == EGL_NO_SURFACE) {
+ DCHECK_NE(current_surface_->GetPlatformHandle(), 0);
+ egl_surface = null_surface_->GetSurface();
+ }
+
+ EGL_CALL(eglMakeCurrent(display_, egl_surface, egl_surface, context_));
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER,
+ current_surface_->GetPlatformHandle()));
+ }
+}
+
void GraphicsContextEGL::MakeCurrent() {
MakeCurrentWithSurface(null_surface_);
}
void GraphicsContextEGL::ReleaseCurrentContext() {
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
EGL_CALL(eglMakeCurrent(
display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
@@ -292,6 +326,15 @@
return render_target;
}
+scoped_refptr<RenderTarget>
+ GraphicsContextEGL::CreateDownloadableOffscreenRenderTarget(
+ const math::Size& dimensions) {
+ scoped_refptr<RenderTarget> render_target(new FramebufferRenderTargetEGL(
+ this, dimensions));
+
+ return render_target;
+}
+
void GraphicsContextEGL::InitializeDebugContext() {
ComputeReadPixelsNeedVerticalFlip();
}
@@ -314,40 +357,54 @@
const scoped_refptr<RenderTarget>& render_target) {
TRACE_EVENT0("cobalt::renderer",
"GraphicsContextEGL::DownloadPixelDataAsRGBA()");
-
- PBufferRenderTargetEGL* pbuffer_render_target =
- base::polymorphic_downcast<PBufferRenderTargetEGL*>(render_target.get());
-
- scoped_ptr<TextureEGL> texture(new TextureEGL(this, pbuffer_render_target));
-
ScopedMakeCurrent scoped_current_context(this);
- // This shouldn't be strictly necessary as glReadPixels() should implicitly
- // call glFinish(), however it doesn't hurt to be safe and guard against
- // potentially different implementations. Performance is not an issue
- // in this function, because it is only used by tests to verify rendered
- // output.
- GL_CALL(glFinish());
-
- GLuint texture_framebuffer;
- GL_CALL(glGenFramebuffers(1, &texture_framebuffer));
- GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, texture_framebuffer));
-
- GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, texture->gl_handle(), 0));
- DCHECK_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
-
int pitch_in_bytes =
- texture->GetSize().width() * BytesPerPixelForGLFormat(GL_RGBA);
-
+ render_target->GetSize().width() * BytesPerPixelForGLFormat(GL_RGBA);
scoped_array<uint8_t> pixels(
- new uint8_t[texture->GetSize().height() * pitch_in_bytes]);
- GL_CALL(glReadPixels(0, 0, texture->GetSize().width(),
- texture->GetSize().height(), GL_RGBA, GL_UNSIGNED_BYTE,
- pixels.get()));
+ new uint8_t[render_target->GetSize().height() * pitch_in_bytes]);
- GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
- GL_CALL(glDeleteFramebuffers(1, &texture_framebuffer));
+ if (render_target->GetPlatformHandle() == 0) {
+ // Need to bind the PBufferSurface to a framebuffer object in order to
+ // read its pixels.
+ PBufferRenderTargetEGL* pbuffer_render_target =
+ base::polymorphic_downcast<PBufferRenderTargetEGL*>
+ (render_target.get());
+
+ scoped_ptr<TextureEGL> texture(new TextureEGL(this, pbuffer_render_target));
+ DCHECK(texture->GetSize() == render_target->GetSize());
+
+ // This shouldn't be strictly necessary as glReadPixels() should implicitly
+ // call glFinish(), however it doesn't hurt to be safe and guard against
+ // potentially different implementations. Performance is not an issue
+ // in this function, because it is only used by tests to verify rendered
+ // output.
+ GL_CALL(glFinish());
+
+ GLuint texture_framebuffer;
+ GL_CALL(glGenFramebuffers(1, &texture_framebuffer));
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, texture_framebuffer));
+
+ GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, texture->gl_handle(), 0));
+ DCHECK_EQ(GL_FRAMEBUFFER_COMPLETE,
+ glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ GL_CALL(glReadPixels(0, 0, texture->GetSize().width(),
+ texture->GetSize().height(), GL_RGBA, GL_UNSIGNED_BYTE,
+ pixels.get()));
+
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+ GL_CALL(glDeleteFramebuffers(1, &texture_framebuffer));
+ } else {
+ // The render target is a framebuffer object, so just bind it and read.
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER,
+ render_target->GetPlatformHandle()));
+ GL_CALL(glReadPixels(0, 0, render_target->GetSize().width(),
+ render_target->GetSize().height(), GL_RGBA,
+ GL_UNSIGNED_BYTE, pixels.get()));
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
+ }
// Vertically flip the resulting pixel data before returning so that the 0th
// pixel is at the top-left. While computing this is not a fast procedure,
@@ -359,7 +416,7 @@
// to return already flipped pixels. So in that case, we flip them again
// before returning here.
VerticallyFlipPixels(pixels.get(), pitch_in_bytes,
- texture->GetSize().height());
+ render_target->GetSize().height());
}
return pixels.Pass();
@@ -404,13 +461,19 @@
void GraphicsContextEGL::SwapBuffers(RenderTargetEGL* surface) {
TRACE_EVENT0("cobalt::renderer", "GraphicsContextEGL::SwapBuffers()");
- eglSwapBuffers(display_, surface->GetSurface());
- EGLint swap_err = eglGetError();
- if (swap_err != EGL_SUCCESS) {
- LOG(WARNING) << "Marking surface bad after swap error "
- << std::hex << swap_err;
- surface->set_surface_bad();
- return;
+ // SwapBuffers should have no effect for offscreen render targets. The
+ // current implementation of eglSwapBuffers() does nothing for PBuffers,
+ // so only check for framebuffer render targets.
+ if (surface->GetPlatformHandle() == 0) {
+ eglSwapInterval(display_, COBALT_EGL_SWAP_INTERVAL);
+ eglSwapBuffers(display_, surface->GetSurface());
+ EGLint swap_err = eglGetError();
+ if (swap_err != EGL_SUCCESS) {
+ LOG(WARNING) << "Marking surface bad after swap error "
+ << std::hex << swap_err;
+ surface->set_surface_bad();
+ return;
+ }
}
surface->increment_swap_count();
@@ -423,9 +486,16 @@
}
void GraphicsContextEGL::SecurityClear() {
+#if defined(COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR)
// Clear the screen to a color that is bright and gross to exaggerate that
// this is a problem if it is witnessed.
GL_CALL(glClearColor(1.0f, 0.4f, 1.0f, 1.0f));
+#else
+ // In release builds we certainly still want to perform this security
+ // clear, but should a poor user actually encounter it, we don't need
+ // to shock them with an ugly pink color.
+ GL_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
+#endif // defined(COBALT_SECURITY_SCREEN_CLEAR_TO_UGLY_COLOR)
GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
}
diff --git a/src/cobalt/renderer/backend/egl/graphics_context.h b/src/cobalt/renderer/backend/egl/graphics_context.h
index 487b33d..6ed533f 100644
--- a/src/cobalt/renderer/backend/egl/graphics_context.h
+++ b/src/cobalt/renderer/backend/egl/graphics_context.h
@@ -55,6 +55,9 @@
scoped_refptr<RenderTarget> CreateOffscreenRenderTarget(
const math::Size& dimensions) OVERRIDE;
+ scoped_refptr<RenderTarget> CreateDownloadableOffscreenRenderTarget(
+ const math::Size& dimensions) OVERRIDE;
+
void InitializeDebugContext() OVERRIDE;
scoped_array<uint8_t> DownloadPixelDataAsRGBA(
@@ -100,6 +103,9 @@
// specified as a surface.
void MakeCurrentWithSurface(RenderTargetEGL* surface);
+ // If this context is current, then forcefully rebind its current surface.
+ void ResetCurrentSurface();
+
// Alternatively, this call can be made to make the context current along
// with a null surface. You would be interested in this method if you don't
// plan to be making any draw calls, such as if you're setting up a texture.
diff --git a/src/cobalt/renderer/backend/egl/graphics_system.cc b/src/cobalt/renderer/backend/egl/graphics_system.cc
index 0c357f5..86a40d1 100644
--- a/src/cobalt/renderer/backend/egl/graphics_system.cc
+++ b/src/cobalt/renderer/backend/egl/graphics_system.cc
@@ -14,6 +14,7 @@
#include "cobalt/renderer/backend/egl/graphics_system.h"
+#include "base/debug/leak_annotations.h"
#if defined(ENABLE_GLIMP_TRACING)
#include "base/debug/trace_event.h"
#endif
@@ -58,13 +59,22 @@
display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
CHECK_NE(EGL_NO_DISPLAY, display_);
CHECK_EQ(EGL_SUCCESS, eglGetError());
- EGL_CALL(eglInitialize(display_, NULL, NULL));
+
+ {
+ // Despite eglTerminate() being used in the destructor, the current
+ // mesa egl drivers still leak memory.
+ ANNOTATE_SCOPED_MEMORY_LEAK;
+ EGL_CALL(eglInitialize(display_, NULL, NULL));
+ }
// Setup our configuration to support RGBA and compatibility with PBuffer
// objects (for offscreen rendering).
EGLint attribute_list[] = {EGL_SURFACE_TYPE, // this must be first
- EGL_WINDOW_BIT | EGL_PBUFFER_BIT |
- EGL_SWAP_BEHAVIOR_PRESERVED_BIT,
+ EGL_WINDOW_BIT | EGL_PBUFFER_BIT
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
+ | EGL_SWAP_BEHAVIOR_PRESERVED_BIT
+#endif // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
+ ,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
@@ -79,19 +89,21 @@
#endif
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
-#if defined(COBALT_FORCE_CUSTOM_RASTERIZER)
+#if defined(COBALT_RASTERIZER_USES_DEPTH_BUFFER)
EGL_DEPTH_SIZE,
16,
#endif
EGL_NONE};
+ EGLint num_configs;
+ eglChooseConfig(display_, attribute_list, &config_, 1, &num_configs);
+
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
// Try to allow preservation of the frame contents between swap calls --
// this will allow rendering of only parts of the frame that have changed.
DCHECK_EQ(EGL_SURFACE_TYPE, attribute_list[0]);
EGLint& surface_type_value = attribute_list[1];
- EGLint num_configs;
- eglChooseConfig(display_, attribute_list, &config_, 1, &num_configs);
if (eglGetError() != EGL_SUCCESS || num_configs == 0) {
// Swap buffer preservation may not be supported. Try to find a config
// without the feature.
@@ -99,6 +111,8 @@
EGL_CALL(
eglChooseConfig(display_, attribute_list, &config_, 1, &num_configs));
}
+#endif // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
+
DCHECK_EQ(1, num_configs);
#if defined(GLES3_SUPPORTED)
diff --git a/src/cobalt/renderer/backend/egl/texture.cc b/src/cobalt/renderer/backend/egl/texture.cc
index 49f83b1..ce9cd43 100644
--- a/src/cobalt/renderer/backend/egl/texture.cc
+++ b/src/cobalt/renderer/backend/egl/texture.cc
@@ -17,7 +17,11 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include "base/bind.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
+#include "cobalt/renderer/backend/egl/pbuffer_render_target.h"
#include "cobalt/renderer/backend/egl/resource_context.h"
#include "cobalt/renderer/backend/egl/texture_data.h"
#include "cobalt/renderer/backend/egl/texture_data_cpu.h"
@@ -27,6 +31,11 @@
namespace renderer {
namespace backend {
+namespace {
+void DoNothing() {
+}
+} // namespace
+
TextureEGL::TextureEGL(GraphicsContextEGL* graphics_context,
scoped_ptr<TextureDataEGL> texture_source_data,
bool bgra_supported)
@@ -62,7 +71,7 @@
TextureEGL::TextureEGL(
GraphicsContextEGL* graphics_context,
- const scoped_refptr<PBufferRenderTargetEGL>& render_target)
+ const scoped_refptr<RenderTargetEGL>& render_target)
: graphics_context_(graphics_context),
size_(render_target->GetSize()),
format_(GL_RGBA),
@@ -71,27 +80,51 @@
source_render_target_ = render_target;
- // First we create the OpenGL texture object and maintain a handle to it.
- GL_CALL(glGenTextures(1, &gl_handle_));
- GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_handle_));
- SetupInitialTextureParameters();
+ if (render_target->GetSurface() != EGL_NO_SURFACE) {
+ // This is a PBufferRenderTargetEGL. Need to bind a texture to the surface.
+ const PBufferRenderTargetEGL* pbuffer_target =
+ base::polymorphic_downcast<const PBufferRenderTargetEGL*>
+ (render_target.get());
- // This call attaches the EGL PBuffer object to the currently bound OpenGL
- // texture object, effectively allowing the PBO render target to be used
- // as a texture by referencing gl_handle_ from now on.
- EGL_CALL(eglBindTexImage(render_target->display(),
- render_target->GetSurface(),
- EGL_BACK_BUFFER));
+ // First we create the OpenGL texture object and maintain a handle to it.
+ GL_CALL(glGenTextures(1, &gl_handle_));
+ GL_CALL(glBindTexture(GL_TEXTURE_2D, gl_handle_));
+ SetupInitialTextureParameters();
- GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
+ // This call attaches the EGL PBuffer object to the currently bound OpenGL
+ // texture object, effectively allowing the PBO render target to be used
+ // as a texture by referencing gl_handle_ from now on.
+ EGL_CALL(eglBindTexImage(pbuffer_target->display(),
+ pbuffer_target->GetSurface(),
+ EGL_BACK_BUFFER));
+
+ GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
+ } else {
+ // This is a FramebufferRenderTargetEGL. Wrap its color texture attachment.
+ const FramebufferRenderTargetEGL* framebuffer_target =
+ base::polymorphic_downcast<const FramebufferRenderTargetEGL*>
+ (render_target.get());
+
+ const TextureEGL* color_attachment = framebuffer_target->GetColorTexture();
+ format_ = color_attachment->GetFormat();
+ target_ = color_attachment->GetTarget();
+ gl_handle_ = color_attachment->gl_handle();
+
+ // Do not destroy the wrapped texture. Let the render target do that.
+ delete_function_ = base::Bind(&DoNothing);
+ }
}
TextureEGL::~TextureEGL() {
GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(graphics_context_);
- if (source_render_target_) {
- EGL_CALL(eglReleaseTexImage(source_render_target_->display(),
- source_render_target_->GetSurface(),
+ if (source_render_target_ &&
+ source_render_target_->GetSurface() != EGL_NO_SURFACE) {
+ const PBufferRenderTargetEGL* pbuffer_target =
+ base::polymorphic_downcast<const PBufferRenderTargetEGL*>
+ (source_render_target_.get());
+ EGL_CALL(eglReleaseTexImage(pbuffer_target->display(),
+ pbuffer_target->GetSurface(),
EGL_BACK_BUFFER));
}
diff --git a/src/cobalt/renderer/backend/egl/texture.h b/src/cobalt/renderer/backend/egl/texture.h
index b723a05..3da9d46 100644
--- a/src/cobalt/renderer/backend/egl/texture.h
+++ b/src/cobalt/renderer/backend/egl/texture.h
@@ -19,7 +19,7 @@
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
-#include "cobalt/renderer/backend/egl/pbuffer_render_target.h"
+#include "cobalt/renderer/backend/egl/render_target.h"
#include "cobalt/renderer/backend/egl/texture_data.h"
namespace cobalt {
@@ -52,10 +52,9 @@
const math::Size& size, GLenum format, GLenum target,
const base::Closure& delete_function);
- // Create a texture from a pre-existing offscreen PBuffer render target.
- explicit TextureEGL(
- GraphicsContextEGL* graphics_context,
- const scoped_refptr<PBufferRenderTargetEGL>& render_target);
+ // Create a texture from a pre-existing offscreen render target.
+ TextureEGL(GraphicsContextEGL* graphics_context,
+ const scoped_refptr<RenderTargetEGL>& render_target);
virtual ~TextureEGL();
const math::Size& GetSize() const { return size_; }
@@ -86,7 +85,7 @@
// If the texture was constructed from a render target, we keep a reference
// to the render target.
- scoped_refptr<PBufferRenderTargetEGL> source_render_target_;
+ scoped_refptr<RenderTargetEGL> source_render_target_;
// If non-null, will be called upon destruction instead of manually deleting
// the texture via glDeleteTextures().
diff --git a/src/cobalt/renderer/backend/graphics_context.h b/src/cobalt/renderer/backend/graphics_context.h
index 2e97703..4504cd4 100644
--- a/src/cobalt/renderer/backend/graphics_context.h
+++ b/src/cobalt/renderer/backend/graphics_context.h
@@ -48,6 +48,14 @@
virtual scoped_refptr<RenderTarget> CreateOffscreenRenderTarget(
const math::Size& dimensions) = 0;
+ // Similar to CreateOffscreenRenderTarget, but this should be used when
+ // the target will be used with DownloadPixelDataAsRGBA. This render target
+ // is more compatible for reading.
+ virtual scoped_refptr<RenderTarget> CreateDownloadableOffscreenRenderTarget(
+ const math::Size& dimensions) {
+ return CreateOffscreenRenderTarget(dimensions);
+ }
+
// Initializes any values that are needed in a debugging context.
virtual void InitializeDebugContext() {}
diff --git a/src/cobalt/renderer/copy_font_data.gypi b/src/cobalt/renderer/copy_font_data.gypi
index 55ddeb3..971f550 100644
--- a/src/cobalt/renderer/copy_font_data.gypi
+++ b/src/cobalt/renderer/copy_font_data.gypi
@@ -18,205 +18,311 @@
{
'includes': [ '../build/contents_dir.gypi' ],
'variables': {
- 'source_all_fonts_dir': '<(static_contents_source_dir)/fonts/all_fonts',
+ 'source_font_files_dir': '<(static_contents_source_dir)/fonts/font_files',
+
+ 'conditions': [
+ [ 'cobalt_font_package == "expanded"', {
+ 'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/common',
+
+ 'package_named_sans_serif': 4,
+ 'package_named_serif': 4,
+ 'package_named_fcc_fonts': 2,
+ 'package_fallback_lang_non_cjk': 2,
+ 'package_fallback_lang_cjk': 2,
+ 'package_fallback_lang_cjk_low_quality': 0,
+ 'package_fallback_lang_jp': 0,
+ 'package_fallback_emoji': 1,
+ 'package_fallback_symbols': 1,
+ }],
+
+ # 'unlimited' is deprecated but is mapped to 'standard'
+ [ 'cobalt_font_package == "standard" or cobalt_font_package == "unlimited"', {
+ 'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/common',
+
+ 'package_named_sans_serif': 3,
+ 'package_named_serif': 3,
+ 'package_named_fcc_fonts': 2,
+ 'package_fallback_lang_non_cjk': 2,
+ 'package_fallback_lang_cjk': 1,
+ 'package_fallback_lang_cjk_low_quality': 0,
+ 'package_fallback_lang_jp': 0,
+ 'package_fallback_emoji': 1,
+ 'package_fallback_symbols': 1,
+ }],
+
+ # '10megabytes' is deprecated but is mapped to 'limited_with_noto_jp'
+ [ 'cobalt_font_package == "limited_with_jp" or cobalt_font_package == "10megabytes"', {
+ 'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/common',
+
+ 'package_named_sans_serif': 2,
+ 'package_named_serif': 0,
+ 'package_named_fcc_fonts': 0,
+ 'package_fallback_lang_non_cjk': 1,
+ 'package_fallback_lang_cjk': 0,
+ 'package_fallback_lang_cjk_low_quality': 1,
+ 'package_fallback_lang_jp': 1,
+ 'package_fallback_emoji': 1,
+ 'package_fallback_symbols': 1,
+ }],
+
+ [ 'cobalt_font_package == "limited"', {
+ 'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/common',
+
+ 'package_named_sans_serif': 2,
+ 'package_named_serif': 0,
+ 'package_named_fcc_fonts': 0,
+ 'package_fallback_lang_non_cjk': 1,
+ 'package_fallback_lang_cjk': 0,
+ 'package_fallback_lang_cjk_low_quality': 1,
+ 'package_fallback_lang_jp': 0,
+ 'package_fallback_emoji': 1,
+ 'package_fallback_symbols': 1,
+ }],
+
+ [ 'cobalt_font_package == "minimal"', {
+ 'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/common',
+
+ 'package_named_sans_serif': 0,
+ 'package_named_serif': 0,
+ 'package_named_fcc_fonts': 0,
+ 'package_fallback_lang_non_cjk': 0,
+ 'package_fallback_lang_cjk': 0,
+ 'package_fallback_lang_cjk_low_quality': 0,
+ 'package_fallback_lang_jp': 0,
+ 'package_fallback_emoji': 0,
+ 'package_fallback_symbols': 0,
+ }],
+
+ [ 'cobalt_font_package == "android_system"', {
+ # fonts.xml contains a superset of what we expect to find on Android
+ # devices. The Android SbFile implementation falls back to system font
+ # files for those not in cobalt content.
+ 'source_xml_file_dir': '<(static_contents_source_dir)/fonts/xml/android',
+
+ # Emojis are currently included because Cobalt's version of Skia does
+ # not support Android's color emojis and will be removed when Skia is
+ # rebased.
+ 'package_named_sans_serif': 0,
+ 'package_named_serif': 0,
+ 'package_named_fcc_fonts': 0,
+ 'package_fallback_lang_non_cjk': 0,
+ 'package_fallback_lang_cjk': 0,
+ 'package_fallback_lang_cjk_low_quality': 0,
+ 'package_fallback_lang_jp': 0,
+ 'package_fallback_emoji': 1,
+ 'package_fallback_symbols': 0,
+ }],
+
+ [ 'cobalt_font_package_override_named_sans_serif >= 0', {
+ 'package_named_sans_serif': '<(cobalt_font_package_override_named_sans_serif)',
+ }],
+ [ 'cobalt_font_package_override_named_serif >= 0', {
+ 'package_named_serif': '<(cobalt_font_package_override_named_serif)',
+ }],
+ [ 'cobalt_font_package_override_named_fcc_fonts >= 0', {
+ 'package_named_fcc_fonts': '<(cobalt_font_package_override_named_fcc_fonts)',
+ }],
+ [ 'cobalt_font_package_override_fallback_lang_non_cjk >= 0', {
+ 'package_fallback_lang_non_cjk': '<(cobalt_font_package_override_fallback_lang_non_cjk)',
+ }],
+ [ 'cobalt_font_package_override_fallback_lang_cjk >= 0', {
+ 'package_fallback_lang_cjk': '<(cobalt_font_package_override_fallback_lang_cjk)',
+ }],
+ [ 'cobalt_font_package_override_fallback_lang_cjk_low_quality >= 0', {
+ 'package_fallback_lang_cjk_low_quality': '<(cobalt_font_package_override_fallback_lang_cjk_low_quality)',
+ }],
+ [ 'cobalt_font_package_override_fallback_lang_jp >= 0', {
+ 'package_fallback_lang_jp': '<(cobalt_font_package_override_fallback_lang_jp)',
+ }],
+ [ 'cobalt_font_package_override_fallback_emoji >= 0', {
+ 'package_fallback_emoji': '<(cobalt_font_package_override_fallback_emoji)',
+ }],
+ [ 'cobalt_font_package_override_fallback_symbols >= 0', {
+ 'package_fallback_symbols': '<(cobalt_font_package_override_fallback_symbols)',
+ }],
+ ],
},
'copies': [
{
'destination': '<(static_contents_output_data_dir)/fonts/',
'files': [
- '<(static_contents_source_dir)/fonts/<(cobalt_font_package)/fonts.xml',
+ '<(source_xml_file_dir)/fonts.xml',
],
'conditions': [
- [ 'cobalt_font_package == "android_system"', {
+ [ 'package_named_sans_serif == 0', {
'files+': [
- # Only include minimal fonts in cobalt content, but fonts.xml
- # contains a superset of what we expect to find on Android devices.
- # The Android SbFile implementation falls back to system font files
- # for those not in cobalt content.
- '<(source_all_fonts_dir)/Roboto-Regular-Subsetted.ttf',
- '<(source_all_fonts_dir)/NotoEmoji-Regular.ttf',
+ '<(source_font_files_dir)/Roboto-Regular-Subsetted.ttf',
],
- }], # android_system
- [ 'cobalt_font_package == "minimal"', {
+ }],
+ [ 'package_named_sans_serif >= 1', {
'files+': [
- '<(source_all_fonts_dir)/Roboto-Regular-Subsetted.ttf',
+ '<(source_font_files_dir)/Roboto-Regular.ttf',
],
- }], # minimal
- [ 'cobalt_font_package == "10megabytes"', {
+ }],
+ [ 'package_named_sans_serif >= 2', {
'files+': [
- '<(source_all_fonts_dir)/Roboto-Regular.ttf',
- '<(source_all_fonts_dir)/NotoNaskhArabicUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansEthiopic-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansHebrew-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansThaiUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansArmenian-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansGeorgian-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansDevanagariUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansGujaratiUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansGurmukhiUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTamilUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansMalayalamUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBengaliUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTeluguUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansKannadaUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansOriyaUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSinhala-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansKhmerUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansLaoUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansThaana-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansCham-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBalinese-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBamum-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBatak-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBuginese-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBuhid-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansCanadianAboriginal-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansCherokee-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansCoptic-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansGlagolitic-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansHanunoo-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansJavanese-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansKayahLi-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansLepcha-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansLimbu-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansLisu-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansMandaic-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansMeeteiMayek-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansNewTaiLue-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansNKo-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansOlChiki-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansRejang-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSaurashtra-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSundanese-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSylotiNagri-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSyriacEstrangela-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTagbanwa-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTaiTham-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTaiViet-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTibetan-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTifinagh-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansVai-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansYi-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSymbols-Regular-Subsetted.ttf',
- '<(source_all_fonts_dir)/NotoSansJP-Regular.otf',
- '<(source_all_fonts_dir)/NotoEmoji-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSymbols-Regular-Subsetted2.ttf',
- '<(source_all_fonts_dir)/DroidSansFallback.ttf',
- '<(source_all_fonts_dir)/NotoSansTaiLe-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansMongolian-Regular.ttf',
+ '<(source_font_files_dir)/Roboto-Bold.ttf',
],
- }], # 10megabytes
- [ 'cobalt_font_package == "unlimited"', {
+ }],
+ [ 'package_named_sans_serif >= 3', {
'files+': [
- '<(source_all_fonts_dir)/Roboto-Thin.ttf',
- '<(source_all_fonts_dir)/Roboto-ThinItalic.ttf',
- '<(source_all_fonts_dir)/Roboto-Light.ttf',
- '<(source_all_fonts_dir)/Roboto-LightItalic.ttf',
- '<(source_all_fonts_dir)/Roboto-Regular.ttf',
- '<(source_all_fonts_dir)/Roboto-Italic.ttf',
- '<(source_all_fonts_dir)/Roboto-Medium.ttf',
- '<(source_all_fonts_dir)/Roboto-MediumItalic.ttf',
- '<(source_all_fonts_dir)/Roboto-Bold.ttf',
- '<(source_all_fonts_dir)/Roboto-BoldItalic.ttf',
- '<(source_all_fonts_dir)/Roboto-Black.ttf',
- '<(source_all_fonts_dir)/Roboto-BlackItalic.ttf',
- '<(source_all_fonts_dir)/NotoSerif-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSerif-Italic.ttf',
- '<(source_all_fonts_dir)/NotoSerif-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSerif-BoldItalic.ttf',
- '<(source_all_fonts_dir)/DroidSansMono.ttf',
- '<(source_all_fonts_dir)/CutiveMono.ttf',
- '<(source_all_fonts_dir)/ComingSoon.ttf',
- '<(source_all_fonts_dir)/DancingScript-Regular.ttf',
- '<(source_all_fonts_dir)/DancingScript-Bold.ttf',
- '<(source_all_fonts_dir)/CarroisGothicSC-Regular.ttf',
- '<(source_all_fonts_dir)/NotoNaskhArabicUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoNaskhArabicUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansEthiopic-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansEthiopic-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansHebrew-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansHebrew-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansThaiUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansThaiUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansArmenian-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansArmenian-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansGeorgian-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansGeorgian-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansDevanagariUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansDevanagariUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansGujaratiUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansGujaratiUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansGurmukhiUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansGurmukhiUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansTamilUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTamilUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansMalayalamUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansMalayalamUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansBengaliUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBengaliUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansTeluguUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTeluguUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansKannadaUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansKannadaUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansOriyaUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansOriyaUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansSinhala-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSinhala-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansKhmerUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansKhmerUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansLaoUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansLaoUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansMyanmarUI-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansMyanmarUI-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansThaana-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansThaana-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansCham-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansCham-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansBalinese-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBamum-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBatak-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBuginese-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansBuhid-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansCanadianAboriginal-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansCherokee-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansCoptic-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansGlagolitic-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansHanunoo-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansJavanese-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansKayahLi-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansLepcha-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansLimbu-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansLisu-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansMandaic-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansMeeteiMayek-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansNewTaiLue-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansNKo-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansOlChiki-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansRejang-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSaurashtra-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSundanese-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSylotiNagri-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSyriacEstrangela-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTagbanwa-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTaiTham-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTaiViet-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTibetan-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansTibetan-Bold.ttf',
- '<(source_all_fonts_dir)/NotoSansTifinagh-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansVai-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansYi-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSymbols-Regular-Subsetted.ttf',
- '<(source_all_fonts_dir)/NotoSansSC-Regular.otf',
- '<(source_all_fonts_dir)/NotoSansTC-Regular.otf',
- '<(source_all_fonts_dir)/NotoSansJP-Regular.otf',
- '<(source_all_fonts_dir)/NotoSansKR-Regular.otf',
- '<(source_all_fonts_dir)/NotoEmoji-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansSymbols-Regular-Subsetted2.ttf',
- '<(source_all_fonts_dir)/NotoSansTaiLe-Regular.ttf',
- '<(source_all_fonts_dir)/NotoSansMongolian-Regular.ttf',
+ '<(source_font_files_dir)/Roboto-Italic.ttf',
+ '<(source_font_files_dir)/Roboto-BoldItalic.ttf',
],
- }], # unlimited
+ }],
+ [ 'package_named_serif >= 1', {
+ 'files+': [
+ '<(source_font_files_dir)/NotoSerif-Regular.ttf',
+ ],
+ }],
+ [ 'package_named_serif >= 2', {
+ 'files+': [
+ '<(source_font_files_dir)/NotoSerif-Bold.ttf',
+ ],
+ }],
+ [ 'package_named_serif >= 3', {
+ 'files+': [
+ '<(source_font_files_dir)/NotoSerif-Italic.ttf',
+ '<(source_font_files_dir)/NotoSerif-BoldItalic.ttf',
+ ],
+ }],
+ [ 'package_named_fcc_fonts >= 1', {
+ 'files+': [
+ # sans-serif-monospace
+ '<(source_font_files_dir)/DroidSansMono.ttf',
+ # serif-monospace
+ '<(source_font_files_dir)/CutiveMono.ttf',
+ # casual
+ '<(source_font_files_dir)/ComingSoon.ttf',
+ # cursive
+ '<(source_font_files_dir)/DancingScript-Regular.ttf',
+ # sans-serif-smallcaps
+ '<(source_font_files_dir)/CarroisGothicSC-Regular.ttf',
+ ],
+ }],
+ [ 'package_named_fcc_fonts >= 2', {
+ 'files+': [
+ # cursive
+ '<(source_font_files_dir)/DancingScript-Bold.ttf',
+ ],
+ }],
+ [ 'package_fallback_lang_non_cjk >= 1', {
+ 'files+': [
+ '<(source_font_files_dir)/NotoNaskhArabicUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansArmenian-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansBalinese-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansBamum-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansBatak-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansBengaliUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansBuginese-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansBuhid-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansCanadianAboriginal-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansCham-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansCherokee-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansCoptic-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansDevanagariUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansEthiopic-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansGeorgian-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansGlagolitic-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansGujaratiUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansGurmukhiUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansHanunoo-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansHebrew-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansJavanese-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansKannadaUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansKayahLi-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansKhmerUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansLaoUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansLepcha-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansLimbu-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansLisu-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansMalayalamUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansMandaic-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansMeeteiMayek-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansMongolian-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansMyanmarUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansNewTaiLue-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansNKo-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansOlChiki-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansOriyaUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansRejang-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansSaurashtra-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansSinhala-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansSundanese-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansSylotiNagri-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansSyriacEstrangela-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansTagbanwa-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansTaiLe-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansTaiTham-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansTaiViet-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansTamilUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansTeluguUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansThaana-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansThaiUI-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansTibetan-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansTifinagh-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansVai-Regular.ttf',
+ '<(source_font_files_dir)/NotoSansYi-Regular.ttf',
+ ],
+ }],
+ [ 'package_fallback_lang_non_cjk >= 2', {
+ 'files+': [
+ '<(source_font_files_dir)/NotoNaskhArabicUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansArmenian-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansBengaliUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansCham-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansDevanagariUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansEthiopic-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansGeorgian-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansGujaratiUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansGurmukhiUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansHebrew-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansKannadaUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansKhmerUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansLaoUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansMalayalamUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansMyanmarUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansOriyaUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansSinhala-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansTamilUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansTeluguUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansThaana-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansThaiUI-Bold.ttf',
+ '<(source_font_files_dir)/NotoSansTibetan-Bold.ttf',
+ ],
+ }],
+ [ 'package_fallback_lang_cjk >= 1', {
+ 'files+': [
+ '<(source_font_files_dir)/NotoSansCJK-Regular.ttc',
+ ],
+ }],
+ [ 'package_fallback_lang_cjk >= 2', {
+ 'files+': [
+ '<(source_font_files_dir)/NotoSansCJK-Bold.ttc',
+ ],
+ }],
+ [ 'package_fallback_lang_cjk_low_quality >= 1', {
+ 'files+': [
+ '<(source_font_files_dir)/DroidSansFallback.ttf',
+ ],
+ }],
+ [ 'package_fallback_lang_jp >= 1', {
+ 'files+': [
+ '<(source_font_files_dir)/NotoSansJP-Regular.otf',
+ ],
+ }],
+ [ 'package_fallback_emoji >= 1', {
+ 'files+': [
+ '<(source_font_files_dir)/NotoEmoji-Regular.ttf',
+ ],
+ }],
+ [ 'package_fallback_symbols >= 1', {
+ 'files+': [
+ '<(source_font_files_dir)/NotoSansSymbols-Regular-Subsetted.ttf',
+ '<(source_font_files_dir)/NotoSansSymbols-Regular-Subsetted2.ttf',
+ ],
+ }],
],
},
],
diff --git a/src/cobalt/renderer/frame_rate_throttler.cc b/src/cobalt/renderer/frame_rate_throttler.cc
deleted file mode 100644
index 82cebd3..0000000
--- a/src/cobalt/renderer/frame_rate_throttler.cc
+++ /dev/null
@@ -1,45 +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/renderer/frame_rate_throttler.h"
-
-#include "base/threading/platform_thread.h"
-
-namespace cobalt {
-namespace renderer {
-
-void FrameRateThrottler::BeginInterval() {
- if (COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS > 0) {
- begin_time_ = base::TimeTicks::HighResNow();
- }
-}
-
-void FrameRateThrottler::EndInterval() {
- // Throttle presentation of new frames if a minimum frame time is specified.
- if (COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS > 0) {
- if (!begin_time_.is_null()) {
- base::TimeDelta elapsed = base::TimeTicks::HighResNow() - begin_time_;
- base::TimeDelta wait_time =
- base::TimeDelta::FromMillisecondsD(
- COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS) -
- elapsed;
- if (wait_time > base::TimeDelta::FromMicroseconds(0)) {
- base::PlatformThread::Sleep(wait_time);
- }
- }
- }
-}
-
-} // namespace renderer
-} // namespace cobalt
diff --git a/src/cobalt/renderer/frame_rate_throttler.h b/src/cobalt/renderer/frame_rate_throttler.h
deleted file mode 100644
index 0a2e4c5..0000000
--- a/src/cobalt/renderer/frame_rate_throttler.h
+++ /dev/null
@@ -1,42 +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_RENDERER_FRAME_RATE_THROTTLER_H_
-#define COBALT_RENDERER_FRAME_RATE_THROTTLER_H_
-
-#include "base/time.h"
-
-namespace cobalt {
-namespace renderer {
-
-// The FrameRateThrottler is used to enforce a minimum frame time. The
-// rasterizer should call the provided hooks just before and after submitting
-// a new frame. This can be used to throttle the frame rate (to 30 Hz for
-// example) when the presentation interval cannot be specified.
-class FrameRateThrottler {
- public:
- // To be called just after submitting a new frame.
- void BeginInterval();
-
- // To be called just before submitting a new frame.
- void EndInterval();
-
- private:
- base::TimeTicks begin_time_;
-};
-
-} // namespace renderer
-} // namespace cobalt
-
-#endif // COBALT_RENDERER_FRAME_RATE_THROTTLER_H_
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 2abe1a3..c70c3ee 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -26,6 +26,7 @@
#include "cobalt/render_tree/brush.h"
#include "cobalt/render_tree/dump_render_tree_to_string.h"
#include "cobalt/render_tree/rect_node.h"
+#include "nb/memory_scope.h"
using cobalt::render_tree::Node;
using cobalt::render_tree::animations::AnimateNode;
@@ -178,6 +179,7 @@
void Pipeline::RasterizeToRGBAPixels(
const Submission& render_tree_submission,
const RasterizationCompleteCallback& complete) {
+ TRACK_MEMORY_SCOPE("Renderer");
TRACE_EVENT0("cobalt::renderer", "Pipeline::RasterizeToRGBAPixels()");
if (MessageLoop::current() != rasterizer_thread_.message_loop()) {
@@ -189,7 +191,8 @@
}
// Create a new target that is the same dimensions as the display target.
scoped_refptr<backend::RenderTarget> offscreen_target =
- graphics_context_->CreateOffscreenRenderTarget(render_target_->GetSize());
+ graphics_context_->CreateDownloadableOffscreenRenderTarget(
+ render_target_->GetSize());
// Rasterize this submission into the newly created target.
RasterizeSubmissionToRenderTarget(render_tree_submission, offscreen_target);
@@ -214,7 +217,8 @@
// platform does not rate limit itself during swaps. This was changed from
// 15ms to accommodate 120fps requirements on some platforms.
rasterize_timer_.emplace(
- FROM_HERE, base::TimeDelta::FromMilliseconds(7),
+ FROM_HERE, base::TimeDelta::FromMillisecondsD(
+ COBALT_MINIMUM_FRAME_TIME_IN_MILLISECONDS),
base::Bind(&Pipeline::RasterizeCurrentTree, base::Unretained(this)),
true, true);
rasterize_timer_->Reset();
@@ -235,6 +239,7 @@
}
void Pipeline::RasterizeCurrentTree() {
+ TRACK_MEMORY_SCOPE("Renderer");
DCHECK(rasterizer_thread_checker_.CalledOnValidThread());
TRACE_EVENT0("cobalt::renderer", "Pipeline::RasterizeCurrentTree()");
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h
index 3a7f257..8fab055 100644
--- a/src/cobalt/renderer/pipeline.h
+++ b/src/cobalt/renderer/pipeline.h
@@ -190,9 +190,6 @@
// did not have active animations.
bool last_render_animations_active_;
- // Tracks whether or not animations are currently playing.
- base::CVal<bool> has_active_animations_c_val_;
-
// Timer tracking the amount of time spent in
// |RasterizeSubmissionToRenderTarget| when the render tree has changed.
// The tracking is flushed when the max count is hit.
@@ -202,6 +199,9 @@
// tracking is flushed when the animations expire.
base::CValCollectionTimerStats<base::CValDebug> rasterize_animations_timer_;
+ // Tracks whether or not animations are currently playing.
+ base::CVal<bool> has_active_animations_c_val_;
+
#if defined(ENABLE_DEBUG_CONSOLE)
// Dumps the current render tree to the console.
base::ConsoleCommandManager::CommandHandler
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
index c551f4c..a67044e 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
@@ -16,6 +16,7 @@
#include <string>
+#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/threading/thread_checker.h"
#if defined(ENABLE_DEBUG_CONSOLE)
@@ -24,7 +25,6 @@
#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/frame_rate_throttler.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"
@@ -43,6 +43,7 @@
class HardwareRasterizer::Impl {
public:
explicit Impl(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
int scratch_surface_size_in_bytes,
int surface_cache_size_in_bytes,
int software_surface_cache_size_in_bytes);
@@ -52,14 +53,19 @@
const scoped_refptr<backend::RenderTarget>& render_target,
const Options& options);
+ void SubmitOffscreenToRenderTarget(
+ const scoped_refptr<render_tree::Node>& render_tree,
+ const scoped_refptr<backend::RenderTarget>& render_target);
+
render_tree::ResourceProvider* GetResourceProvider();
private:
#if defined(ENABLE_DEBUG_CONSOLE)
void OnToggleHighlightSoftwareDraws(const std::string& message);
#endif
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
void SetupLastFrameSurface(int width, int height);
-
+#endif // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
base::ThreadChecker thread_checker_;
backend::GraphicsContextBlitter* context_;
@@ -80,8 +86,6 @@
CachedSoftwareRasterizer software_surface_cache_;
LinearGradientCache linear_gradient_cache_;
- FrameRateThrottler frame_rate_throttler_;
-
#if defined(ENABLE_DEBUG_CONSOLE)
// Debug command to toggle cache highlights to help visualize which nodes
// are being cached.
@@ -92,6 +96,7 @@
};
HardwareRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
int scratch_surface_size_in_bytes,
int surface_cache_size_in_bytes,
int software_surface_cache_size_in_bytes)
@@ -118,9 +123,12 @@
"scene software rasterization is occurring.")
#endif // defined(ENABLE_DEBUG_CONSOLE)
{
- resource_provider_ = scoped_ptr<render_tree::ResourceProvider>(
- new ResourceProvider(context_->GetSbBlitterDevice(),
- software_surface_cache_.GetResourceProvider()));
+ resource_provider_ =
+ scoped_ptr<render_tree::ResourceProvider>(new ResourceProvider(
+ context_->GetSbBlitterDevice(),
+ software_surface_cache_.GetResourceProvider(),
+ base::Bind(&HardwareRasterizer::Impl::SubmitOffscreenToRenderTarget,
+ base::Unretained(this))));
if (surface_cache_size_in_bytes > 0) {
surface_cache_delegate_.emplace(context_->GetSbBlitterDevice(),
@@ -131,7 +139,11 @@
}
}
-HardwareRasterizer::Impl::~Impl() { SbBlitterDestroySurface(current_frame_); }
+HardwareRasterizer::Impl::~Impl() {
+ if (SbBlitterIsSurfaceValid(current_frame_)) {
+ SbBlitterDestroySurface(current_frame_);
+ }
+}
#if defined(ENABLE_DEBUG_CONSOLE)
void HardwareRasterizer::Impl::OnToggleHighlightSoftwareDraws(
@@ -146,6 +158,7 @@
const scoped_refptr<backend::RenderTarget>& render_target,
const Options& options) {
TRACE_EVENT0("cobalt::renderer", "Rasterizer::Submit()");
+ DCHECK(thread_checker_.CalledOnValidThread());
int width = render_target->GetSize().width();
int height = render_target->GetSize().height();
@@ -156,12 +169,19 @@
base::polymorphic_downcast<backend::RenderTargetBlitter*>(
render_target.get());
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
if (!SbBlitterIsSurfaceValid(current_frame_)) {
SetupLastFrameSurface(width, height);
}
+#endif // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
- CHECK(SbBlitterSetRenderTarget(
- context, SbBlitterGetRenderTargetFromSurface(current_frame_)));
+ SbBlitterRenderTarget visitor_render_target = kSbBlitterInvalidRenderTarget;
+ if (SbBlitterIsSurfaceValid(current_frame_)) {
+ visitor_render_target = SbBlitterGetRenderTargetFromSurface(current_frame_);
+ } else {
+ visitor_render_target = render_target_blitter->GetSbRenderTarget();
+ }
+ CHECK(SbBlitterSetRenderTarget(context, visitor_render_target));
// Update our surface cache to do per-frame calculations such as deciding
// which render tree nodes are candidates for caching in this upcoming
@@ -191,14 +211,15 @@
// Visit the render tree with our Blitter API visitor.
BoundsStack start_bounds(context_->GetSbBlitterContext(),
math::Rect(render_target_blitter->GetSize()));
- if (options.dirty && !cleared) {
+
+ if (SbBlitterIsSurfaceValid(current_frame_) && options.dirty && !cleared) {
// If a dirty rectangle was specified, limit our redrawing to within it.
start_bounds.Push(*options.dirty);
}
- RenderState initial_render_state(
- SbBlitterGetRenderTargetFromSurface(current_frame_), Transform(),
- start_bounds);
+ RenderState initial_render_state(visitor_render_target, Transform(),
+ start_bounds);
+
#if defined(ENABLE_DEBUG_CONSOLE)
initial_render_state.highlight_software_draws =
toggle_highlight_software_draws_;
@@ -212,37 +233,74 @@
render_tree->Accept(&visitor);
}
- // Finally flip the surface to make visible the rendered results.
- CHECK(SbBlitterSetRenderTarget(context,
- render_target_blitter->GetSbRenderTarget()));
- CHECK(SbBlitterSetBlending(context, false));
- CHECK(SbBlitterSetModulateBlitsWithColor(context, false));
- CHECK(SbBlitterBlitRectToRect(context, current_frame_,
- SbBlitterMakeRect(0, 0, width, height),
- SbBlitterMakeRect(0, 0, width, height)));
+ if (SbBlitterIsSurfaceValid(current_frame_)) {
+ // Finally flip the surface to make visible the rendered results.
+ CHECK(SbBlitterSetRenderTarget(context,
+ render_target_blitter->GetSbRenderTarget()));
+ CHECK(SbBlitterSetBlending(context, false));
+ CHECK(SbBlitterSetModulateBlitsWithColor(context, false));
+ CHECK(SbBlitterBlitRectToRect(context, current_frame_,
+ SbBlitterMakeRect(0, 0, width, height),
+ SbBlitterMakeRect(0, 0, width, height)));
+ }
+
CHECK(SbBlitterFlushContext(context));
- frame_rate_throttler_.EndInterval();
render_target_blitter->Flip();
- frame_rate_throttler_.BeginInterval();
++submit_count_;
}
+void HardwareRasterizer::Impl::SubmitOffscreenToRenderTarget(
+ const scoped_refptr<render_tree::Node>& render_tree,
+ const scoped_refptr<backend::RenderTarget>& render_target) {
+ TRACE_EVENT0("cobalt::renderer",
+ "Rasterizer::SubmitOffscreenToRenderTarget()");
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ SbBlitterContext context = context_->GetSbBlitterContext();
+
+ backend::RenderTargetBlitter* render_target_blitter =
+ base::polymorphic_downcast<backend::RenderTargetBlitter*>(
+ render_target.get());
+
+ SbBlitterRenderTarget visitor_render_target =
+ render_target_blitter->GetSbRenderTarget();
+ CHECK(SbBlitterSetRenderTarget(context, visitor_render_target));
+
+ BoundsStack start_bounds(context_->GetSbBlitterContext(),
+ math::Rect(render_target_blitter->GetSize()));
+
+ RenderState initial_render_state(visitor_render_target, Transform(),
+ start_bounds);
+
+ RenderTreeNodeVisitor visitor(
+ context_->GetSbBlitterDevice(), context_->GetSbBlitterContext(),
+ initial_render_state, NULL, NULL, NULL, NULL, NULL);
+ render_tree->Accept(&visitor);
+
+ CHECK(SbBlitterFlushContext(context));
+}
+
render_tree::ResourceProvider* HardwareRasterizer::Impl::GetResourceProvider() {
return resource_provider_.get();
}
+#if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
void HardwareRasterizer::Impl::SetupLastFrameSurface(int width, int height) {
current_frame_ =
SbBlitterCreateRenderTargetSurface(context_->GetSbBlitterDevice(), width,
height, kSbBlitterSurfaceFormatRGBA8);
}
+#endif // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY)
HardwareRasterizer::HardwareRasterizer(
backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
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,
+ : impl_(new Impl(graphics_context,
+ skia_atlas_width, skia_atlas_height,
+ scratch_surface_size_in_bytes,
surface_cache_size_in_bytes,
software_surface_cache_size_in_bytes)) {}
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
index 9ae47e8..20afd3e 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
@@ -16,6 +16,7 @@
#define COBALT_RENDERER_RASTERIZER_BLITTER_HARDWARE_RASTERIZER_H_
#include "base/memory/scoped_ptr.h"
+#include "cobalt/render_tree/node.h"
#include "cobalt/render_tree/resource_provider.h"
#include "cobalt/renderer/backend/graphics_context.h"
#include "cobalt/renderer/backend/render_target.h"
@@ -36,6 +37,7 @@
class HardwareRasterizer : public Rasterizer {
public:
explicit HardwareRasterizer(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
int scratch_surface_size_in_bytes,
int surface_cache_size_in_bytes,
int software_surface_cache_size_in_bytes);
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.cc b/src/cobalt/renderer/rasterizer/blitter/image.cc
index b44ffdc..f51569e 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/image.cc
@@ -14,7 +14,9 @@
#include "cobalt/renderer/rasterizer/blitter/image.h"
+#include "base/bind.h"
#include "cobalt/render_tree/image.h"
+#include "cobalt/renderer/backend/blitter/surface_render_target.h"
#include "cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.h"
#include "cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h"
#include "cobalt/renderer/rasterizer/skia/image.h"
@@ -89,7 +91,25 @@
size_ = math::Size(info.width, info.height);
}
-bool SinglePlaneImage::EnsureInitialized() { return false; }
+SinglePlaneImage::SinglePlaneImage(
+ const scoped_refptr<render_tree::Node>& root,
+ SubmitOffscreenCallback submit_offscreen_callback, SbBlitterDevice device)
+ : size_(static_cast<int>(root->GetBounds().right()),
+ static_cast<int>(root->GetBounds().bottom())),
+ is_opaque_(false) {
+ initialize_image_ = base::Bind(
+ &SinglePlaneImage::InitializeImageFromRenderTree, base::Unretained(this),
+ root, submit_offscreen_callback, device);
+}
+
+bool SinglePlaneImage::EnsureInitialized() {
+ if (!initialize_image_.is_null()) {
+ initialize_image_.Run();
+ initialize_image_.Reset();
+ return true;
+ }
+ return false;
+}
const SkBitmap* SinglePlaneImage::GetBitmap() const {
// This function will only ever get called if the Skia software renderer needs
@@ -128,6 +148,18 @@
}
}
+void SinglePlaneImage::InitializeImageFromRenderTree(
+ const scoped_refptr<render_tree::Node>& root,
+ const SubmitOffscreenCallback& submit_offscreen_callback,
+ SbBlitterDevice device) {
+ scoped_refptr<backend::SurfaceRenderTargetBlitter> render_target(
+ new backend::SurfaceRenderTargetBlitter(device, size_));
+
+ submit_offscreen_callback.Run(root, render_target);
+ surface_ = render_target->TakeSbSurface();
+ CHECK(SbBlitterIsSurfaceValid(surface_));
+}
+
} // namespace blitter
} // namespace rasterizer
} // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.h b/src/cobalt/renderer/rasterizer/blitter/image.h
index fefb1f0..6489d4f 100644
--- a/src/cobalt/renderer/rasterizer/blitter/image.h
+++ b/src/cobalt/renderer/rasterizer/blitter/image.h
@@ -26,6 +26,7 @@
#include "cobalt/render_tree/font_provider.h"
#include "cobalt/render_tree/image.h"
#include "cobalt/render_tree/resource_provider.h"
+#include "cobalt/renderer/backend/blitter/render_target.h"
#include "cobalt/renderer/rasterizer/skia/image.h"
#include "starboard/blitter.h"
@@ -36,6 +37,10 @@
namespace rasterizer {
namespace blitter {
+typedef base::Callback<void(const scoped_refptr<render_tree::Node>& render_tree,
+ const scoped_refptr<backend::RenderTarget>&
+ render_target)> SubmitOffscreenCallback;
+
// render_tree::ImageData objects are implemented in the Blitter API via the
// SbBlitterPixelData objects, which is conceptually an exact match for
// ImageData.
@@ -75,6 +80,10 @@
SinglePlaneImage(SbBlitterSurface surface, bool is_opaque,
const base::Closure& delete_function);
+ SinglePlaneImage(const scoped_refptr<render_tree::Node>& root,
+ SubmitOffscreenCallback submit_offscreen_callback,
+ SbBlitterDevice device);
+
const math::Size& GetSize() const OVERRIDE { return size_; }
SbBlitterSurface surface() const { return surface_; }
@@ -92,6 +101,11 @@
private:
~SinglePlaneImage() OVERRIDE;
+ void InitializeImageFromRenderTree(
+ const scoped_refptr<render_tree::Node>& root,
+ const SubmitOffscreenCallback& submit_offscreen_callback,
+ SbBlitterDevice device);
+
math::Size size_;
SbBlitterSurface surface_;
@@ -104,6 +118,10 @@
// If |delete_function| is provided, it will be called in the destructor
// instead of manually calling SbBlitterDestroySurface(surface_).
base::Closure delete_function_;
+
+ // This closure binds the image construction parameters so that we can delay
+ // construction of it until it is accessed by the rasterizer thread.
+ base::Closure initialize_image_;
};
} // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
index 1cb1ac1..d1e161a 100644
--- a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
@@ -61,6 +61,14 @@
'<(DEPTH)/starboard/starboard.gyp:starboard',
'common',
],
+
+ 'conditions': [
+ ['render_dirty_region_only==1', {
+ 'defines': [
+ 'COBALT_RENDER_DIRTY_REGION_ONLY',
+ ],
+ }],
+ ],
},
# The Blitter software rasterizer uses Skia to rasterize render trees on
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 b727b35..4a95847 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
@@ -28,6 +28,7 @@
#include "cobalt/renderer/rasterizer/blitter/linear_gradient.h"
#include "cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h"
#include "cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h"
+#include "cobalt/renderer/rasterizer/common/utils.h"
#include "starboard/blitter.h"
#if SB_HAS(BLITTER)
@@ -111,27 +112,6 @@
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) {
TRACE_EVENT0_IF_ENABLED("Visit(FilterNode)");
@@ -170,7 +150,7 @@
// we know that opacity is in the range (0, 1), exclusive.
float opacity = filter_node->data().opacity_filter->opacity();
- if (SourceCanRenderWithOpacity(source)) {
+ if (common::utils::NodeCanRenderWithOpacity(source)) {
float original_opacity = render_state_.opacity;
render_state_.opacity *= opacity;
@@ -237,6 +217,14 @@
SinglePlaneImage* blitter_image =
base::polymorphic_downcast<SinglePlaneImage*>(skia_image);
+ // Ensure any required backend processing is done to create the necessary
+ // GPU resource.
+ if (blitter_image->EnsureInitialized()) {
+ // Restore the original render target, since it is possible that rendering
+ // took place to another render target in this call.
+ SbBlitterSetRenderTarget(context_, render_state_.render_target);
+ }
+
// Apply the local image coordinate transform to the source rectangle. Note
// that the render tree local transform matrix is normalized, but the Blitter
// API source rectangle is specified in pixel units, so we must multiply the
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
index 7932be5..2315836 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -15,7 +15,6 @@
#include "cobalt/renderer/rasterizer/blitter/resource_provider.h"
#include "base/bind.h"
-#include "cobalt/renderer/rasterizer/blitter/image.h"
#include "cobalt/renderer/rasterizer/blitter/render_tree_blitter_conversions.h"
#include "starboard/blitter.h"
@@ -37,8 +36,15 @@
ResourceProvider::ResourceProvider(
SbBlitterDevice device,
- render_tree::ResourceProvider* skia_resource_provider)
- : device_(device), skia_resource_provider_(skia_resource_provider) {}
+ render_tree::ResourceProvider* skia_resource_provider,
+ SubmitOffscreenCallback submit_offscreen_callback)
+ : device_(device),
+ skia_resource_provider_(skia_resource_provider),
+ submit_offscreen_callback_(submit_offscreen_callback) {
+#if SB_API_VERSION >= 4
+ decode_target_graphics_context_provider_.device = device;
+#endif // SB_API_VERSION >= 4
+}
bool ResourceProvider::PixelFormatSupported(PixelFormat pixel_format) {
return SbBlitterIsPixelFormatSupportedByPixelData(
@@ -77,7 +83,7 @@
scoped_refptr<render_tree::Image>
ResourceProvider::CreateImageFromSbDecodeTarget(SbDecodeTarget decode_target) {
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
SbDecodeTargetFormat format = SbDecodeTargetGetFormat(decode_target);
if (format == kSbDecodeTargetFormat1PlaneRGBA) {
SbBlitterSurface surface =
@@ -90,7 +96,7 @@
SbDecodeTargetDestroy(decode_target);
return make_scoped_refptr(
new SinglePlaneImage(surface, is_opaque, base::Closure()));
-#else // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // SB_API_VERSION < 4
SbDecodeTargetInfo info;
SbMemorySet(&info, 0, sizeof(info));
CHECK(SbDecodeTargetGetInfo(decode_target, &info));
@@ -109,16 +115,16 @@
return make_scoped_refptr(new SinglePlaneImage(
plane.surface, info.is_opaque,
base::Bind(&SbDecodeTargetRelease, decode_target)));
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
}
NOTREACHED()
<< "Only format kSbDecodeTargetFormat1PlaneRGBA is currently supported.";
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
SbDecodeTargetDestroy(decode_target);
-#else // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // SB_API_VERSION < 4
SbDecodeTargetRelease(decode_target);
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
return NULL;
}
@@ -151,7 +157,7 @@
scoped_refptr<render_tree::Typeface>
ResourceProvider::GetLocalTypefaceByFaceNameIfAvailable(
- const std::string& font_face_name) {
+ const char* font_face_name) {
return skia_resource_provider_->GetLocalTypefaceByFaceNameIfAvailable(
font_face_name);
}
@@ -201,6 +207,12 @@
return scoped_refptr<render_tree::Mesh>(NULL);
}
+scoped_refptr<render_tree::Image> ResourceProvider::DrawOffscreenImage(
+ const scoped_refptr<render_tree::Node>& root) {
+ return make_scoped_refptr(
+ new SinglePlaneImage(root, submit_offscreen_callback_, device_));
+}
+
} // namespace blitter
} // namespace rasterizer
} // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
index ed891df..3f01c64 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -23,6 +23,7 @@
#include "cobalt/render_tree/font_provider.h"
#include "cobalt/render_tree/image.h"
#include "cobalt/render_tree/resource_provider.h"
+#include "cobalt/renderer/rasterizer/blitter/image.h"
#include "starboard/blitter.h"
#include "starboard/decode_target.h"
@@ -35,21 +36,28 @@
class ResourceProvider : public render_tree::ResourceProvider {
public:
- explicit ResourceProvider(
- SbBlitterDevice device,
- render_tree::ResourceProvider* skia_resource_provider);
+ ResourceProvider(SbBlitterDevice device,
+ render_tree::ResourceProvider* skia_resource_provider,
+ SubmitOffscreenCallback submit_offscreen_callback);
~ResourceProvider() OVERRIDE {}
void Finish() OVERRIDE {}
-#if SB_VERSION(3)
+#if SB_API_VERSION >= 3
scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
SbDecodeTarget decode_target) OVERRIDE;
- SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
-
bool SupportsSbDecodeTarget() OVERRIDE { return true; }
-#endif // SB_VERSION(3) && SB_HAS(GRAPHICS)
+#endif // SB_API_VERSION >= 3
+
+#if SB_API_VERSION >= 4
+ SbDecodeTargetGraphicsContextProvider*
+ GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+ return &decode_target_graphics_context_provider_;
+ }
+#elif SB_API_VERSION >= 3
+ SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
+#endif // SB_API_VERSION >= 4
bool PixelFormatSupported(render_tree::PixelFormat pixel_format) OVERRIDE;
bool AlphaFormatSupported(render_tree::AlphaFormat alpha_format) OVERRIDE;
@@ -74,7 +82,7 @@
const char* font_family_name, render_tree::FontStyle font_style) OVERRIDE;
scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
- const std::string& font_face_name) OVERRIDE;
+ const char* font_face_name) OVERRIDE;
scoped_refptr<render_tree::Typeface> GetCharacterFallbackTypeface(
int32 utf32_character, render_tree::FontStyle font_style,
@@ -102,6 +110,9 @@
scoped_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
render_tree::Mesh::DrawMode draw_mode) OVERRIDE;
+ scoped_refptr<render_tree::Image> DrawOffscreenImage(
+ const scoped_refptr<render_tree::Node>& root) OVERRIDE;
+
private:
SbBlitterDevice device_;
@@ -109,6 +120,13 @@
// ResourceProvider, as the Blitter API does not natively support font
// rendering.
render_tree::ResourceProvider* skia_resource_provider_;
+
+ SubmitOffscreenCallback submit_offscreen_callback_;
+
+#if SB_API_VERSION >= 4
+ SbDecodeTargetGraphicsContextProvider
+ decode_target_graphics_context_provider_;
+#endif // SB_API_VERSION >= 4
};
} // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
index 207d3c6..f8e683c 100644
--- a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
@@ -93,9 +93,7 @@
SbBlitterDestroySurface(surface);
- frame_rate_throttler_.EndInterval();
render_target_blitter->Flip();
- frame_rate_throttler_.BeginInterval();
}
render_tree::ResourceProvider* SoftwareRasterizer::GetResourceProvider() {
diff --git a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
index 85aabf6..b868493 100644
--- a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
@@ -24,7 +24,6 @@
#include "cobalt/renderer/backend/blitter/graphics_context.h"
#include "cobalt/renderer/backend/graphics_context.h"
#include "cobalt/renderer/backend/render_target.h"
-#include "cobalt/renderer/frame_rate_throttler.h"
#include "cobalt/renderer/rasterizer/rasterizer.h"
#include "cobalt/renderer/rasterizer/skia/software_rasterizer.h"
@@ -50,7 +49,6 @@
private:
backend::GraphicsContextBlitter* context_;
skia::SoftwareRasterizer skia_rasterizer_;
- FrameRateThrottler frame_rate_throttler_;
};
} // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/common/common.gyp b/src/cobalt/renderer/rasterizer/common/common.gyp
index 3db2828..af0e808 100644
--- a/src/cobalt/renderer/rasterizer/common/common.gyp
+++ b/src/cobalt/renderer/rasterizer/common/common.gyp
@@ -27,6 +27,8 @@
'streaming_best_fit_line.h',
'surface_cache.cc',
'surface_cache.h',
+ 'utils.h',
+ 'utils.cc',
],
'dependencies': [
@@ -35,4 +37,4 @@
],
},
],
-}
\ No newline at end of file
+}
diff --git a/src/cobalt/renderer/rasterizer/common/utils.cc b/src/cobalt/renderer/rasterizer/common/utils.cc
new file mode 100644
index 0000000..1a7d107
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/common/utils.cc
@@ -0,0 +1,59 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/common/utils.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/render_tree/composition_node.h"
+#include "cobalt/render_tree/image_node.h"
+#include "cobalt/render_tree/matrix_transform_node.h"
+#include "cobalt/render_tree/rect_node.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace common {
+namespace utils {
+
+bool NodeCanRenderWithOpacity(render_tree::Node* node) {
+ base::TypeId node_type = node->GetTypeId();
+
+ if (node_type == base::GetTypeId<render_tree::ImageNode>() ||
+ node_type == base::GetTypeId<render_tree::RectNode>()) {
+ return true;
+ } else if (node_type == base::GetTypeId<render_tree::MatrixTransformNode>()) {
+ render_tree::MatrixTransformNode* transform_node =
+ base::polymorphic_downcast<render_tree::MatrixTransformNode*>(node);
+ return NodeCanRenderWithOpacity(transform_node->data().source);
+ } else if (node_type == base::GetTypeId<render_tree::CompositionNode>()) {
+ // If we are a composition of non-overlapping valid children, then we can
+ // also be rendered directly onscreen. As a simplification, just check for
+ // the case of 1 valid child.
+ render_tree::CompositionNode* composition_node =
+ base::polymorphic_downcast<render_tree::CompositionNode*>(node);
+ const render_tree::CompositionNode::Children& children =
+ composition_node->data().children();
+ if (children.size() == 1) {
+ return NodeCanRenderWithOpacity(children[0]);
+ }
+ }
+
+ return false;
+}
+
+} // namespace utils
+} // namespace common
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/common/utils.h b/src/cobalt/renderer/rasterizer/common/utils.h
new file mode 100644
index 0000000..a9b17fd
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/common/utils.h
@@ -0,0 +1,41 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_COMMON_UTILS_H_
+#define COBALT_RENDERER_RASTERIZER_COMMON_UTILS_H_
+
+#include "cobalt/render_tree/node.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace common {
+namespace utils {
+
+// Returns whether the given node (or its child[ren]) can be rendered directly
+// onto the main render target with an opacity value. The alternative is that
+// the node should be rendered onto an offscreen render target, then that
+// target be rendered onscreen using opacity. The difference can best be seen
+// when |node| represents overlapping objects -- rendering them individually
+// with opacity will look different than rendering them fully opaque onto an
+// offscreen target, then rendering that target with opacity.
+bool NodeCanRenderWithOpacity(render_tree::Node* node);
+
+} // namespace utils
+} // namespace common
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_COMMON_UTILS_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.cc b/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.cc
new file mode 100644
index 0000000..dc6664c
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.cc
@@ -0,0 +1,98 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
+
+#include <GLES2/gl2.h>
+
+#include "cobalt/renderer/backend/egl/utils.h"
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+DrawDepthStencil::DrawDepthStencil(GraphicsState* graphics_state,
+ const BaseState& base_state, const math::RectF& include_scissor,
+ const math::RectF& exclude_scissor)
+ : DrawPolyColor(base_state),
+ include_scissor_first_vert_(-1),
+ exclude_scissor_first_vert_(-1) {
+ attributes_.reserve(MaxVertsNeededForStencil());
+ AddStencil(include_scissor, exclude_scissor);
+ graphics_state->ReserveVertexData(
+ attributes_.size() * sizeof(VertexAttributes));
+}
+
+DrawDepthStencil::DrawDepthStencil(const BaseState& base_state)
+ : DrawPolyColor(base_state),
+ include_scissor_first_vert_(-1),
+ exclude_scissor_first_vert_(-1) {
+}
+
+void DrawDepthStencil::ExecuteOnscreenRasterize(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ SetupShader(graphics_state, program_manager);
+ DrawStencil(graphics_state);
+}
+
+void DrawDepthStencil::UndoStencilState(GraphicsState* graphics_state) {
+ graphics_state->ResetDepthFunc();
+}
+
+void DrawDepthStencil::DrawStencil(GraphicsState* graphics_state) {
+ // This must occur during the transparency pass.
+ SB_DCHECK(graphics_state->IsBlendEnabled());
+ SB_DCHECK(!graphics_state->IsDepthWriteEnabled());
+ SB_DCHECK(graphics_state->IsDepthTestEnabled());
+
+ // Set depth of pixels in the scissor rects.
+ graphics_state->EnableDepthWrite();
+ if (exclude_scissor_first_vert_ >= 0) {
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, exclude_scissor_first_vert_, 4));
+ }
+ SB_DCHECK(include_scissor_first_vert_ >= 0);
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, include_scissor_first_vert_, 4));
+ graphics_state->DisableDepthWrite();
+
+ // Set the depth test function for subsequent draws to occur within the
+ // stencil. Be sure to call UndoStencilState() when drawing in the
+ // stencilled area is no longer desired.
+ GL_CALL(glDepthFunc(GL_EQUAL));
+}
+
+void DrawDepthStencil::AddStencil(
+ const math::RectF& include_scissor,
+ const math::RectF& exclude_scissor) {
+ // At least the include_scissor must be specified.
+ SB_DCHECK(!include_scissor.IsEmpty());
+
+ include_scissor_first_vert_ = static_cast<GLint>(attributes_.size());
+ AddRect(include_scissor, 0);
+ if (!exclude_scissor.IsEmpty()) {
+ // Exclude scissor must use the next closest depth.
+ float depth = base_state_.depth;
+ base_state_.depth = GraphicsState::NextClosestDepth(depth);
+ exclude_scissor_first_vert_ = static_cast<GLint>(attributes_.size());
+ AddRect(exclude_scissor, 0);
+ base_state_.depth = depth;
+ }
+}
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.h b/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.h
new file mode 100644
index 0000000..5233355
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_depth_stencil.h
@@ -0,0 +1,73 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_DEPTH_STENCIL_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_DEPTH_STENCIL_H_
+
+#include <vector>
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/color_rgba.h"
+#include "cobalt/renderer/rasterizer/egl/draw_poly_color.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Handles creation of a depth stencil. Pixels within the include scissor will
+// have their depth set to base_state.depth. Pixels within the exclude scissor,
+// if specified, will have their depth set to the next closest depth value. An
+// |include_scissor| must always be specified.
+//
+// NOTE: This object leaves the graphics state modified so that only the
+// stencilled pixels are affected by later draw calls. Use UndoStencilState
+// once finished with the stencil.
+// NOTE: If an |exclude_scissor| is specified, then this object uses two depth
+// values -- the incoming value in base_state.depth and the next closest.
+// NOTE: Since scissor rects pollute the depth buffer, they should only be
+// used during the tranparency pass because subsequent draws are guaranteed to
+// be above (or not overlap) these pixels.
+class DrawDepthStencil : public DrawPolyColor {
+ public:
+ DrawDepthStencil(GraphicsState* graphics_state,
+ const BaseState& base_state,
+ const math::RectF& include_scissor,
+ const math::RectF& exclude_scissor);
+
+ void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+
+ // ExecuteOnscreenRasterize / DrawDepthStencil change the graphics state
+ // so that subsequent draw calls affect only the stencilled pixels. This
+ // function reverts the graphics state back to normal.
+ void UndoStencilState(GraphicsState* graphics_state);
+
+ protected:
+ explicit DrawDepthStencil(const BaseState& base_state);
+ void AddStencil(const math::RectF& include_scissor,
+ const math::RectF& exclude_scissor);
+ void DrawStencil(GraphicsState* graphics_state);
+ static size_t MaxVertsNeededForStencil() { return 8; }
+
+ GLint include_scissor_first_vert_;
+ GLint exclude_scissor_first_vert_;
+};
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_EGL_DRAW_DEPTH_STENCIL_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
index 7c71e9d..30963a0 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object_manager.h
@@ -37,6 +37,8 @@
kOnscreenRectTexture = 0,
kOnscreenRectColorTexture,
kOnscreenPolyColor,
+ kOnscreenRectShadow,
+ kOnscreenRectShadowBlur,
kOnscreenTransparent,
kOnscreenCount,
};
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
index 93b6c95..dc4c7d1 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.cc
@@ -28,15 +28,17 @@
DrawPolyColor::DrawPolyColor(GraphicsState* graphics_state,
const BaseState& base_state, const math::RectF& rect,
const render_tree::ColorRGBA& color)
- : DrawObject(base_state) {
- uint32_t color32 = GetGLRGBA(color * base_state_.opacity);
+ : DrawObject(base_state),
+ vertex_buffer_(NULL) {
attributes_.reserve(4);
- AddVertex(rect.x(), rect.y(), color32);
- AddVertex(rect.x(), rect.bottom(), color32);
- AddVertex(rect.right(), rect.y(), color32);
- AddVertex(rect.right(), rect.bottom(), color32);
- graphics_state->ReserveVertexData(attributes_.size() *
- sizeof(VertexAttributes));
+ AddRect(rect, GetGLRGBA(color * base_state_.opacity));
+ graphics_state->ReserveVertexData(
+ attributes_.size() * sizeof(VertexAttributes));
+}
+
+DrawPolyColor::DrawPolyColor(const BaseState& base_state)
+ : DrawObject(base_state),
+ vertex_buffer_(NULL) {
}
void DrawPolyColor::ExecuteOnscreenUpdateVertexBuffer(
@@ -51,6 +53,12 @@
void DrawPolyColor::ExecuteOnscreenRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
+ SetupShader(graphics_state, program_manager);
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
+}
+
+void DrawPolyColor::SetupShader(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
ShaderProgram<ShaderVertexColor,
ShaderFragmentColor>* program;
program_manager->GetProgram(&program);
@@ -71,7 +79,13 @@
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, color));
graphics_state->VertexAttribFinish();
- GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
+}
+
+void DrawPolyColor::AddRect(const math::RectF& rect, uint32_t color) {
+ AddVertex(rect.x(), rect.y(), color);
+ AddVertex(rect.x(), rect.bottom(), color);
+ AddVertex(rect.right(), rect.y(), color);
+ AddVertex(rect.right(), rect.bottom(), color);
}
void DrawPolyColor::AddVertex(float x, float y, uint32_t color) {
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
index 769299c..bc549f6 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_poly_color.h
@@ -39,7 +39,11 @@
void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
- private:
+ protected:
+ explicit DrawPolyColor(const BaseState& base_state);
+ void SetupShader(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager);
+ void AddRect(const math::RectF& rect, uint32_t color);
void AddVertex(float x, float y, uint32_t color);
struct VertexAttributes {
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
index d3f48c9..2c04709 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.cc
@@ -16,6 +16,7 @@
#include <GLES2/gl2.h>
+#include "base/basictypes.h"
#include "cobalt/renderer/backend/egl/utils.h"
#include "egl/generated_shader_impl.h"
#include "starboard/memory.h"
@@ -37,12 +38,15 @@
const BaseState& base_state,
const math::RectF& rect, const render_tree::ColorRGBA& color,
const backend::TextureEGL* texture,
- const math::Matrix3F& texcoord_transform)
+ const math::Matrix3F& texcoord_transform,
+ bool clamp_texcoords)
: DrawObject(base_state),
texcoord_transform_(texcoord_transform),
rect_(rect),
texture_(texture),
- vertex_buffer_(NULL) {
+ vertex_buffer_(NULL),
+ clamp_texcoords_(clamp_texcoords),
+ tile_texture_(false) {
color_ = GetGLRGBA(color * base_state_.opacity);
graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
}
@@ -50,13 +54,19 @@
DrawRectColorTexture::DrawRectColorTexture(GraphicsState* graphics_state,
const BaseState& base_state,
const math::RectF& rect, const render_tree::ColorRGBA& color,
- const GenerateTextureFunction& generate_texture)
+ const backend::TextureEGL* texture,
+ const math::Matrix3F& texcoord_transform,
+ const base::Closure& draw_offscreen,
+ const base::Closure& draw_onscreen)
: DrawObject(base_state),
- texcoord_transform_(math::Matrix3F::Identity()),
+ texcoord_transform_(texcoord_transform),
rect_(rect),
- texture_(NULL),
- generate_texture_(generate_texture),
- vertex_buffer_(NULL) {
+ texture_(texture),
+ draw_offscreen_(draw_offscreen),
+ draw_onscreen_(draw_onscreen),
+ vertex_buffer_(NULL),
+ clamp_texcoords_(false),
+ tile_texture_(false) {
color_ = GetGLRGBA(color * base_state_.opacity);
graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
}
@@ -64,8 +74,8 @@
void DrawRectColorTexture::ExecuteOffscreenRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- if (!generate_texture_.is_null()) {
- generate_texture_.Run(&texture_, &texcoord_transform_);
+ if (!draw_offscreen_.is_null()) {
+ draw_offscreen_.Run();
}
}
@@ -92,11 +102,48 @@
vertex_buffer_ = graphics_state->AllocateVertexData(
sizeof(attributes));
SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
+
+ // Find minimum and maximum texcoord values.
+ texcoord_clamp_[0] = attributes[0].texcoord[0];
+ texcoord_clamp_[1] = attributes[0].texcoord[1];
+ texcoord_clamp_[2] = attributes[0].texcoord[0];
+ texcoord_clamp_[3] = attributes[0].texcoord[1];
+ for (int i = 1; i < arraysize(attributes); ++i) {
+ float texcoord_u = attributes[i].texcoord[0];
+ float texcoord_v = attributes[i].texcoord[1];
+ if (texcoord_clamp_[0] > texcoord_u) {
+ texcoord_clamp_[0] = texcoord_u;
+ } else if (texcoord_clamp_[2] < texcoord_u) {
+ texcoord_clamp_[2] = texcoord_u;
+ }
+ if (texcoord_clamp_[1] > texcoord_v) {
+ texcoord_clamp_[1] = texcoord_v;
+ } else if (texcoord_clamp_[3] < texcoord_v) {
+ texcoord_clamp_[3] = texcoord_v;
+ }
+ }
+
+ tile_texture_ = texcoord_clamp_[0] < 0.0f || texcoord_clamp_[1] < 0.0f ||
+ texcoord_clamp_[2] > 1.0f || texcoord_clamp_[3] > 1.0f;
+
+ if (clamp_texcoords_) {
+ // Inset 0.5-epsilon so the border texels are still sampled, but nothing
+ // beyond.
+ const float kTexelInset = 0.499f;
+ texcoord_clamp_[0] += kTexelInset / texture_->GetSize().width();
+ texcoord_clamp_[1] += kTexelInset / texture_->GetSize().height();
+ texcoord_clamp_[2] -= kTexelInset / texture_->GetSize().width();
+ texcoord_clamp_[3] -= kTexelInset / texture_->GetSize().height();
+ }
}
void DrawRectColorTexture::ExecuteOnscreenRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
+ if (!draw_onscreen_.is_null()) {
+ draw_onscreen_.Run();
+ }
+
ShaderProgram<ShaderVertexColorTexcoord,
ShaderFragmentColorTexcoord>* program;
program_manager->GetProgram(&program);
@@ -121,10 +168,23 @@
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, texcoord));
graphics_state->VertexAttribFinish();
- graphics_state->ActiveBindTexture(
- program->GetFragmentShader().u_texture_texunit(),
- texture_->GetTarget(), texture_->gl_handle());
- GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+ GL_CALL(glUniform4fv(program->GetFragmentShader().u_texcoord_clamp(),
+ 1, texcoord_clamp_));
+
+ if (tile_texture_) {
+ graphics_state->ActiveBindTexture(
+ program->GetFragmentShader().u_texture_texunit(),
+ texture_->GetTarget(), texture_->gl_handle(), GL_REPEAT);
+ GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+ graphics_state->ActiveBindTexture(
+ program->GetFragmentShader().u_texture_texunit(),
+ texture_->GetTarget(), texture_->gl_handle(), GL_CLAMP_TO_EDGE);
+ } else {
+ graphics_state->ActiveBindTexture(
+ program->GetFragmentShader().u_texture_texunit(),
+ texture_->GetTarget(), texture_->gl_handle());
+ GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+ }
}
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
index d459196..11cec54 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h
@@ -30,22 +30,22 @@
// Handles drawing a textured rectangle modulated by a given color.
class DrawRectColorTexture : public DrawObject {
public:
- typedef base::Callback<void(const backend::TextureEGL** out_texture,
- math::Matrix3F* out_texcoord_transform)>
- GenerateTextureFunction;
+ DrawRectColorTexture(GraphicsState* graphics_state,
+ const BaseState& base_state,
+ const math::RectF& rect,
+ const render_tree::ColorRGBA& color,
+ const backend::TextureEGL* texture,
+ const math::Matrix3F& texcoord_transform,
+ bool clamp_texcoords);
DrawRectColorTexture(GraphicsState* graphics_state,
const BaseState& base_state,
const math::RectF& rect,
const render_tree::ColorRGBA& color,
const backend::TextureEGL* texture,
- const math::Matrix3F& texcoord_transform);
-
- DrawRectColorTexture(GraphicsState* graphics_state,
- const BaseState& base_state,
- const math::RectF& rect,
- const render_tree::ColorRGBA& color,
- const GenerateTextureFunction& generate_texture);
+ const math::Matrix3F& texcoord_transform,
+ const base::Closure& draw_offscreen,
+ const base::Closure& draw_onscreen);
void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
@@ -59,9 +59,13 @@
math::RectF rect_;
uint32_t color_;
const backend::TextureEGL* texture_;
- GenerateTextureFunction generate_texture_;
+ base::Closure draw_offscreen_;
+ base::Closure draw_onscreen_;
uint8_t* vertex_buffer_;
+ float texcoord_clamp_[4]; // texcoord clamping (min u, min v, max u, max v)
+ bool clamp_texcoords_;
+ bool tile_texture_;
};
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.cc
new file mode 100644
index 0000000..bf27f37
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.cc
@@ -0,0 +1,225 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h"
+
+#include <GLES2/gl2.h>
+#include <algorithm>
+
+#include "cobalt/math/transform_2d.h"
+#include "cobalt/renderer/backend/egl/utils.h"
+#include "egl/generated_shader_impl.h"
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+namespace {
+const float kEpsilon = 0.001f;
+
+size_t MaxVertsNeededForAlignedGradient(
+ const render_tree::LinearGradientBrush& brush) {
+ // Triangle strip for an axis-aligned rectangle. Two vertices are required
+ // per color stop in addition to possible start and/or end vertices.
+ return 4 + 2 * brush.color_stops().size();
+}
+}
+
+DrawRectLinearGradient::DrawRectLinearGradient(GraphicsState* graphics_state,
+ const BaseState& base_state,
+ const math::RectF& rect,
+ const render_tree::LinearGradientBrush& brush)
+ : DrawDepthStencil(base_state),
+ first_rect_vert_(0),
+ gradient_angle_(0.0f) {
+ if (std::abs(brush.dest().y() - brush.source().y()) < kEpsilon) {
+ attributes_.reserve(MaxVertsNeededForAlignedGradient(brush));
+ AddRectWithHorizontalGradient(
+ rect, brush.source(), brush.dest(), brush.color_stops());
+ } else if (std::abs(brush.dest().x() - brush.source().x()) < kEpsilon) {
+ attributes_.reserve(MaxVertsNeededForAlignedGradient(brush));
+ AddRectWithVerticalGradient(
+ rect, brush.source(), brush.dest(), brush.color_stops());
+ } else {
+ AddRectWithAngledGradient(rect, brush);
+ }
+ graphics_state->ReserveVertexData(
+ attributes_.size() * sizeof(VertexAttributes));
+}
+
+void DrawRectLinearGradient::ExecuteOnscreenRasterize(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ SetupShader(graphics_state, program_manager);
+
+ if (first_rect_vert_ > 0) {
+ // Draw using stencil.
+ DrawStencil(graphics_state);
+
+ // Update the transform for the shader to rotate the horizontal gradient
+ // into the desired angled gradient.
+ ShaderProgram<ShaderVertexColor,
+ ShaderFragmentColor>* program;
+ program_manager->GetProgram(&program);
+ SB_DCHECK(graphics_state->GetProgram() == program->GetHandle());
+ graphics_state->UpdateTransformMatrix(
+ program->GetVertexShader().u_view_matrix(),
+ base_state_.transform * math::RotateMatrix(gradient_angle_));
+
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, first_rect_vert_,
+ attributes_.size() - first_rect_vert_));
+ UndoStencilState(graphics_state);
+ } else {
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
+ }
+}
+
+void DrawRectLinearGradient::AddRectWithHorizontalGradient(
+ const math::RectF& rect, const math::PointF& source,
+ const math::PointF& dest, const render_tree::ColorStopList& color_stops) {
+ SB_DCHECK(source.x() >= rect.x() - kEpsilon &&
+ source.x() <= rect.right() + kEpsilon &&
+ dest.x() >= rect.x() - kEpsilon &&
+ dest.x() <= rect.right() + kEpsilon);
+
+ size_t num_colors = color_stops.size();
+ float start = std::max(rect.x(), std::min(source.x(), rect.right()));
+ float length = std::max(rect.x(), std::min(dest.x(), rect.right())) - start;
+ float position = color_stops[0].position * length + start;
+ uint32_t color32 = GetGLColor(color_stops[0]);
+
+ // Draw a horizontal triangle strip.
+ if (length >= 0.0f) {
+ if (position > rect.x()) {
+ AddVertex(rect.x(), rect.y(), color32);
+ AddVertex(rect.x(), rect.bottom(), color32);
+ }
+ } else if (position < rect.right()) {
+ AddVertex(rect.right(), rect.y(), color32);
+ AddVertex(rect.right(), rect.bottom(), color32);
+ }
+
+ for (size_t i = 0; i < num_colors; ++i) {
+ position = color_stops[i].position * length + start;
+ color32 = GetGLColor(color_stops[i]);
+ AddVertex(position, rect.y(), color32);
+ AddVertex(position, rect.bottom(), color32);
+ }
+
+ if (length >= 0.0f) {
+ if (position < rect.right()) {
+ AddVertex(rect.right(), rect.y(), color32);
+ AddVertex(rect.right(), rect.bottom(), color32);
+ }
+ } else if (position > rect.x()) {
+ AddVertex(rect.x(), rect.y(), color32);
+ AddVertex(rect.x(), rect.bottom(), color32);
+ }
+}
+
+void DrawRectLinearGradient::AddRectWithVerticalGradient(
+ const math::RectF& rect, const math::PointF& source,
+ const math::PointF& dest, const render_tree::ColorStopList& color_stops) {
+ SB_DCHECK(source.y() >= rect.y() - kEpsilon &&
+ source.y() <= rect.bottom() + kEpsilon &&
+ dest.y() >= rect.y() - kEpsilon &&
+ dest.y() <= rect.bottom() + kEpsilon);
+
+ size_t num_colors = color_stops.size();
+ float start = std::max(rect.y(), std::min(source.y(), rect.bottom()));
+ float length = std::max(rect.y(), std::min(dest.y(), rect.bottom())) - start;
+ float position = color_stops[0].position * length + start;
+ uint32_t color32 = GetGLColor(color_stops[0]);
+
+ // Draw a vertical triangle strip.
+ if (length >= 0) {
+ if (position > rect.y()) {
+ AddVertex(rect.x(), rect.y(), color32);
+ AddVertex(rect.right(), rect.y(), color32);
+ }
+ } else if (position < rect.bottom()) {
+ AddVertex(rect.x(), rect.bottom(), color32);
+ AddVertex(rect.right(), rect.bottom(), color32);
+ }
+
+ for (size_t i = 0; i < num_colors; ++i) {
+ position = color_stops[i].position * length + start;
+ color32 = GetGLColor(color_stops[i]);
+ AddVertex(rect.x(), position, color32);
+ AddVertex(rect.right(), position, color32);
+ }
+
+ if (length >= 0) {
+ if (position < rect.bottom()) {
+ AddVertex(rect.x(), rect.bottom(), color32);
+ AddVertex(rect.right(), rect.bottom(), color32);
+ }
+ } else if (position > rect.y()) {
+ AddVertex(rect.x(), rect.y(), color32);
+ AddVertex(rect.right(), rect.y(), color32);
+ }
+}
+
+void DrawRectLinearGradient::AddRectWithAngledGradient(
+ const math::RectF& rect, const render_tree::LinearGradientBrush& brush) {
+ // Use an inclusive scissor stencil to draw within the desired area. Then
+ // draw a rect with horizontal gradient that will be rotated to cover the
+ // desired rect with angled gradient. This is not as efficient as calculating
+ // a triangle strip to describe the rect with angled gradient, but is simpler.
+ attributes_.reserve(MaxVertsNeededForStencil() +
+ MaxVertsNeededForAlignedGradient(brush));
+ AddStencil(rect, math::RectF());
+
+ // Calculate the angle needed to rotate a horizontal gradient into the
+ // angled gradient. (Flip vertical distance because the input coordinate
+ // system's origin is at the top-left.)
+ gradient_angle_ = atan2(brush.source().y() - brush.dest().y(),
+ brush.dest().x() - brush.source().x());
+
+ // Calculate the endpoints for the horizontal gradient that, when rotated
+ // by gradient_angle_, will turn into the original angled gradient.
+ math::Matrix3F inverse_transform = math::RotateMatrix(-gradient_angle_);
+ math::PointF mapped_source(inverse_transform * brush.source());
+ math::PointF mapped_dest(inverse_transform * brush.dest());
+ SB_DCHECK(mapped_source.x() <= mapped_dest.x());
+
+ // Calculate a rect large enough that, when rotated by gradient_angle_, it
+ // will contain the original rect.
+ math::RectF mapped_rect = inverse_transform.MapRect(rect);
+
+ // Adjust the mapped_rect to include the gradient endpoints if needed.
+ float left = std::min(mapped_rect.x(), mapped_source.x());
+ float right = std::max(mapped_rect.right(), mapped_dest.x());
+ mapped_rect.SetRect(left, mapped_rect.y(),
+ right - left, mapped_rect.height());
+
+ first_rect_vert_ = attributes_.size();
+ AddRectWithHorizontalGradient(mapped_rect, mapped_source, mapped_dest,
+ brush.color_stops());
+}
+
+uint32_t DrawRectLinearGradient::GetGLColor(
+ const render_tree::ColorStop& color_stop) {
+ const render_tree::ColorRGBA& color = color_stop.color;
+ float alpha = base_state_.opacity * color.a();
+ return GetGLRGBA(color.r() * alpha, color.g() * alpha, color.b() * alpha,
+ alpha);
+}
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h
new file mode 100644
index 0000000..e138460
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_LINEAR_GRADIENT_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_LINEAR_GRADIENT_H_
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/brush.h"
+#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Handles drawing a rectangle with a linear color gradient. This may use a
+// depth stencil, so it must be processed with other transparent draws.
+class DrawRectLinearGradient : public DrawDepthStencil {
+ public:
+ DrawRectLinearGradient(GraphicsState* graphics_state,
+ const BaseState& base_state,
+ const math::RectF& rect,
+ const render_tree::LinearGradientBrush& brush);
+
+ void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+
+ private:
+ void AddRectWithHorizontalGradient(
+ const math::RectF& rect, const math::PointF& source,
+ const math::PointF& dest, const render_tree::ColorStopList& color_stops);
+ void AddRectWithVerticalGradient(
+ const math::RectF& rect, const math::PointF& source,
+ const math::PointF& dest, const render_tree::ColorStopList& color_stops);
+ void AddRectWithAngledGradient(
+ const math::RectF& rect, const render_tree::LinearGradientBrush& brush);
+ uint32_t GetGLColor(const render_tree::ColorStop& color_stop);
+
+ // Index of the first vertex for the rect with gradient.
+ size_t first_rect_vert_;
+
+ // For angled gradients, this is the rotation angle needed to transform the
+ // submitted rect with horizontal gradient into the desired rect with angled
+ // gradient.
+ float gradient_angle_;
+};
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_LINEAR_GRADIENT_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
new file mode 100644
index 0000000..6090782
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
@@ -0,0 +1,149 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h"
+
+#include <GLES2/gl2.h>
+#include <algorithm>
+
+#include "cobalt/renderer/backend/egl/utils.h"
+#include "egl/generated_shader_impl.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+DrawRectShadowBlur::DrawRectShadowBlur(GraphicsState* graphics_state,
+ const BaseState& base_state, const math::RectF& inner_rect,
+ const math::RectF& outer_rect, const math::RectF& blur_edge,
+ const render_tree::ColorRGBA& color, const math::RectF& exclude_scissor,
+ float blur_sigma, bool inset)
+ : DrawObject(base_state),
+ draw_stencil_(graphics_state, base_state, outer_rect, exclude_scissor),
+ blur_center_(blur_edge.CenterPoint()),
+ // The sigma scale is used to transform pixel distances to sigma-relative
+ // distances. The 0.5 multiplier is used to match skia's implementation.
+ blur_sigma_scale_(0.5f / blur_sigma),
+ vertex_buffer_(NULL) {
+ float color_scale = 1.0f;
+ attributes_.reserve(10);
+
+ // The blur radius dictates the distance from blur center at which the
+ // shadow should be about 50% opacity of the shadow color. This is expressed
+ // in terms of sigma.
+ blur_radius_[0] = blur_edge.width() * 0.5f * blur_sigma_scale_;
+ blur_radius_[1] = blur_edge.height() * 0.5f * blur_sigma_scale_;
+
+ if (inset) {
+ // Invert the shadow by using (1.0 - weight) for the alpha scale.
+ blur_scale_add_[0] = -1.0f;
+ blur_scale_add_[1] = 1.0f;
+ } else {
+ // The calculated weight works for outset shadows.
+ blur_scale_add_[0] = 1.0f;
+ blur_scale_add_[1] = 0.0f;
+
+ // Scale opacity down if the shadow's edge is smaller than the blur kernel.
+ // Use a quick linear scale as the dimension goes below 1.5 sigma (which is
+ // the distance at which the approximated gaussian integral reaches 0).
+ const float size_scale = 1.0f / (1.5f * blur_sigma);
+ color_scale = std::min(blur_edge.width() * size_scale, 1.0f) *
+ std::min(blur_edge.height() * size_scale, 1.0f);
+ }
+
+ // Add a triangle-strip to draw the area between outer_rect and inner_rect.
+ // This is the area which the shadow covers.
+ uint32_t color32 = GetGLRGBA(color * color_scale * base_state_.opacity);
+ AddVertex(outer_rect.x(), outer_rect.y(), color32);
+ AddVertex(inner_rect.x(), inner_rect.y(), color32);
+ AddVertex(outer_rect.right(), outer_rect.y(), color32);
+ AddVertex(inner_rect.right(), inner_rect.y(), color32);
+ AddVertex(outer_rect.right(), outer_rect.bottom(), color32);
+ AddVertex(inner_rect.right(), inner_rect.bottom(), color32);
+ AddVertex(outer_rect.x(), outer_rect.bottom(), color32);
+ AddVertex(inner_rect.x(), inner_rect.bottom(), color32);
+ AddVertex(outer_rect.x(), outer_rect.y(), color32);
+ AddVertex(inner_rect.x(), inner_rect.y(), color32);
+
+ graphics_state->ReserveVertexData(
+ attributes_.size() * sizeof(VertexAttributes));
+}
+
+void DrawRectShadowBlur::ExecuteOnscreenUpdateVertexBuffer(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ draw_stencil_.ExecuteOnscreenUpdateVertexBuffer(
+ graphics_state, program_manager);
+ vertex_buffer_ = graphics_state->AllocateVertexData(
+ attributes_.size() * sizeof(VertexAttributes));
+ SbMemoryCopy(vertex_buffer_, &attributes_[0],
+ attributes_.size() * sizeof(VertexAttributes));
+}
+
+void DrawRectShadowBlur::ExecuteOnscreenRasterize(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ // Create a stencil for the pixels to be modified.
+ draw_stencil_.ExecuteOnscreenRasterize(
+ graphics_state, program_manager);
+
+ // Draw the blurred shadow in the stencilled area.
+ ShaderProgram<ShaderVertexColorBlur,
+ ShaderFragmentColorBlur>* program;
+ program_manager->GetProgram(&program);
+ graphics_state->UseProgram(program->GetHandle());
+ graphics_state->UpdateClipAdjustment(
+ program->GetVertexShader().u_clip_adjustment());
+ graphics_state->UpdateTransformMatrix(
+ program->GetVertexShader().u_view_matrix(),
+ base_state_.transform);
+ graphics_state->Scissor(base_state_.scissor.x(), base_state_.scissor.y(),
+ base_state_.scissor.width(), base_state_.scissor.height());
+ graphics_state->VertexAttribPointer(
+ program->GetVertexShader().a_position(), 3, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributes), vertex_buffer_ +
+ offsetof(VertexAttributes, position));
+ graphics_state->VertexAttribPointer(
+ program->GetVertexShader().a_color(), 4, GL_UNSIGNED_BYTE, GL_TRUE,
+ sizeof(VertexAttributes), vertex_buffer_ +
+ offsetof(VertexAttributes, color));
+ graphics_state->VertexAttribPointer(
+ program->GetVertexShader().a_blur_position(), 2, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributes), vertex_buffer_ +
+ offsetof(VertexAttributes, blur_position));
+ graphics_state->VertexAttribFinish();
+ GL_CALL(glUniform2fv(program->GetFragmentShader().u_blur_radius(), 1,
+ blur_radius_));
+ GL_CALL(glUniform2fv(program->GetFragmentShader().u_scale_add(), 1,
+ blur_scale_add_));
+
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_.size()));
+ draw_stencil_.UndoStencilState(graphics_state);
+}
+
+void DrawRectShadowBlur::AddVertex(float x, float y, uint32_t color) {
+ float blur_x = (x - blur_center_.x()) * blur_sigma_scale_;
+ float blur_y = (y - blur_center_.y()) * blur_sigma_scale_;
+ VertexAttributes attribute = { { x, y, base_state_.depth }, color,
+ { blur_x, blur_y } };
+ attributes_.push_back(attribute);
+}
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
new file mode 100644
index 0000000..4d17b25
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
@@ -0,0 +1,101 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_BLUR_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_BLUR_H_
+
+#include <vector>
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/color_rgba.h"
+#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
+#include "cobalt/renderer/rasterizer/egl/draw_object.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Example CSS box shadow (outset):
+// +-------------------------------------+
+// | Box shadow "blur" region |
+// | +-----------------------------+ |
+// | | Box shadow "spread" region | |
+// | | +---------------------+ | |
+// | | | Box shadow rect | | |
+// | | | (exclude scissor) | | |
+// | | +---------------------+ | |
+// | | | |
+// | +-----------------------------+ |
+// | (include scissor) |
+// +-------------------------------------+
+// NOTE: Despite the CSS naming, the actual blur effect starts inside the
+// "spread" region.
+
+// Handles drawing a box shadow with blur. This uses a gaussian kernel to fade
+// the "blur" region. A stencil is used to ensure only the desired pixels are
+// touched.
+//
+// This uses a shader to mimic skia's SkBlurMask.cpp.
+// See also http://stereopsis.com/shadowrect/ as reference for the formula
+// used to approximate the gaussian integral (which controls the opacity of
+// the shadow).
+class DrawRectShadowBlur : public DrawObject {
+ public:
+ // Draw a blurred box shadow.
+ // The box shadow exists in the area between |inner_rect| and |outer_rect|.
+ // |blur_edge| specifies the area where the "spread" region transitions
+ // to the "blur" region. It has 50% opacity of the shadow color.
+ DrawRectShadowBlur(GraphicsState* graphics_state,
+ const BaseState& base_state,
+ const math::RectF& inner_rect,
+ const math::RectF& outer_rect,
+ const math::RectF& blur_edge,
+ const render_tree::ColorRGBA& color,
+ const math::RectF& exclude_scissor,
+ float blur_sigma, bool inset);
+
+ void ExecuteOnscreenUpdateVertexBuffer(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+ void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+
+ private:
+ void AddVertex(float x, float y, uint32_t color);
+
+ struct VertexAttributes {
+ float position[3];
+ uint32_t color;
+ float blur_position[2]; // Expressed in terms of blur_sigma from center.
+ };
+
+ DrawDepthStencil draw_stencil_;
+ std::vector<VertexAttributes> attributes_;
+ math::PointF blur_center_;
+ float blur_radius_[2]; // Expressed in terms of blur_sigma.
+ float blur_scale_add_[2];
+
+ // The sigma scale is used to transform pixel distances to sigma-relative
+ // distances.
+ float blur_sigma_scale_;
+
+ uint8_t* vertex_buffer_;
+};
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_BLUR_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
new file mode 100644
index 0000000..8f61e32
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
@@ -0,0 +1,67 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h"
+
+#include <GLES2/gl2.h>
+
+#include "cobalt/renderer/backend/egl/utils.h"
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+DrawRectShadowSpread::DrawRectShadowSpread(GraphicsState* graphics_state,
+ const BaseState& base_state, const math::RectF& inner_rect,
+ const math::RectF& outer_rect, const render_tree::ColorRGBA& color,
+ const math::RectF& include_scissor, const math::RectF& exclude_scissor)
+ : DrawDepthStencil(base_state) {
+ // Reserve space for the box shadow's spread and possible scissor rects.
+ attributes_.reserve(10 + MaxVertsNeededForStencil());
+
+ // Draw the box shadow's spread. This is a triangle strip covering the area
+ // between outer rect and inner rect.
+ uint32_t color32 = GetGLRGBA(color * base_state_.opacity);
+ AddVertex(outer_rect.x(), outer_rect.y(), color32);
+ AddVertex(inner_rect.x(), inner_rect.y(), color32);
+ AddVertex(outer_rect.right(), outer_rect.y(), color32);
+ AddVertex(inner_rect.right(), inner_rect.y(), color32);
+ AddVertex(outer_rect.right(), outer_rect.bottom(), color32);
+ AddVertex(inner_rect.right(), inner_rect.bottom(), color32);
+ AddVertex(outer_rect.x(), outer_rect.bottom(), color32);
+ AddVertex(inner_rect.x(), inner_rect.bottom(), color32);
+ AddVertex(outer_rect.x(), outer_rect.y(), color32);
+ AddVertex(inner_rect.x(), inner_rect.y(), color32);
+
+ AddStencil(include_scissor, exclude_scissor);
+
+ graphics_state->ReserveVertexData(
+ attributes_.size() * sizeof(VertexAttributes));
+}
+
+void DrawRectShadowSpread::ExecuteOnscreenRasterize(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ SetupShader(graphics_state, program_manager);
+ DrawStencil(graphics_state);
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 10));
+ UndoStencilState(graphics_state);
+}
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
new file mode 100644
index 0000000..28d1cef
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
@@ -0,0 +1,69 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_SPREAD_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_SPREAD_H_
+
+#include <vector>
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/color_rgba.h"
+#include "cobalt/renderer/rasterizer/egl/draw_depth_stencil.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Example CSS box shadow (outset):
+// +-------------------------------------+
+// | Box shadow "blur" region |
+// | +-----------------------------+ |
+// | | Box shadow "spread" region | |
+// | | +---------------------+ | |
+// | | | Box shadow rect | | |
+// | | | (exclude scissor) | | |
+// | | +---------------------+ | |
+// | | | |
+// | +-----------------------------+ |
+// | (include scissor) |
+// +-------------------------------------+
+// When an offset is used for the shadow, the include and exclude scissors
+// play a critical role.
+
+// Handles drawing the solid "spread" portion of a box shadow. This also
+// creates a stencil, using the depth buffer, for the pixels inside
+// |include_scissor| excluding those in |exclude_scissor|.
+class DrawRectShadowSpread : public DrawDepthStencil {
+ public:
+ // Fill the area between |inner_rect| and |outer_rect| with the specified
+ // color.
+ DrawRectShadowSpread(GraphicsState* graphics_state,
+ const BaseState& base_state,
+ const math::RectF& inner_rect,
+ const math::RectF& outer_rect,
+ const render_tree::ColorRGBA& color,
+ const math::RectF& include_scissor,
+ const math::RectF& exclude_scissor);
+
+ void ExecuteOnscreenRasterize(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
+};
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_SPREAD_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
index f4099d8..107f103 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.cc
@@ -16,6 +16,7 @@
#include <GLES2/gl2.h>
+#include "base/basictypes.h"
#include "cobalt/renderer/backend/egl/utils.h"
#include "egl/generated_shader_impl.h"
#include "starboard/memory.h"
@@ -34,34 +35,41 @@
DrawRectTexture::DrawRectTexture(GraphicsState* graphics_state,
const BaseState& base_state,
- const math::RectF& rect, const backend::TextureEGL* texture,
+ const math::RectF& rect,
+ const backend::TextureEGL* texture,
const math::Matrix3F& texcoord_transform)
: DrawObject(base_state),
texcoord_transform_(texcoord_transform),
rect_(rect),
texture_(texture),
- vertex_buffer_(NULL) {
+ vertex_buffer_(NULL),
+ tile_texture_(false) {
graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
}
DrawRectTexture::DrawRectTexture(GraphicsState* graphics_state,
const BaseState& base_state,
const math::RectF& rect,
- const GenerateTextureFunction& generate_texture)
+ const backend::TextureEGL* texture,
+ const math::Matrix3F& texcoord_transform,
+ const base::Closure& draw_offscreen,
+ const base::Closure& draw_onscreen)
: DrawObject(base_state),
- texcoord_transform_(math::Matrix3F::Identity()),
+ texcoord_transform_(texcoord_transform),
rect_(rect),
- texture_(NULL),
- generate_texture_(generate_texture),
- vertex_buffer_(NULL) {
+ texture_(texture),
+ draw_offscreen_(draw_offscreen),
+ draw_onscreen_(draw_onscreen),
+ vertex_buffer_(NULL),
+ tile_texture_(false) {
graphics_state->ReserveVertexData(4 * sizeof(VertexAttributes));
}
void DrawRectTexture::ExecuteOffscreenRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- if (!generate_texture_.is_null()) {
- generate_texture_.Run(&texture_, &texcoord_transform_);
+ if (!draw_offscreen_.is_null()) {
+ draw_offscreen_.Run();
}
}
@@ -88,11 +96,21 @@
vertex_buffer_ = graphics_state->AllocateVertexData(
sizeof(attributes));
SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
+
+ for (int i = 0; i < arraysize(attributes); ++i) {
+ tile_texture_ = tile_texture_ ||
+ attributes[i].texcoord[0] < 0.0f || attributes[i].texcoord[0] > 1.0f ||
+ attributes[i].texcoord[1] < 0.0f || attributes[i].texcoord[1] > 1.0f;
+ }
}
void DrawRectTexture::ExecuteOnscreenRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
+ if (!draw_onscreen_.is_null()) {
+ draw_onscreen_.Run();
+ }
+
ShaderProgram<ShaderVertexTexcoord,
ShaderFragmentTexcoord>* program;
program_manager->GetProgram(&program);
@@ -113,10 +131,21 @@
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, texcoord));
graphics_state->VertexAttribFinish();
- graphics_state->ActiveBindTexture(
- program->GetFragmentShader().u_texture_texunit(),
- texture_->GetTarget(), texture_->gl_handle());
- GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+
+ if (tile_texture_) {
+ graphics_state->ActiveBindTexture(
+ program->GetFragmentShader().u_texture_texunit(),
+ texture_->GetTarget(), texture_->gl_handle(), GL_REPEAT);
+ GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+ graphics_state->ActiveBindTexture(
+ program->GetFragmentShader().u_texture_texunit(),
+ texture_->GetTarget(), texture_->gl_handle(), GL_CLAMP_TO_EDGE);
+ } else {
+ graphics_state->ActiveBindTexture(
+ program->GetFragmentShader().u_texture_texunit(),
+ texture_->GetTarget(), texture_->gl_handle());
+ GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
+ }
}
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
index 767c3eb..a0a284b 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_texture.h
@@ -29,10 +29,6 @@
// Handles drawing a textured rectangle.
class DrawRectTexture : public DrawObject {
public:
- typedef base::Callback<void(const backend::TextureEGL** out_texture,
- math::Matrix3F* out_texcoord_transform)>
- GenerateTextureFunction;
-
DrawRectTexture(GraphicsState* graphics_state,
const BaseState& base_state,
const math::RectF& rect,
@@ -42,7 +38,10 @@
DrawRectTexture(GraphicsState* graphics_state,
const BaseState& base_state,
const math::RectF& rect,
- const GenerateTextureFunction& generate_texture);
+ const backend::TextureEGL* texture,
+ const math::Matrix3F& texcoord_transform,
+ const base::Closure& draw_offscreen,
+ const base::Closure& draw_onscreen);
void ExecuteOffscreenRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
@@ -55,9 +54,11 @@
math::Matrix3F texcoord_transform_;
math::RectF rect_;
const backend::TextureEGL* texture_;
- GenerateTextureFunction generate_texture_;
+ base::Closure draw_offscreen_;
+ base::Closure draw_onscreen_;
uint8_t* vertex_buffer_;
+ bool tile_texture_;
};
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/graphics_state.cc b/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
index 904b6ee..3c2d1e9 100644
--- a/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
+++ b/src/cobalt/renderer/rasterizer/egl/graphics_state.cc
@@ -35,6 +35,12 @@
depth_test_enabled_ = false;
depth_write_enabled_ = true;
Reset();
+
+ // These settings should only need to be set once. Nothing should touch them.
+ GL_CALL(glDepthRangef(0.0f, 1.0f));
+ GL_CALL(glDisable(GL_DITHER));
+ GL_CALL(glDisable(GL_CULL_FACE));
+ GL_CALL(glDisable(GL_STENCIL_TEST));
}
GraphicsState::~GraphicsState() {
@@ -73,6 +79,7 @@
GL_CALL(glDisable(GL_BLEND));
GL_CALL(glDisable(GL_DEPTH_TEST));
GL_CALL(glDepthMask(GL_TRUE));
+ GL_CALL(glUseProgram(0));
// Since the GL state was changed without going through the cache, mark it
// as dirty.
@@ -173,6 +180,10 @@
}
}
+void GraphicsState::ResetDepthFunc() {
+ GL_CALL(glDepthFunc(GL_LESS));
+}
+
void GraphicsState::ActiveBindTexture(GLenum texture_unit, GLenum target,
GLuint texture) {
int texunit_index = texture_unit - GL_TEXTURE0;
@@ -194,6 +205,29 @@
}
}
+void GraphicsState::ActiveBindTexture(GLenum texture_unit, GLenum target,
+ GLuint texture, GLint texture_wrap_mode) {
+ int texunit_index = texture_unit - GL_TEXTURE0;
+
+ if (texture_unit_ != texture_unit) {
+ texture_unit_ = texture_unit;
+ GL_CALL(glActiveTexture(texture_unit));
+ GL_CALL(glBindTexture(target, texture));
+ } else if (texunit_index >= kNumTextureUnitsCached ||
+ texunit_target_[texunit_index] != target ||
+ texunit_texture_[texunit_index] != texture) {
+ GL_CALL(glBindTexture(target, texture));
+ }
+
+ if (texunit_index < kNumTextureUnitsCached) {
+ texunit_target_[texunit_index] = target;
+ texunit_texture_[texunit_index] = texture;
+ }
+
+ GL_CALL(glTexParameteri(target, GL_TEXTURE_WRAP_S, texture_wrap_mode));
+ GL_CALL(glTexParameteri(target, GL_TEXTURE_WRAP_T, texture_wrap_mode));
+}
+
void GraphicsState::SetClipAdjustment(const math::Size& render_target_size) {
render_target_size_ = render_target_size;
clip_adjustment_dirty_ = true;
@@ -325,10 +359,10 @@
void GraphicsState::Reset() {
program_ = 0;
- GL_CALL(glUseProgram(0));
Viewport(viewport_.x(), viewport_.y(), viewport_.width(), viewport_.height());
Scissor(scissor_.x(), scissor_.y(), scissor_.width(), scissor_.height());
+ GL_CALL(glEnable(GL_SCISSOR_TEST));
array_buffer_handle_ = 0;
texture_unit_ = 0;
@@ -338,6 +372,11 @@
disable_vertex_attrib_array_mask_ = 0;
clip_adjustment_dirty_ = true;
+ if (vertex_data_buffer_updated_) {
+ array_buffer_handle_ = vertex_data_buffer_handle_[frame_index_];
+ GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, array_buffer_handle_));
+ }
+
if (blend_enabled_) {
GL_CALL(glEnable(GL_BLEND));
} else {
@@ -351,13 +390,7 @@
GL_CALL(glDisable(GL_DEPTH_TEST));
}
GL_CALL(glDepthMask(depth_write_enabled_ ? GL_TRUE : GL_FALSE));
- GL_CALL(glDepthFunc(GL_LESS));
- GL_CALL(glDepthRangef(0.0f, 1.0f));
-
- GL_CALL(glDisable(GL_DITHER));
- GL_CALL(glDisable(GL_CULL_FACE));
- GL_CALL(glDisable(GL_STENCIL_TEST));
- GL_CALL(glEnable(GL_SCISSOR_TEST));
+ ResetDepthFunc();
state_dirty_ = false;
}
diff --git a/src/cobalt/renderer/rasterizer/egl/graphics_state.h b/src/cobalt/renderer/rasterizer/egl/graphics_state.h
index 489b926..a8c48f4 100644
--- a/src/cobalt/renderer/rasterizer/egl/graphics_state.h
+++ b/src/cobalt/renderer/rasterizer/egl/graphics_state.h
@@ -50,6 +50,7 @@
// Set the current shader program to be used.
void UseProgram(GLuint program);
+ GLuint GetProgram() const { return program_; }
// Set the viewport.
void Viewport(int x, int y, int width, int height);
@@ -63,21 +64,32 @@
// Default = disabled.
void EnableBlend();
void DisableBlend();
+ bool IsBlendEnabled() const { return blend_enabled_; }
// Control depth testing.
// Default = enabled.
void EnableDepthTest();
void DisableDepthTest();
+ bool IsDepthTestEnabled() const { return depth_test_enabled_; }
// Control writing to the depth buffer.
// Default = enabled.
void EnableDepthWrite();
void DisableDepthWrite();
+ bool IsDepthWriteEnabled() const { return depth_write_enabled_; }
+
+ // Reset to the default depth function.
+ void ResetDepthFunc();
// Bind a texture to a given texture unit. Combines glActiveTexture and
// glBindTexture.
void ActiveBindTexture(GLenum texture_unit, GLenum target, GLuint texture);
+ // Bind a texture to the specified texture unit and set its texture wrap
+ // mode.
+ void ActiveBindTexture(GLenum texture_unit, GLenum target, GLuint texture,
+ GLint texture_wrap_mode);
+
// Set the clip adjustment to be used with vertex shaders. This transforms
// the vertex coordinates from view space to clip space.
void SetClipAdjustment(const math::Size& render_target_size);
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
index 59958a5..2cc8005 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
@@ -20,55 +20,30 @@
#include "base/debug/trace_event.h"
#include "base/memory/scoped_vector.h"
#include "base/threading/thread_checker.h"
-#include "cobalt/math/size.h"
-#include "cobalt/renderer/backend/egl/framebuffer.h"
+#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
#include "cobalt/renderer/backend/egl/graphics_system.h"
#include "cobalt/renderer/backend/egl/texture.h"
#include "cobalt/renderer/backend/egl/utils.h"
-#include "cobalt/renderer/frame_rate_throttler.h"
#include "cobalt/renderer/rasterizer/egl/draw_object_manager.h"
#include "cobalt/renderer/rasterizer/egl/graphics_state.h"
-#include "cobalt/renderer/rasterizer/egl/rect_allocator.h"
+#include "cobalt/renderer/rasterizer/egl/offscreen_target_manager.h"
#include "cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h"
#include "cobalt/renderer/rasterizer/egl/shader_program_manager.h"
+#include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
#include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/core/SkSurfaceProps.h"
#include "third_party/skia/include/gpu/GrContext.h"
-#include "third_party/skia/include/gpu/GrRenderTarget.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace egl {
-namespace {
-
-int32_t NextPowerOf2(int32_t num) {
- // Return the smallest power of 2 that is greater than or equal to num.
- // This flips on all bits <= num, then num+1 will be the next power of 2.
- --num;
- num |= num >> 1;
- num |= num >> 2;
- num |= num >> 4;
- num |= num >> 8;
- num |= num >> 16;
- return num + 1;
-}
-
-bool IsPowerOf2(int32_t num) {
- return (num & (num - 1)) == 0;
-}
-
-} // namespace
-
class HardwareRasterizer::Impl {
public:
explicit Impl(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int surface_cache_size_in_bytes);
@@ -80,75 +55,52 @@
void SubmitToFallbackRasterizer(
const scoped_refptr<render_tree::Node>& render_tree,
- const math::RectF& viewport,
- const backend::TextureEGL** out_texture,
- math::Matrix3F* out_texcoord_transform);
+ const math::Matrix3F& transform,
+ const OffscreenTargetManager::TargetInfo& target);
render_tree::ResourceProvider* GetResourceProvider() {
return fallback_rasterizer_->GetResourceProvider();
}
private:
- // Use an atlas for offscreen targets.
- struct OffscreenAtlas {
- explicit OffscreenAtlas(const math::Size& size) : allocator(size) {}
- RectAllocator allocator;
- scoped_ptr<backend::FramebufferEGL> framebuffer;
- SkAutoTUnref<SkSurface> skia_surface;
- };
-
GrContext* GetFallbackContext() {
return fallback_rasterizer_->GetGrContext();
}
- void RasterizeTree(const scoped_refptr<render_tree::Node>& render_tree);
+ void ResetFallbackContextDuringFrame();
- void AllocateOffscreenTarget(const math::SizeF& size,
- OffscreenAtlas** out_atlas, math::RectF* out_target_rect);
-
- OffscreenAtlas* AddOffscreenAtlas(const math::Size& size);
+ void RasterizeTree(const scoped_refptr<render_tree::Node>& render_tree,
+ backend::RenderTargetEGL* render_target);
scoped_ptr<skia::HardwareRasterizer> fallback_rasterizer_;
scoped_ptr<GraphicsState> graphics_state_;
scoped_ptr<ShaderProgramManager> shader_program_manager_;
+ scoped_ptr<OffscreenTargetManager> offscreen_target_manager_;
backend::GraphicsContextEGL* graphics_context_;
- FrameRateThrottler frame_rate_throttler_;
base::ThreadChecker thread_checker_;
-
- typedef ScopedVector<OffscreenAtlas> OffscreenAtlasList;
- OffscreenAtlasList offscreen_atlases_;
-
- // Size of the smallest offscreen target atlas that can hold all offscreen
- // targets requested this frame.
- math::Size offscreen_atlas_size_;
-
- // Align offscreen targets to a particular size to more efficiently use the
- // offscreen target atlas. Use a power of 2 for the alignment so that a bit
- // mask can be used for the alignment calculation.
- math::Size offscreen_target_size_mask_;
};
-HardwareRasterizer::Impl::Impl(
- backend::GraphicsContext* graphics_context,
- int skia_cache_size_in_bytes,
- int scratch_surface_cache_size_in_bytes,
- int surface_cache_size_in_bytes)
+HardwareRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
+ int skia_cache_size_in_bytes,
+ int scratch_surface_cache_size_in_bytes,
+ int surface_cache_size_in_bytes)
: fallback_rasterizer_(new skia::HardwareRasterizer(
- graphics_context,
- skia_cache_size_in_bytes,
- scratch_surface_cache_size_in_bytes,
- surface_cache_size_in_bytes)),
+ graphics_context, skia_atlas_width, skia_atlas_height,
+ skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes,
+ 0 /* fallback rasterizer should not use a surface cache */)),
graphics_context_(
base::polymorphic_downcast<backend::GraphicsContextEGL*>(
- graphics_context)),
- offscreen_atlas_size_(0, 0),
- offscreen_target_size_mask_(0, 0) {
+ graphics_context)) {
+ DLOG(INFO) << "surface_cache_size_in_bytes: " << surface_cache_size_in_bytes;
+
backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_);
-
graphics_state_.reset(new GraphicsState());
shader_program_manager_.reset(new ShaderProgramManager());
+ offscreen_target_manager_.reset(new OffscreenTargetManager(
+ graphics_context_, GetFallbackContext(), surface_cache_size_in_bytes));
}
HardwareRasterizer::Impl::~Impl() {
@@ -156,8 +108,7 @@
graphics_context_);
GL_CALL(glFinish());
- offscreen_atlases_.clear();
-
+ offscreen_target_manager_.reset();
shader_program_manager_.reset();
graphics_state_.reset();
}
@@ -174,6 +125,16 @@
backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_, render_target_egl);
+ // Make sure this render target has a depth buffer. This is only relevant
+ // for framebuffer render targets. Other render target types should already
+ // have a depth buffer set up by the graphics system's config.
+ if (render_target_egl->GetSurface() == EGL_NO_SURFACE) {
+ backend::FramebufferRenderTargetEGL* framebuffer_render_target =
+ base::polymorphic_downcast<backend::FramebufferRenderTargetEGL*>(
+ render_target_egl);
+ framebuffer_render_target->EnsureDepthBufferAttached(GL_DEPTH_COMPONENT16);
+ }
+
fallback_rasterizer_->AdvanceFrame();
const math::Size& target_size = render_target->GetSize();
@@ -189,81 +150,75 @@
graphics_state_->Scissor(0, 0, target_size.width(), target_size.height());
}
- // Set initial characteristics for offscreen target handling.
- if (offscreen_atlas_size_.IsEmpty()) {
- if (target_size.width() >= 64 && target_size.height() >= 64) {
- offscreen_atlas_size_.SetSize(
- NextPowerOf2(target_size.width() / 16),
- NextPowerOf2(target_size.height() / 16));
- offscreen_target_size_mask_.SetSize(
- NextPowerOf2(target_size.width() / 64) - 1,
- NextPowerOf2(target_size.height() / 64) - 1);
- } else {
- offscreen_atlas_size_.SetSize(16, 16);
- offscreen_target_size_mask_.SetSize(0, 0);
- }
- }
+ offscreen_target_manager_->Update(target_size);
- // Keep only the largest offscreen target atlas.
- while (offscreen_atlases_.size() > 1) {
- offscreen_atlases_.erase(offscreen_atlases_.begin());
- }
+ RasterizeTree(render_tree, render_target_egl);
- if (!offscreen_atlases_.empty()) {
- offscreen_atlases_.back()->allocator.Reset();
- offscreen_atlases_.back()->skia_surface->getCanvas()->clear(
- SK_ColorTRANSPARENT);
- }
-
- RasterizeTree(render_tree);
-
- frame_rate_throttler_.EndInterval();
graphics_context_->SwapBuffers(render_target_egl);
- frame_rate_throttler_.BeginInterval();
}
void HardwareRasterizer::Impl::SubmitToFallbackRasterizer(
const scoped_refptr<render_tree::Node>& render_tree,
- const math::RectF& viewport,
- const backend::TextureEGL** out_texture,
- math::Matrix3F* out_texcoord_transform) {
+ const math::Matrix3F& transform,
+ const OffscreenTargetManager::TargetInfo& target) {
DCHECK(thread_checker_.CalledOnValidThread());
TRACE_EVENT0("cobalt::renderer", "SubmitToFallbackRasterizer");
- math::RectF target_rect(0, 0, 0, 0);
- OffscreenAtlas* atlas = NULL;
- AllocateOffscreenTarget(viewport.size(), &atlas, &target_rect);
-
- backend::FramebufferEGL* framebuffer = atlas->framebuffer.get();
- SkCanvas* canvas = atlas->skia_surface->getCanvas();
-
// Use skia to rasterize to the allocated offscreen target.
- canvas->save();
- canvas->clipRect(SkRect::MakeXYWH(
- target_rect.x(), target_rect.y(),
- viewport.width() + 0.5f, viewport.height() + 0.5f));
- canvas->translate(viewport.x() + target_rect.x(),
- viewport.y() + target_rect.y());
- fallback_rasterizer_->SubmitOffscreen(render_tree, canvas);
- canvas->restore();
+ target.skia_canvas->save();
- float scale_x = 1.0f / framebuffer->GetSize().width();
- float scale_y = 1.0f / framebuffer->GetSize().height();
- *out_texcoord_transform = math::Matrix3F::FromValues(
- viewport.width() * scale_x, 0, target_rect.x() * scale_x,
- 0, viewport.height() * scale_y, target_rect.y() * scale_y,
- 0, 0, 1);
- *out_texture = framebuffer->GetColorTexture();
+ if (target.is_scratch_surface) {
+ // The scratch surface is used immediately after rendering to it. So we
+ // are switching from this rasterizer to skia, then will switch back to
+ // our rasterizer context.
+ ResetFallbackContextDuringFrame();
+ target.skia_canvas->clear(SK_ColorTRANSPARENT);
+ }
+
+ target.skia_canvas->clipRect(SkRect::MakeXYWH(
+ target.region.x(), target.region.y(),
+ target.region.width(), target.region.height()));
+ target.skia_canvas->translate(target.region.x(), target.region.y());
+ target.skia_canvas->concat(skia::CobaltMatrixToSkia(transform));
+ fallback_rasterizer_->SubmitOffscreen(render_tree, target.skia_canvas);
+
+ if (target.is_scratch_surface) {
+ // Flush the skia draw calls so the contents can be used immediately.
+ target.skia_canvas->flush();
+
+ // Switch back to the current render target and context.
+ graphics_context_->ResetCurrentSurface();
+ graphics_state_->SetDirty();
+ }
+
+ target.skia_canvas->restore();
+}
+
+void HardwareRasterizer::Impl::ResetFallbackContextDuringFrame() {
+ // Perform a minimal reset of the fallback context. Only need to invalidate
+ // states that this rasterizer pollutes.
+ uint32_t untouched_states = kMSAAEnable_GrGLBackendState |
+ kStencil_GrGLBackendState | kPixelStore_GrGLBackendState |
+ kFixedFunction_GrGLBackendState | kPathRendering_GrGLBackendState;
+
+ // Manually reset a subset of kMisc_GrGLBackendState
+ untouched_states |= kMisc_GrGLBackendState;
+ GL_CALL(glDisable(GL_DEPTH_TEST));
+ GL_CALL(glDepthMask(GL_FALSE));
+
+ GetFallbackContext()->resetContext(~untouched_states & kAll_GrBackendState);
}
void HardwareRasterizer::Impl::RasterizeTree(
- const scoped_refptr<render_tree::Node>& render_tree) {
+ const scoped_refptr<render_tree::Node>& render_tree,
+ backend::RenderTargetEGL* render_target) {
DrawObjectManager draw_object_manager;
RenderTreeNodeVisitor::FallbackRasterizeFunction fallback_rasterize =
base::Bind(&HardwareRasterizer::Impl::SubmitToFallbackRasterizer,
base::Unretained(this));
RenderTreeNodeVisitor visitor(graphics_state_.get(),
&draw_object_manager,
+ offscreen_target_manager_.get(),
&fallback_rasterize);
// Traverse the render tree to populate the draw object manager.
@@ -277,6 +232,8 @@
// Rasterize to offscreen targets using skia.
{
TRACE_EVENT0("cobalt::renderer", "OffscreenRasterize");
+ backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
+ graphics_context_, render_target);
// Reset the skia graphics context since the egl rasterizer dirtied it.
GetFallbackContext()->resetContext();
@@ -285,10 +242,7 @@
{
TRACE_EVENT0("cobalt::renderer", "Skia Flush");
- for (OffscreenAtlasList::iterator iter = offscreen_atlases_.begin();
- iter != offscreen_atlases_.end(); ++iter) {
- (*iter)->skia_surface->getCanvas()->flush();
- }
+ offscreen_target_manager_->Flush();
}
// Reset the egl graphics state since skia dirtied it.
@@ -314,104 +268,14 @@
graphics_state_->EndFrame();
}
-void HardwareRasterizer::Impl::AllocateOffscreenTarget(
- const math::SizeF& size,
- OffscreenAtlas** out_atlas, math::RectF* out_target_rect) {
- // Get an offscreen target for rendering. Align up the requested target size
- // to improve usage of the atlas (since more requests will have the same
- // aligned width or height).
- DCHECK(IsPowerOf2(offscreen_target_size_mask_.width() + 1));
- DCHECK(IsPowerOf2(offscreen_target_size_mask_.height() + 1));
- math::Size target_size(
- (static_cast<int>(size.width() + 0.5f) +
- offscreen_target_size_mask_.width()) &
- ~offscreen_target_size_mask_.width(),
- (static_cast<int>(size.height() + 0.5f) +
- offscreen_target_size_mask_.height()) &
- ~offscreen_target_size_mask_.height());
- math::Rect target_rect(0, 0, 0, 0);
- OffscreenAtlas* atlas = NULL;
-
- // See if there's room in the most recently created offscreen target atlas.
- // Don't search any other atlases since we want to quickly find an offscreen
- // atlas size large enough to hold all offscreen targets needed in a frame.
- if (!offscreen_atlases_.empty()) {
- atlas = offscreen_atlases_.back();
- target_rect = atlas->allocator.Allocate(target_size);
- }
-
- if (target_rect.IsEmpty()) {
- // Create a new offscreen atlas, bigger than the previous, so that
- // eventually only one offscreen atlas is needed per frame.
- bool grew = false;
- if (offscreen_atlas_size_.width() < target_size.width()) {
- offscreen_atlas_size_.set_width(NextPowerOf2(target_size.width()));
- grew = true;
- }
- if (offscreen_atlas_size_.height() < target_size.height()) {
- offscreen_atlas_size_.set_height(NextPowerOf2(target_size.height()));
- grew = true;
- }
- if (!grew) {
- // Grow the offscreen atlas while keeping it square-ish.
- if (offscreen_atlas_size_.width() <= offscreen_atlas_size_.height()) {
- offscreen_atlas_size_.set_width(offscreen_atlas_size_.width() * 2);
- } else {
- offscreen_atlas_size_.set_height(offscreen_atlas_size_.height() * 2);
- }
- }
-
- atlas = AddOffscreenAtlas(offscreen_atlas_size_);
- target_rect = atlas->allocator.Allocate(target_size);
- }
- DCHECK(!target_rect.IsEmpty());
-
- *out_atlas = atlas;
- *out_target_rect = target_rect;
-}
-
-HardwareRasterizer::Impl::OffscreenAtlas*
- HardwareRasterizer::Impl::AddOffscreenAtlas(const math::Size& size) {
- OffscreenAtlas* atlas = new OffscreenAtlas(offscreen_atlas_size_);
- offscreen_atlases_.push_back(atlas);
-
- // Create a new framebuffer.
- atlas->framebuffer.reset(new backend::FramebufferEGL(
- graphics_context_, offscreen_atlas_size_, GL_RGBA, GL_NONE));
-
- // Wrap the framebuffer as a skia surface.
- GrBackendRenderTargetDesc skia_desc;
- skia_desc.fWidth = offscreen_atlas_size_.width();
- skia_desc.fHeight = offscreen_atlas_size_.height();
- skia_desc.fConfig = kRGBA_8888_GrPixelConfig;
- skia_desc.fOrigin = kTopLeft_GrSurfaceOrigin;
- skia_desc.fSampleCnt = 0;
- skia_desc.fStencilBits = 0;
- skia_desc.fRenderTargetHandle = atlas->framebuffer->gl_handle();
-
- SkAutoTUnref<GrRenderTarget> skia_render_target(
- GetFallbackContext()->wrapBackendRenderTarget(skia_desc));
- SkSurfaceProps skia_surface_props(
- SkSurfaceProps::kUseDistanceFieldFonts_Flag,
- SkSurfaceProps::kLegacyFontHost_InitType);
- atlas->skia_surface.reset(SkSurface::NewRenderTargetDirect(
- skia_render_target, &skia_surface_props));
-
- atlas->skia_surface->getCanvas()->clear(SK_ColorTRANSPARENT);
-
- return atlas;
-}
-
HardwareRasterizer::HardwareRasterizer(
- backend::GraphicsContext* graphics_context,
- int skia_cache_size_in_bytes,
- int scratch_surface_cache_size_in_bytes,
- int surface_cache_size_in_bytes)
- : impl_(new Impl(graphics_context,
+ backend::GraphicsContext* graphics_context, int skia_atlas_width,
+ int skia_atlas_height, int skia_cache_size_in_bytes,
+ int scratch_surface_cache_size_in_bytes, int surface_cache_size_in_bytes)
+ : impl_(new Impl(graphics_context, skia_atlas_width, skia_atlas_height,
skia_cache_size_in_bytes,
scratch_surface_cache_size_in_bytes,
- surface_cache_size_in_bytes)) {
-}
+ surface_cache_size_in_bytes)) {}
void HardwareRasterizer::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
index e136b7f..dc5c9b4 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
@@ -46,6 +46,7 @@
// a surface cache such that expensive render tree nodes seen multiple times
// will get saved to offscreen surfaces.
explicit HardwareRasterizer(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int surface_cache_size_in_bytes);
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
new file mode 100644
index 0000000..a530a84
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
@@ -0,0 +1,365 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/egl/offscreen_target_manager.h"
+
+#include <algorithm>
+
+#include "base/hash_tables.h"
+#include "cobalt/renderer/rasterizer/egl/rect_allocator.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/core/SkSurfaceProps.h"
+#include "third_party/skia/include/gpu/GrRenderTarget.h"
+#include "third_party/skia/include/gpu/GrTypes.h"
+
+namespace {
+// Structure describing the key for render target allocations in a given
+// offscreen target atlas.
+struct AllocationKey {
+ AllocationKey(const cobalt::render_tree::Node* tree_node,
+ const cobalt::math::SizeF& alloc_size)
+ : node(tree_node),
+ size(alloc_size) {}
+
+ bool operator==(const AllocationKey& other) const {
+ return node == other.node && size == other.size;
+ }
+
+ bool operator!=(const AllocationKey& other) const {
+ return node != other.node || size != other.size;
+ }
+
+ bool operator<(const AllocationKey& rhs) const {
+ return (node < rhs.node) ||
+ (node == rhs.node &&
+ (size.width() < rhs.size.width() ||
+ (size.width() == rhs.size.width() &&
+ size.height() < rhs.size.height())));
+ }
+
+ const void* node;
+ cobalt::math::SizeF size;
+};
+} // namespace
+
+namespace BASE_HASH_NAMESPACE {
+#if defined(BASE_HASH_USE_HASH_STRUCT)
+template <>
+struct hash<AllocationKey> {
+ size_t operator()(const AllocationKey& key) const {
+ return reinterpret_cast<size_t>(key.node);
+ }
+};
+#else
+template <>
+inline size_t hash_value<AllocationKey>(const AllocationKey& key) {
+ return reinterpret_cast<size_t>(key.node);
+}
+#endif
+} // namespace BASE_HASH_NAMESPACE
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+namespace {
+
+typedef base::hash_map<AllocationKey, math::Rect> AllocationMap;
+
+int32_t NextPowerOf2(int32_t num) {
+ // Return the smallest power of 2 that is greater than or equal to num.
+ // This flips on all bits <= num, then num+1 will be the next power of 2.
+ --num;
+ num |= num >> 1;
+ num |= num >> 2;
+ num |= num >> 4;
+ num |= num >> 8;
+ num |= num >> 16;
+ return num + 1;
+}
+
+bool IsPowerOf2(int32_t num) {
+ return (num & (num - 1)) == 0;
+}
+
+size_t GetMemorySize(const math::Size& target_size) {
+ // RGBA uses 4 bytes per pixel. Assume no rounding to the nearest power of 2.
+ return target_size.width() * target_size.height() * 4;
+}
+
+} // namespace
+
+struct OffscreenTargetManager::OffscreenAtlas {
+ explicit OffscreenAtlas(const math::Size& size)
+ : allocator(size),
+ allocations_used(0),
+ needs_flush(false) {}
+
+ RectAllocator allocator;
+ AllocationMap allocation_map;
+ size_t allocations_used;
+ scoped_ptr<backend::FramebufferEGL> framebuffer;
+ SkAutoTUnref<SkSurface> skia_surface;
+ bool needs_flush;
+};
+
+OffscreenTargetManager::OffscreenTargetManager(
+ backend::GraphicsContextEGL* graphics_context, GrContext* skia_context,
+ size_t memory_limit)
+ : graphics_context_(graphics_context),
+ skia_context_(skia_context),
+ offscreen_target_size_mask_(0, 0),
+ memory_limit_(memory_limit) {
+}
+
+OffscreenTargetManager::~OffscreenTargetManager() {
+}
+
+void OffscreenTargetManager::Update(const math::Size& frame_size) {
+ if (offscreen_atlases_.empty()) {
+ InitializeTargets(frame_size);
+ }
+
+ // If any of the current atlases have more allocations used than the
+ // current cache, then use that as the new cache.
+ size_t most_used_atlas_index = 0;
+ for (size_t index = 1; index < offscreen_atlases_.size(); ++index) {
+ if (offscreen_atlases_[most_used_atlas_index]->allocations_used <
+ offscreen_atlases_[index]->allocations_used) {
+ most_used_atlas_index = index;
+ }
+ }
+
+ OffscreenAtlas* most_used_atlas = offscreen_atlases_[most_used_atlas_index];
+ if (offscreen_cache_->allocations_used < most_used_atlas->allocations_used) {
+ OffscreenAtlas* new_atlas = offscreen_cache_.release();
+ offscreen_cache_.reset(offscreen_atlases_[most_used_atlas_index]);
+ offscreen_atlases_.weak_erase(offscreen_atlases_.begin() +
+ most_used_atlas_index);
+ offscreen_atlases_.push_back(new_atlas);
+ }
+ offscreen_cache_->allocations_used = 0;
+
+ // Reset all current atlases for use this frame.
+ for (size_t index = 0; index < offscreen_atlases_.size(); ++index) {
+ OffscreenAtlas* atlas = offscreen_atlases_[index];
+ atlas->allocator.Reset();
+ atlas->allocation_map.clear();
+ atlas->allocations_used = 0;
+ }
+}
+
+void OffscreenTargetManager::Flush() {
+ if (offscreen_cache_->needs_flush) {
+ offscreen_cache_->needs_flush = false;
+ offscreen_cache_->skia_surface->getCanvas()->flush();
+ }
+ for (size_t index = 0; index < offscreen_atlases_.size(); ++index) {
+ if (offscreen_atlases_[index]->needs_flush) {
+ offscreen_atlases_[index]->needs_flush = false;
+ offscreen_atlases_[index]->skia_surface->getCanvas()->flush();
+ }
+ }
+}
+
+bool OffscreenTargetManager::GetCachedOffscreenTarget(
+ const render_tree::Node* node, const math::SizeF& size,
+ TargetInfo* out_target_info) {
+ AllocationMap::iterator iter = offscreen_cache_->allocation_map.find(
+ AllocationKey(node, size));
+ if (iter != offscreen_cache_->allocation_map.end()) {
+ offscreen_cache_->allocations_used += 1;
+ out_target_info->framebuffer = offscreen_cache_->framebuffer.get();
+ out_target_info->skia_canvas = offscreen_cache_->skia_surface->getCanvas();
+ out_target_info->region = iter->second;
+ out_target_info->is_scratch_surface = false;
+ return true;
+ }
+ return false;
+}
+
+void OffscreenTargetManager::AllocateOffscreenTarget(
+ const render_tree::Node* node, const math::SizeF& size,
+ TargetInfo* out_target_info) {
+ // Pad the offscreen target size to prevent interpolation with unwanted
+ // texels when rendering the results.
+ const int kInterpolatePad = 1;
+
+ // Get an offscreen target for rendering. Align up the requested target size
+ // to improve usage of the atlas (since more requests will have the same
+ // aligned width or height).
+ DCHECK(IsPowerOf2(offscreen_target_size_mask_.width() + 1));
+ DCHECK(IsPowerOf2(offscreen_target_size_mask_.height() + 1));
+ math::Size target_size(
+ (static_cast<int>(size.width()) + 2 * kInterpolatePad +
+ offscreen_target_size_mask_.width()) &
+ ~offscreen_target_size_mask_.width(),
+ (static_cast<int>(size.height()) + 2 * kInterpolatePad +
+ offscreen_target_size_mask_.height()) &
+ ~offscreen_target_size_mask_.height());
+ math::Rect target_rect(0, 0, 0, 0);
+ OffscreenAtlas* atlas = NULL;
+
+ // See if there's room in the offscreen cache for additional targets.
+ atlas = offscreen_cache_.get();
+ target_rect = atlas->allocator.Allocate(target_size);
+
+ if (target_rect.IsEmpty()) {
+ // See if there's room in the other atlases.
+ for (size_t index = offscreen_atlases_.size(); index > 0;) {
+ atlas = offscreen_atlases_[--index];
+ target_rect = atlas->allocator.Allocate(target_size);
+ if (!target_rect.IsEmpty()) {
+ break;
+ }
+ }
+ }
+
+ // Use the scratch surface if needed.
+ bool scratch_surface_used = false;
+ if (target_rect.IsEmpty()) {
+ scratch_surface_used = true;
+ atlas = scratch_surface_.get();
+ target_rect = math::Rect(atlas->framebuffer->GetSize());
+ } else {
+ // Inset to prevent interpolation with unwanted pixels at the edge.
+ target_rect.Inset(kInterpolatePad, kInterpolatePad);
+
+ // Clear the atlas if this will be the first draw into it.
+ if (atlas->allocation_map.empty()) {
+ atlas->skia_surface->getCanvas()->clear(SK_ColorTRANSPARENT);
+ }
+
+ atlas->allocation_map.insert(AllocationMap::value_type(
+ AllocationKey(node, size), target_rect));
+ atlas->allocations_used += 1;
+ atlas->needs_flush = true;
+ }
+
+ out_target_info->framebuffer = atlas->framebuffer.get();
+ out_target_info->skia_canvas = atlas->skia_surface->getCanvas();
+ out_target_info->region = target_rect;
+ out_target_info->is_scratch_surface = scratch_surface_used;
+}
+
+void OffscreenTargetManager::InitializeTargets(const math::Size& frame_size) {
+ DLOG(INFO) << "offscreen render target memory limit: " << memory_limit_;
+
+ if (frame_size.width() >= 64 && frame_size.height() >= 64) {
+ offscreen_target_size_mask_.SetSize(
+ NextPowerOf2(frame_size.width() / 64) - 1,
+ NextPowerOf2(frame_size.height() / 64) - 1);
+ } else {
+ offscreen_target_size_mask_.SetSize(0, 0);
+ }
+
+ math::Size max_size(NextPowerOf2(frame_size.width()) / 2,
+ NextPowerOf2(frame_size.height()) / 2);
+ max_size.SetToMax(math::Size(1, 1));
+
+ // A full-screen scratch surface is required. This avoids pixelation when an
+ // offscreen target is needed.
+ scratch_surface_.reset(CreateOffscreenAtlas(frame_size));
+
+ // Other offscreen render targets are optional but highly recommended. These
+ // allow caching of render results for improved performance. At least two
+ // must exist -- one for the cache and the other a working scratch.
+ size_t half_memory_limit = memory_limit_ / 2;
+ math::Size atlas_size(1, 1);
+ for (;;) {
+ if (atlas_size == max_size) {
+ break;
+ }
+ if (GetMemorySize(atlas_size) * 2 <= half_memory_limit) {
+ // Memory limit allows a bigger atlas.
+ if (atlas_size.width() <= atlas_size.height()) {
+ // Prefer growing by width.
+ if (atlas_size.width() < max_size.width()) {
+ atlas_size.set_width(atlas_size.width() * 2);
+ } else {
+ atlas_size.set_height(atlas_size.height() * 2);
+ }
+ } else {
+ // Prefer growing by height.
+ if (atlas_size.height() < max_size.height()) {
+ atlas_size.set_height(atlas_size.height() * 2);
+ } else {
+ atlas_size.set_width(atlas_size.width() * 2);
+ }
+ }
+ } else {
+ break;
+ }
+ }
+
+ // It is better to have fewer, large atlases than many small atlases to
+ // minimize the cost of switching render targets. Consider changing the
+ // max_size logic if there's plenty of memory to spare.
+ const int kMaxAtlases = 4;
+ int num_atlases = memory_limit_ / GetMemorySize(atlas_size);
+ if (num_atlases < 2) {
+ // Must have at least two atlases -- even if they are of a token size.
+ // This simplifies code elsewhere.
+ DCHECK(atlas_size.width() == 1 && atlas_size.height() == 1);
+ num_atlases = 2;
+ } else if (num_atlases > kMaxAtlases) {
+ DCHECK(atlas_size == max_size);
+ num_atlases = kMaxAtlases;
+ DLOG(WARNING) << "More memory was allotted for offscreen render targets"
+ << " than will be used.";
+ }
+ offscreen_cache_.reset(CreateOffscreenAtlas(atlas_size));
+ for (int i = 1; i < num_atlases; ++i) {
+ offscreen_atlases_.push_back(CreateOffscreenAtlas(atlas_size));
+ }
+
+ DLOG(INFO) << "Created " << num_atlases << " offscreen atlases of size "
+ << atlas_size.width() << " x " << atlas_size.height();
+}
+
+OffscreenTargetManager::OffscreenAtlas*
+ OffscreenTargetManager::CreateOffscreenAtlas(const math::Size& size) {
+ OffscreenAtlas* atlas = new OffscreenAtlas(size);
+
+ // Create a new framebuffer.
+ atlas->framebuffer.reset(new backend::FramebufferEGL(
+ graphics_context_, size, GL_RGBA, GL_NONE));
+
+ // Wrap the framebuffer as a skia surface.
+ GrBackendRenderTargetDesc skia_desc;
+ skia_desc.fWidth = size.width();
+ skia_desc.fHeight = size.height();
+ skia_desc.fConfig = kRGBA_8888_GrPixelConfig;
+ skia_desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+ skia_desc.fSampleCnt = 0;
+ skia_desc.fStencilBits = 0;
+ skia_desc.fRenderTargetHandle = atlas->framebuffer->gl_handle();
+
+ SkAutoTUnref<GrRenderTarget> skia_render_target(
+ skia_context_->wrapBackendRenderTarget(skia_desc));
+ SkSurfaceProps skia_surface_props(
+ SkSurfaceProps::kUseDistanceFieldFonts_Flag,
+ SkSurfaceProps::kLegacyFontHost_InitType);
+ atlas->skia_surface.reset(SkSurface::NewRenderTargetDirect(
+ skia_render_target, &skia_surface_props));
+
+ return atlas;
+}
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
new file mode 100644
index 0000000..5d23334
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
@@ -0,0 +1,102 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_EGL_OFFSCREEN_TARGET_MANAGER_H_
+#define COBALT_RENDERER_RASTERIZER_EGL_OFFSCREEN_TARGET_MANAGER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "cobalt/math/rect_f.h"
+#include "cobalt/math/size.h"
+#include "cobalt/render_tree/node.h"
+#include "cobalt/renderer/backend/egl/framebuffer.h"
+#include "cobalt/renderer/backend/egl/graphics_context.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace egl {
+
+// Manage allocating and caching offscreen render targets for render_tree::Node
+// rendering. This uses atlases instead of individual render targets to
+// minimize the cost of switching render targets.
+class OffscreenTargetManager {
+ public:
+ struct TargetInfo {
+ TargetInfo()
+ : framebuffer(NULL),
+ skia_canvas(NULL),
+ is_scratch_surface(false) {}
+ backend::FramebufferEGL* framebuffer;
+ SkCanvas* skia_canvas;
+ math::RectF region;
+ bool is_scratch_surface;
+ };
+
+ OffscreenTargetManager(backend::GraphicsContextEGL* graphics_context,
+ GrContext* skia_context, size_t memory_limit);
+ ~OffscreenTargetManager();
+
+ // Update must be called once per frame, before any allocation requests are
+ // made for that frame.
+ void Update(const math::Size& frame_size);
+
+ // Flush all render targets that have been used thus far.
+ void Flush();
+
+ // Return whether a cached version of the requested render target is
+ // available. If a cache does exist, then the output parameters are set,
+ // otherwise, they are untouched.
+ // The returned values are only valid until the next call to Update().
+ bool GetCachedOffscreenTarget(
+ const render_tree::Node* node, const math::SizeF& size,
+ TargetInfo* out_target_info);
+
+ // Allocate an offscreen target of the specified size.
+ // The returned values are only valid until the next call to Update().
+ void AllocateOffscreenTarget(
+ const render_tree::Node* node, const math::SizeF& size,
+ TargetInfo* out_target_info);
+
+ private:
+ // Use an atlas for offscreen targets.
+ struct OffscreenAtlas;
+
+ void InitializeTargets(const math::Size& frame_size);
+ OffscreenAtlas* CreateOffscreenAtlas(const math::Size& size);
+
+ backend::GraphicsContextEGL* graphics_context_;
+ GrContext* skia_context_;
+
+ ScopedVector<OffscreenAtlas> offscreen_atlases_;
+ scoped_ptr<OffscreenAtlas> offscreen_cache_;
+ scoped_ptr<OffscreenAtlas> scratch_surface_;
+
+ // Align offscreen targets to a particular size to more efficiently use the
+ // offscreen target atlas. Use a power of 2 for the alignment so that a bit
+ // mask can be used for the alignment calculation.
+ math::Size offscreen_target_size_mask_;
+
+ // Maximum number of bytes that can be used for offscreen atlases.
+ size_t memory_limit_;
+};
+
+} // namespace egl
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_EGL_OFFSCREEN_TARGET_MANAGER_H_
diff --git a/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp b/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
index ebb05ff..6ff941a 100644
--- a/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/egl/rasterizer.gyp
@@ -37,6 +37,8 @@
'type': 'static_library',
'sources': [
+ 'draw_depth_stencil.h',
+ 'draw_depth_stencil.cc',
'draw_object.h',
'draw_object.cc',
'draw_object_manager.h',
@@ -45,12 +47,20 @@
'draw_poly_color.cc',
'draw_rect_color_texture.h',
'draw_rect_color_texture.cc',
+ 'draw_rect_linear_gradient.h',
+ 'draw_rect_linear_gradient.cc',
+ 'draw_rect_shadow_spread.h',
+ 'draw_rect_shadow_spread.cc',
+ 'draw_rect_shadow_blur.h',
+ 'draw_rect_shadow_blur.cc',
'draw_rect_texture.h',
'draw_rect_texture.cc',
'graphics_state.h',
'graphics_state.cc',
'hardware_rasterizer.cc',
'hardware_rasterizer.h',
+ 'offscreen_target_manager.h',
+ 'offscreen_target_manager.cc',
'rect_allocator.h',
'rect_allocator.cc',
'render_tree_node_visitor.h',
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index bc07839..9622bb7 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -18,11 +18,17 @@
#include <cmath>
#include "base/debug/trace_event.h"
+#include "base/optional.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/base/type_id.h"
#include "cobalt/math/matrix3_f.h"
+#include "cobalt/math/transform_2d.h"
+#include "cobalt/renderer/rasterizer/common/utils.h"
#include "cobalt/renderer/rasterizer/egl/draw_poly_color.h"
#include "cobalt/renderer/rasterizer/egl/draw_rect_color_texture.h"
+#include "cobalt/renderer/rasterizer/egl/draw_rect_linear_gradient.h"
+#include "cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h"
+#include "cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h"
#include "cobalt/renderer/rasterizer/egl/draw_rect_texture.h"
#include "cobalt/renderer/rasterizer/skia/hardware_image.h"
#include "cobalt/renderer/rasterizer/skia/image.h"
@@ -47,33 +53,46 @@
matrix(0, 1) == 0 && matrix(1, 0) == 0;
}
+math::Matrix3F GetTexcoordTransform(
+ const OffscreenTargetManager::TargetInfo& target) {
+ float scale_x = 1.0f / target.framebuffer->GetSize().width();
+ float scale_y = 1.0f / target.framebuffer->GetSize().height();
+ return math::Matrix3F::FromValues(
+ target.region.width() * scale_x, 0, target.region.x() * scale_x,
+ 0, target.region.height() * scale_y, target.region.y() * scale_y,
+ 0, 0, 1);
+}
+
} // namespace
RenderTreeNodeVisitor::RenderTreeNodeVisitor(GraphicsState* graphics_state,
DrawObjectManager* draw_object_manager,
+ OffscreenTargetManager* offscreen_target_manager,
const FallbackRasterizeFunction* fallback_rasterize)
: graphics_state_(graphics_state),
draw_object_manager_(draw_object_manager),
+ offscreen_target_manager_(offscreen_target_manager),
fallback_rasterize_(fallback_rasterize) {
// Let the first draw object render in front of the clear depth.
- draw_state_.depth = graphics_state_->NextClosestDepth(draw_state_.depth);
+ draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
+ draw_state_.scissor.Intersect(graphics_state->GetViewport());
draw_state_.scissor.Intersect(graphics_state->GetScissor());
}
void RenderTreeNodeVisitor::Visit(
render_tree::CompositionNode* composition_node) {
const render_tree::CompositionNode::Builder& data = composition_node->data();
- draw_state_.transform(0, 2) += data.offset().x();
- draw_state_.transform(1, 2) += data.offset().y();
+ math::Matrix3F old_transform = draw_state_.transform;
+ draw_state_.transform = draw_state_.transform *
+ math::TranslateMatrix(data.offset().x(), data.offset().y());
const render_tree::CompositionNode::Children& children =
data.children();
for (render_tree::CompositionNode::Children::const_iterator iter =
children.begin(); iter != children.end(); ++iter) {
(*iter)->Accept(this);
}
- draw_state_.transform(0, 2) -= data.offset().x();
- draw_state_.transform(1, 2) -= data.offset().y();
+ draw_state_.transform = old_transform;
}
void RenderTreeNodeVisitor::Visit(
@@ -139,10 +158,41 @@
!data.viewport_filter &&
!data.blur_filter &&
!data.map_to_mesh_filter) {
- float old_opacity = draw_state_.opacity;
- draw_state_.opacity *= data.opacity_filter->opacity();
+ int opacity = static_cast<int>(data.opacity_filter->opacity() * 255.0f);
+ if (opacity <= 0) {
+ // Totally transparent. Ignore the source.
+ return;
+ } else if (opacity >= 255) {
+ // Totally opaque. Render like normal.
+ data.source->Accept(this);
+ return;
+ } else if (common::utils::NodeCanRenderWithOpacity(data.source)) {
+ float old_opacity = draw_state_.opacity;
+ draw_state_.opacity *= data.opacity_filter->opacity();
+ data.source->Accept(this);
+ draw_state_.opacity = old_opacity;
+ return;
+ }
+ }
+
+ // Handle blur-only filter.
+ if (data.blur_filter &&
+ !data.viewport_filter &&
+ !data.opacity_filter &&
+ !data.map_to_mesh_filter) {
+ if (data.blur_filter->blur_sigma() == 0.0f) {
+ // Ignorable blur request. Render normally.
+ data.source->Accept(this);
+ return;
+ }
+ }
+
+ // No filter.
+ if (!data.opacity_filter &&
+ !data.viewport_filter &&
+ !data.blur_filter &&
+ !data.map_to_mesh_filter) {
data.source->Accept(this);
- draw_state_.opacity = old_opacity;
return;
}
@@ -165,6 +215,8 @@
skia::Image* skia_image =
base::polymorphic_downcast<skia::Image*>(data.source.get());
+ bool clamp_texcoords = false;
+ bool is_opaque = skia_image->IsOpaque() && draw_state_.opacity == 1.0f;
// Ensure any required backend processing is done to create the necessary
// GPU resource.
@@ -182,36 +234,53 @@
data.local_transform(0, 2);
texcoord_transform(1, 2) = -texcoord_transform(1, 1) *
data.local_transform(1, 2);
+ if (texcoord_transform(0, 0) < 1.0f || texcoord_transform(1, 1) < 1.0f) {
+ // Edges may interpolate with texels outside the designated region.
+ // Use a fragment shader that clamps the texture coordinates to prevent
+ // that from happening.
+ clamp_texcoords = true;
+ }
} else {
texcoord_transform = data.local_transform.Inverse();
}
// Different shaders are used depending on whether the image has a single
// plane or multiple planes.
+ scoped_ptr<DrawObject> draw;
+ DrawObjectManager::OnscreenType onscreen_type;
+
if (skia_image->GetTypeId() == base::GetTypeId<skia::SinglePlaneImage>()) {
skia::HardwareFrontendImage* hardware_image =
base::polymorphic_downcast<skia::HardwareFrontendImage*>(skia_image);
- if (hardware_image->IsOpaque() && draw_state_.opacity == 1.0f) {
- scoped_ptr<DrawObject> draw(new DrawRectTexture(graphics_state_,
- draw_state_, data.destination_rect,
- hardware_image->GetTextureEGL(), texcoord_transform));
- AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenRectTexture,
- DrawObjectManager::kOffscreenNone);
- } else {
- scoped_ptr<DrawObject> draw(new DrawRectColorTexture(graphics_state_,
- draw_state_, data.destination_rect,
+ if (clamp_texcoords || !is_opaque) {
+ onscreen_type = DrawObjectManager::kOnscreenRectColorTexture;
+ draw.reset(new DrawRectColorTexture(graphics_state_, draw_state_,
+ data.destination_rect,
render_tree::ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
- hardware_image->GetTextureEGL(), texcoord_transform));
- AddTransparentDraw(draw.Pass(),
- DrawObjectManager::kOnscreenRectColorTexture,
- DrawObjectManager::kOffscreenNone, image_node->GetBounds());
+ hardware_image->GetTextureEGL(), texcoord_transform,
+ clamp_texcoords));
+ } else {
+ onscreen_type = DrawObjectManager::kOnscreenRectTexture;
+ draw.reset(new DrawRectTexture(graphics_state_, draw_state_,
+ data.destination_rect, hardware_image->GetTextureEGL(),
+ texcoord_transform));
}
} else if (skia_image->GetTypeId() ==
base::GetTypeId<skia::MultiPlaneImage>()) {
FallbackRasterize(image_node,
DrawObjectManager::kOffscreenSkiaMultiPlaneImage);
+ return;
} else {
NOTREACHED();
+ return;
+ }
+
+ if (is_opaque) {
+ AddOpaqueDraw(draw.Pass(), onscreen_type,
+ DrawObjectManager::kOffscreenNone);
+ } else {
+ AddTransparentDraw(draw.Pass(), onscreen_type,
+ DrawObjectManager::kOffscreenNone, image_node->GetBounds());
}
}
@@ -244,8 +313,9 @@
const scoped_ptr<render_tree::Brush>& brush = data.background_brush;
// Only solid color brushes are supported at this time.
- const bool brush_supported = !brush || brush->GetTypeId() ==
- base::GetTypeId<render_tree::SolidColorBrush>();
+ const bool brush_supported = !brush ||
+ brush->GetTypeId() == base::GetTypeId<render_tree::SolidColorBrush>() ||
+ brush->GetTypeId() == base::GetTypeId<render_tree::LinearGradientBrush>();
// Borders are not supported natively by this rasterizer at this time. The
// difficulty lies in getting anti-aliased borders and minimizing state
@@ -267,21 +337,35 @@
// Handle drawing the content.
if (brush) {
- const render_tree::SolidColorBrush* solid_brush =
- base::polymorphic_downcast<const render_tree::SolidColorBrush*>
- (brush.get());
- const render_tree::ColorRGBA& brush_color(solid_brush->color());
- render_tree::ColorRGBA content_color(
- brush_color.r() * brush_color.a(),
- brush_color.g() * brush_color.a(),
- brush_color.b() * brush_color.a(),
- brush_color.a());
- scoped_ptr<DrawObject> draw(new DrawPolyColor(graphics_state_,
- draw_state_, content_rect, content_color));
- if (draw_state_.opacity * content_color.a() == 1.0f) {
- AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
- DrawObjectManager::kOffscreenNone);
+ base::TypeId brush_type = brush->GetTypeId();
+ if (brush_type == base::GetTypeId<render_tree::SolidColorBrush>()) {
+ const render_tree::SolidColorBrush* solid_brush =
+ base::polymorphic_downcast<const render_tree::SolidColorBrush*>
+ (brush.get());
+ const render_tree::ColorRGBA& brush_color(solid_brush->color());
+ render_tree::ColorRGBA content_color(
+ brush_color.r() * brush_color.a(),
+ brush_color.g() * brush_color.a(),
+ brush_color.b() * brush_color.a(),
+ brush_color.a());
+ scoped_ptr<DrawObject> draw(new DrawPolyColor(graphics_state_,
+ draw_state_, content_rect, content_color));
+ if (draw_state_.opacity * content_color.a() == 1.0f) {
+ AddOpaqueDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
+ DrawObjectManager::kOffscreenNone);
+ } else {
+ AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
+ DrawObjectManager::kOffscreenNone, rect_node->GetBounds());
+ }
} else {
+ const render_tree::LinearGradientBrush* linear_brush =
+ base::polymorphic_downcast<const render_tree::LinearGradientBrush*>
+ (brush.get());
+ scoped_ptr<DrawObject> draw(new DrawRectLinearGradient(graphics_state_,
+ draw_state_, content_rect, *linear_brush));
+ // The draw may use transparent colors or a depth stencil (but only
+ // the inclusive one, so only one depth value is needed), so make it
+ // a transparent draw.
AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenPolyColor,
DrawObjectManager::kOffscreenNone, rect_node->GetBounds());
}
@@ -290,11 +374,95 @@
}
void RenderTreeNodeVisitor::Visit(render_tree::RectShadowNode* shadow_node) {
- if (!IsVisible(shadow_node->GetBounds())) {
+ math::RectF node_bounds(shadow_node->GetBounds());
+ if (!IsVisible(node_bounds)) {
return;
}
- FallbackRasterize(shadow_node, DrawObjectManager::kOffscreenSkiaShadow);
+ const render_tree::RectShadowNode::Builder& data = shadow_node->data();
+ if (data.rounded_corners) {
+ FallbackRasterize(shadow_node, DrawObjectManager::kOffscreenSkiaShadow);
+ return;
+ }
+
+ scoped_ptr<DrawObject> draw;
+ render_tree::ColorRGBA shadow_color(
+ data.shadow.color.r() * data.shadow.color.a(),
+ data.shadow.color.g() * data.shadow.color.a(),
+ data.shadow.color.b() * data.shadow.color.a(),
+ data.shadow.color.a());
+ DrawObjectManager::OnscreenType onscreen_type =
+ DrawObjectManager::kOnscreenRectShadow;
+
+ math::RectF spread_rect(data.rect);
+ spread_rect.Offset(data.shadow.offset);
+ if (data.inset) {
+ // data.rect is outermost.
+ // spread_rect is in the middle.
+ // blur_rect is innermost.
+ spread_rect.Inset(data.spread, data.spread);
+ spread_rect.Intersect(data.rect);
+ if (spread_rect.IsEmpty()) {
+ // Spread covers the whole data.rect.
+ spread_rect.set_origin(data.rect.CenterPoint());
+ spread_rect.set_size(math::SizeF());
+ }
+ if (!spread_rect.IsEmpty() && data.shadow.blur_sigma > 0.0f) {
+ math::RectF blur_rect(spread_rect);
+ math::Vector2dF blur_extent(data.shadow.BlurExtent());
+ blur_rect.Inset(blur_extent.x(), blur_extent.y());
+ blur_rect.Intersect(spread_rect);
+ if (blur_rect.IsEmpty()) {
+ // Blur covers all of spread.
+ blur_rect.set_origin(spread_rect.CenterPoint());
+ blur_rect.set_size(math::SizeF());
+ }
+ draw.reset(new DrawRectShadowBlur(graphics_state_, draw_state_,
+ blur_rect, data.rect, spread_rect, shadow_color, math::RectF(),
+ data.shadow.blur_sigma, data.inset));
+ onscreen_type = DrawObjectManager::kOnscreenRectShadowBlur;
+ } else {
+ draw.reset(new DrawRectShadowSpread(graphics_state_, draw_state_,
+ spread_rect, data.rect, shadow_color, data.rect, math::RectF()));
+ }
+ } else {
+ // blur_rect is outermost.
+ // spread_rect is in the middle (barring negative |spread| values).
+ // data.rect is innermost (though it may not overlap due to offset).
+ spread_rect.Outset(data.spread, data.spread);
+ if (spread_rect.IsEmpty()) {
+ // Negative spread shenanigans! Nothing to draw.
+ return;
+ }
+ math::RectF blur_rect(spread_rect);
+ if (data.shadow.blur_sigma > 0.0f) {
+ math::Vector2dF blur_extent(data.shadow.BlurExtent());
+ blur_rect.Outset(blur_extent.x(), blur_extent.y());
+ draw.reset(new DrawRectShadowBlur(graphics_state_, draw_state_,
+ data.rect, blur_rect, spread_rect, shadow_color, data.rect,
+ data.shadow.blur_sigma, data.inset));
+ onscreen_type = DrawObjectManager::kOnscreenRectShadowBlur;
+ } else {
+ draw.reset(new DrawRectShadowSpread(graphics_state_, draw_state_,
+ data.rect, spread_rect, shadow_color, spread_rect, data.rect));
+ }
+ node_bounds.Union(blur_rect);
+ }
+
+ // Include or exclude scissor will touch these pixels.
+ node_bounds.Union(data.rect);
+
+ // Since the depth buffer is polluted to create a stencil for pixels to be
+ // modified by the shadow, this draw must occur during the transparency
+ // pass. During this pass, all subsequent draws are guaranteed to be closer
+ // (i.e. pass the depth test) than pixels modified by previous transparency
+ // draws.
+ AddTransparentDraw(draw.Pass(), onscreen_type,
+ DrawObjectManager::kOffscreenNone, node_bounds);
+
+ // Since the box shadow draw objects use the depth stencil object, two depth
+ // values were used. So skip an additional depth value.
+ draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
}
void RenderTreeNodeVisitor::Visit(render_tree::TextNode* text_node) {
@@ -305,42 +473,113 @@
FallbackRasterize(text_node, DrawObjectManager::kOffscreenSkiaText);
}
-void RenderTreeNodeVisitor::FallbackRasterize(render_tree::Node* node,
+void RenderTreeNodeVisitor::FallbackRasterize(
+ scoped_refptr<render_tree::Node> node,
DrawObjectManager::OffscreenType offscreen_type) {
DCHECK_NE(offscreen_type, DrawObjectManager::kOffscreenNone);
- // Use fallback_rasterize_ to render to an offscreen target. Add a small
- // buffer to allow anti-aliased edges (e.g. rendered text).
- const int kBorderWidth = 1;
math::RectF node_bounds(node->GetBounds());
- math::RectF viewport(kBorderWidth, kBorderWidth,
- node_bounds.right() + 2 * kBorderWidth,
- node_bounds.bottom() + 2 * kBorderWidth);
+ math::RectF mapped_bounds(draw_state_.transform.MapRect(node_bounds));
+ if (mapped_bounds.IsEmpty()) {
+ return;
+ }
- // Adjust the draw rect to accomodate the extra border and align texels with
- // pixels. Perform the smallest fractional pixel shift for alignment.
- float trans_x = std::floor(draw_state_.transform(0, 2) + 0.5f) -
- draw_state_.transform(0, 2);
- float trans_y = std::floor(draw_state_.transform(1, 2) + 0.5f) -
- draw_state_.transform(1, 2);
- math::RectF draw_rect(-kBorderWidth + trans_x, -kBorderWidth + trans_y,
- viewport.width(), viewport.height());
+ // Use the fallback rasterizer to render the tree. In order to preserve
+ // sharpness, let the fallback rasterizer handle the current transform.
+ math::Matrix3F old_transform = draw_state_.transform;
+ draw_state_.transform = math::Matrix3F::Identity();
+ // Request a slightly larger render target than the calculated bounds. The
+ // fallback rasterizer may use an extra pixel along the edge of anti-aliased
+ // objects.
+ const float kBorderWidth = 1.0f;
+
+ // The render target cache keys off the render_tree Node and target size.
+ // Since the size is rounded, it is possible to be a fraction of a pixel
+ // off. However, this in turn allows for a cache hit for "close-enough"
+ // renderings. To minimize offset errors, use the nearest full pixel offset.
+ const float kFractionPad = 1.0f;
+ float offset_x = std::floor(mapped_bounds.x() + 0.5f);
+ float offset_y = std::floor(mapped_bounds.y() + 0.5f);
+ math::PointF content_offset(
+ // Shift contents towards the origin of the render target.
+ kBorderWidth + kFractionPad - offset_x,
+ kBorderWidth + kFractionPad - offset_y);
+ math::SizeF content_size(
+ std::ceil(mapped_bounds.width() + 2.0f * kBorderWidth + kFractionPad),
+ std::ceil(mapped_bounds.height() + 2.0f * kBorderWidth + kFractionPad));
+ OffscreenTargetManager::TargetInfo target_info;
+ bool is_cached = offscreen_target_manager_->GetCachedOffscreenTarget(node,
+ content_size, &target_info);
+ if (!is_cached) {
+ offscreen_target_manager_->AllocateOffscreenTarget(node,
+ content_size, &target_info);
+ }
+
+ // If the render target is the scratch surface, then just render what fits
+ // onscreen for better performance.
+ if (target_info.is_scratch_surface) {
+ mapped_bounds.Outset(kBorderWidth, kBorderWidth);
+ mapped_bounds.Intersect(draw_state_.scissor);
+ if (mapped_bounds.IsEmpty()) {
+ return;
+ }
+ float left = std::floor(mapped_bounds.x());
+ float right = std::ceil(mapped_bounds.right());
+ float top = std::floor(mapped_bounds.y());
+ float bottom = std::ceil(mapped_bounds.bottom());
+ content_offset.SetPoint(-left, -top);
+ content_size.SetSize(right - left, bottom - top);
+ }
+
+ // The returned target may be larger than actually needed. Clamp to just the
+ // needed size.
+ DCHECK_LE(content_size.width(), target_info.region.width());
+ DCHECK_LE(content_size.height(), target_info.region.height());
+ target_info.region.set_size(content_size);
+ math::RectF draw_rect(-content_offset.x(), -content_offset.y(),
+ content_size.width(), content_size.height());
+
+ // Setup draw callbacks as needed.
+ base::Closure draw_offscreen;
+ base::Closure draw_onscreen;
+ if (!is_cached) {
+ // Pre-translate the contents so it starts near the origin.
+ math::Matrix3F content_transform(old_transform);
+ content_transform(0, 2) += content_offset.x();
+ content_transform(1, 2) += content_offset.y();
+
+ if (target_info.is_scratch_surface) {
+ draw_onscreen = base::Bind(*fallback_rasterize_,
+ scoped_refptr<render_tree::Node>(node), content_transform,
+ target_info);
+ } else {
+ draw_offscreen = base::Bind(*fallback_rasterize_,
+ scoped_refptr<render_tree::Node>(node), content_transform,
+ target_info);
+ }
+ }
+
+ // Create the appropriate draw object to call the draw callback, then render
+ // its results onscreen.
+ backend::TextureEGL* texture = target_info.framebuffer->GetColorTexture();
+ math::Matrix3F texcoord_transform = GetTexcoordTransform(target_info);
if (draw_state_.opacity == 1.0f) {
scoped_ptr<DrawObject> draw(new DrawRectTexture(graphics_state_,
- draw_state_, draw_rect, base::Bind(*fallback_rasterize_,
- scoped_refptr<render_tree::Node>(node), viewport)));
+ draw_state_, draw_rect, texture, texcoord_transform,
+ draw_offscreen, draw_onscreen));
AddTransparentDraw(draw.Pass(), DrawObjectManager::kOnscreenRectTexture,
offscreen_type, draw_rect);
} else {
scoped_ptr<DrawObject> draw(new DrawRectColorTexture(graphics_state_,
draw_state_, draw_rect, render_tree::ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
- base::Bind(*fallback_rasterize_,
- scoped_refptr<render_tree::Node>(node), viewport)));
+ texture, texcoord_transform, draw_offscreen, draw_onscreen));
AddTransparentDraw(draw.Pass(),
DrawObjectManager::kOnscreenRectColorTexture, offscreen_type,
draw_rect);
}
+
+ draw_state_.transform = old_transform;
}
bool RenderTreeNodeVisitor::IsVisible(const math::RectF& bounds) {
@@ -354,7 +593,7 @@
DrawObjectManager::OffscreenType offscreen_type) {
draw_object_manager_->AddOpaqueDraw(object.Pass(), onscreen_type,
offscreen_type);
- draw_state_.depth = graphics_state_->NextClosestDepth(draw_state_.depth);
+ draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
}
void RenderTreeNodeVisitor::AddTransparentDraw(scoped_ptr<DrawObject> object,
@@ -363,7 +602,7 @@
const math::RectF& local_bounds) {
draw_object_manager_->AddTransparentDraw(object.Pass(), onscreen_type,
offscreen_type, draw_state_.transform.MapRect(local_bounds));
- draw_state_.depth = graphics_state_->NextClosestDepth(draw_state_.depth);
+ draw_state_.depth = GraphicsState::NextClosestDepth(draw_state_.depth);
}
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
index 704b4bb..c4e70d3 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.h
@@ -36,6 +36,7 @@
#include "cobalt/renderer/rasterizer/egl/draw_object.h"
#include "cobalt/renderer/rasterizer/egl/draw_object_manager.h"
#include "cobalt/renderer/rasterizer/egl/graphics_state.h"
+#include "cobalt/renderer/rasterizer/egl/offscreen_target_manager.h"
namespace cobalt {
namespace renderer {
@@ -48,13 +49,13 @@
public:
typedef base::Callback<void(
const scoped_refptr<render_tree::Node>& render_tree,
- const math::RectF& viewport,
- const backend::TextureEGL** out_texture,
- math::Matrix3F* out_texcoord_transform)>
+ const math::Matrix3F& transform,
+ const OffscreenTargetManager::TargetInfo& target)>
FallbackRasterizeFunction;
RenderTreeNodeVisitor(GraphicsState* graphics_state,
DrawObjectManager* draw_object_manager,
+ OffscreenTargetManager* offscreen_target_manager,
const FallbackRasterizeFunction* fallback_rasterize);
void Visit(render_tree::animations::AnimateNode* /* animate */) OVERRIDE {
@@ -71,7 +72,7 @@
void Visit(render_tree::TextNode* text_node) OVERRIDE;
private:
- void FallbackRasterize(render_tree::Node* node,
+ void FallbackRasterize(scoped_refptr<render_tree::Node> node,
DrawObjectManager::OffscreenType offscreen_type);
bool IsVisible(const math::RectF& bounds);
void AddOpaqueDraw(scoped_ptr<DrawObject> object,
@@ -84,6 +85,7 @@
GraphicsState* graphics_state_;
DrawObjectManager* draw_object_manager_;
+ OffscreenTargetManager* offscreen_target_manager_;
const FallbackRasterizeFunction* fallback_rasterize_;
DrawObject::BaseState draw_state_;
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
new file mode 100644
index 0000000..a7831c4
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
@@ -0,0 +1,46 @@
+precision mediump float;
+uniform vec2 u_blur_radius;
+uniform vec2 u_scale_add;
+varying vec2 v_blur_position; // Relative to the blur center.
+varying vec4 v_color;
+void main() {
+ // Distance from the blur radius.
+ // Both v_blur_position and u_blur_radius are expressed in terms of the
+ // blur sigma.
+ vec2 pos = abs(v_blur_position) - u_blur_radius;
+ vec2 pos2 = pos * pos;
+ vec2 pos3 = pos2 * pos;
+ vec4 posx = vec4(1.0, pos.x, pos2.x, pos3.x);
+ vec4 posy = vec4(1.0, pos.y, pos2.y, pos3.y);
+
+ // Approximation of the gaussian integral from [x, +inf).
+ // http://stereopsis.com/shadowrect/
+ const vec4 klower = vec4(0.4375, -1.125, -0.75, -0.1666666);
+ const vec4 kmiddle = vec4(0.5, -0.75, 0.0, 0.3333333);
+ const vec4 kupper = vec4(0.5625, -1.125, 0.75, -0.1666666);
+
+ float gaussx = 0.0;
+ if (pos.x < -1.5) {
+ gaussx = 1.0;
+ } else if (pos.x < -0.5) {
+ gaussx = dot(posx, klower);
+ } else if (pos.x < 0.5) {
+ gaussx = dot(posx, kmiddle);
+ } else if (pos.x < 1.5) {
+ gaussx = dot(posx, kupper);
+ }
+
+ float gaussy = 0.0;
+ if (pos.y < -1.5) {
+ gaussy = 1.0;
+ } else if (pos.y < -0.5) {
+ gaussy = dot(posy, klower);
+ } else if (pos.y < 0.5) {
+ gaussy = dot(posy, kmiddle);
+ } else if (pos.y < 1.5) {
+ gaussy = dot(posy, kupper);
+ }
+
+ float alpha_scale = gaussx * gaussy * u_scale_add.x + u_scale_add.y;
+ gl_FragColor = v_color * alpha_scale;
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl
index 7a49bb4..47ec196 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl
@@ -1,7 +1,9 @@
precision mediump float;
+uniform vec4 u_texcoord_clamp;
uniform sampler2D u_texture;
varying vec4 v_color;
varying vec2 v_texcoord;
void main() {
- gl_FragColor = v_color * texture2D(u_texture, v_texcoord);
+ gl_FragColor = v_color * texture2D(u_texture,
+ clamp(v_texcoord, u_texcoord_clamp.xy, u_texcoord_clamp.zw));
}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
index a348ff6..140b6b5 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
@@ -21,9 +21,11 @@
'shader_sources': [
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_texcoord.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_texcoord.glsl',
],
},
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl
new file mode 100644
index 0000000..0b543d1
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_color_blur.glsl
@@ -0,0 +1,14 @@
+uniform vec4 u_clip_adjustment;
+uniform mat3 u_view_matrix;
+attribute vec3 a_position;
+attribute vec4 a_color;
+attribute vec2 a_blur_position;
+varying vec4 v_color;
+varying vec2 v_blur_position;
+void main() {
+ vec3 pos2d = u_view_matrix * vec3(a_position.xy, 1);
+ gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
+ u_clip_adjustment.zw, a_position.z, pos2d.z);
+ v_color = a_color;
+ v_blur_position = a_blur_position;
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
index a8811c8..319f6ed 100644
--- a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
@@ -90,9 +90,7 @@
context_, render_target_egl);
context_->Blit(output_texture->gl_handle(), 0, 0, width, height);
- frame_rate_throttler_.EndInterval();
context_->SwapBuffers(render_target_egl);
- frame_rate_throttler_.BeginInterval();
}
render_tree::ResourceProvider* SoftwareRasterizer::GetResourceProvider() {
diff --git a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
index 12387ad..ce906c0 100644
--- a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
@@ -19,7 +19,6 @@
#include "cobalt/render_tree/resource_provider.h"
#include "cobalt/renderer/backend/graphics_context.h"
#include "cobalt/renderer/backend/render_target.h"
-#include "cobalt/renderer/frame_rate_throttler.h"
#include "cobalt/renderer/rasterizer/rasterizer.h"
#include "cobalt/renderer/rasterizer/skia/software_rasterizer.h"
@@ -50,7 +49,6 @@
private:
backend::GraphicsContextEGL* context_;
skia::SoftwareRasterizer skia_rasterizer_;
- FrameRateThrottler frame_rate_throttler_;
};
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/lib/README.md b/src/cobalt/renderer/rasterizer/lib/README.md
new file mode 100644
index 0000000..55af499
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/README.md
@@ -0,0 +1,9 @@
+This directory provides an API for clients that build Cobalt into a lib using
+the 'cobalt_enable_lib' flag.
+
+It is highly experimental at this point and is expected to change significantly
+across releases.
+
+As a general convention, functions whose symbols are exported are defined in an
+'exported' subdirectory and functions that are required to be implemented by
+clients are defined in a 'imported' subdirectory.
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
new file mode 100644
index 0000000..8ac80c8
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
@@ -0,0 +1,145 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/lib/external_rasterizer.h"
+
+#include <cmath>
+#include <vector>
+
+#include "base/threading/thread_checker.h"
+#include "cobalt/renderer/backend/egl/graphics_context.h"
+#include "cobalt/renderer/backend/egl/render_target.h"
+#include "cobalt/renderer/rasterizer/lib/imported/graphics.h"
+#include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
+#include "starboard/shared/gles/gl_call.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace lib {
+
+class ExternalRasterizer::Impl {
+ public:
+ Impl(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
+ int skia_cache_size_in_bytes,
+ int scratch_surface_cache_size_in_bytes,
+ int surface_cache_size_in_bytes);
+ ~Impl();
+
+ void Submit(const scoped_refptr<render_tree::Node>& render_tree,
+ const scoped_refptr<backend::RenderTarget>& render_target);
+
+ render_tree::ResourceProvider* GetResourceProvider();
+
+ private:
+ base::ThreadChecker thread_checker_;
+
+ backend::GraphicsContextEGL* graphics_context_;
+
+ skia::HardwareRasterizer hardware_rasterizer_;
+ skia::HardwareRasterizer::Options options_;
+
+ // The main offscreen render target to use when rendering UI or rectangular
+ // video.
+ scoped_refptr<backend::RenderTarget> main_offscreen_render_target_;
+ scoped_ptr<backend::TextureEGL> main_texture_;
+ // TODO: Add in a RenderTarget for the video texture.
+};
+
+ExternalRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
+ int skia_cache_size_in_bytes,
+ int scratch_surface_cache_size_in_bytes,
+ int surface_cache_size_in_bytes)
+ : graphics_context_(
+ base::polymorphic_downcast<backend::GraphicsContextEGL*>(
+ graphics_context)),
+ hardware_rasterizer_(graphics_context, skia_atlas_width,
+ skia_atlas_height, skia_cache_size_in_bytes,
+ scratch_surface_cache_size_in_bytes,
+ surface_cache_size_in_bytes) {
+ options_.flags = skia::HardwareRasterizer::kSubmitFlags_Clear;
+ graphics_context_->MakeCurrent();
+
+ // TODO: Import the correct size for this and any other textures from the lib
+ // client and re-generate the size as appropriate.
+ main_offscreen_render_target_ =
+ graphics_context_->CreateOffscreenRenderTarget(math::Size(1920, 1080));
+ main_texture_.reset(new backend::TextureEGL(
+ graphics_context_,
+ make_scoped_refptr(base::polymorphic_downcast<backend::RenderTargetEGL*>(
+ main_offscreen_render_target_.get()))));
+
+ CbLibOnGraphicsContextCreated();
+}
+
+ExternalRasterizer::Impl::~Impl() { graphics_context_->MakeCurrent(); }
+
+void ExternalRasterizer::Impl::Submit(
+ const scoped_refptr<render_tree::Node>& render_tree,
+ const scoped_refptr<backend::RenderTarget>& render_target) {
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(0);
+ backend::RenderTargetEGL* render_target_egl =
+ base::polymorphic_downcast<backend::RenderTargetEGL*>(
+ render_target.get());
+ graphics_context_->MakeCurrentWithSurface(render_target_egl);
+
+ backend::RenderTargetEGL* main_texture_render_target_egl =
+ base::polymorphic_downcast<backend::RenderTargetEGL*>(
+ main_offscreen_render_target_.get());
+ hardware_rasterizer_.Submit(render_tree, main_offscreen_render_target_,
+ options_);
+
+ const intptr_t texture_handle = main_texture_->GetPlatformHandle();
+ // TODO: Provide mesh data to clients for map-to-mesh playbacks and a separate
+ // video texture handle.
+ // TODO: Allow clients to specify arbitrary subtrees to render into different
+ // textures?
+ CbLibRenderFrame(texture_handle);
+
+ graphics_context_->SwapBuffers(render_target_egl);
+}
+
+render_tree::ResourceProvider* ExternalRasterizer::Impl::GetResourceProvider() {
+ return hardware_rasterizer_.GetResourceProvider();
+}
+
+ExternalRasterizer::ExternalRasterizer(
+ backend::GraphicsContext* graphics_context, int skia_atlas_width,
+ int skia_atlas_height, int skia_cache_size_in_bytes,
+ int scratch_surface_cache_size_in_bytes, int surface_cache_size_in_bytes)
+ : impl_(new Impl(graphics_context, skia_atlas_width, skia_atlas_height,
+ skia_cache_size_in_bytes,
+ scratch_surface_cache_size_in_bytes,
+ surface_cache_size_in_bytes)) {}
+
+ExternalRasterizer::~ExternalRasterizer() {}
+
+void ExternalRasterizer::Submit(
+ const scoped_refptr<render_tree::Node>& render_tree,
+ const scoped_refptr<backend::RenderTarget>& render_target,
+ const Options& options) {
+ impl_->Submit(render_tree, render_target);
+}
+
+render_tree::ResourceProvider* ExternalRasterizer::GetResourceProvider() {
+ return impl_->GetResourceProvider();
+}
+
+} // namespace lib
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
new file mode 100644
index 0000000..49ffc32
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
@@ -0,0 +1,61 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_LIB_EXTERNAL_RASTERIZER_H_
+#define COBALT_RENDERER_RASTERIZER_LIB_EXTERNAL_RASTERIZER_H_
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/render_tree/resource_provider.h"
+#include "cobalt/renderer/backend/graphics_context.h"
+#include "cobalt/renderer/backend/render_target.h"
+#include "cobalt/renderer/rasterizer/rasterizer.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace lib {
+
+// The ExternalRasterizer is the Rasterizer used by Cobalt client libs to enable
+// them to rasterize into Cobalt's system window.
+// The primary responsibilities of the ExternalRasterizer are to provide clients
+// with a window and GL context as well as rasterizing RenderTrees into
+// textures that are provided to clients to render into their own GL scene.
+class ExternalRasterizer : public Rasterizer {
+ public:
+ ExternalRasterizer(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
+ int skia_cache_size_in_bytes,
+ int scratch_surface_cache_size_in_bytes,
+ int surface_cache_size_in_bytes);
+ virtual ~ExternalRasterizer();
+
+ void Submit(const scoped_refptr<render_tree::Node>& render_tree,
+ const scoped_refptr<backend::RenderTarget>& render_target,
+ const Options& options) OVERRIDE;
+
+ // Note, this will simply pipe through the HardwareRasterizer's
+ // ResourceProvider.
+ render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
+
+ private:
+ class Impl;
+ scoped_ptr<Impl> impl_;
+};
+
+} // namespace lib
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_LIB_EXTERNAL_RASTERIZER_H_
diff --git a/src/cobalt/renderer/rasterizer/lib/imported/graphics.h b/src/cobalt/renderer/rasterizer/lib/imported/graphics.h
new file mode 100644
index 0000000..125f85f
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/imported/graphics.h
@@ -0,0 +1,40 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// All imported functions defined below MUST be implemented by client
+// applications.
+
+#ifndef COBALT_RENDERER_RASTERIZER_LIB_IMPORTED_GRAPHICS_H_
+#define COBALT_RENDERER_RASTERIZER_LIB_IMPORTED_GRAPHICS_H_
+
+// TODO: Use starboard/types.h instead.
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Invoked from the rasterization thread after the GL context has been created.
+void CbLibOnGraphicsContextCreated();
+
+// Invoked as often as the platform can swap buffers from the rasterization
+// thread. |render_tree_texture_handle| corresponds to a GLint texture ID for
+// the current RenderTree.
+void CbLibRenderFrame(intptr_t render_tree_texture_handle);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // COBALT_RENDERER_RASTERIZER_LIB_IMPORTED_GRAPHICS_H_
diff --git a/src/cobalt/renderer/rasterizer/lib/lib.gyp b/src/cobalt/renderer/rasterizer/lib/lib.gyp
new file mode 100644
index 0000000..3ff61a8
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/lib.gyp
@@ -0,0 +1,38 @@
+# 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': 'external_rasterizer',
+ 'type': 'static_library',
+ 'includes': [
+ '../../renderer_parameters_setup.gypi',
+ ],
+ 'sources': [
+ 'external_rasterizer.h',
+ 'external_rasterizer.cc',
+ 'renderer_module_default_options_lib.cc'
+ ],
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
+ '<(DEPTH)/cobalt/renderer/rasterizer/skia/common.gyp:common',
+ '<(DEPTH)/cobalt/renderer/rasterizer/skia/rasterizer.gyp:hardware_rasterizer',
+ '<(DEPTH)/cobalt/renderer/rasterizer/skia/skia/skia.gyp:skia',
+ '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
+ ],
+ }
+ ],
+}
diff --git a/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc b/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc
new file mode 100644
index 0000000..43a1a0d
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc
@@ -0,0 +1,54 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/renderer_module.h"
+#include "cobalt/renderer/rasterizer/lib/external_rasterizer.h"
+
+namespace cobalt {
+namespace renderer {
+
+namespace {
+scoped_ptr<rasterizer::Rasterizer> CreateRasterizer(
+ backend::GraphicsContext* graphics_context,
+ const RendererModule::Options& options) {
+ return scoped_ptr<rasterizer::Rasterizer>(
+ new rasterizer::lib::ExternalRasterizer(
+ graphics_context,
+ options.skia_texture_atlas_dimensions.width(),
+ options.skia_texture_atlas_dimensions.height(),
+ options.skia_cache_size_in_bytes,
+ options.scratch_surface_cache_size_in_bytes,
+ options.surface_cache_size_in_bytes));
+}
+} // namespace
+
+void RendererModule::Options::SetPerPlatformDefaultOptions() {
+ // Set default options from the current build's configuration.
+ surface_cache_size_in_bytes = COBALT_SURFACE_CACHE_SIZE_IN_BYTES;
+
+ // Default to 4MB, but this may be modified externally.
+ skia_cache_size_in_bytes = 4 * 1024 * 1024;
+
+ scratch_surface_cache_size_in_bytes =
+ COBALT_SCRATCH_SURFACE_CACHE_SIZE_IN_BYTES;
+
+ // Ensure the scene is re-rasterized even if the render tree is unchanged so
+ // that headset look changes are properly rendered.
+ submit_even_if_render_tree_is_unchanged = true;
+
+ create_rasterizer_function = base::Bind(&CreateRasterizer);
+}
+
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index ba69cde..5700075 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -2846,6 +2846,14 @@
TestTree(new CompositionNode(builder.Pass()));
}
+TEST_F(PixelTest, DrawOffscreenImage) {
+ scoped_refptr<Node> root = CreateCascadedRectsOfDifferentColors(
+ ScaleSize(output_surface_size(), 0.5f, 0.5f));
+ scoped_refptr<Image> offscreen_image =
+ GetResourceProvider()->DrawOffscreenImage(root);
+ TestTree(new ImageNode(offscreen_image));
+}
+
} // namespace rasterizer
} // namespace renderer
} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/skia/font.cc b/src/cobalt/renderer/rasterizer/skia/font.cc
index d90bc42..bd8d0af 100644
--- a/src/cobalt/renderer/rasterizer/skia/font.cc
+++ b/src/cobalt/renderer/rasterizer/skia/font.cc
@@ -52,7 +52,9 @@
glyph_bounds_thread_checker_.DetachFromThread();
}
-SkTypeface* Font::GetSkTypeface() const { return typeface_->GetSkTypeface(); }
+SkTypeface_Cobalt* Font::GetSkTypeface() const {
+ return typeface_->GetSkTypeface();
+}
render_tree::TypefaceId Font::GetTypefaceId() const {
return typeface_->GetId();
diff --git a/src/cobalt/renderer/rasterizer/skia/font.h b/src/cobalt/renderer/rasterizer/skia/font.h
index c262783..410c3a2 100644
--- a/src/cobalt/renderer/rasterizer/skia/font.h
+++ b/src/cobalt/renderer/rasterizer/skia/font.h
@@ -25,7 +25,6 @@
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkTypeface.h"
namespace cobalt {
namespace renderer {
@@ -41,7 +40,11 @@
public:
Font(SkiaTypeface* typeface, SkScalar size);
- SkTypeface* GetSkTypeface() const;
+ // Returns the contained SkTypeface_Cobalt object, which has its reference
+ // count incremented.
+ // NOTE: The caller is responsible for decrementing the reference count after
+ // finishing with the object.
+ SkTypeface_Cobalt* GetSkTypeface() const;
// Returns the pixel size described by this font.
SkScalar size() const { return size_; }
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_image.cc b/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
index 1d118f5..432ae5a 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_image.cc
@@ -16,6 +16,7 @@
#include "base/bind.h"
#include "base/debug/trace_event.h"
+#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
#include "cobalt/renderer/backend/egl/texture.h"
#include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
#include "cobalt/renderer/rasterizer/skia/gl_format_conversions.h"
@@ -223,6 +224,23 @@
base::Unretained(this), gr_context);
}
+HardwareFrontendImage::HardwareFrontendImage(
+ const scoped_refptr<render_tree::Node>& root,
+ const SubmitOffscreenCallback& submit_offscreen_callback,
+ backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context,
+ MessageLoop* rasterizer_message_loop)
+ : is_opaque_(false),
+ size_(static_cast<int>(root->GetBounds().right()),
+ static_cast<int>(root->GetBounds().bottom())),
+ rasterizer_message_loop_(rasterizer_message_loop) {
+ TRACE_EVENT0("cobalt::renderer",
+ "HardwareFrontendImage::HardwareFrontendImage()");
+ initialize_backend_image_ =
+ base::Bind(&HardwareFrontendImage::InitializeBackendImageFromRenderTree,
+ base::Unretained(this), root, submit_offscreen_callback,
+ cobalt_context, gr_context);
+}
+
HardwareFrontendImage::~HardwareFrontendImage() {
TRACE_EVENT0("cobalt::renderer",
"HardwareFrontendImage::~HardwareFrontendImage()");
@@ -294,6 +312,24 @@
backend_image_->CommonInitialize(gr_context);
}
+void HardwareFrontendImage::InitializeBackendImageFromRenderTree(
+ const scoped_refptr<render_tree::Node>& root,
+ const SubmitOffscreenCallback& submit_offscreen_callback,
+ backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context) {
+ DCHECK_EQ(rasterizer_message_loop_, MessageLoop::current());
+
+ scoped_refptr<backend::FramebufferRenderTargetEGL> render_target(
+ new backend::FramebufferRenderTargetEGL(cobalt_context, size_));
+
+ submit_offscreen_callback.Run(root, render_target);
+
+ scoped_ptr<backend::TextureEGL> texture(
+ new backend::TextureEGL(cobalt_context, render_target));
+
+ backend_image_.reset(new HardwareBackendImage(texture.Pass()));
+ backend_image_->CommonInitialize(gr_context);
+}
+
HardwareMultiPlaneImage::HardwareMultiPlaneImage(
scoped_ptr<HardwareRawImageMemory> raw_image_memory,
const render_tree::MultiPlaneImageDataDescriptor& descriptor,
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_image.h b/src/cobalt/renderer/rasterizer/skia/hardware_image.h
index 5865c94..e6d71f2 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_image.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_image.h
@@ -31,6 +31,10 @@
namespace rasterizer {
namespace skia {
+typedef base::Callback<void(const scoped_refptr<render_tree::Node>& render_tree,
+ const scoped_refptr<backend::RenderTarget>&
+ render_target)> SubmitOffscreenCallback;
+
// Wraps a Cobalt backend::TextureEGL with a Skia GrTexture, and returns the
// Skia ref-counted GrTexture object (that takes ownership of the cobalt
// texture).
@@ -93,6 +97,11 @@
GrContext* gr_context,
scoped_ptr<math::Rect> content_region,
MessageLoop* rasterizer_message_loop);
+ HardwareFrontendImage(
+ const scoped_refptr<render_tree::Node>& root,
+ const SubmitOffscreenCallback& submit_offscreen_callback,
+ backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context,
+ MessageLoop* rasterizer_message_loop);
const math::Size& GetSize() const OVERRIDE { return size_; }
@@ -130,6 +139,10 @@
intptr_t offset, const render_tree::ImageDataDescriptor& descriptor,
backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context);
void InitializeBackendImageFromTexture(GrContext* gr_context);
+ void InitializeBackendImageFromRenderTree(
+ const scoped_refptr<render_tree::Node>& root,
+ const SubmitOffscreenCallback& submit_offscreen_callback,
+ 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.
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index d3c5415..e569def 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -22,7 +22,6 @@
#include "cobalt/renderer/backend/egl/graphics_system.h"
#include "cobalt/renderer/backend/egl/texture.h"
#include "cobalt/renderer/backend/egl/utils.h"
-#include "cobalt/renderer/frame_rate_throttler.h"
#include "cobalt/renderer/rasterizer/common/surface_cache.h"
#include "cobalt/renderer/rasterizer/egl/textured_mesh_renderer.h"
#include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
@@ -55,7 +54,8 @@
class HardwareRasterizer::Impl {
public:
- Impl(backend::GraphicsContext* graphics_context, int skia_cache_size_in_bytes,
+ Impl(backend::GraphicsContext* graphics_context, int skia_atlas_width,
+ int skia_atlas_height, int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int surface_cache_size_in_bytes);
~Impl();
@@ -69,6 +69,10 @@
void SubmitOffscreen(const scoped_refptr<render_tree::Node>& render_tree,
SkCanvas* canvas);
+ void SubmitOffscreenToRenderTarget(
+ const scoped_refptr<render_tree::Node>& render_tree,
+ const scoped_refptr<backend::RenderTarget>& render_target);
+
render_tree::ResourceProvider* GetResourceProvider();
GrContext* GetGrContext();
@@ -98,6 +102,12 @@
scoped_ptr<RenderTreeNodeVisitor::ScratchSurface> CreateScratchSurface(
const math::Size& size);
+ void RasterizeRenderTreeToCanvas(
+ const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas);
+
+ SkCanvas* GetCanvasFromRenderTarget(
+ const scoped_refptr<backend::RenderTarget>& render_target);
+
void ResetSkiaState();
void RenderTextureEGL(const render_tree::ImageNode* image_node,
@@ -122,8 +132,6 @@
base::optional<common::SurfaceCache> surface_cache_;
base::optional<egl::TexturedMeshRenderer> textured_mesh_renderer_;
-
- FrameRateThrottler frame_rate_throttler_;
};
namespace {
@@ -252,6 +260,42 @@
gr_context_->resetContext();
}
+namespace {
+
+// For stereoscopic video, the actual video is split (either horizontally or
+// vertically) in two, one video for the left eye and one for the right eye.
+// This function will adjust the content region rectangle to match only the
+// left eye's video region, since we are ultimately presenting to a monoscopic
+// display.
+math::Rect AdjustContentRegionForStereoMode(render_tree::StereoMode stereo_mode,
+ const math::Rect& content_region) {
+ switch (stereo_mode) {
+ case render_tree::kLeftRight: {
+ // Use the left half (left eye) of the video only.
+ math::Rect adjusted_content_region(content_region);
+ adjusted_content_region.set_width(content_region.width() / 2);
+ return adjusted_content_region;
+ }
+
+ case render_tree::kTopBottom: {
+ // Use the top half (left eye) of the video only.
+ math::Rect adjusted_content_region(content_region);
+ adjusted_content_region.set_height(content_region.height() / 2);
+ return adjusted_content_region;
+ }
+
+ case render_tree::kMono:
+ case render_tree::kLeftRightUnadjustedTextureCoords:
+ // No modifications needed here, pass content region through unchanged.
+ return content_region;
+ }
+
+ NOTREACHED();
+ return content_region;
+}
+
+} // namespace
+
void HardwareRasterizer::Impl::RenderTextureWithMeshFilterEGL(
const render_tree::ImageNode* image_node,
const render_tree::MapToMeshFilter& mesh_filter,
@@ -296,20 +340,28 @@
content_region = *image->GetContentRegion();
}
- const VertexBufferObject* left_vbo =
- base::polymorphic_downcast<HardwareMesh*>(mesh_filter.mono_mesh().get())
+ const VertexBufferObject* mono_vbo =
+ base::polymorphic_downcast<HardwareMesh*>(
+ mesh_filter.mono_mesh(math::Size(content_region.width(),
+ content_region.height()))
+ .get())
->GetVBO();
+
+ math::Rect stereo_adjusted_content_region = AdjustContentRegionForStereoMode(
+ mesh_filter.stereo_mode(), content_region);
+
// Invoke out TexturedMeshRenderer to actually perform the draw call.
- textured_mesh_renderer_->RenderVBO(left_vbo->GetHandle(),
- left_vbo->GetVertexCount(),
- left_vbo->GetDrawMode(), texture,
- content_region, draw_state->transform_3d);
+ textured_mesh_renderer_->RenderVBO(
+ mono_vbo->GetHandle(), mono_vbo->GetVertexCount(),
+ mono_vbo->GetDrawMode(), texture, stereo_adjusted_content_region,
+ draw_state->transform_3d);
// Let Skia know that we've modified GL state.
gr_context_->resetContext();
}
HardwareRasterizer::Impl::Impl(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int surface_cache_size_in_bytes)
@@ -327,8 +379,9 @@
// Create a GrContext object that wraps the passed in Cobalt GraphicsContext
// object.
gr_context_.reset(GrContext::Create(
- kCobalt_GrBackend,
- reinterpret_cast<GrBackendContext>(graphics_context_)));
+ kCobalt_GrBackend, reinterpret_cast<GrBackendContext>(graphics_context_),
+ skia_atlas_width, skia_atlas_height));
+
DCHECK(gr_context_);
// The GrContext manages a resource cache internally using GrResourceCache
// which by default caches 96MB of resources. This is used for helping with
@@ -348,8 +401,10 @@
// Setup a resource provider for resources to be used with a hardware
// accelerated Skia rasterizer.
- resource_provider_.reset(
- new HardwareResourceProvider(graphics_context_, gr_context_));
+ resource_provider_.reset(new HardwareResourceProvider(
+ graphics_context_, gr_context_,
+ base::Bind(&HardwareRasterizer::Impl::SubmitOffscreenToRenderTarget,
+ base::Unretained(this))));
graphics_context_->ReleaseCurrentContext();
@@ -416,39 +471,9 @@
AdvanceFrame();
- SkSurface* sk_output_surface;
- SkSurfaceMap::iterator iter = sk_output_surface_map_.find(render_target);
- if (iter == sk_output_surface_map_.end()) {
- // Remove the least recently used SkSurface from the map if we exceed the
- // max allowed saved surfaces.
- if (sk_output_surface_map_.size() > kMaxSkSurfaceCount) {
- SkSurfaceMap::iterator iter = sk_output_surface_map_.begin();
- DLOG(WARNING) << "Erasing the SkSurface for RenderTarget " << iter->first
- << " this should not happen often or else it means the surface map is"
- << " probably thrashing.";
- iter->second->unref();
- sk_output_surface_map_.erase(iter);
- }
- // Setup a Skia render target that wraps the passed in Cobalt render target.
- SkAutoTUnref<GrRenderTarget> skia_render_target(
- gr_context_->wrapBackendRenderTarget(
- CobaltRenderTargetToSkiaBackendRenderTargetDesc(
- render_target.get())));
-
- // Create an SkSurface from the render target so that we can acquire a
- // SkCanvas object from it in Submit().
- sk_output_surface = CreateSkiaRenderTargetSurface(skia_render_target);
- sk_output_surface_map_[render_target] = sk_output_surface;
- } else {
- sk_output_surface = sk_output_surface_map_[render_target];
- // Mark this RenderTarget/SkCanvas pair as the most recently used by
- // popping it and re-adding it.
- sk_output_surface_map_.erase(iter);
- sk_output_surface_map_[render_target] = sk_output_surface;
- }
-
// Get a SkCanvas that outputs to our hardware render target.
- SkCanvas* canvas = sk_output_surface->getCanvas();
+ SkCanvas* canvas = GetCanvasFromRenderTarget(render_target);
+
canvas->save();
if (options.flags & Rasterizer::kSubmitFlags_Clear) {
@@ -461,63 +486,43 @@
}
}
- {
- TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
- // Rasterize the passed in render tree to our hardware render target.
- RenderTreeNodeVisitor::CreateScratchSurfaceFunction
- create_scratch_surface_function =
- base::Bind(&HardwareRasterizer::Impl::CreateScratchSurface,
- base::Unretained(this));
- RenderTreeNodeVisitor visitor(
- canvas, &create_scratch_surface_function,
- base::Bind(&HardwareRasterizer::Impl::ResetSkiaState,
- base::Unretained(this)),
- base::Bind(&HardwareRasterizer::Impl::RenderTextureEGL,
- base::Unretained(this)),
- base::Bind(&HardwareRasterizer::Impl::RenderTextureWithMeshFilterEGL,
- base::Unretained(this)),
- surface_cache_delegate_ ? &surface_cache_delegate_.value() : NULL,
- surface_cache_ ? &surface_cache_.value() : NULL);
- render_tree->Accept(&visitor);
- }
+ // Rasterize the passed in render tree to our hardware render target.
+ RasterizeRenderTreeToCanvas(render_tree, canvas);
{
TRACE_EVENT0("cobalt::renderer", "Skia Flush");
canvas->flush();
}
- frame_rate_throttler_.EndInterval();
graphics_context_->SwapBuffers(render_target_egl);
- frame_rate_throttler_.BeginInterval();
canvas->restore();
}
void HardwareRasterizer::Impl::SubmitOffscreen(
+ const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ RasterizeRenderTreeToCanvas(render_tree, canvas);
+}
+
+void HardwareRasterizer::Impl::SubmitOffscreenToRenderTarget(
const scoped_refptr<render_tree::Node>& render_tree,
- SkCanvas* canvas) {
+ const scoped_refptr<backend::RenderTarget>& render_target) {
DCHECK(thread_checker_.CalledOnValidThread());
- // Caller is expected to reset gr_context as needed.
+ // Create a canvas from the render target.
+ GrBackendRenderTargetDesc skia_desc =
+ CobaltRenderTargetToSkiaBackendRenderTargetDesc(render_target.get());
+ skia_desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+ SkAutoTUnref<GrRenderTarget> skia_render_target(
+ gr_context_->wrapBackendRenderTarget(skia_desc));
+ SkSurface* sk_output_surface =
+ CreateSkiaRenderTargetSurface(skia_render_target);
+ SkCanvas* canvas = sk_output_surface->getCanvas();
- {
- TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
- // Rasterize the passed in render tree to our hardware render target.
- RenderTreeNodeVisitor::CreateScratchSurfaceFunction
- create_scratch_surface_function =
- base::Bind(&HardwareRasterizer::Impl::CreateScratchSurface,
- base::Unretained(this));
- RenderTreeNodeVisitor visitor(
- canvas, &create_scratch_surface_function,
- base::Bind(&HardwareRasterizer::Impl::ResetSkiaState,
- base::Unretained(this)),
- base::Bind(&HardwareRasterizer::Impl::RenderTextureEGL,
- base::Unretained(this)),
- base::Bind(&HardwareRasterizer::Impl::RenderTextureWithMeshFilterEGL,
- base::Unretained(this)),
- surface_cache_delegate_ ? &surface_cache_delegate_.value() : NULL,
- surface_cache_ ? &surface_cache_.value() : NULL);
- render_tree->Accept(&visitor);
- }
+ // Render to the canvas and clean up.
+ RasterizeRenderTreeToCanvas(render_tree, canvas);
+ canvas->flush();
+ sk_output_surface->unref();
}
render_tree::ResourceProvider* HardwareRasterizer::Impl::GetResourceProvider() {
@@ -581,12 +586,73 @@
}
}
+SkCanvas* HardwareRasterizer::Impl::GetCanvasFromRenderTarget(
+ const scoped_refptr<backend::RenderTarget>& render_target) {
+ SkSurface* sk_output_surface;
+ SkSurfaceMap::iterator iter = sk_output_surface_map_.find(render_target);
+ if (iter == sk_output_surface_map_.end()) {
+ // Remove the least recently used SkSurface from the map if we exceed the
+ // max allowed saved surfaces.
+ if (sk_output_surface_map_.size() > kMaxSkSurfaceCount) {
+ SkSurfaceMap::iterator iter = sk_output_surface_map_.begin();
+ DLOG(WARNING)
+ << "Erasing the SkSurface for RenderTarget " << iter->first
+ << ". This may happen nominally during movement-triggered "
+ << "replacement of SkSurfaces or else it may indicate the surface "
+ << "map is thrashing because the total number of RenderTargets ("
+ << kMaxSkSurfaceCount << ") has been exceeded.";
+ iter->second->unref();
+ sk_output_surface_map_.erase(iter);
+ }
+ // Setup a Skia render target that wraps the passed in Cobalt render target.
+ SkAutoTUnref<GrRenderTarget> skia_render_target(
+ gr_context_->wrapBackendRenderTarget(
+ CobaltRenderTargetToSkiaBackendRenderTargetDesc(
+ render_target.get())));
+
+ // Create an SkSurface from the render target so that we can acquire a
+ // SkCanvas object from it in Submit().
+ sk_output_surface = CreateSkiaRenderTargetSurface(skia_render_target);
+ sk_output_surface_map_[render_target] = sk_output_surface;
+ } else {
+ sk_output_surface = sk_output_surface_map_[render_target];
+ // Mark this RenderTarget/SkCanvas pair as the most recently used by
+ // popping it and re-adding it.
+ sk_output_surface_map_.erase(iter);
+ sk_output_surface_map_[render_target] = sk_output_surface;
+ }
+ return sk_output_surface->getCanvas();
+}
+
+void HardwareRasterizer::Impl::RasterizeRenderTreeToCanvas(
+ const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas) {
+ TRACE_EVENT0("cobalt::renderer", "RasterizeRenderTreeToCanvas");
+ RenderTreeNodeVisitor::CreateScratchSurfaceFunction
+ create_scratch_surface_function =
+ base::Bind(&HardwareRasterizer::Impl::CreateScratchSurface,
+ base::Unretained(this));
+ RenderTreeNodeVisitor visitor(
+ canvas, &create_scratch_surface_function,
+ base::Bind(&HardwareRasterizer::Impl::ResetSkiaState,
+ base::Unretained(this)),
+ base::Bind(&HardwareRasterizer::Impl::RenderTextureEGL,
+ base::Unretained(this)),
+ base::Bind(&HardwareRasterizer::Impl::RenderTextureWithMeshFilterEGL,
+ base::Unretained(this)),
+ surface_cache_delegate_ ? &surface_cache_delegate_.value() : NULL,
+ surface_cache_ ? &surface_cache_.value() : NULL);
+ DCHECK(render_tree);
+ render_tree->Accept(&visitor);
+}
+
void HardwareRasterizer::Impl::ResetSkiaState() { gr_context_->resetContext(); }
HardwareRasterizer::HardwareRasterizer(
- backend::GraphicsContext* graphics_context, int skia_cache_size_in_bytes,
+ backend::GraphicsContext* graphics_context, int skia_atlas_width,
+ int skia_atlas_height, int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes, int surface_cache_size_in_bytes)
- : impl_(new Impl(graphics_context, skia_cache_size_in_bytes,
+ : impl_(new Impl(graphics_context, skia_atlas_width, skia_atlas_height,
+ skia_cache_size_in_bytes,
scratch_surface_cache_size_in_bytes,
surface_cache_size_in_bytes)) {}
@@ -602,8 +668,7 @@
}
void HardwareRasterizer::SubmitOffscreen(
- const scoped_refptr<render_tree::Node>& render_tree,
- SkCanvas* canvas) {
+ const scoped_refptr<render_tree::Node>& render_tree, SkCanvas* canvas) {
TRACE_EVENT0("cobalt::renderer", "HardwareRasterizer::SubmitOffscreen()");
impl_->SubmitOffscreen(render_tree, canvas);
}
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
index e410883..7343ca0 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
@@ -51,6 +51,7 @@
// a surface cache such that expensive render tree nodes seen multiple times
// will get saved to offscreen surfaces.
explicit HardwareRasterizer(backend::GraphicsContext* graphics_context,
+ int skia_atlas_width, int skia_atlas_height,
int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int surface_cache_size_in_bytes);
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index 0a44a22..2554c09 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -28,6 +28,7 @@
#include "cobalt/renderer/rasterizer/skia/hardware_image.h"
#include "cobalt/renderer/rasterizer/skia/hardware_mesh.h"
#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
#include "cobalt/renderer/rasterizer/skia/typeface.h"
#include "third_party/ots/include/opentype-sanitiser.h"
#include "third_party/ots/include/ots-memory-stream.h"
@@ -39,59 +40,31 @@
using cobalt::render_tree::ImageData;
using cobalt::render_tree::RawImageMemory;
-namespace {
-
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
- SB_HAS(GRAPHICS)
-
-void DecodeTargetAcquireOnRasterizer(
- cobalt::renderer::backend::GraphicsContextEGL* context_egl,
- SbDecodeTargetFormat format, int width, int height,
- SbDecodeTarget* out_decode_target, base::WaitableEvent* done_event) {
- cobalt::renderer::backend::GraphicsContextEGL::ScopedMakeCurrent
- scoped_context(context_egl);
-
- *out_decode_target =
- SbDecodeTargetCreate(context_egl->system_egl()->GetDisplay(),
- context_egl->GetContext(), format, width, height);
-
- done_event->Signal();
-}
-
-void DecodeTargetReleaseOnRasterizer(
- cobalt::renderer::backend::GraphicsContextEGL* context_egl,
- SbDecodeTarget decode_target) {
- cobalt::renderer::backend::GraphicsContextEGL::ScopedMakeCurrent
- scoped_context(context_egl);
-
- SbDecodeTargetRelease(decode_target);
-}
-
-#endif // SB_VERSION(SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION) && \
- SB_HAS(GRAPHICS)
-
-} // namespace
-
namespace cobalt {
namespace renderer {
namespace rasterizer {
namespace skia {
HardwareResourceProvider::HardwareResourceProvider(
- backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context)
+ backend::GraphicsContextEGL* cobalt_context, GrContext* gr_context,
+ SubmitOffscreenCallback submit_offscreen_callback)
: cobalt_context_(cobalt_context),
gr_context_(gr_context),
+ submit_offscreen_callback_(submit_offscreen_callback),
self_message_loop_(MessageLoop::current()) {
// Initialize the font manager now to ensure that it doesn't get initialized
// on multiple threads simultaneously later.
SkSafeUnref(SkFontMgr::RefDefault());
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
- SB_HAS(GRAPHICS)
- decode_target_provider_.acquire = &DecodeTargetAcquire;
- decode_target_provider_.release = &DecodeTargetRelease;
- decode_target_provider_.context = this;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
+#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+ decode_target_graphics_context_provider_.egl_display =
+ cobalt_context_->system_egl()->GetDisplay();
+ decode_target_graphics_context_provider_.egl_context =
+ cobalt_context_->GetContext();
+ decode_target_graphics_context_provider_.gles_context_runner =
+ &HardwareResourceProvider::GraphicsContextRunner;
+ decode_target_graphics_context_provider_.gles_context_runner_context = this;
+#endif // SB_API_VERSION >= 4 && \
SB_HAS(GRAPHICS)
}
@@ -161,8 +134,7 @@
self_message_loop_));
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
- SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
scoped_refptr<render_tree::Image>
HardwareResourceProvider::CreateImageFromSbDecodeTarget(
SbDecodeTarget decode_target) {
@@ -198,39 +170,48 @@
content_region.Pass(), self_message_loop_));
}
+namespace {
+void RunGraphicsContextRunnerOnRasterizerThread(
+ SbDecodeTargetGlesContextRunnerTarget target_function,
+ void* target_function_context,
+ backend::GraphicsContextEGL* graphics_context,
+ base::WaitableEvent* done_event) {
+ backend::GraphicsContextEGL::ScopedMakeCurrent make_current(graphics_context);
+ target_function(target_function_context);
+ done_event->Signal();
+}
+} // namespace
+
// static
-SbDecodeTarget HardwareResourceProvider::DecodeTargetAcquire(void* context,
- SbDecodeTargetFormat format, int width, int height) {
+void HardwareResourceProvider::GraphicsContextRunner(
+ SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
+ SbDecodeTargetGlesContextRunnerTarget target_function,
+ void* target_function_context) {
SbDecodeTarget decode_target = kSbDecodeTargetInvalid;
HardwareResourceProvider* provider =
- reinterpret_cast<HardwareResourceProvider*>(context);
+ reinterpret_cast<HardwareResourceProvider*>(
+ graphics_context_provider->gles_context_runner_context);
- base::WaitableEvent done_event(true, false);
-
- // Texture creation must occur on the rasterizer thread.
- provider->self_message_loop_->PostTask(FROM_HERE,
- base::Bind(&DecodeTargetAcquireOnRasterizer,
- provider->cobalt_context_,
- format, width, height,
- &decode_target, &done_event));
- done_event.Wait();
-
- return decode_target;
+ if (MessageLoop::current() != provider->self_message_loop_) {
+ // Post a task to the rasterizer thread to have it run the requested
+ // function, and wait for it to complete before returning.
+ base::WaitableEvent done_event(true, false);
+ provider->self_message_loop_->PostTask(
+ FROM_HERE, base::Bind(&RunGraphicsContextRunnerOnRasterizerThread,
+ target_function, target_function_context,
+ provider->cobalt_context_, &done_event));
+ done_event.Wait();
+ } else {
+ // If we are already on the rasterizer thread, just run the function
+ // directly.
+ backend::GraphicsContextEGL::ScopedMakeCurrent make_current(
+ provider->cobalt_context_);
+ target_function(target_function_context);
+ }
}
-// static
-void HardwareResourceProvider::DecodeTargetRelease(void* context,
- SbDecodeTarget decode_target) {
- HardwareResourceProvider* provider =
- reinterpret_cast<HardwareResourceProvider*>(context);
-
- // Texture deletion must occur on the rasterizer thread.
- provider->self_message_loop_->PostTask(
- FROM_HERE, base::Bind(&DecodeTargetReleaseOnRasterizer,
- provider->cobalt_context_, decode_target));
-}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
+#endif // SB_API_VERSION >= 4 && \
SB_HAS(GRAPHICS)
scoped_ptr<RawImageMemory> HardwareResourceProvider::AllocateRawImageMemory(
@@ -283,14 +264,16 @@
"HardwareResourceProvider::GetLocalTypeface()");
SkAutoTUnref<SkFontMgr> font_manager(SkFontMgr::RefDefault());
- SkAutoTUnref<SkTypeface> typeface(font_manager->matchFamilyStyle(
- font_family_name, CobaltFontStyleToSkFontStyle(font_style)));
+ SkAutoTUnref<SkTypeface_Cobalt> typeface(
+ base::polymorphic_downcast<SkTypeface_Cobalt*>(
+ font_manager->matchFamilyStyle(
+ font_family_name, CobaltFontStyleToSkFontStyle(font_style))));
return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
}
scoped_refptr<render_tree::Typeface>
HardwareResourceProvider::GetLocalTypefaceByFaceNameIfAvailable(
- const std::string& font_face_name) {
+ const char* font_face_name) {
TRACE_EVENT0("cobalt::renderer",
"HardwareResourceProvider::GetLocalTypefaceIfAvailable()");
@@ -298,7 +281,8 @@
SkFontMgr_Cobalt* cobalt_font_manager =
base::polymorphic_downcast<SkFontMgr_Cobalt*>(font_manager.get());
- SkTypeface* typeface = cobalt_font_manager->matchFaceName(font_face_name);
+ SkTypeface_Cobalt* typeface = base::polymorphic_downcast<SkTypeface_Cobalt*>(
+ cobalt_font_manager->MatchFaceName(font_face_name));
if (typeface != NULL) {
SkAutoTUnref<SkTypeface> typeface_unref_helper(typeface);
return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
@@ -315,9 +299,11 @@
"HardwareResourceProvider::GetCharacterFallbackTypeface()");
SkAutoTUnref<SkFontMgr> font_manager(SkFontMgr::RefDefault());
- SkAutoTUnref<SkTypeface> typeface(font_manager->matchFamilyStyleCharacter(
- 0, CobaltFontStyleToSkFontStyle(font_style), language.c_str(),
- character));
+ SkAutoTUnref<SkTypeface_Cobalt> typeface(
+ base::polymorphic_downcast<SkTypeface_Cobalt*>(
+ font_manager->matchFamilyStyleCharacter(
+ 0, CobaltFontStyleToSkFontStyle(font_style), language.c_str(),
+ character)));
return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
}
@@ -347,7 +333,9 @@
sanitized_data.get(), static_cast<size_t>(sanitized_data.Tell())));
SkAutoTUnref<SkStreamAsset> stream(new SkMemoryStream(skia_data));
- SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromStream(stream));
+ SkAutoTUnref<SkTypeface_Cobalt> typeface(
+ base::polymorphic_downcast<SkTypeface_Cobalt*>(
+ SkTypeface::CreateFromStream(stream)));
if (typeface) {
return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
} else {
@@ -385,6 +373,13 @@
return new HardwareMesh(vertices.Pass(), draw_mode);
}
+scoped_refptr<render_tree::Image> HardwareResourceProvider::DrawOffscreenImage(
+ const scoped_refptr<render_tree::Node>& root) {
+ return make_scoped_refptr(new HardwareFrontendImage(
+ root, submit_offscreen_callback_, cobalt_context_, gr_context_,
+ self_message_loop_));
+}
+
} // namespace skia
} // namespace rasterizer
} // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
index 3fa75af..6532ff2 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
@@ -18,6 +18,8 @@
#include <string>
#include <vector>
+#include "cobalt/math/size.h"
+#include "cobalt/render_tree/node.h"
#include "cobalt/render_tree/resource_provider.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
#include "cobalt/renderer/rasterizer/skia/hardware_image.h"
@@ -36,7 +38,8 @@
class HardwareResourceProvider : public render_tree::ResourceProvider {
public:
HardwareResourceProvider(backend::GraphicsContextEGL* cobalt_context,
- GrContext* gr_context);
+ GrContext* gr_context,
+ SubmitOffscreenCallback submit_offscreen_callback);
void Finish() OVERRIDE;
@@ -51,15 +54,16 @@
scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE;
#if SB_HAS(GRAPHICS)
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
SbDecodeTarget decode_target) OVERRIDE;
// Return the associated SbDecodeTargetProvider with the ResourceProvider,
// if it exists. Returns NULL if SbDecodeTarget is not supported.
- SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE {
- return &decode_target_provider_;
+ SbDecodeTargetGraphicsContextProvider*
+ GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+ return &decode_target_graphics_context_provider_;
}
// Whether SbDecodeTargetIsSupported or not.
@@ -82,7 +86,7 @@
// Whether SbDecodeTargetIsSupported or not.
bool SupportsSbDecodeTarget() OVERRIDE { return false; }
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
#endif // SB_HAS(GRAPHICS)
scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory(
@@ -98,7 +102,7 @@
const char* font_family_name, render_tree::FontStyle font_style) OVERRIDE;
scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
- const std::string& font_face_name) OVERRIDE;
+ const char* font_face_name) OVERRIDE;
scoped_refptr<render_tree::Typeface> GetCharacterFallbackTypeface(
int32 character, render_tree::FontStyle font_style,
@@ -129,20 +133,25 @@
scoped_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
render_tree::Mesh::DrawMode draw_mode) OVERRIDE;
+ scoped_refptr<render_tree::Image> DrawOffscreenImage(
+ const scoped_refptr<render_tree::Node>& root) OVERRIDE;
+
private:
backend::GraphicsContextEGL* cobalt_context_;
GrContext* gr_context_;
+ SubmitOffscreenCallback submit_offscreen_callback_;
TextShaper text_shaper_;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
- SB_HAS(GRAPHICS)
- static SbDecodeTarget DecodeTargetAcquire(void* context,
- SbDecodeTargetFormat format,
- int width, int height);
- static void DecodeTargetRelease(void* context, SbDecodeTarget decode_target);
- SbDecodeTargetProvider decode_target_provider_;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION && \
+#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+ static void GraphicsContextRunner(
+ SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
+ SbDecodeTargetGlesContextRunnerTarget target_function,
+ void* target_function_context);
+
+ SbDecodeTargetGraphicsContextProvider
+ decode_target_graphics_context_provider_;
+#endif // SB_API_VERSION >= 4 && \
SB_HAS(GRAPHICS)
// We keep a handle to the message loop that this resource provider was
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 9a9fa80..1e2f1cf 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -38,6 +38,7 @@
#include "cobalt/render_tree/rounded_corners.h"
#include "cobalt/render_tree/text_node.h"
#include "cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h"
+#include "cobalt/renderer/rasterizer/common/utils.h"
#include "cobalt/renderer/rasterizer/skia/cobalt_skia_type_conversions.h"
#include "cobalt/renderer/rasterizer/skia/font.h"
#include "cobalt/renderer/rasterizer/skia/glyph_buffer.h"
@@ -353,25 +354,6 @@
return false;
}
-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;
-}
-
// Tries to render a mapped-to-mesh node, returning false if it fails (which
// would happen if we try to apply a map-to-mesh filter to a render tree node
// that we don't currently support).
@@ -478,7 +460,7 @@
// If an opacity filter is being applied, we must render to a separate
// texture first.
(!filter_node->data().opacity_filter ||
- SourceCanRenderWithOpacity(filter_node->data().source)) &&
+ common::utils::NodeCanRenderWithOpacity(filter_node->data().source)) &&
// If transforms are applied to the viewport, then we will render to
// a separate texture first.
total_matrix.rectStaysRect() &&
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi b/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi
index 5be8703..41bb8a1 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi
+++ b/src/cobalt/renderer/rasterizer/skia/skia/skia_cobalt.gypi
@@ -27,13 +27,23 @@
'src/ports/SkFontMgr_cobalt.cc',
'src/ports/SkFontMgr_cobalt.h',
'src/ports/SkFontMgr_cobalt_factory.cc',
+ 'src/ports/SkFontStyleSet_cobalt.cc',
+ 'src/ports/SkFontStyleSet_cobalt.h',
'src/ports/SkFontUtil_cobalt.cc',
'src/ports/SkFontUtil_cobalt.h',
'src/ports/SkOSFile_cobalt.cc',
+ 'src/ports/SkStream_cobalt.cc',
+ 'src/ports/SkStream_cobalt.h',
+ 'src/ports/SkTypeface_cobalt.cc',
+ 'src/ports/SkTypeface_cobalt.h',
'src/ports/SkTLS_cobalt.cc',
'src/ports/SkTime_cobalt.cc',
],
+ 'defines': [
+ 'COBALT_SYSTEM_TYPEFACE_CACHE_CAPACITY_IN_BYTES=<(local_font_cache_size_in_bytes)',
+ ],
+
'include_dirs': [
'<(SHARED_INTERMEDIATE_DIR)',
],
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc
index e7bf46f..be914e6 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.cc
@@ -298,26 +298,6 @@
const char* value = attributes[i + 1];
switch (strlen(name)) {
- case 9:
- if (strncmp("font_name", name, 9) == 0) {
- file->full_font_name = value;
- continue;
- }
- break;
- case 15:
- if (strncmp("postscript_name", name, 15) == 0) {
- file->postscript_name = value;
- continue;
- }
- break;
- case 6:
- if (strncmp("weight", name, 6) == 0) {
- if (!ParseNonNegativeInteger(value, &file->weight)) {
- DLOG(WARNING) << "Invalid font weight [" << value << "]";
- }
- continue;
- }
- break;
case 5:
if (strncmp("index", name, 5) == 0) {
if (!ParseNonNegativeInteger(value, &file->index)) {
@@ -336,6 +316,35 @@
}
}
break;
+ case 6:
+ if (strncmp("weight", name, 6) == 0) {
+ if (!ParseNonNegativeInteger(value, &file->weight)) {
+ DLOG(WARNING) << "Invalid font weight [" << value << "]";
+ }
+ continue;
+ }
+ break;
+ case 9:
+ if (strncmp("font_name", name, 9) == 0) {
+ SkAutoAsciiToLC to_lowercase(value);
+ file->full_font_name = to_lowercase.lc();
+ continue;
+ }
+ break;
+ case 15:
+ if (strncmp("postscript_name", name, 15) == 0) {
+ SkAutoAsciiToLC to_lowercase(value);
+ file->postscript_name = to_lowercase.lc();
+ continue;
+ }
+ break;
+ case 25:
+ if (strncmp("disable_synthetic_bolding", name, 25) == 0) {
+ file->disable_synthetic_bolding =
+ strcmp("true", value) == 0 || strcmp("1", value) == 0;
+ continue;
+ }
+ break;
default:
break;
}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
index cb09a67..d77460a 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
@@ -14,618 +14,21 @@
#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
-#include <sys/stat.h>
-#include <cmath>
-
-#include "base/at_exit.h"
-#include "base/bind.h"
#include "base/debug/trace_event.h"
-#include "base/lazy_instance.h"
-#include "base/memory/singleton.h"
-#include "cobalt/base/c_val.h"
#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontConfigParser_cobalt.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
#include "SkData.h"
-#include "SkGraphics.h"
-#include "SkOSFile.h"
#include "SkStream.h"
#include "SkString.h"
-#include "SkTArray.h"
#include "SkTSearch.h"
-#include "third_party/skia/src/ports/SkFontHost_FreeType_common.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-namespace {
-
-#ifndef SK_TYPEFACE_COBALT_SYSTEM_OPEN_STREAM_CACHE_LIMIT
-#define SK_TYPEFACE_COBALT_SYSTEM_OPEN_STREAM_CACHE_LIMIT (10 * 1024 * 1024)
-#endif
-
-const int32_t kPeriodicProcessingTimerDelayMs = 5000;
-const int32_t kReleaseInactiveSystemTypefaceOpenStreamsDelayMs = 300000;
-const int32_t kPeriodicFontCachePurgeDelayMs = 900000;
-
-// base::CVal tracking specific to fonts.
-// NOTE: These are put in their own Singleton, because Skia's lazy instance
-// implementation does not guarantee that only one SkFontMgr_Cobalt will be
-// created at a time (see skia/src/core/SkLazyPtr.h), and we cannot have
-// multiple copies of the same CVal.
-class SkFontMgrCVals {
- public:
- static SkFontMgrCVals* GetInstance() {
- return Singleton<SkFontMgrCVals,
- DefaultSingletonTraits<SkFontMgrCVals> >::get();
- }
-
- const base::CVal<base::cval::SizeInBytes, base::CValPublic>&
- system_typeface_open_stream_cache_limit_in_bytes() {
- return system_typeface_open_stream_cache_limit_in_bytes_;
- }
-
- base::CVal<base::cval::SizeInBytes, base::CValPublic>&
- system_typeface_open_stream_cache_size_in_bytes() {
- return system_typeface_open_stream_cache_size_in_bytes_;
- }
-
- void IncrementFontFilesLoadedCount() { ++font_files_loaded_count_; }
-
- private:
- SkFontMgrCVals()
- : system_typeface_open_stream_cache_limit_in_bytes_(
- "Memory.Font.SystemTypeface.Capacity",
- SK_TYPEFACE_COBALT_SYSTEM_OPEN_STREAM_CACHE_LIMIT,
- "The capacity, in bytes, of the system fonts. Exceeding this "
- "results in *inactive* fonts being released."),
- system_typeface_open_stream_cache_size_in_bytes_(
- "Memory.Font.SystemTypeface.Size", 0,
- "Total number of bytes currently used by the cache."),
- font_files_loaded_count_(
- "Count.Font.FilesLoaded", 0,
- "Total number of font file loads that have occurred.") {}
-
- const base::CVal<base::cval::SizeInBytes, base::CValPublic>
- system_typeface_open_stream_cache_limit_in_bytes_;
- mutable base::CVal<base::cval::SizeInBytes, base::CValPublic>
- system_typeface_open_stream_cache_size_in_bytes_;
-
- base::CVal<int> font_files_loaded_count_;
-
- friend struct DefaultSingletonTraits<SkFontMgrCVals>;
- DISALLOW_COPY_AND_ASSIGN(SkFontMgrCVals);
-};
-
-// The timer used to trigger periodic processing of open streams, which
-// handles moving open streams from the active to the inactive list and
-// releasing inactive open streams. Create it as a lazy instance to ensure it
-// gets cleaned up correctly at exit, even though the SkFontMgr_Cobalt may be
-// created multiple times and the surviving instance isn't deleted until after
-// the thread has been stopped.
-base::LazyInstance<scoped_ptr<base::PollerWithThread> >
- process_system_typefaces_with_open_streams_poller =
- LAZY_INSTANCE_INITIALIZER;
-
-base::LazyInstance<base::Lock> poller_lock = LAZY_INSTANCE_INITIALIZER;
-
-// NOTE: It is the responsibility of the caller to call Unref() on the SkData.
-SkData* NewDataFromFile(const SkString& file_path) {
- TRACE_EVENT1("cobalt::renderer", "SkFontMgr_cobalt::NewDataFromFile()",
- "file_path", TRACE_STR_COPY(file_path.c_str()));
- LOG(INFO) << "Loading font file: " << file_path.c_str();
- SkFontMgrCVals::GetInstance()->IncrementFontFilesLoadedCount();
-
- SkAutoTUnref<SkStreamAsset> file_stream(
- SkStream::NewFromFile(file_path.c_str()));
- if (file_stream == NULL) {
- LOG(ERROR) << "Failed to open font file: " << file_path.c_str();
- return NULL;
- }
-
- SkAutoDataUnref data(
- SkData::NewFromStream(file_stream, file_stream->getLength()));
- if (data == NULL) {
- LOG(ERROR) << "Failed to read font file: " << file_path.c_str();
- return NULL;
- }
-
- return data.detach();
-}
-
-int match_score(const SkFontStyle& pattern, const SkFontStyle& candidate) {
- int score = 0;
- score += std::abs((pattern.width() - candidate.width()) * 100);
- score += (pattern.isItalic() == candidate.isItalic()) ? 0 : 1000;
- score += std::abs(pattern.weight() - candidate.weight());
- return score;
-}
-
-} // namespace
-
-///////////////////////////////////////////////////////////////////////////////
-
-class SkTypeface_Cobalt : public SkTypeface_FreeType {
- public:
- SkTypeface_Cobalt(int index, Style style, bool is_fixed_pitch,
- const SkString family_name)
- : INHERITED(style, SkTypefaceCache::NewFontID(), is_fixed_pitch),
- index_(index),
- family_name_(family_name) {}
-
- protected:
- virtual void onGetFamilyName(SkString* family_name) const SK_OVERRIDE {
- *family_name = family_name_;
- }
-
- int index_;
- SkString family_name_;
-
- private:
- typedef SkTypeface_FreeType INHERITED;
-};
-
-class SkTypeface_CobaltStream : public SkTypeface_Cobalt {
- public:
- SkTypeface_CobaltStream(SkStreamAsset* stream, int index, Style style,
- bool is_fixed_pitch, const SkString family_name)
- : INHERITED(index, style, is_fixed_pitch, family_name),
- stream_(SkRef(stream)) {
- LOG(INFO) << "Created SkTypeface_CobaltStream: " << family_name.c_str()
- << "(" << style << "); Size: " << stream_->getLength()
- << " bytes";
- }
-
- virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
- bool* serialize) const SK_OVERRIDE {
- SkASSERT(descriptor);
- SkASSERT(serialize);
- descriptor->setFamilyName(family_name_.c_str());
- descriptor->setFontFileName(NULL);
- descriptor->setFontIndex(index_);
- *serialize = true;
- }
-
- virtual SkStreamAsset* onOpenStream(int* ttc_index) const SK_OVERRIDE {
- *ttc_index = index_;
- return stream_->duplicate();
- }
-
- private:
- typedef SkTypeface_Cobalt INHERITED;
-
- SkAutoTUnref<SkStreamAsset> stream_;
-};
-
-class SkTypeface_CobaltSystem : public SkTypeface_Cobalt {
- public:
- SkTypeface_CobaltSystem(const SkFontMgr_Cobalt* manager, SkString path_name,
- SkMemoryStream* stream, int index, Style style,
- bool is_fixed_pitch, const SkString family_name)
- : INHERITED(index, style, is_fixed_pitch, family_name),
- font_manager_(manager),
- path_name_(path_name),
- stream_(SkRef(stream)) {
- LOG(INFO) << "Created SkTypeface_CobaltSystem: " << family_name.c_str()
- << "(" << style << "); File: " << path_name.c_str()
- << "; Size: " << stream_->getLength() << " bytes";
-
- SkAutoMutexAcquire scoped_mutex(
- font_manager_->GetSystemTypefaceStreamMutex());
- font_manager_->AddSystemTypefaceWithActiveOpenStream(this);
- }
-
- virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
- bool* serialize) const SK_OVERRIDE {
- SkASSERT(descriptor);
- SkASSERT(serialize);
- descriptor->setFamilyName(family_name_.c_str());
- descriptor->setFontFileName(path_name_.c_str());
- descriptor->setFontIndex(index_);
- *serialize = false;
- }
-
- virtual SkStreamAsset* onOpenStream(int* ttc_index) const SK_OVERRIDE {
- TRACE_EVENT0("cobalt::renderer", "SkTypeface_CobaltSystem::onOpenStream()");
- *ttc_index = index_;
-
- // Scope the initial mutex lock.
- {
- // Check for the stream still being open. In this case, the typeface
- // stream does not need to be re-loaded. Simply notify the manager of the
- // stream activity, as it potentially needs to move the stream from the
- // inactive list to the active list.
- SkAutoMutexAcquire scoped_mutex(
- font_manager_->GetSystemTypefaceStreamMutex());
- if (stream_ != NULL) {
- font_manager_->NotifySystemTypefaceOfOpenStreamActivity(this);
- return stream_->duplicate();
- }
- }
-
- TRACE_EVENT0("cobalt::renderer", "Load data from file");
- // This is where the bulk of the time will be spent, so load the font data
- // outside of a mutex lock.
- SkAutoTUnref<SkData> data(NewDataFromFile(path_name_));
-
- // Re-lock the mutex now that the data has been loaded.
- SkAutoMutexAcquire scoped_mutex(
- font_manager_->GetSystemTypefaceStreamMutex());
-
- // Verify that the data was not loaded on another thread while it was being
- // loaded on this one. If that's the case, then simply use that data.
- // Otherwise, update the stream and notify the font manager that the stream
- // is open.
- if (stream_ == NULL) {
- stream_.reset(new SkMemoryStream(data));
- LOG(INFO) << "Setting font stream: " << path_name_.c_str()
- << "; Size: " << stream_->getLength() << " bytes";
- font_manager_->AddSystemTypefaceWithActiveOpenStream(this);
- }
-
- return stream_->duplicate();
- }
-
- size_t GetOpenStreamSizeInBytes() const { return stream_->getLength(); }
-
- bool IsOpenStreamInactive() const {
- SkAutoTUnref<SkData> data(stream_->copyToData());
- // A refcount of 2 indicates that there are no external references to the
- // data. The first reference comes from SkMemoryStream and the second comes
- // from the call to copyToData().
- return data->getRefCnt() == 2;
- }
-
- void ReleaseStream() const {
- LOG(INFO) << "Releasing font stream: " << path_name_.c_str()
- << "; Size: " << stream_->getLength() << " bytes";
- stream_.reset(NULL);
- }
-
- private:
- typedef SkTypeface_Cobalt INHERITED;
-
- const SkFontMgr_Cobalt* font_manager_;
- const SkString path_name_;
-
- // |stream_| is mutable so that it can be modified within onOpenStream().
- mutable SkAutoTUnref<SkMemoryStream> stream_;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// SkFontStyleSet_Cobalt
-//
-// This class is used by SkFontMgr_Cobalt to hold SkTypeface_CobaltSystem
-// families.
-SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt(
- const SkFontMgr_Cobalt* const manager, const FontFamily& family,
- const char* base_path, SkMutex* const manager_owned_mutex)
- : font_manager_(manager),
- manager_owned_mutex_(manager_owned_mutex),
- is_fallback_family_(family.is_fallback_family),
- language_(family.language),
- page_ranges_(family.page_ranges),
- is_character_map_generated_(false) {
- TRACE_EVENT0("cobalt::renderer",
- "SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt()");
- DCHECK(manager_owned_mutex_);
-
- if (family.names.count() == 0) {
- return;
- }
-
- family_name_ = family.names[0];
-
- for (int i = 0; i < family.fonts.count(); ++i) {
- const FontFileInfo& font_file = family.fonts[i];
-
- SkString path_name(SkOSPath::Join(base_path, font_file.file_name.c_str()));
-
- // Sanity check that something exists at this location.
- if (!sk_exists(path_name.c_str(), kRead_SkFILE_Flag)) {
- LOG(ERROR) << "Failed to find font file: " << path_name.c_str();
- continue;
- }
-
- SkFontStyle style(font_file.weight, SkFontStyle::kNormal_Width,
- font_file.style == FontFileInfo::kItalic_FontStyle
- ? SkFontStyle::kItalic_Slant
- : SkFontStyle::kUpright_Slant);
-
- std::string full_font_name;
- if (!font_file.full_font_name.isEmpty()) {
- full_font_name = std::string(font_file.full_font_name.c_str(),
- font_file.full_font_name.size());
- }
- std::string postscript_name;
- if (!font_file.postscript_name.isEmpty()) {
- postscript_name = std::string(font_file.postscript_name.c_str(),
- font_file.postscript_name.size());
- }
-
- styles_.push_back().reset(SkNEW_ARGS(
- SkFontStyleSetEntry_Cobalt,
- (path_name, font_file.index, style, full_font_name, postscript_name)));
- }
-}
-
-int SkFontStyleSet_Cobalt::count() {
- SkAutoMutexAcquire scoped_mutex(*manager_owned_mutex_);
- return styles_.count();
-}
-
-void SkFontStyleSet_Cobalt::getStyle(int index, SkFontStyle* style,
- SkString* name) {
- // SkFontStyleSet_Cobalt does not support publicly interacting with entries
- // via index, as entries can potentially be removed, thereby invalidating the
- // indices.
- NOTREACHED();
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::createTypeface(int index) {
- // SkFontStyleSet_Cobalt does not support publicly interacting with entries
- // via index, as entries can potentially be removed, thereby invalidating the
- // indices.
- NOTREACHED();
- return NULL;
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::matchStyle(const SkFontStyle& pattern) {
- SkAutoMutexAcquire scoped_mutex(*manager_owned_mutex_);
- return MatchStyleWithoutLocking(pattern);
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::MatchStyleWithoutLocking(
- const SkFontStyle& pattern) {
- SkTypeface* typeface = NULL;
- while (typeface == NULL && styles_.count() > 0) {
- typeface = TryRetrieveTypefaceAndRemoveStyleOnFailure(
- GetClosestStyleIndex(pattern));
- }
- return typeface;
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::MatchFullFontName(const std::string& name) {
- for (int i = 0; i < styles_.count(); ++i) {
- if (styles_[i]->full_font_name == name) {
- return TryRetrieveTypefaceAndRemoveStyleOnFailure(i);
- }
- }
- return NULL;
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::MatchFontPostScriptName(
- const std::string& name) {
- for (int i = 0; i < styles_.count(); ++i) {
- if (styles_[i]->font_postscript_name == name) {
- return TryRetrieveTypefaceAndRemoveStyleOnFailure(i);
- }
- }
- return NULL;
-}
-
-SkTypeface* SkFontStyleSet_Cobalt::TryRetrieveTypefaceAndRemoveStyleOnFailure(
- int style_index) {
- DCHECK(style_index >= 0 && style_index < styles_.count());
- SkFontStyleSetEntry_Cobalt* style = styles_[style_index];
- // If the typeface doesn't already exist, then attempt to create it.
- if (style->typeface == NULL) {
- CreateSystemTypeface(style);
- // If the creation attempt failed and the typeface is still NULL, then
- // remove the entry from the set's styles.
- if (style->typeface == NULL) {
- styles_[style_index].swap(&styles_.back());
- styles_.pop_back();
- return NULL;
- }
- }
- return SkRef(style->typeface.get());
-}
-
-bool SkFontStyleSet_Cobalt::ContainsTypeface(const SkTypeface* typeface) {
- for (int i = 0; i < styles_.count(); ++i) {
- if (styles_[i]->typeface == typeface) {
- return true;
- }
- }
- return false;
-}
-
-bool SkFontStyleSet_Cobalt::ContainsCharacter(const SkFontStyle& style,
- SkUnichar character) {
- // If page ranges exist for this style set, then verify that this character
- // falls within the ranges. Otherwise, the style set is treated as having a
- // page range containing all characters.
- size_t num_ranges = page_ranges_.count();
- if (num_ranges > 0) {
- int16 page = font_character_map::GetPage(character);
-
- // Verify that the last page range is not less than the page containing the
- // character. If it's less, then this character can't be contained
- // within the pages. Otherwise, this last range acts as a sentinel for the
- // search.
- if (page_ranges_[num_ranges - 1].second < page) {
- return false;
- }
-
- int range_index = 0;
- while (page_ranges_[range_index].second < page) {
- ++range_index;
- }
-
- if (page_ranges_[range_index].first > page) {
- return false;
- }
- }
-
- // If this point is reached, then the character is contained within one of
- // the font style set's page ranges. Now, verify that the specific character
- // is supported by the font style set.
-
- // The character map is lazily generated. Generate it now if it isn't already
- // generated.
- if (!is_character_map_generated_) {
- TRACE_EVENT0("cobalt::renderer",
- "SkFontStyleSet_Cobalt::ContainsCharacter() and "
- "!is_character_map_generated_");
- // Attempt to load the closest font style from the set. If it fails to load,
- // it will be removed from the set and, as long as font styles remain in the
- // set, the logic will be attempted again.
- while (styles_.count() > 0) {
- int style_index = GetClosestStyleIndex(style);
- SkFontStyleSetEntry_Cobalt* closest_style = styles_[style_index];
-
- // Load the font data from the file and use it to generate the character
- // map.
- SkAutoTUnref<SkData> font_data(
- NewDataFromFile(closest_style->font_file_path));
- // The font failed to load. Remove it from the styles and start the loop
- // over.
- if (font_data == NULL) {
- styles_[style_index].swap(&styles_.back());
- styles_.pop_back();
- continue;
- }
-
- GenerateCharacterMapFromData(font_data, closest_style->ttc_index);
-
- // If the character is contained within the font style set, then go ahead
- // and create the typeface with the loaded font data. Otherwise, creating
- // the typeface would require an additional load of the file.
- bool contains_character = CharacterMapContainsCharacter(character);
- if (contains_character) {
- CreateSystemTypefaceFromData(closest_style, font_data);
- }
- return contains_character;
- }
-
- // If this point is reached, none of the fonts were successfully loaded.
- is_character_map_generated_ = true;
- return false;
- } else {
- return CharacterMapContainsCharacter(character);
- }
-}
-
-bool SkFontStyleSet_Cobalt::CharacterMapContainsCharacter(SkUnichar character) {
- font_character_map::CharacterMap::iterator page_iterator =
- character_map_.find(font_character_map::GetPage(character));
- return page_iterator != character_map_.end() &&
- page_iterator->second.test(
- font_character_map::GetPageCharacterIndex(character));
-}
-
-void SkFontStyleSet_Cobalt::GenerateCharacterMapFromData(SkData* font_data,
- int ttc_index) {
- if (is_character_map_generated_) {
- return;
- }
- TRACE_EVENT0("cobalt::renderer", "GenerateCharacterMapFromData()");
-
- FT_Library freetype_lib;
- if (FT_Init_FreeType(&freetype_lib) != 0) {
- return;
- }
-
- FT_Face face;
- FT_Error err = FT_New_Memory_Face(freetype_lib, font_data->bytes(),
- font_data->size(), ttc_index, &face);
- if (!err) {
- // map out this font's characters.
- FT_UInt glyph_index;
-
- int last_page = -1;
- font_character_map::PageCharacters* page_characters = NULL;
-
- SkUnichar code_point = FT_Get_First_Char(face, &glyph_index);
- while (glyph_index) {
- int page = font_character_map::GetPage(code_point);
- if (page != last_page) {
- page_characters = &character_map_[page];
- last_page = page;
- }
- page_characters->set(
- font_character_map::GetPageCharacterIndex(code_point));
-
- code_point = FT_Get_Next_Char(face, code_point, &glyph_index);
- }
-
- // release this font.
- FT_Done_Face(face);
- }
-
- // shut down FreeType.
- FT_Done_FreeType(freetype_lib);
-
- is_character_map_generated_ = true;
-}
-
-int SkFontStyleSet_Cobalt::GetClosestStyleIndex(const SkFontStyle& pattern) {
- int closest_index = 0;
- int min_score = std::numeric_limits<int>::max();
- for (int i = 0; i < styles_.count(); ++i) {
- int score = match_score(pattern, styles_[i]->font_style);
- if (score < min_score) {
- closest_index = i;
- min_score = score;
- }
- }
- return closest_index;
-}
-
-void SkFontStyleSet_Cobalt::CreateSystemTypeface(
- SkFontStyleSetEntry_Cobalt* style_entry) {
- TRACE_EVENT0("cobalt::renderer",
- "SkFontStyleSet_Cobalt::CreateSystemTypeface()");
- SkAutoTUnref<SkData> font_data(NewDataFromFile(style_entry->font_file_path));
- if (font_data != NULL) {
- CreateSystemTypefaceFromData(style_entry, font_data);
- }
-}
-
-void SkFontStyleSet_Cobalt::CreateSystemTypefaceFromData(
- SkFontStyleSetEntry_Cobalt* style_entry, SkData* data) {
- TRACE_EVENT0("cobalt::renderer",
- "SkFontStyleSet_Cobalt::CreateSystemTypefaceFromData()");
- DCHECK(!style_entry->typeface);
-
- // Since the font data is available, generate the character map if this is a
- // fallback font (which means that it'll need the character map for character
- // lookups during fallback).
- if (is_fallback_family_) {
- GenerateCharacterMapFromData(data, style_entry->ttc_index);
- }
-
- // Pass the SkData into the SkMemoryStream.
- SkAutoTUnref<SkMemoryStream> stream(new SkMemoryStream(data));
-
- bool is_fixed_pitch;
- SkTypeface::Style style;
- SkString name;
- if (SkTypeface_FreeType::ScanFont(stream, style_entry->ttc_index, &name,
- &style, &is_fixed_pitch)) {
- LOG(INFO) << "Scanned font: " << name.c_str() << "(" << style << ")";
- style_entry->typeface.reset(SkNEW_ARGS(
- SkTypeface_CobaltSystem,
- (font_manager_, style_entry->font_file_path, stream,
- style_entry->ttc_index, style, is_fixed_pitch, family_name_)));
- } else {
- LOG(ERROR) << "Failed to scan font: "
- << style_entry->font_file_path.c_str();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// SkFontMgr_Cobalt
-//
SkFontMgr_Cobalt::SkFontMgr_Cobalt(
- const char* directory, const SkTArray<SkString, true>& default_fonts)
- : default_family_(NULL),
- last_font_cache_purge_time_(base::TimeTicks::Now()) {
+ const char* directory, const SkTArray<SkString, true>& default_families)
+ : system_typeface_stream_manager_(
+ "Font.SystemTypeface",
+ COBALT_SYSTEM_TYPEFACE_CACHE_CAPACITY_IN_BYTES),
+ default_family_(NULL) {
TRACE_EVENT0("cobalt::renderer", "SkFontMgr_Cobalt::SkFontMgr_Cobalt()");
- // Ensure that both the CValManager and SkFontMgrCVals are initialized. The
- // CValManager is created first as it must outlast the SkFontMgrCVals.
- base::CValManager::GetInstance();
- SkFontMgrCVals::GetInstance();
SkTDArray<FontFamily*> font_families;
{
@@ -635,29 +38,17 @@
BuildNameToFamilyMap(directory, &font_families);
font_families.deleteAll();
- FindDefaultFont(default_fonts);
-
- {
- // Create the poller. The lazy instance ensures we only get one copy, and
- // that it will be cleaned up at appl exit, but we still need to lock
- // so we only construct it once.
- base::AutoLock lock(poller_lock.Get());
- if (!process_system_typefaces_with_open_streams_poller.Get()) {
- process_system_typefaces_with_open_streams_poller.Get().reset(
- new base::PollerWithThread(
- base::Bind(&SkFontMgr_Cobalt::HandlePeriodicProcessing,
- base::Unretained(this)),
- base::TimeDelta::FromMilliseconds(
- kPeriodicProcessingTimerDelayMs)));
- }
- }
+ FindDefaultFamily(default_families);
}
-SkTypeface* SkFontMgr_Cobalt::matchFaceName(const std::string& font_face_name) {
- if (font_face_name.empty()) {
+SkTypeface* SkFontMgr_Cobalt::MatchFaceName(const char face_name[]) {
+ if (!face_name) {
return NULL;
}
+ SkAutoAsciiToLC face_name_to_lc(face_name);
+ std::string face_name_string(face_name_to_lc.lc(), face_name_to_lc.length());
+
// Lock the style sets mutex prior to accessing them.
SkAutoMutexAcquire scoped_mutex(style_sets_mutex_);
@@ -669,12 +60,12 @@
: full_font_name_to_style_set_map_;
NameToStyleSetMap::iterator style_set_iterator =
- name_to_style_set_map.find(font_face_name);
+ name_to_style_set_map.find(face_name_string);
if (style_set_iterator != name_to_style_set_map.end()) {
SkFontStyleSet_Cobalt* style_set = style_set_iterator->second;
SkTypeface* typeface =
- i == 0 ? style_set->MatchFontPostScriptName(font_face_name)
- : style_set->MatchFullFontName(font_face_name);
+ i == 0 ? style_set->MatchFontPostScriptName(face_name_string)
+ : style_set->MatchFullFontName(face_name_string);
if (typeface != NULL) {
return typeface;
} else {
@@ -719,10 +110,10 @@
return NULL;
}
- SkAutoAsciiToLC tolc(family_name);
+ SkAutoAsciiToLC family_name_to_lc(family_name);
- NameToStyleSetMap::const_iterator family_iterator =
- name_to_family_map_.find(tolc.lc());
+ NameToStyleSetMap::const_iterator family_iterator = name_to_family_map_.find(
+ std::string(family_name_to_lc.lc(), family_name_to_lc.length()));
if (family_iterator != name_to_family_map_.end()) {
return SkRef(family_iterator->second);
}
@@ -732,18 +123,18 @@
SkTypeface* SkFontMgr_Cobalt::onMatchFamilyStyle(
const char family_name[], const SkFontStyle& style) const {
- SkTypeface* tf = NULL;
+ SkTypeface* typeface = NULL;
if (family_name) {
- SkAutoTUnref<SkFontStyleSet> sset(matchFamily(family_name));
- tf = sset->matchStyle(style);
+ SkAutoTUnref<SkFontStyleSet> style_set(matchFamily(family_name));
+ typeface = style_set->matchStyle(style);
}
- if (NULL == tf) {
- tf = default_family_->matchStyle(style);
+ if (NULL == typeface) {
+ typeface = default_family_->matchStyle(style);
}
- return tf;
+ return typeface;
}
SkTypeface* SkFontMgr_Cobalt::onMatchFaceStyle(const SkTypeface* family_member,
@@ -760,17 +151,15 @@
return NULL;
}
-#ifdef SK_FM_NEW_MATCH_FAMILY_STYLE_CHARACTER
-SkTypeface* SkFontMgr_Cobalt::onMatchFamilyStyleCharacter(
- const char family_name[], const SkFontStyle& style, const char* bcp47[],
- int bcp47_count, SkUnichar character) const {
-#else
SkTypeface* SkFontMgr_Cobalt::onMatchFamilyStyleCharacter(
const char family_name[], const SkFontStyle& style, const char bcp47_val[],
SkUnichar character) const {
const char** bcp47 = &bcp47_val;
int bcp47_count = bcp47_val ? 1 : 0;
-#endif
+
+ // Remove const from the manager. SkFontMgr_Cobalt modifies its internals
+ // within FindFamilyStyleCharacter().
+ SkFontMgr_Cobalt* font_mgr = const_cast<SkFontMgr_Cobalt*>(this);
// Lock the style sets mutex prior to calling |FindFamilyStyleCharacter|. It
// expects the mutex to already be locked.
@@ -782,8 +171,8 @@
for (int bcp47_index = bcp47_count; bcp47_index-- > 0;) {
SkLanguage language(bcp47[bcp47_index]);
while (!language.GetTag().isEmpty()) {
- SkTypeface* matching_typeface =
- FindFamilyStyleCharacter(style, language.GetTag(), character);
+ SkTypeface* matching_typeface = font_mgr->FindFamilyStyleCharacter(
+ style, language.GetTag(), character);
if (matching_typeface) {
return matching_typeface;
}
@@ -796,7 +185,7 @@
// requirement. This will select the first encountered family that contains
// the character.
SkTypeface* matching_typeface =
- FindFamilyStyleCharacter(style, SkString(), character);
+ font_mgr->FindFamilyStyleCharacter(style, SkString(), character);
// If no family was found that supports the character, then just fall back
// to the default family.
@@ -805,30 +194,30 @@
}
SkTypeface* SkFontMgr_Cobalt::onCreateFromData(SkData* data,
- int ttc_index) const {
+ int face_index) const {
SkAutoTUnref<SkStreamAsset> stream(new SkMemoryStream(data));
- return createFromStream(stream, ttc_index);
+ return createFromStream(stream, face_index);
}
SkTypeface* SkFontMgr_Cobalt::onCreateFromStream(SkStreamAsset* stream,
- int ttc_index) const {
+ int face_index) const {
TRACE_EVENT0("cobalt::renderer", "SkFontMgr_Cobalt::onCreateFromStream()");
bool is_fixed_pitch;
SkTypeface::Style style;
SkString name;
- if (!SkTypeface_FreeType::ScanFont(stream, ttc_index, &name, &style,
+ if (!SkTypeface_FreeType::ScanFont(stream, face_index, &name, &style,
&is_fixed_pitch)) {
return NULL;
}
return SkNEW_ARGS(SkTypeface_CobaltStream,
- (stream, ttc_index, style, is_fixed_pitch, name));
+ (stream, face_index, style, is_fixed_pitch, name));
}
SkTypeface* SkFontMgr_Cobalt::onCreateFromFile(const char path[],
- int ttc_index) const {
+ int face_index) const {
TRACE_EVENT0("cobalt::renderer", "SkFontMgr_Cobalt::onCreateFromFile()");
SkAutoTUnref<SkStreamAsset> stream(SkStream::NewFromFile(path));
- return stream.get() ? createFromStream(stream, ttc_index) : NULL;
+ return stream.get() ? createFromStream(stream, face_index) : NULL;
}
SkTypeface* SkFontMgr_Cobalt::onLegacyCreateTypeface(
@@ -857,8 +246,10 @@
}
}
- SkAutoTUnref<SkFontStyleSet_Cobalt> new_set(SkNEW_ARGS(
- SkFontStyleSet_Cobalt, (this, family, base_path, &style_sets_mutex_)));
+ SkAutoTUnref<SkFontStyleSet_Cobalt> new_set(
+ SkNEW_ARGS(SkFontStyleSet_Cobalt,
+ (family, base_path, &system_typeface_stream_manager_,
+ &style_sets_mutex_)));
// Verify that the set successfully added entries.
if (new_set->styles_.count() == 0) {
@@ -879,8 +270,6 @@
fallback_families_.push_back(new_set.get());
}
- // Handle adding the font face names for the style set entries. These are
- // optional, so many may not have them.
for (SkAutoTUnref<SkFontStyleSet_Cobalt::SkFontStyleSetEntry_Cobalt>*
font_style_set_entry = new_set->styles_.begin();
font_style_set_entry != new_set->styles_.end();
@@ -918,62 +307,46 @@
}
}
-void SkFontMgr_Cobalt::FindDefaultFont(
- const SkTArray<SkString, true>& default_fonts) {
- SkASSERT(!font_style_sets_.empty());
+void SkFontMgr_Cobalt::FindDefaultFamily(
+ const SkTArray<SkString, true>& default_families) {
+ CHECK(!font_style_sets_.empty());
- for (size_t i = 0; i < default_fonts.count(); ++i) {
- SkAutoTUnref<SkFontStyleSet_Cobalt> set(
- onMatchFamily(default_fonts[i].c_str()));
- if (NULL == set) {
+ for (size_t i = 0; i < default_families.count(); ++i) {
+ SkAutoTUnref<SkFontStyleSet_Cobalt> check_style_set(
+ onMatchFamily(default_families[i].c_str()));
+ if (NULL == check_style_set) {
continue;
}
- SkAutoTUnref<SkTypeface> tf(set->MatchStyleWithoutLocking(SkFontStyle()));
- if (NULL == tf) {
- continue;
- }
- default_family_ = set.get();
- default_typeface_.swap(&tf);
- break;
- }
- if (NULL == default_typeface_) {
- default_family_ = font_style_sets_[0].get();
- default_typeface_.reset(
- default_family_->MatchStyleWithoutLocking(SkFontStyle()));
- }
-
- SkASSERT(default_family_);
- SkASSERT(default_typeface_);
-
- // Remove the default typeface from the tracked system typefaces with open
- // streams. We never want to release it.
- for (int i = 0; i < system_typefaces_with_active_open_streams_.count(); ++i) {
- if (system_typefaces_with_active_open_streams_[i]->uniqueID() ==
- default_typeface_->uniqueID()) {
- system_typefaces_with_active_open_streams_.removeShuffle(i);
+ SkAutoTUnref<SkTypeface> check_typeface(
+ check_style_set->MatchStyleWithoutLocking(SkFontStyle()));
+ if (NULL != check_typeface) {
+ default_family_ = check_style_set.get();
break;
}
}
+
+ if (NULL == default_family_) {
+ SkAutoTUnref<SkTypeface> check_typeface(
+ font_style_sets_[0]->MatchStyleWithoutLocking(SkFontStyle()));
+ if (NULL != check_typeface) {
+ default_family_ = font_style_sets_[0].get();
+ }
+ }
+
+ CHECK(default_family_);
}
SkTypeface* SkFontMgr_Cobalt::FindFamilyStyleCharacter(
const SkFontStyle& style, const SkString& language_tag,
- SkUnichar character) const {
+ SkUnichar character) {
if (!font_character_map::IsCharacterValid(character)) {
return NULL;
}
- for (int i = 0; i < fallback_families_.count(); ++i) {
- SkFontStyleSet_Cobalt* family = fallback_families_[i];
-
- // If a language tag was provided and this family doesn't meet the language
- // requirement, then just skip over the family.
- if (!language_tag.isEmpty() &&
- !family->language_.GetTag().startsWith(language_tag.c_str())) {
- continue;
- }
-
+ StyleSetArray* fallback_families = GetMatchingFallbackFamilies(language_tag);
+ for (int i = 0; i < fallback_families->size(); ++i) {
+ SkFontStyleSet_Cobalt* family = (*fallback_families)[i];
if (family->ContainsCharacter(style, character)) {
SkTypeface* matching_typeface = family->MatchStyleWithoutLocking(style);
if (matching_typeface) {
@@ -985,134 +358,31 @@
return NULL;
}
-void SkFontMgr_Cobalt::HandlePeriodicProcessing() {
- base::TimeTicks current_time = base::TimeTicks::Now();
-
- ProcessSystemTypefacesWithOpenStreams(current_time);
-
- // If the required delay has elapsed since the last font cache purge, then
- // it's time to force another. This is accomplished by setting the limit to
- // 1 byte smaller than it's current size, which initiates a partial purge (it
- // always purges at least 25% of its contents). After this is done, the cache
- // is set back to its previous size.
- if ((current_time - last_font_cache_purge_time_).InMilliseconds() >=
- kPeriodicFontCachePurgeDelayMs) {
- last_font_cache_purge_time_ = current_time;
-
- size_t font_cache_used = SkGraphics::GetFontCacheUsed();
- if (font_cache_used > 0) {
- size_t font_cache_limit = SkGraphics::GetFontCacheLimit();
- SkGraphics::SetFontCacheLimit(font_cache_used - 1);
- SkGraphics::SetFontCacheLimit(font_cache_limit);
- }
- }
-}
-
-// NOTE: It is the responsibility of the caller to lock
-// |system_typeface_stream_mutex_|
-void SkFontMgr_Cobalt::AddSystemTypefaceWithActiveOpenStream(
- const SkTypeface_CobaltSystem* system_typeface) const {
- SkFontMgrCVals::GetInstance()
- ->system_typeface_open_stream_cache_size_in_bytes() +=
- system_typeface->GetOpenStreamSizeInBytes();
- system_typefaces_with_active_open_streams_.push_back(system_typeface);
-
- LOG(INFO) << "Total system typeface memory: "
- << SkFontMgrCVals::GetInstance()
- ->system_typeface_open_stream_cache_size_in_bytes()
- << " bytes";
-}
-
-// NOTE: It is the responsibility of the caller to lock
-// |system_typeface_stream_mutex_|
-void SkFontMgr_Cobalt::NotifySystemTypefaceOfOpenStreamActivity(
- const SkTypeface_CobaltSystem* system_typeface) const {
- // Walk through the system_typefaces with inactive open streams, searching for
- // the typeface with activity. It potentially is still in the active list and
- // won't be found here.
- for (int i = 0; i < system_typefaces_with_inactive_open_streams_.count();
- ++i) {
- // We found the typeface in the inactive list. Move it back to the active
- // list and break out. The typeface will never be in the list more than
- // once, so we don't need to continue searching.
- if (system_typefaces_with_inactive_open_streams_[i].typeface ==
- system_typeface) {
- system_typefaces_with_active_open_streams_.push_back(system_typeface);
- system_typefaces_with_inactive_open_streams_.removeShuffle(i);
- break;
- }
- }
-}
-
-void SkFontMgr_Cobalt::ProcessSystemTypefacesWithOpenStreams(
- const base::TimeTicks& current_time) const {
- SkAutoMutexAcquire scoped_mutex(system_typeface_stream_mutex_);
-
- // First, release the inactive open streams that meet the following
- // conditions:
- // 1. The cache is over its memory limit.
- // 2. The stream has been inactive for the required period of time.
- // Given that the streams are in temporal order, only the first stream in the
- // list ever needs to be checked. If it cannot be released, then all
- // subsequent streams are also guaranteed to not be releasable.
- while (!system_typefaces_with_inactive_open_streams_.empty()) {
- // The cache size is not over the memory limit. No open streams are released
- // while the cache is under its limit. Simply break out.
- if (SkFontMgrCVals::GetInstance()
- ->system_typeface_open_stream_cache_size_in_bytes()
- .value() < SkFontMgrCVals::GetInstance()
- ->system_typeface_open_stream_cache_limit_in_bytes()
- .value()) {
- break;
- }
-
- const TimedSystemTypeface& timed_system_typeface =
- system_typefaces_with_inactive_open_streams_[0];
-
- // The current stream has not been inactive long enough to be releasable. No
- // subsequent streams can meet the threshold either. Simply break out.
- if ((current_time - timed_system_typeface.time).InMilliseconds() <
- kReleaseInactiveSystemTypefaceOpenStreamsDelayMs) {
- break;
- }
-
- const SkTypeface_CobaltSystem* system_typeface =
- timed_system_typeface.typeface;
- DCHECK(system_typeface->IsOpenStreamInactive());
-
- SkFontMgrCVals::GetInstance()
- ->system_typeface_open_stream_cache_size_in_bytes() -=
- system_typeface->GetOpenStreamSizeInBytes();
- system_typeface->ReleaseStream();
- system_typefaces_with_inactive_open_streams_.removeShuffle(0);
-
- LOG(INFO) << "Total system typeface memory: "
- << SkFontMgrCVals::GetInstance()
- ->system_typeface_open_stream_cache_size_in_bytes()
- << " bytes";
+SkFontMgr_Cobalt::StyleSetArray* SkFontMgr_Cobalt::GetMatchingFallbackFamilies(
+ const SkString& language_tag) {
+ if (language_tag.isEmpty()) {
+ return &fallback_families_;
}
- // Now, walk the active open streams. Any that are found to be inactive are
- // moved from the active list to the inactive list, with the current time
- // included for tracking when it is time to release the inactive stream.
- // NOTE: The active streams are processed after the inactive streams so that
- // regardless of |kReleaseInactiveSystemTypefaceOpenStreamsDelayMs|, it is
- // guaranteed that active streams cannot immediately be released upon being
- // moved to the inactive list. They must be retained in the inactive list for
- // at least |kProcessSystemTypefacesWithOpenStreamsTimerDelayMs|. If this
- // desire changes, then the active open stream processing should be moved
- // above the inactive open stream processing.
- for (int i = 0; i < system_typefaces_with_active_open_streams_.count(); ++i) {
- const SkTypeface_CobaltSystem* system_typeface =
- system_typefaces_with_active_open_streams_[i];
- if (system_typeface->IsOpenStreamInactive()) {
- system_typefaces_with_active_open_streams_.removeShuffle(i--);
- system_typefaces_with_inactive_open_streams_.push_back(
- TimedSystemTypeface(system_typeface, current_time));
+ StyleSetArray*& language_fallback_families =
+ language_fallback_families_map_[language_tag.c_str()];
+
+ // The fallback families for a specific language tag are lazily populated. If
+ // this is the first time that this tag has been encountered, then create and
+ // populate the fallback families now.
+ if (language_fallback_families == NULL) {
+ language_fallback_families_array_.push_back(new StyleSetArray);
+ language_fallback_families = *language_fallback_families_array_.rbegin();
+
+ for (StyleSetArray::iterator iter = fallback_families_.begin();
+ iter != fallback_families_.end(); ++iter) {
+ SkFontStyleSet_Cobalt* fallback_family = *iter;
+ if (fallback_family->language_.GetTag().startsWith(
+ language_tag.c_str())) {
+ language_fallback_families->push_back(fallback_family);
+ }
}
}
-}
-SkMutex& SkFontMgr_Cobalt::GetSystemTypefaceStreamMutex() const {
- return system_typeface_stream_mutex_;
+ return language_fallback_families;
}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h
index a1a983c..081408e 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h
@@ -15,117 +15,18 @@
#ifndef COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTMGR_COBALT_H_
#define COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTMGR_COBALT_H_
-#include <bitset>
-#include <limits>
#include <string>
-#include <utility>
+#include <vector>
+#include "base/containers/small_map.h"
#include "base/hash_tables.h"
-#include "cobalt/base/poller.h"
+#include "base/memory/scoped_vector.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h"
#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h"
-#include "SkFontHost.h"
-#include "SkFontDescriptor.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h"
#include "SkFontMgr.h"
-#include "SkThread.h"
-#include "SkTypefaceCache.h"
-
-class SkFontMgr_Cobalt;
-class SkTypeface_CobaltSystem;
-
-// This class is used by SkFontMgr_Cobalt to hold SkTypeface_Cobalt families.
-//
-// To avoid the memory hit from keeping all fonts around, both the full
-// character map for fonts and the font faces for fonts are lazily loaded
-// when needed. After this, they are retained in memory.
-//
-// The style sets contain an array of page ranges, providing information on
-// characters that are potentially contained. To determine whether or not a
-// character is actually contained, the full character map is generated.
-class SkFontStyleSet_Cobalt : public SkFontStyleSet {
- public:
- struct SkFontStyleSetEntry_Cobalt : public SkRefCnt {
- // NOTE: |SkFontStyleSetEntry_Cobalt| objects are not guaranteed to last for
- // the lifetime of |SkFontMgr_Cobalt| and can be removed by their owning
- // |SkFontStyleSet_Cobalt| if their typeface fails to load properly. As a
- // result, it is not safe to store their pointers outside of
- // |SkFontStyleSet_Cobalt|.
- SkFontStyleSetEntry_Cobalt(const SkString& file_path, const int index,
- const SkFontStyle& style,
- const std::string& full_name,
- const std::string& postscript_name)
- : font_file_path(file_path),
- ttc_index(index),
- font_style(style),
- full_font_name(full_name),
- font_postscript_name(postscript_name),
- typeface(NULL) {}
-
- const SkString font_file_path;
- const int ttc_index;
- const SkFontStyle font_style;
-
- // these two members are declared as std::string since we have a hasher
- // for std::string, but not SkString at this point.
- const std::string full_font_name;
- const std::string font_postscript_name;
-
- SkAutoTUnref<SkTypeface> typeface;
- };
-
- SkFontStyleSet_Cobalt(const SkFontMgr_Cobalt* const manager,
- const FontFamily& family, const char* base_path,
- SkMutex* const manager_owned_mutex);
-
- // From SkFontStyleSet
- virtual int count() SK_OVERRIDE;
- // NOTE: SkFontStyleSet_Cobalt does not support |getStyle|, as publicly
- // accessing styles by index is unsafe.
- virtual void getStyle(int index, SkFontStyle* style,
- SkString* name) SK_OVERRIDE;
- // NOTE: SkFontStyleSet_Cobalt does not support |createTypeface|, as
- // publicly accessing styles by index is unsafe.
- virtual SkTypeface* createTypeface(int index) SK_OVERRIDE;
-
- virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE;
-
- const SkString& get_family_name() const { return family_name_; }
-
- private:
- // NOTE: It is the responsibility of the caller to lock the mutex before
- // calling any of the non-const private functions.
-
- SkTypeface* MatchStyleWithoutLocking(const SkFontStyle& pattern);
- SkTypeface* MatchFullFontName(const std::string& name);
- SkTypeface* MatchFontPostScriptName(const std::string& name);
- SkTypeface* TryRetrieveTypefaceAndRemoveStyleOnFailure(int style_index);
- bool ContainsTypeface(const SkTypeface* typeface);
-
- bool ContainsCharacter(const SkFontStyle& style, SkUnichar character);
- bool CharacterMapContainsCharacter(SkUnichar character);
- void GenerateCharacterMapFromData(SkData* font_data, int ttc_index);
-
- int GetClosestStyleIndex(const SkFontStyle& pattern);
- void CreateSystemTypeface(SkFontStyleSetEntry_Cobalt* style);
- void CreateSystemTypefaceFromData(SkFontStyleSetEntry_Cobalt* style,
- SkData* data);
-
- // NOTE: The following variables can safely be accessed outside of a lock.
- const SkFontMgr_Cobalt* const font_manager_;
- SkMutex* const manager_owned_mutex_;
-
- SkString family_name_;
- bool is_fallback_family_;
- SkLanguage language_;
- font_character_map::PageRanges page_ranges_;
-
- // NOTE: The following characters require locking when being accessed.
- bool is_character_map_generated_;
- font_character_map::CharacterMap character_map_;
-
- SkTArray<SkAutoTUnref<SkFontStyleSetEntry_Cobalt>, true> styles_;
-
- friend class SkFontMgr_Cobalt;
-};
+#include "SkTArray.h"
+#include "SkTypeface.h"
// This class is essentially a collection of SkFontStyleSet_Cobalt, one
// SkFontStyleSet_Cobalt for each family. This class may be modified to load
@@ -141,11 +42,13 @@
// fallback fonts are checked in order.
class SkFontMgr_Cobalt : public SkFontMgr {
public:
+ typedef std::vector<SkFontStyleSet_Cobalt*> StyleSetArray;
+
SkFontMgr_Cobalt(const char* directory,
const SkTArray<SkString, true>& default_fonts);
// NOTE: This returns NULL if a match is not found.
- SkTypeface* matchFaceName(const std::string& font_face_name);
+ SkTypeface* MatchFaceName(const char face_name[]);
protected:
// From SkFontMgr
@@ -168,15 +71,9 @@
// NOTE: This always returns a non-NULL value. If no match can be found, then
// the best match among the default family is returned.
-#ifdef SK_FM_NEW_MATCH_FAMILY_STYLE_CHARACTER
- virtual SkTypeface* onMatchFamilyStyleCharacter(
- const char family_name[], const SkFontStyle& style, const char* bcp47[],
- int bcp47_count, SkUnichar character) const SK_OVERRIDE;
-#else
virtual SkTypeface* onMatchFamilyStyleCharacter(
const char family_name[], const SkFontStyle& style, const char bcp47[],
SkUnichar character) const SK_OVERRIDE;
-#endif
// NOTE: This returns NULL if a match is not found.
virtual SkTypeface* onMatchFaceStyle(const SkTypeface* family_member,
@@ -185,15 +82,15 @@
// NOTE: This returns NULL if the typeface cannot be created.
virtual SkTypeface* onCreateFromData(SkData* data,
- int ttc_index) const SK_OVERRIDE;
+ int face_index) const SK_OVERRIDE;
// NOTE: This returns NULL if the typeface cannot be created.
virtual SkTypeface* onCreateFromStream(SkStreamAsset* stream,
- int ttc_index) const SK_OVERRIDE;
+ int face_index) const SK_OVERRIDE;
// NOTE: This returns NULL if the typeface cannot be created.
virtual SkTypeface* onCreateFromFile(const char path[],
- int ttc_index) const SK_OVERRIDE;
+ int face_index) const SK_OVERRIDE;
// NOTE: This always returns a non-NULL value. If no match can be found, then
// the best match among the default family is returned.
@@ -201,51 +98,27 @@
const char family_name[], unsigned style_bits) const SK_OVERRIDE;
private:
- struct TimedSystemTypeface {
- TimedSystemTypeface(const SkTypeface_CobaltSystem* system_typeface,
- const base::TimeTicks& event_time)
- : typeface(system_typeface), time(event_time) {}
-
- const SkTypeface_CobaltSystem* typeface;
- base::TimeTicks time;
- };
-
typedef base::hash_map<std::string, SkFontStyleSet_Cobalt*> NameToStyleSetMap;
+ typedef base::SmallMap<base::hash_map<std::string, StyleSetArray*> >
+ NameToStyleSetArrayMap;
void BuildNameToFamilyMap(const char* base_path,
SkTDArray<FontFamily*>* families);
- void FindDefaultFont(const SkTArray<SkString, true>& default_fonts);
+ void FindDefaultFamily(const SkTArray<SkString, true>& default_families);
+ // Returns the first encountered fallback family that matches the language tag
+ // and supports the specified character.
// NOTE: |style_sets_mutex_| should be locked prior to calling this function.
SkTypeface* FindFamilyStyleCharacter(const SkFontStyle& style,
- const SkString& lang_tag,
- SkUnichar character) const;
+ const SkString& language_tag,
+ SkUnichar character);
- void HandlePeriodicProcessing();
+ // Returns every fallback family that matches the language tag. If the tag is
+ // empty, then all fallback families are returned.
+ // NOTE: |style_sets_mutex_| should be locked prior to calling this function.
+ StyleSetArray* GetMatchingFallbackFamilies(const SkString& language_tag);
- // System typeface stream related
-
- // Called by SkTypeface_CobaltSystem when it opens a new stream, this adds
- // the system typeface to the active open stream list and adds the stream's
- // bytes into the open stream cache size.
- // NOTE1: This assumes that the typeface is not already in the list.
- // NOTE2: This requires the caller to lock |system_typeface_stream_mutex_|
- void AddSystemTypefaceWithActiveOpenStream(
- const SkTypeface_CobaltSystem* system_typeface) const;
- // Called by SkTypeface_CobaltSystem when an already open stream shows
- // activity, this moves the system typeface from the inactive open stream list
- // to the active open stream list.
- // NOTE: This requires the caller to lock |system_typeface_stream_mutex_|
- void NotifySystemTypefaceOfOpenStreamActivity(
- const SkTypeface_CobaltSystem* system_typeface) const;
- // Called by |system_typeface_open_stream_timer_|, this handles periodic
- // processing on the open streams, including moving typefaces from the
- // active to the inactive list and releasing inactive open streams.
- // NOTE: Handles locking |system_typeface_stream_mutex_| internally
- void ProcessSystemTypefacesWithOpenStreams(
- const base::TimeTicks& current_time) const;
- // Mutex shared by all system typefaces for accessing/modifying stream data.
- SkMutex& GetSystemTypefaceStreamMutex() const;
+ SkFileMemoryChunkStreamManager system_typeface_stream_manager_;
SkTArray<SkAutoTUnref<SkFontStyleSet_Cobalt>, true> font_style_sets_;
@@ -256,41 +129,20 @@
NameToStyleSetMap full_font_name_to_style_set_map_;
NameToStyleSetMap font_postscript_name_to_style_set_map_;
- SkTArray<SkFontStyleSet_Cobalt*> fallback_families_;
+ // Fallback families that are used during character fallback.
+ // All fallback families, regardless of language.
+ StyleSetArray fallback_families_;
+ // Language-specific fallback families. These are lazily populated from
+ // |fallback_families_| when a new language tag is requested.
+ ScopedVector<StyleSetArray> language_fallback_families_array_;
+ NameToStyleSetArrayMap language_fallback_families_map_;
+ // The default family that is used when no specific match is found during a
+ // request.
SkFontStyleSet_Cobalt* default_family_;
- SkAutoTUnref<SkTypeface> default_typeface_;
-
- // System typeface stream related
- // NOTE: mutable is required on all variables potentially modified via calls
- // from SkFontStyleSet_Cobalt::onOpenStream(), which is const.
-
- // Tracking of active and inactive open streams among the system typefaces.
- // The streams are initially active and are moved to the inactive list when
- // the stream's underlying SkData no longer has any external references.
- // Inactive open streams are stored and released in temporal order.
- mutable SkTArray<const SkTypeface_CobaltSystem*>
- system_typefaces_with_active_open_streams_;
- mutable SkTArray<TimedSystemTypeface>
- system_typefaces_with_inactive_open_streams_;
-
- // This mutex is used when accessing/modifying both the system typeface stream
- // data itself and the variables used with tracking that data.
- mutable SkMutex system_typeface_stream_mutex_;
// Mutex shared by all style sets for accessing their modifiable data.
mutable SkMutex style_sets_mutex_;
-
- // The last time that a partial purge of Skia's font cache was forced. This is
- // done periodically to ensure that unused fonts are not indefinitely
- // referenced by Skia.
- base::TimeTicks last_font_cache_purge_time_;
-
- // SkTypeface_CobaltSystem is a friend so that it can make system typeface
- // open stream calls into SkFontMgr_Cobalt from
- // SkTypeface_CobaltSystem::onOpenStream(). Those calls should not be
- // accessible publicly.
- friend class SkTypeface_CobaltSystem;
};
#endif // COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTMGR_COBALT_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt_factory.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt_factory.cc
index aea69d6..9722f21 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt_factory.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt_factory.cc
@@ -23,8 +23,8 @@
CHECK(PathService::Get(base::DIR_EXE, &font_directory));
font_directory = font_directory.Append(FILE_PATH_LITERAL("fonts"));
- SkTArray<SkString, true> default_fonts;
- default_fonts.push_back(SkString("sans-serif"));
+ SkTArray<SkString, true> default_families;
+ default_families.push_back(SkString("sans-serif"));
- return new SkFontMgr_Cobalt(font_directory.value().c_str(), default_fonts);
+ return new SkFontMgr_Cobalt(font_directory.value().c_str(), default_families);
}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
new file mode 100644
index 0000000..4a4f6c1
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.cc
@@ -0,0 +1,397 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h"
+
+#include <cmath>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <limits>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
+#include "SkOSFile.h"
+
+namespace {
+
+int MatchScore(const SkFontStyle& pattern, const SkFontStyle& candidate) {
+ int score = 0;
+ score += std::abs((pattern.width() - candidate.width()) * 100);
+ score += (pattern.isItalic() == candidate.isItalic()) ? 0 : 1000;
+ score += std::abs(pattern.weight() - candidate.weight());
+ return score;
+}
+
+} // namespace
+
+// These functions are used by FreeType during FT_Open_Face.
+extern "C" {
+
+static unsigned long sk_cobalt_ft_stream_io(FT_Stream ftStream,
+ unsigned long offset,
+ unsigned char* buffer,
+ unsigned long count) {
+ SkStreamAsset* stream =
+ static_cast<SkStreamAsset*>(ftStream->descriptor.pointer);
+ stream->seek(offset);
+ return stream->read(buffer, count);
+}
+
+static void sk_cobalt_ft_stream_close(FT_Stream) {}
+}
+
+// This class is used by SkFontMgr_Cobalt to hold SkTypeface_CobaltSystem
+// families.
+SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt(
+ const FontFamily& family, const char* base_path,
+ SkFileMemoryChunkStreamManager* const system_typeface_stream_manager,
+ SkMutex* const manager_owned_mutex)
+ : system_typeface_stream_manager_(system_typeface_stream_manager),
+ manager_owned_mutex_(manager_owned_mutex),
+ is_fallback_family_(family.is_fallback_family),
+ language_(family.language),
+ page_ranges_(family.page_ranges),
+ is_character_map_generated_(!is_fallback_family_) {
+ TRACE_EVENT0("cobalt::renderer",
+ "SkFontStyleSet_Cobalt::SkFontStyleSet_Cobalt()");
+ DCHECK(manager_owned_mutex_);
+
+ if (family.names.count() == 0) {
+ return;
+ }
+
+ family_name_ = family.names[0];
+
+ for (int i = 0; i < family.fonts.count(); ++i) {
+ const FontFileInfo& font_file = family.fonts[i];
+
+ SkString file_path(SkOSPath::Join(base_path, font_file.file_name.c_str()));
+
+ // Sanity check that something exists at this location.
+ if (!sk_exists(file_path.c_str(), kRead_SkFILE_Flag)) {
+ LOG(ERROR) << "Failed to find font file: " << file_path.c_str();
+ continue;
+ }
+
+ SkFontStyle style(font_file.weight, SkFontStyle::kNormal_Width,
+ font_file.style == FontFileInfo::kItalic_FontStyle
+ ? SkFontStyle::kItalic_Slant
+ : SkFontStyle::kUpright_Slant);
+
+ std::string full_font_name;
+ if (!font_file.full_font_name.isEmpty()) {
+ full_font_name = std::string(font_file.full_font_name.c_str(),
+ font_file.full_font_name.size());
+ }
+ std::string postscript_name;
+ if (!font_file.postscript_name.isEmpty()) {
+ postscript_name = std::string(font_file.postscript_name.c_str(),
+ font_file.postscript_name.size());
+ }
+
+ styles_.push_back().reset(
+ SkNEW_ARGS(SkFontStyleSetEntry_Cobalt,
+ (file_path, font_file.index, style, full_font_name,
+ postscript_name, font_file.disable_synthetic_bolding)));
+ }
+}
+
+int SkFontStyleSet_Cobalt::count() {
+ SkAutoMutexAcquire scoped_mutex(*manager_owned_mutex_);
+ return styles_.count();
+}
+
+void SkFontStyleSet_Cobalt::getStyle(int index, SkFontStyle* style,
+ SkString* name) {
+ // SkFontStyleSet_Cobalt does not support publicly interacting with entries
+ // via index, as entries can potentially be removed, thereby invalidating the
+ // indices.
+ NOTREACHED();
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::createTypeface(int index) {
+ // SkFontStyleSet_Cobalt does not support publicly interacting with entries
+ // via index, as entries can potentially be removed, thereby invalidating the
+ // indices.
+ NOTREACHED();
+ return NULL;
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::matchStyle(const SkFontStyle& pattern) {
+ SkAutoMutexAcquire scoped_mutex(*manager_owned_mutex_);
+ return MatchStyleWithoutLocking(pattern);
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::MatchStyleWithoutLocking(
+ const SkFontStyle& pattern) {
+ SkTypeface* typeface = NULL;
+ while (typeface == NULL && styles_.count() > 0) {
+ typeface = TryRetrieveTypefaceAndRemoveStyleOnFailure(
+ GetClosestStyleIndex(pattern));
+ }
+ return typeface;
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::MatchFullFontName(const std::string& name) {
+ for (int i = 0; i < styles_.count(); ++i) {
+ if (styles_[i]->full_font_name == name) {
+ return TryRetrieveTypefaceAndRemoveStyleOnFailure(i);
+ }
+ }
+ return NULL;
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::MatchFontPostScriptName(
+ const std::string& name) {
+ for (int i = 0; i < styles_.count(); ++i) {
+ if (styles_[i]->font_postscript_name == name) {
+ return TryRetrieveTypefaceAndRemoveStyleOnFailure(i);
+ }
+ }
+ return NULL;
+}
+
+SkTypeface* SkFontStyleSet_Cobalt::TryRetrieveTypefaceAndRemoveStyleOnFailure(
+ int style_index) {
+ DCHECK(style_index >= 0 && style_index < styles_.count());
+ SkFontStyleSetEntry_Cobalt* style = styles_[style_index];
+ // If the typeface doesn't already exist, then attempt to create it.
+ if (style->typeface == NULL) {
+ CreateSystemTypeface(style);
+ // If the creation attempt failed and the typeface is still NULL, then
+ // remove the entry from the set's styles.
+ if (style->typeface == NULL) {
+ styles_[style_index].swap(&styles_.back());
+ styles_.pop_back();
+ return NULL;
+ }
+ }
+ return SkRef(style->typeface.get());
+}
+
+bool SkFontStyleSet_Cobalt::ContainsTypeface(const SkTypeface* typeface) {
+ for (int i = 0; i < styles_.count(); ++i) {
+ if (styles_[i]->typeface == typeface) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SkFontStyleSet_Cobalt::ContainsCharacter(const SkFontStyle& style,
+ SkUnichar character) {
+ // If page ranges exist for this style set, then verify that this character
+ // falls within the ranges. Otherwise, the style set is treated as having a
+ // page range containing all characters.
+ size_t num_ranges = page_ranges_.count();
+ if (num_ranges > 0) {
+ int16 page = font_character_map::GetPage(character);
+
+ // Verify that the last page range is not less than the page containing the
+ // character. If it's less, then this character can't be contained
+ // within the pages. Otherwise, this last range acts as a sentinel for the
+ // search.
+ if (page_ranges_[num_ranges - 1].second < page) {
+ return false;
+ }
+
+ int range_index = 0;
+ while (page_ranges_[range_index].second < page) {
+ ++range_index;
+ }
+
+ if (page_ranges_[range_index].first > page) {
+ return false;
+ }
+ }
+
+ // If this point is reached, then the character is contained within one of
+ // the font style set's page ranges. Now, verify that the specific character
+ // is supported by the font style set.
+
+ // The character map is lazily generated. Generate it now if it isn't already
+ // generated.
+ if (!is_character_map_generated_) {
+ TRACE_EVENT0("cobalt::renderer",
+ "SkFontStyleSet_Cobalt::ContainsCharacter() and "
+ "!is_character_map_generated_");
+ // Attempt to load the closest font style from the set. If it fails to load,
+ // it will be removed from the set and, as long as font styles remain in the
+ // set, the logic will be attempted again.
+ while (styles_.count() > 0) {
+ int style_index = GetClosestStyleIndex(style);
+ SkFontStyleSetEntry_Cobalt* closest_style = styles_[style_index];
+
+ SkFileMemoryChunkStreamProvider* stream_provider =
+ system_typeface_stream_manager_->GetStreamProvider(
+ closest_style->font_file_path.c_str());
+
+ // Create a snapshot prior to loading any additional memory chunks. In the
+ // case where the typeface does not end up being created, this enables the
+ // stream provider to purge newly created memory chunks, while retaining
+ // any pre-existing ones.
+ scoped_ptr<const SkFileMemoryChunks> memory_chunks_snapshot(
+ stream_provider->CreateMemoryChunksSnapshot());
+
+ SkAutoTUnref<SkFileMemoryChunkStream> stream(
+ stream_provider->OpenStream());
+ if (GenerateStyleFaceInfo(closest_style, stream)) {
+ if (CharacterMapContainsCharacter(character)) {
+ CreateSystemTypeface(closest_style, stream_provider);
+ return true;
+ } else {
+ // If a typeface was not created, destroy the stream and purge any
+ // newly created memory chunks. The stream must be destroyed first or
+ // it will retain references to memory chunks, preventing them from
+ // being purged.
+ stream.reset(NULL);
+ stream_provider->PurgeUnusedMemoryChunks();
+ return false;
+ }
+ }
+
+ styles_[style_index].swap(&styles_.back());
+ styles_.pop_back();
+ }
+
+ is_character_map_generated_ = true;
+ }
+
+ return CharacterMapContainsCharacter(character);
+}
+
+bool SkFontStyleSet_Cobalt::CharacterMapContainsCharacter(SkUnichar character) {
+ font_character_map::CharacterMap::iterator page_iterator =
+ character_map_.find(font_character_map::GetPage(character));
+ return page_iterator != character_map_.end() &&
+ page_iterator->second.test(
+ font_character_map::GetPageCharacterIndex(character));
+}
+
+bool SkFontStyleSet_Cobalt::GenerateStyleFaceInfo(
+ SkFontStyleSetEntry_Cobalt* style, SkStreamAsset* stream) {
+ if (style->is_face_info_generated) {
+ return true;
+ }
+
+ TRACE_EVENT0("cobalt::renderer", "GenerateStyleFaceInfo()");
+
+ FT_Library freetype_lib;
+ if (FT_Init_FreeType(&freetype_lib) != 0) {
+ return false;
+ }
+
+ FT_StreamRec streamRec;
+ memset(&streamRec, 0, sizeof(streamRec));
+ streamRec.size = stream->getLength();
+ streamRec.descriptor.pointer = stream;
+ streamRec.read = sk_cobalt_ft_stream_io;
+ streamRec.close = sk_cobalt_ft_stream_close;
+
+ FT_Open_Args args;
+ memset(&args, 0, sizeof(args));
+ args.flags = FT_OPEN_STREAM;
+ args.stream = &streamRec;
+
+ FT_Face face;
+ FT_Error err = FT_Open_Face(freetype_lib, &args, style->face_index, &face);
+ if (err) {
+ FT_Done_FreeType(freetype_lib);
+ return false;
+ }
+
+ int face_style = SkTypeface::kNormal;
+ if (face->style_flags & FT_STYLE_FLAG_BOLD) {
+ face_style |= SkTypeface::kBold;
+ }
+ if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ face_style |= SkTypeface::kItalic;
+ }
+
+ style->face_name.set(face->family_name);
+ style->face_style = static_cast<SkTypeface::Style>(face_style);
+ style->face_is_fixed_pitch = FT_IS_FIXED_WIDTH(face);
+ style->is_face_info_generated = true;
+
+ // Map out this family's characters if they haven't been generated yet.
+ if (!is_character_map_generated_) {
+ FT_UInt glyph_index;
+
+ int last_page = -1;
+ font_character_map::PageCharacters* page_characters = NULL;
+
+ SkUnichar code_point = FT_Get_First_Char(face, &glyph_index);
+ while (glyph_index) {
+ int page = font_character_map::GetPage(code_point);
+ if (page != last_page) {
+ page_characters = &character_map_[page];
+ last_page = page;
+ }
+ page_characters->set(
+ font_character_map::GetPageCharacterIndex(code_point));
+
+ code_point = FT_Get_Next_Char(face, code_point, &glyph_index);
+ }
+
+ is_character_map_generated_ = true;
+ }
+
+ // release this font.
+ FT_Done_Face(face);
+
+ // shut down FreeType.
+ FT_Done_FreeType(freetype_lib);
+ return true;
+}
+
+int SkFontStyleSet_Cobalt::GetClosestStyleIndex(const SkFontStyle& pattern) {
+ int closest_index = 0;
+ int min_score = std::numeric_limits<int>::max();
+ for (int i = 0; i < styles_.count(); ++i) {
+ int score = MatchScore(pattern, styles_[i]->font_style);
+ if (score < min_score) {
+ closest_index = i;
+ min_score = score;
+ }
+ }
+ return closest_index;
+}
+
+void SkFontStyleSet_Cobalt::CreateSystemTypeface(
+ SkFontStyleSetEntry_Cobalt* style_entry,
+ SkFileMemoryChunkStreamProvider* stream_provider /*=NULL*/) {
+ TRACE_EVENT0("cobalt::renderer",
+ "SkFontStyleSet_Cobalt::CreateSystemTypeface()");
+
+ if (!stream_provider) {
+ stream_provider = system_typeface_stream_manager_->GetStreamProvider(
+ style_entry->font_file_path.c_str());
+ }
+
+ SkAutoTUnref<SkFileMemoryChunkStream> stream(stream_provider->OpenStream());
+ if (GenerateStyleFaceInfo(style_entry, stream)) {
+ LOG(ERROR) << "Scanned font from file: " << style_entry->face_name.c_str()
+ << "(" << style_entry->face_style << ")";
+ style_entry->typeface.reset(
+ SkNEW_ARGS(SkTypeface_CobaltSystem,
+ (stream_provider, style_entry->face_index,
+ style_entry->face_style, style_entry->face_is_fixed_pitch,
+ family_name_, style_entry->disable_synthetic_bolding)));
+ } else {
+ LOG(ERROR) << "Failed to scan font: "
+ << style_entry->font_file_path.c_str();
+ }
+}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h
new file mode 100644
index 0000000..da1b701
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontStyleSet_cobalt.h
@@ -0,0 +1,135 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTSTYLESET_COBALT_H_
+#define COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTSTYLESET_COBALT_H_
+
+#include <string>
+
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h"
+#include "SkFontMgr.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTArray.h"
+#include "SkTypeface.h"
+
+// This class is used by SkFontMgr_Cobalt to hold SkTypeface_Cobalt families.
+//
+// To avoid the memory hit from keeping all fonts around, both the full
+// character map for fonts and the font faces for fonts are lazily loaded
+// when needed. After this, they are retained in memory.
+//
+// The style sets contain an array of page ranges, providing information on
+// characters that are potentially contained. To determine whether or not a
+// character is actually contained, the full character map is generated.
+class SkFontStyleSet_Cobalt : public SkFontStyleSet {
+ public:
+ struct SkFontStyleSetEntry_Cobalt : public SkRefCnt {
+ // NOTE: |SkFontStyleSetEntry_Cobalt| objects are not guaranteed to last for
+ // the lifetime of |SkFontMgr_Cobalt| and can be removed by their owning
+ // |SkFontStyleSet_Cobalt| if their typeface fails to load properly. As a
+ // result, it is not safe to store their pointers outside of
+ // |SkFontStyleSet_Cobalt|.
+ SkFontStyleSetEntry_Cobalt(const SkString& file_path, const int face_index,
+ const SkFontStyle& style,
+ const std::string& full_name,
+ const std::string& postscript_name,
+ const bool disable_synthetic_bolding)
+ : font_file_path(file_path),
+ face_index(face_index),
+ font_style(style),
+ full_font_name(full_name),
+ font_postscript_name(postscript_name),
+ disable_synthetic_bolding(disable_synthetic_bolding),
+ is_face_info_generated(false),
+ face_style(SkTypeface::kNormal),
+ face_is_fixed_pitch(false),
+ typeface(NULL) {}
+
+ const SkString font_file_path;
+ const int face_index;
+ const SkFontStyle font_style;
+ const std::string full_font_name;
+ const std::string font_postscript_name;
+ const bool disable_synthetic_bolding;
+
+ // Face info is generated from the font file.
+ bool is_face_info_generated;
+ SkString face_name;
+ SkTypeface::Style face_style;
+ bool face_is_fixed_pitch;
+
+ SkAutoTUnref<SkTypeface> typeface;
+ };
+
+ SkFontStyleSet_Cobalt(
+ const FontFamily& family, const char* base_path,
+ SkFileMemoryChunkStreamManager* const system_typeface_stream_manager,
+ SkMutex* const manager_owned_mutex);
+
+ // From SkFontStyleSet
+ virtual int count() SK_OVERRIDE;
+ // NOTE: SkFontStyleSet_Cobalt does not support |getStyle|, as publicly
+ // accessing styles by index is unsafe.
+ virtual void getStyle(int index, SkFontStyle* style,
+ SkString* name) SK_OVERRIDE;
+ // NOTE: SkFontStyleSet_Cobalt does not support |createTypeface|, as
+ // publicly accessing styles by index is unsafe.
+ virtual SkTypeface* createTypeface(int index) SK_OVERRIDE;
+
+ virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE;
+
+ const SkString& get_family_name() const { return family_name_; }
+
+ private:
+ // NOTE: It is the responsibility of the caller to lock the mutex before
+ // calling any of the non-const private functions.
+
+ SkTypeface* MatchStyleWithoutLocking(const SkFontStyle& pattern);
+ SkTypeface* MatchFullFontName(const std::string& name);
+ SkTypeface* MatchFontPostScriptName(const std::string& name);
+ SkTypeface* TryRetrieveTypefaceAndRemoveStyleOnFailure(int style_index);
+ bool ContainsTypeface(const SkTypeface* typeface);
+
+ bool ContainsCharacter(const SkFontStyle& style, SkUnichar character);
+ bool CharacterMapContainsCharacter(SkUnichar character);
+
+ bool GenerateStyleFaceInfo(SkFontStyleSetEntry_Cobalt* style,
+ SkStreamAsset* stream);
+
+ int GetClosestStyleIndex(const SkFontStyle& pattern);
+ void CreateSystemTypeface(
+ SkFontStyleSetEntry_Cobalt* style,
+ SkFileMemoryChunkStreamProvider* stream_provider = NULL);
+
+ // NOTE: The following variables can safely be accessed outside of the mutex.
+ SkFileMemoryChunkStreamManager* const system_typeface_stream_manager_;
+ SkMutex* const manager_owned_mutex_;
+
+ SkString family_name_;
+ bool is_fallback_family_;
+ SkLanguage language_;
+ font_character_map::PageRanges page_ranges_;
+
+ // NOTE: The following characters require locking when being accessed.
+ bool is_character_map_generated_;
+ font_character_map::CharacterMap character_map_;
+
+ SkTArray<SkAutoTUnref<SkFontStyleSetEntry_Cobalt>, true> styles_;
+
+ friend class SkFontMgr_Cobalt;
+};
+
+#endif // COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKFONTSTYLESET_COBALT_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h
index b09b2c1..ef1af47 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontUtil_cobalt.h
@@ -101,12 +101,17 @@
};
typedef uint32_t FontStyle;
- FontFileInfo() : index(0), weight(400), style(kNormal_FontStyle) {}
+ FontFileInfo()
+ : index(0),
+ weight(400),
+ style(kNormal_FontStyle),
+ disable_synthetic_bolding(false) {}
SkString file_name;
int index;
int weight;
FontStyle style;
+ bool disable_synthetic_bolding;
SkString full_font_name;
SkString postscript_name;
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkOSFile_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkOSFile_cobalt.cc
index 5179439..7267c89 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkOSFile_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkOSFile_cobalt.cc
@@ -57,8 +57,13 @@
} // namespace
SkFILE* sk_fopen(const char path[], SkFILE_Flags sk_flags) {
- return ToSkFILE(base::CreatePlatformFile(
- FilePath(path), ToPlatformFlags(sk_flags), NULL, NULL));
+ base::PlatformFile platform_file = base::CreatePlatformFile(
+ FilePath(path), ToPlatformFlags(sk_flags), NULL, NULL);
+ if (platform_file == base::kInvalidPlatformFileValue) {
+ return NULL;
+ }
+
+ return ToSkFILE(platform_file);
}
void sk_fclose(SkFILE* sk_file) {
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.cc
new file mode 100644
index 0000000..60d30dc
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.cc
@@ -0,0 +1,336 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h"
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "SkOSFile.h"
+
+SkFileMemoryChunkStreamManager::SkFileMemoryChunkStreamManager(
+ const std::string& name, int cache_size_in_bytes)
+ : available_chunk_count_(cache_size_in_bytes /
+ SkFileMemoryChunk::kSizeInBytes),
+ cache_limit_in_bytes_(StringPrintf("Memory.%s.Capacity", name.c_str()),
+ cache_size_in_bytes,
+ "The byte capacity of the cache."),
+ cache_size_in_bytes_(
+ StringPrintf("Memory.%s.Size", name.c_str()), 0,
+ "Total number of bytes currently used by the cache.") {}
+
+SkFileMemoryChunkStreamProvider*
+SkFileMemoryChunkStreamManager::GetStreamProvider(
+ const std::string& file_path) {
+ SkAutoMutexAcquire scoped_mutex(stream_provider_mutex_);
+
+ // Return a pre-existing stream provider if it exists.
+ SkFileMemoryChunkStreamProviderMap::iterator iter =
+ stream_provider_map_.find(file_path);
+ if (iter != stream_provider_map_.end()) {
+ return iter->second;
+ }
+
+ // Otherwise, create a new stream provider, add it to the containers, and
+ // return it. This stream provider will be returned for any subsequent
+ // requests specifying this file, ensuring that memory chunks will be shared.
+ stream_provider_array_.push_back(
+ new SkFileMemoryChunkStreamProvider(file_path, this));
+ SkFileMemoryChunkStreamProvider* stream_provider =
+ *stream_provider_array_.rbegin();
+ stream_provider_map_[file_path] = stream_provider;
+ return stream_provider;
+}
+
+bool SkFileMemoryChunkStreamManager::TryReserveMemoryChunk() {
+ // First check to see if the count is already 0. If it is, then there's no
+ // available memory chunk to try to reserve. Simply return failure.
+ if (base::subtle::NoBarrier_Load(&available_chunk_count_) <= 0) {
+ return false;
+ }
+
+ // Decrement the available count behind a memory barrier. If the return value
+ // is less than 0, then another requester reserved the last available memory
+ // chunk first. In that case, restore the chunk to the count and return
+ // failure.
+ if (base::subtle::Barrier_AtomicIncrement(&available_chunk_count_, -1) < 0) {
+ base::subtle::NoBarrier_AtomicIncrement(&available_chunk_count_, 1);
+ return false;
+ }
+
+ // The chunk was successfully reserved. Add the reserved chunk to the used
+ // cache size.
+ cache_size_in_bytes_ += SkFileMemoryChunk::kSizeInBytes;
+ return true;
+}
+
+void SkFileMemoryChunkStreamManager::ReleaseReservedMemoryChunks(size_t count) {
+ base::subtle::NoBarrier_AtomicIncrement(&available_chunk_count_, count);
+ cache_size_in_bytes_ -= count * SkFileMemoryChunk::kSizeInBytes;
+}
+
+SkFileMemoryChunkStreamProvider::SkFileMemoryChunkStreamProvider(
+ const std::string& file_path, SkFileMemoryChunkStreamManager* manager)
+ : file_path_(file_path), manager_(manager) {}
+
+SkFileMemoryChunkStream* SkFileMemoryChunkStreamProvider::OpenStream() const {
+ return SkNEW_ARGS(SkFileMemoryChunkStream,
+ (const_cast<SkFileMemoryChunkStreamProvider*>(this)));
+}
+
+scoped_ptr<const SkFileMemoryChunks>
+SkFileMemoryChunkStreamProvider::CreateMemoryChunksSnapshot() {
+ SkAutoMutexAcquire scoped_mutex(memory_chunks_mutex_);
+ return make_scoped_ptr<const SkFileMemoryChunks>(
+ new SkFileMemoryChunks(memory_chunks_));
+}
+
+void SkFileMemoryChunkStreamProvider::PurgeUnusedMemoryChunks() {
+ size_t purge_count = 0;
+
+ // Scope the logic that accesses the memory chunks so that releasing reserved
+ // memory chunks does not happen within the lock.
+ {
+ SkAutoMutexAcquire scoped_mutex(memory_chunks_mutex_);
+
+ // Walk the memory chunks, adding any with a single ref. These are not
+ // externally referenced and can be purged.
+ std::vector<size_t> purge_indices;
+ for (SkFileMemoryChunks::iterator iter = memory_chunks_.begin();
+ iter != memory_chunks_.end(); ++iter) {
+ if (iter->second && iter->second->HasOneRef()) {
+ purge_indices.push_back(iter->first);
+ }
+ }
+
+ // If the memory chunks and purge indices have the same size, then every
+ // memory chunk is being purged and they can simply be cleared. Otherwise,
+ // the purge indices must individually be removed from the memory chunks.
+ if (memory_chunks_.size() == purge_indices.size()) {
+ memory_chunks_.clear();
+ } else {
+ for (std::vector<size_t>::iterator iter = purge_indices.begin();
+ iter != purge_indices.end(); ++iter) {
+ memory_chunks_.erase(*iter);
+ }
+ }
+
+ purge_count = purge_indices.size();
+ }
+
+ // Make any memory chunks that were purged available again.
+ if (purge_count > 0) {
+ manager_->ReleaseReservedMemoryChunks(purge_count);
+ }
+}
+
+scoped_refptr<const SkFileMemoryChunk>
+SkFileMemoryChunkStreamProvider::TryGetMemoryChunk(
+ size_t index, SkFileMemoryChunkStream* stream) {
+ // Scope the logic that initially accesses the memory chunks. This allows the
+ // creation of a new memory chunk and the read from the stream into its memory
+ // to occur outside of a lock.
+ {
+ SkAutoMutexAcquire scoped_mutex(memory_chunks_mutex_);
+ SkFileMemoryChunks::const_iterator iter = memory_chunks_.find(index);
+ if (iter != memory_chunks_.end()) {
+ return iter->second;
+ }
+ }
+
+ // Verify that the new memory chunk can be reserved before attempting to
+ // create it.
+ if (!manager_->TryReserveMemoryChunk()) {
+ return NULL;
+ }
+
+ // Create the memory chunk and attempt to read from the stream into it. If
+ // this fails, clear out |new_chunk|, which causes it to be deleted; however,
+ // do not return. Subsequent attempts will also fail, so the map is populated
+ // with a NULL value for this index to prevent the attempts from occurring.
+ scoped_refptr<SkFileMemoryChunk> new_chunk(new SkFileMemoryChunk);
+ if (!stream->ReadIndexIntoMemoryChunk(index, new_chunk)) {
+ new_chunk = NULL;
+ }
+
+ // Re-lock the mutex. It's time to potentially add the newly created chunk to
+ // |memory_chunks_|.
+ SkAutoMutexAcquire scoped_mutex(memory_chunks_mutex_);
+ scoped_refptr<const SkFileMemoryChunk>& chunk = memory_chunks_[index];
+ // If there isn't a pre-existing chunk (this can occur if there was a race
+ // between two calls to TryGetMemoryChunk() and the other call won), and the
+ // new chunk exists, then set the reference so that it'll be available for
+ // subsequent calls.
+ if (chunk == NULL && new_chunk != NULL) {
+ chunk = new_chunk;
+ } else {
+ // The new chunk will not be retained, so make the reserved chunk
+ // available again.
+ manager_->ReleaseReservedMemoryChunks(1);
+ }
+
+ return chunk;
+}
+
+SkFileMemoryChunkStream::SkFileMemoryChunkStream(
+ SkFileMemoryChunkStreamProvider* stream_provider)
+ : stream_provider_(stream_provider),
+ file_(sk_fopen(stream_provider->file_path().c_str(), kRead_SkFILE_Flag)),
+ file_position_(0),
+ stream_position_(0) {
+ file_length_ = file_ ? sk_fgetsize(file_) : 0;
+}
+
+SkFileMemoryChunkStream::~SkFileMemoryChunkStream() {
+ if (file_) {
+ sk_fclose(file_);
+ }
+}
+
+size_t SkFileMemoryChunkStream::read(void* buffer, size_t size) {
+ if (!file_) {
+ return 0;
+ }
+
+ size_t start_stream_position = stream_position_;
+ size_t end_stream_position = stream_position_ + size;
+
+ size_t start_index = start_stream_position / SkFileMemoryChunk::kSizeInBytes;
+ size_t end_index = end_stream_position / SkFileMemoryChunk::kSizeInBytes;
+
+ // Iterate through all of the chunk indices included within the read. Each is
+ // read into the buffer separately.
+ for (size_t current_index = start_index; current_index <= end_index;
+ ++current_index) {
+ size_t current_index_end_stream_position =
+ current_index == end_index
+ ? end_stream_position
+ : ((current_index + 1) * SkFileMemoryChunk::kSizeInBytes);
+ size_t index_desired_read_size =
+ current_index_end_stream_position - stream_position_;
+
+ // Look for the chunk within the stream's cached chunks. If it isn't there,
+ // then attempt to retrieve it from the stream provider and cache the
+ // result.
+ scoped_refptr<const SkFileMemoryChunk> memory_chunk;
+ SkFileMemoryChunks::const_iterator iter =
+ memory_chunks_.find(current_index);
+ if (iter == memory_chunks_.end()) {
+ memory_chunk = memory_chunks_[current_index] =
+ stream_provider_->TryGetMemoryChunk(current_index, this);
+ } else {
+ memory_chunk = iter->second;
+ }
+
+ // It's time to read the data specified by the index into the buffer. If
+ // the memory chunk exists, then the data can be directly copied from its
+ // memory; otherwise, the data needs to be read from the stream's file.
+ size_t index_actual_read_size;
+ if (memory_chunk) {
+ size_t index_start_position =
+ current_index * SkFileMemoryChunk::kSizeInBytes;
+ memcpy(buffer,
+ memory_chunk->memory + (stream_position_ - index_start_position),
+ index_desired_read_size);
+ index_actual_read_size = index_desired_read_size;
+ } else {
+ // Ensure that the file position matches the stream's position. If the
+ // seek fails, then set file position to an invalid value to ensure that
+ // the next read triggers a new seek, and break out.
+ if (file_position_ != stream_position_ &&
+ !sk_fseek(file_, stream_position_)) {
+ file_position_ = std::numeric_limits<size_t>::max();
+ break;
+ }
+
+ index_actual_read_size = sk_fread(buffer, index_desired_read_size, file_);
+ file_position_ = stream_position_ + index_actual_read_size;
+ }
+
+ stream_position_ += index_actual_read_size;
+
+ // Verify that the read succeeded. If it failed, then break out because
+ // nothing more can be processed; otherwise, if there are additional indices
+ // to process, update the buffer's pointer so that they will write to the
+ // correct memory location.
+ if (index_desired_read_size != index_actual_read_size) {
+ break;
+ } else if (current_index < end_index) {
+ buffer = reinterpret_cast<uint8_t*>(buffer) + index_actual_read_size;
+ }
+ }
+
+ return stream_position_ - start_stream_position;
+}
+
+bool SkFileMemoryChunkStream::isAtEnd() const {
+ return stream_position_ == file_length_;
+}
+
+bool SkFileMemoryChunkStream::rewind() {
+ stream_position_ = 0;
+ return true;
+}
+
+SkFileMemoryChunkStream* SkFileMemoryChunkStream::duplicate() const {
+ return stream_provider_->OpenStream();
+}
+
+size_t SkFileMemoryChunkStream::getPosition() const { return stream_position_; }
+
+bool SkFileMemoryChunkStream::seek(size_t position) {
+ stream_position_ = std::min(position, file_length_);
+ return true;
+}
+
+bool SkFileMemoryChunkStream::move(long offset) {
+ // Rewind back to the start of the stream if the offset would move it to a
+ // negative position.
+ if (offset < 0 && std::abs(offset) > stream_position_) {
+ return rewind();
+ }
+ return seek(stream_position_ + offset);
+}
+
+SkFileMemoryChunkStream* SkFileMemoryChunkStream::fork() const {
+ SkAutoTUnref<SkFileMemoryChunkStream> that(duplicate());
+ that->seek(stream_position_);
+ return that.detach();
+}
+
+size_t SkFileMemoryChunkStream::getLength() const { return file_length_; }
+
+bool SkFileMemoryChunkStream::ReadIndexIntoMemoryChunk(
+ size_t index, SkFileMemoryChunk* chunk) {
+ size_t index_position = index * SkFileMemoryChunk::kSizeInBytes;
+
+ // Ensure that the file position matches the index's position. If the seek
+ // fails, then set file position to an invalid value to ensure that the next
+ // read triggers a new seek, and return false.
+ if (file_position_ != index_position && !sk_fseek(file_, index_position)) {
+ file_position_ = std::numeric_limits<size_t>::max();
+ return false;
+ }
+
+ const size_t kChunkMaxReadSize = SkFileMemoryChunk::kSizeInBytes;
+ size_t desired_read_size =
+ std::min(file_length_ - index_position, kChunkMaxReadSize);
+ size_t actual_read_size = sk_fread(chunk->memory, desired_read_size, file_);
+ file_position_ = index_position + actual_read_size;
+
+ return desired_read_size == actual_read_size;
+}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h
new file mode 100644
index 0000000..7adb62f
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h
@@ -0,0 +1,212 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKSTREAM_COBALT_H_
+#define COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKSTREAM_COBALT_H_
+
+#include <string>
+
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+#include "base/containers/small_map.h"
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "cobalt/base/c_val.h"
+#include "SkStream.h"
+
+// The SkFileMemoryChunkStream classes provide a stream type that mixes features
+// of both file streams and memory streams. While the stream initially reads
+// from its file like a file stream, it lazily caches newly encountered 32KB
+// chunks of the file into memory, enabling it to avoid reading sections of the
+// file more than once--subsequent reads into those cached sections are made
+// directly from memory. This caching occurs until the manager's cache limit is
+// exhausted. After this, reads into uncached sections of the stream are always
+// made to the file and not cached, functioning similarly to a file stream.
+// Reads into the already cached sections are still handled in memory.
+//
+// This stream type provides much of the speed benefits of memory streams, while
+// also guaranteeing that memory will not be wasted on sections of files that
+// are never used and that the combined memory usage of the streams will never
+// exceed the specified limit. If the cache limit is set to 0, then the streams
+// do no caching and fully function like file streams.
+
+class SkFileMemoryChunk;
+class SkFileMemoryChunkStream;
+class SkFileMemoryChunkStreamProvider;
+
+typedef base::SmallMap<
+ base::hash_map<size_t, scoped_refptr<const SkFileMemoryChunk> >, 8>
+ SkFileMemoryChunks;
+typedef base::hash_map<std::string, SkFileMemoryChunkStreamProvider*>
+ SkFileMemoryChunkStreamProviderMap;
+
+class SkFileMemoryChunk : public base::RefCountedThreadSafe<SkFileMemoryChunk> {
+ public:
+ static const size_t kSizeInBytes = 32 * 1024;
+ uint8_t memory[kSizeInBytes];
+};
+
+// SkFileMemoryChunkStreamManager is a thread-safe class that generates
+// file-specific SkFileMemoryChunkStreamProvider objects, which can be
+// used to create streams for those files. It also handles tracking the memory
+// available for memory chunks within the cache and determining whether or not
+// additional chunks can be created.
+class SkFileMemoryChunkStreamManager {
+ public:
+ SkFileMemoryChunkStreamManager(const std::string& name,
+ int cache_size_in_bytes);
+
+ // Returns the stream provider associated with the file path. If it does not
+ // exist then it is created.
+ SkFileMemoryChunkStreamProvider* GetStreamProvider(
+ const std::string& file_path);
+
+ private:
+ friend SkFileMemoryChunkStreamProvider;
+
+ // Attempts to reserve an available memory chunk and returns true if
+ // successful. On success, |available_chunk_count_| is decremented by one.
+ bool TryReserveMemoryChunk();
+ // Adds the specified count to |available_chunk_count_|.
+ void ReleaseReservedMemoryChunks(size_t count);
+
+ SkMutex stream_provider_mutex_;
+ ScopedVector<SkFileMemoryChunkStreamProvider> stream_provider_array_;
+ SkFileMemoryChunkStreamProviderMap stream_provider_map_;
+
+ base::subtle::Atomic32 available_chunk_count_;
+
+ const base::CVal<base::cval::SizeInBytes, base::CValPublic>
+ cache_limit_in_bytes_;
+ base::CVal<base::cval::SizeInBytes, base::CValPublic> cache_size_in_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(SkFileMemoryChunkStreamManager);
+};
+
+// SkFileMemoryChunkStreamProvider is a thread-safe class that handles creating
+// streams for its specified file and processes requests from those streams for
+// memory chunks. This processing work includes verifying that additional memory
+// chunks are available with the manager, creating and caching new memory
+// chunks, and sharing cached memory chunks between all of the streams that it
+// created.
+class SkFileMemoryChunkStreamProvider {
+ public:
+ const std::string& file_path() const { return file_path_; }
+
+ // Returns a newly created SkFileMemoryChunkStream. It is the caller's
+ // responsibility to destroy it.
+ SkFileMemoryChunkStream* OpenStream() const;
+
+ // Returns a newly created snapshot of the provider's current memory chunks.
+ // While the snapshot exists, it retains references to all of those memory
+ // chunks, guaranteeing that they will be retained when
+ // PurgeUnusedMemoryChunks() is called.
+ scoped_ptr<const SkFileMemoryChunks> CreateMemoryChunksSnapshot();
+
+ // Purges all of the provider's memory chunks that are only referenced by the
+ // provider.
+ void PurgeUnusedMemoryChunks();
+
+ private:
+ friend SkFileMemoryChunkStream;
+ friend SkFileMemoryChunkStreamManager;
+
+ SkFileMemoryChunkStreamProvider(const std::string& file_path,
+ SkFileMemoryChunkStreamManager* manager);
+
+ // Returns the specified memory chunk if it exists. If it does not, attempts
+ // to create the memory chunk and populate it with data from the stream at the
+ // specified index. On success, the memory chunk is returned; otherwise, NULL
+ // is returned.
+ scoped_refptr<const SkFileMemoryChunk> TryGetMemoryChunk(
+ size_t index, SkFileMemoryChunkStream* stream);
+
+ const std::string file_path_;
+ SkFileMemoryChunkStreamManager* const manager_;
+
+ // The provider maintains a cache of all memory chunks that have been
+ // requested by any streams. This enables it share the memory chunks between
+ // all of the streams that it creates. The cache must be accessed behind a
+ // lock because the stream requests may come from multiple threads.
+ SkMutex memory_chunks_mutex_;
+ SkFileMemoryChunks memory_chunks_;
+
+ DISALLOW_COPY_AND_ASSIGN(SkFileMemoryChunkStreamProvider);
+};
+
+// SkFileMemoryChunkStream is a non-thread safe stream used within Skia, which
+// opens a file handle to the file specified by its provider. It can read data
+// from either memory chunks returned by its provider or from the file itself.
+// While it maintains an internal cache to previously retrieved memory chunks,
+// it relies on its provider for memory chunks that it has not already cached.
+// If a memory chunk is available for a location, the stream reads that location
+// directly from memory; otherwise, it reads the location from the file.
+//
+// NOTE: Although the provider handles creating new memory chunks, it does not
+// directly access the file and uses the requesting stream to read the data into
+// memory.
+class SkFileMemoryChunkStream : public SkStreamAsset {
+ public:
+ ~SkFileMemoryChunkStream();
+
+ // Required by SkStream
+ virtual size_t read(void* buffer, size_t size) SK_OVERRIDE;
+ virtual bool isAtEnd() const SK_OVERRIDE;
+
+ // Required by SkStreamRewindable
+ virtual bool rewind() SK_OVERRIDE;
+ virtual SkFileMemoryChunkStream* duplicate() const SK_OVERRIDE;
+
+ // Required by SkStreamSeekable
+ virtual size_t getPosition() const SK_OVERRIDE;
+ virtual bool seek(size_t position) SK_OVERRIDE;
+ virtual bool move(long offset) SK_OVERRIDE;
+ virtual SkFileMemoryChunkStream* fork() const SK_OVERRIDE;
+
+ // Required by SkStreamAsset
+ virtual size_t getLength() const SK_OVERRIDE;
+
+ private:
+ friend SkFileMemoryChunkStreamProvider;
+
+ explicit SkFileMemoryChunkStream(
+ SkFileMemoryChunkStreamProvider* stream_provider);
+
+ // Attempts to read the file position specified by the index into the chunk's
+ // memory. Returns true if the read was successful.
+ bool ReadIndexIntoMemoryChunk(size_t index, SkFileMemoryChunk* chunk);
+
+ SkFileMemoryChunkStreamProvider* const stream_provider_;
+
+ SkFILE* const file_;
+ size_t file_length_;
+ size_t file_position_;
+
+ size_t stream_position_;
+
+ // The stream maintains its own references to any memory chunks that it has
+ // requested from the provider. This allows it faster access to them, as it
+ // does not need to worry about thread safety within its locally cached
+ // chunks and can directly access them, whereas a lock is required when
+ // the provider accesses its own memory chunks. The stream's memory chunk
+ // references also enable the provider to know which memory chunks are being
+ // used and which can be purged.
+ SkFileMemoryChunks memory_chunks_;
+
+ DISALLOW_COPY_AND_ASSIGN(SkFileMemoryChunkStream);
+};
+
+#endif // COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKSTREAM_COBALT_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc
new file mode 100644
index 0000000..9d9f0de
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.cc
@@ -0,0 +1,81 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
+
+#include "SkFontDescriptor.h"
+#include "SkTypefaceCache.h"
+
+SkTypeface_Cobalt::SkTypeface_Cobalt(int face_index, Style style,
+ bool is_fixed_pitch,
+ const SkString family_name)
+ : INHERITED(style, SkTypefaceCache::NewFontID(), is_fixed_pitch),
+ face_index_(face_index),
+ family_name_(family_name),
+ synthesizes_bold_(!isBold()) {}
+
+void SkTypeface_Cobalt::onGetFamilyName(SkString* family_name) const {
+ *family_name = family_name_;
+}
+
+SkTypeface_CobaltStream::SkTypeface_CobaltStream(SkStreamAsset* stream,
+ int face_index, Style style,
+ bool is_fixed_pitch,
+ const SkString family_name)
+ : INHERITED(face_index, style, is_fixed_pitch, family_name),
+ stream_(SkRef(stream)) {
+ LOG(INFO) << "Created SkTypeface_CobaltMemory: " << family_name.c_str() << "("
+ << style << "); Size: " << stream_->getLength() << " bytes";
+}
+
+void SkTypeface_CobaltStream::onGetFontDescriptor(SkFontDescriptor* descriptor,
+ bool* serialize) const {
+ SkASSERT(descriptor);
+ SkASSERT(serialize);
+ descriptor->setFamilyName(family_name_.c_str());
+ descriptor->setFontFileName(NULL);
+ descriptor->setFontIndex(face_index_);
+ *serialize = true;
+}
+
+SkStreamAsset* SkTypeface_CobaltStream::onOpenStream(int* face_index) const {
+ *face_index = face_index_;
+ return stream_->duplicate();
+}
+
+SkTypeface_CobaltSystem::SkTypeface_CobaltSystem(
+ SkFileMemoryChunkStreamProvider* stream_provider, int face_index,
+ Style style, bool is_fixed_pitch, const SkString family_name,
+ bool disable_synthetic_bolding)
+ : INHERITED(face_index, style, is_fixed_pitch, family_name),
+ stream_provider_(stream_provider) {
+ if (disable_synthetic_bolding) {
+ synthesizes_bold_ = false;
+ }
+}
+
+void SkTypeface_CobaltSystem::onGetFontDescriptor(SkFontDescriptor* descriptor,
+ bool* serialize) const {
+ SkASSERT(descriptor);
+ SkASSERT(serialize);
+ descriptor->setFamilyName(family_name_.c_str());
+ descriptor->setFontFileName(stream_provider_->file_path().c_str());
+ descriptor->setFontIndex(face_index_);
+ *serialize = false;
+}
+
+SkStreamAsset* SkTypeface_CobaltSystem::onOpenStream(int* face_index) const {
+ *face_index = face_index_;
+ return stream_provider_->OpenStream();
+}
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h
new file mode 100644
index 0000000..0299cf2
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h
@@ -0,0 +1,78 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKTYPEFACE_COBALT_H_
+#define COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKTYPEFACE_COBALT_H_
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkStream_cobalt.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "third_party/skia/src/ports/SkFontHost_FreeType_common.h"
+
+class SkFontMgr_Cobalt;
+
+class SkTypeface_Cobalt : public SkTypeface_FreeType {
+ public:
+ SkTypeface_Cobalt(int face_index, Style style, bool is_fixed_pitch,
+ const SkString family_name);
+
+ bool synthesizes_bold() const { return synthesizes_bold_; }
+
+ protected:
+ virtual void onGetFamilyName(SkString* family_name) const SK_OVERRIDE;
+
+ int face_index_;
+ SkString family_name_;
+ bool synthesizes_bold_;
+
+ private:
+ typedef SkTypeface_FreeType INHERITED;
+};
+
+class SkTypeface_CobaltStream : public SkTypeface_Cobalt {
+ public:
+ SkTypeface_CobaltStream(SkStreamAsset* stream, int face_index, Style style,
+ bool is_fixed_pitch, const SkString family_name);
+
+ virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
+ bool* serialize) const SK_OVERRIDE;
+
+ virtual SkStreamAsset* onOpenStream(int* face_index) const SK_OVERRIDE;
+
+ private:
+ typedef SkTypeface_Cobalt INHERITED;
+
+ SkAutoTUnref<SkStreamAsset> stream_;
+};
+
+class SkTypeface_CobaltSystem : public SkTypeface_Cobalt {
+ public:
+ SkTypeface_CobaltSystem(SkFileMemoryChunkStreamProvider* stream_provider,
+ int face_index, Style style, bool is_fixed_pitch,
+ const SkString family_name,
+ bool disable_synthetic_bolding);
+
+ virtual void onGetFontDescriptor(SkFontDescriptor* descriptor,
+ bool* serialize) const SK_OVERRIDE;
+
+ virtual SkStreamAsset* onOpenStream(int* face_index) const SK_OVERRIDE;
+
+ private:
+ typedef SkTypeface_Cobalt INHERITED;
+
+ SkFileMemoryChunkStreamProvider* const stream_provider_;
+};
+
+#endif // COBALT_RENDERER_RASTERIZER_SKIA_SKIA_SRC_PORTS_SKTYPEFACE_COBALT_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
index 195d9dc..05e9723 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
@@ -20,6 +20,7 @@
#include "cobalt/renderer/rasterizer/skia/font.h"
#include "cobalt/renderer/rasterizer/skia/glyph_buffer.h"
#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
#include "cobalt/renderer/rasterizer/skia/software_image.h"
#include "cobalt/renderer/rasterizer/skia/software_mesh.h"
#include "cobalt/renderer/rasterizer/skia/typeface.h"
@@ -117,14 +118,16 @@
"SoftwareResourceProvider::GetLocalTypeface()");
SkAutoTUnref<SkFontMgr> font_manager(SkFontMgr::RefDefault());
- SkAutoTUnref<SkTypeface> typeface(font_manager->matchFamilyStyle(
- font_family_name, CobaltFontStyleToSkFontStyle(font_style)));
+ SkAutoTUnref<SkTypeface_Cobalt> typeface(
+ base::polymorphic_downcast<SkTypeface_Cobalt*>(
+ font_manager->matchFamilyStyle(
+ font_family_name, CobaltFontStyleToSkFontStyle(font_style))));
return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
}
scoped_refptr<render_tree::Typeface>
SoftwareResourceProvider::GetLocalTypefaceByFaceNameIfAvailable(
- const std::string& font_face_name) {
+ const char* font_face_name) {
TRACE_EVENT0("cobalt::renderer",
"SoftwareResourceProvider::GetLocalTypefaceIfAvailable()");
@@ -132,7 +135,8 @@
SkFontMgr_Cobalt* cobalt_font_manager =
base::polymorphic_downcast<SkFontMgr_Cobalt*>(font_manager.get());
- SkTypeface* typeface = cobalt_font_manager->matchFaceName(font_face_name);
+ SkTypeface_Cobalt* typeface = base::polymorphic_downcast<SkTypeface_Cobalt*>(
+ cobalt_font_manager->MatchFaceName(font_face_name));
if (typeface != NULL) {
SkAutoTUnref<SkTypeface> typeface_unref_helper(typeface);
return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
@@ -149,9 +153,11 @@
"SoftwareResourceProvider::GetCharacterFallbackTypeface()");
SkAutoTUnref<SkFontMgr> font_manager(SkFontMgr::RefDefault());
- SkAutoTUnref<SkTypeface> typeface(font_manager->matchFamilyStyleCharacter(
- 0, CobaltFontStyleToSkFontStyle(font_style), language.c_str(),
- character));
+ SkAutoTUnref<SkTypeface_Cobalt> typeface(
+ base::polymorphic_downcast<SkTypeface_Cobalt*>(
+ font_manager->matchFamilyStyleCharacter(
+ 0, CobaltFontStyleToSkFontStyle(font_style), language.c_str(),
+ character)));
return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
}
@@ -181,7 +187,9 @@
sanitized_data.get(), static_cast<size_t>(sanitized_data.Tell())));
SkAutoTUnref<SkStreamAsset> stream(new SkMemoryStream(skia_data));
- SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromStream(stream));
+ SkAutoTUnref<SkTypeface_Cobalt> typeface(
+ base::polymorphic_downcast<SkTypeface_Cobalt*>(
+ SkTypeface::CreateFromStream(stream)));
if (typeface) {
return scoped_refptr<render_tree::Typeface>(new SkiaTypeface(typeface));
} else {
@@ -219,6 +227,12 @@
return new SoftwareMesh(vertices.Pass(), draw_mode);
}
+scoped_refptr<render_tree::Image> SoftwareResourceProvider::DrawOffscreenImage(
+ const scoped_refptr<render_tree::Node>& root) {
+ UNREFERENCED_PARAMETER(root);
+ return scoped_refptr<render_tree::Image>(NULL);
+}
+
} // namespace skia
} // namespace rasterizer
} // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
index f74491b..4ed9bde 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
@@ -45,22 +45,34 @@
scoped_refptr<render_tree::Image> CreateImage(
scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE;
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 4
scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
SbDecodeTarget decode_target) OVERRIDE {
NOTREACHED();
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
- SbDecodeTargetDestroy(decode_target);
-#else // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
SbDecodeTargetRelease(decode_target);
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+ return NULL;
+ }
+
+ SbDecodeTargetGraphicsContextProvider*
+ GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
+ return NULL;
+ }
+
+ bool SupportsSbDecodeTarget() OVERRIDE { return false; }
+#elif SB_API_VERSION >= 3
+ scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
+ SbDecodeTarget decode_target) OVERRIDE {
+ NOTREACHED();
+ SbDecodeTargetDestroy(decode_target);
return NULL;
}
SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
bool SupportsSbDecodeTarget() OVERRIDE { return false; }
-#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif // SB_API_VERSION >= 4
+#endif // SB_HAS(GRAPHICS)
scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory(
size_t size_in_bytes, size_t alignment) OVERRIDE;
@@ -75,7 +87,7 @@
const char* font_family_name, render_tree::FontStyle font_style) OVERRIDE;
scoped_refptr<render_tree::Typeface> GetLocalTypefaceByFaceNameIfAvailable(
- const std::string& font_face_name) OVERRIDE;
+ const char* font_face_name) OVERRIDE;
scoped_refptr<render_tree::Typeface> GetCharacterFallbackTypeface(
int32 character, render_tree::FontStyle font_style,
@@ -106,6 +118,9 @@
scoped_ptr<std::vector<render_tree::Mesh::Vertex> > vertices,
render_tree::Mesh::DrawMode draw_mode) OVERRIDE;
+ scoped_refptr<render_tree::Image> DrawOffscreenImage(
+ const scoped_refptr<render_tree::Node>& root) OVERRIDE;
+
private:
TextShaper text_shaper_;
};
diff --git a/src/cobalt/renderer/rasterizer/skia/text_shaper.cc b/src/cobalt/renderer/rasterizer/skia/text_shaper.cc
index 8a2e86e..5d8ee0f 100644
--- a/src/cobalt/renderer/rasterizer/skia/text_shaper.cc
+++ b/src/cobalt/renderer/rasterizer/skia/text_shaper.cc
@@ -35,7 +35,11 @@
class SingleFontFontProvider : public render_tree::FontProvider {
public:
explicit SingleFontFontProvider(const scoped_refptr<render_tree::Font>& font)
- : font_(font) {}
+ : size_(base::polymorphic_downcast<Font*>(font.get())->size()),
+ font_(font) {}
+
+ const render_tree::FontStyle& style() const OVERRIDE { return style_; }
+ float size() const OVERRIDE { return size_; }
const scoped_refptr<render_tree::Font>& GetCharacterFont(
int32 utf32_character, render_tree::GlyphIndex* glyph_index) OVERRIDE {
@@ -44,10 +48,12 @@
}
private:
+ render_tree::FontStyle style_;
+ float size_;
scoped_refptr<render_tree::Font> font_;
};
-void TryAddFontToUsedFonts(const scoped_refptr<render_tree::Font>& font,
+void TryAddFontToUsedFonts(Font* font,
render_tree::FontVector* maybe_used_fonts) {
if (!maybe_used_fonts) {
return;
@@ -56,7 +62,7 @@
// Verify that the font has not already been added to the used fonts, before
// adding it to the end.
for (int i = 0; i < maybe_used_fonts->size(); ++i) {
- if ((*maybe_used_fonts)[i]->GetTypefaceId() == font->GetTypefaceId()) {
+ if ((*maybe_used_fonts)[i] == font) {
return;
}
}
@@ -64,6 +70,20 @@
maybe_used_fonts->push_back(font);
}
+bool ShouldFakeBoldText(const render_tree::FontProvider* font_provider,
+ const Font* font) {
+ // A font-weight greater than 500 indicates bold if it is available. Use
+ // synthetic bolding if this is a bold weight and the selected font
+ // synthesizes bold.
+ // https://www.w3.org/TR/css-fonts-3/#font-weight-prop
+ // https://www.w3.org/TR/css-fonts-3/#font-style-matching
+ if (font_provider->style().weight > 500) {
+ SkAutoTUnref<SkTypeface_Cobalt> typeface(font->GetSkTypeface());
+ return typeface->synthesizes_bold();
+ }
+ return false;
+}
+
} // namespace
TextShaper::TextShaper()
@@ -232,7 +252,7 @@
// 2. If the characters use different scripts, the next script isn't
// inherited, and the next character doesn't support the current
// script.
- if ((current_font->GetTypefaceId() != next_font->GetTypefaceId()) ||
+ if ((current_font != next_font) ||
((current_script != next_script) &&
(next_script != USCRIPT_INHERITED) &&
(!uscript_hasScript(next_character, current_script)))) {
@@ -310,6 +330,7 @@
if (maybe_builder) {
SkPaint paint = script_run.font->GetSkPaint();
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setFakeBoldText(ShouldFakeBoldText(font_provider, script_run.font));
run_buffer = &(maybe_builder->allocRunPos(paint, glyph_count));
}
@@ -426,22 +447,13 @@
// If there's a builder (meaning that a glyph buffer is being generated),
// then we need to update the glyph buffer data for the new glyph.
if (maybe_builder) {
- // If at least one glyph has previously been added and the typeface has
- // changed, then the current run has ended and we need to add the run
- // into the glyph buffer. Allocate space within the text blob for the
- // glyphs and copy them into the blob.
- if (glyph_count > 0 &&
- last_font->GetTypefaceId() != current_font->GetTypefaceId()) {
+ // If at least one glyph has previously been added and the font has
+ // changed, then the current run has ended and we need to add the font run
+ // into the glyph buffer.
+ if (glyph_count > 0 && last_font != current_font) {
TryAddFontToUsedFonts(last_font, maybe_used_fonts);
-
- SkPaint paint = last_font->GetSkPaint();
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- const SkTextBlobBuilder::RunBuffer& buffer =
- maybe_builder->allocRunPosH(paint, glyph_count, 0);
- std::copy(&local_glyphs_[0], &local_glyphs_[0] + glyph_count,
- buffer.glyphs);
- std::copy(&local_positions_[0], &local_positions_[0] + glyph_count,
- buffer.pos);
+ AddFontRunToGlyphBuffer(font_provider, last_font, glyph_count,
+ maybe_builder);
glyph_count = 0;
}
@@ -464,22 +476,31 @@
}
// If there's a builder (meaning that a glyph buffer is being generated), and
- // the at least one glyph was generated, then allocate space within the text
- // blob for the glyphs and copy them into the blob.
+ // at least one glyph was generated, then we need to add the final font run
+ // into the glyph buffer.
if (maybe_builder && glyph_count > 0) {
TryAddFontToUsedFonts(last_font, maybe_used_fonts);
-
- SkPaint paint = last_font->GetSkPaint();
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- const SkTextBlobBuilder::RunBuffer& buffer =
- maybe_builder->allocRunPosH(paint, glyph_count, 0);
- std::copy(&local_glyphs_[0], &local_glyphs_[0] + glyph_count,
- buffer.glyphs);
- std::copy(&local_positions_[0], &local_positions_[0] + glyph_count,
- buffer.pos);
+ AddFontRunToGlyphBuffer(font_provider, last_font, glyph_count,
+ maybe_builder);
}
}
+void TextShaper::AddFontRunToGlyphBuffer(
+ const render_tree::FontProvider* font_provider, const Font* font,
+ const int glyph_count, SkTextBlobBuilder* builder) {
+ SkPaint paint = font->GetSkPaint();
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setFakeBoldText(ShouldFakeBoldText(font_provider, font));
+
+ // Allocate space within the text blob for the glyphs and copy them into the
+ // blob.
+ const SkTextBlobBuilder::RunBuffer& buffer =
+ builder->allocRunPosH(paint, glyph_count, 0);
+ std::copy(&local_glyphs_[0], &local_glyphs_[0] + glyph_count, buffer.glyphs);
+ std::copy(&local_positions_[0], &local_positions_[0] + glyph_count,
+ buffer.pos);
+}
+
void TextShaper::EnsureLocalGlyphArraysHaveSize(size_t size) {
if (local_glyph_array_size_ < size) {
local_glyph_array_size_ = size;
diff --git a/src/cobalt/renderer/rasterizer/skia/text_shaper.h b/src/cobalt/renderer/rasterizer/skia/text_shaper.h
index 0f00de3..982e1d3 100644
--- a/src/cobalt/renderer/rasterizer/skia/text_shaper.h
+++ b/src/cobalt/renderer/rasterizer/skia/text_shaper.h
@@ -164,6 +164,12 @@
render_tree::FontVector* maybe_used_fonts,
float* current_width);
+ // Add the glyphs from a run of a single font within a simple text run to the
+ // glyph buffer.
+ void AddFontRunToGlyphBuffer(const render_tree::FontProvider* font_provider,
+ const Font* font, const int glyph_count,
+ SkTextBlobBuilder* builder);
+
// Verifies that the glyph arrays have the required size allocated. If they do
// not, then the arrays are re-allocated with the required size.
void EnsureLocalGlyphArraysHaveSize(size_t size);
diff --git a/src/cobalt/renderer/rasterizer/skia/typeface.cc b/src/cobalt/renderer/rasterizer/skia/typeface.cc
index b1006d5..e4396fe 100644
--- a/src/cobalt/renderer/rasterizer/skia/typeface.cc
+++ b/src/cobalt/renderer/rasterizer/skia/typeface.cc
@@ -29,11 +29,12 @@
namespace rasterizer {
namespace skia {
-SkiaTypeface::SkiaTypeface(SkTypeface* typeface) : typeface_(SkRef(typeface)) {
+SkiaTypeface::SkiaTypeface(SkTypeface_Cobalt* typeface)
+ : typeface_(SkRef(typeface)) {
character_glyph_thread_checker_.DetachFromThread();
}
-SkTypeface* SkiaTypeface::GetSkTypeface() const {
+SkTypeface_Cobalt* SkiaTypeface::GetSkTypeface() const {
return SkRef(typeface_.get());
}
diff --git a/src/cobalt/renderer/rasterizer/skia/typeface.h b/src/cobalt/renderer/rasterizer/skia/typeface.h
index ebfb5ac..54ae436 100644
--- a/src/cobalt/renderer/rasterizer/skia/typeface.h
+++ b/src/cobalt/renderer/rasterizer/skia/typeface.h
@@ -20,10 +20,10 @@
#include "base/threading/thread_checker.h"
#include "cobalt/render_tree/font.h"
#include "cobalt/render_tree/typeface.h"
+#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkTypeface_cobalt.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkRefCnt.h"
-#include "third_party/skia/include/core/SkTypeface.h"
namespace cobalt {
namespace renderer {
@@ -36,13 +36,13 @@
// than the thread making the character glyph queries.
class SkiaTypeface : public render_tree::Typeface {
public:
- explicit SkiaTypeface(SkTypeface* typeface);
+ explicit SkiaTypeface(SkTypeface_Cobalt* typeface);
- // Returns the contained SkTypeface object, which has its reference count
- // incremented.
+ // Returns the contained SkTypeface_Cobalt object, which has its reference
+ // count incremented.
// NOTE: The caller is responsible for decrementing the reference count after
// finishing with the object.
- SkTypeface* GetSkTypeface() const;
+ SkTypeface_Cobalt* GetSkTypeface() const;
// From render_tree::Typeface
@@ -69,7 +69,7 @@
typedef base::hash_map<int32, render_tree::GlyphIndex> CharacterToGlyphMap;
// The underlying SkTypeface that was used to create this typeface.
- SkAutoTUnref<SkTypeface> typeface_;
+ SkAutoTUnref<SkTypeface_Cobalt> typeface_;
// The glyphs for characters are lazily computed and cached to speed up later
// lookups. The page containing indices 0-255 is optimized within an array.
diff --git a/src/cobalt/renderer/rasterizer/testdata/DrawOffscreenImage-expected.png b/src/cobalt/renderer/rasterizer/testdata/DrawOffscreenImage-expected.png
new file mode 100644
index 0000000..378c5c7
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/DrawOffscreenImage-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/render_tree_pixel_tester.cc b/src/cobalt/renderer/render_tree_pixel_tester.cc
index 601e6a2..eb92656 100644
--- a/src/cobalt/renderer/render_tree_pixel_tester.cc
+++ b/src/cobalt/renderer/render_tree_pixel_tester.cc
@@ -64,7 +64,8 @@
// Create our offscreen surface that will be the target of our test
// rasterizations.
test_surface_ =
- graphics_context_->CreateOffscreenRenderTarget(test_surface_dimensions);
+ graphics_context_->CreateDownloadableOffscreenRenderTarget(
+ test_surface_dimensions);
// Create the rasterizer using the platform default RenderModule options.
RendererModule::Options render_module_options;
diff --git a/src/cobalt/renderer/renderer.gyp b/src/cobalt/renderer/renderer.gyp
index 9c50da9..778b895 100644
--- a/src/cobalt/renderer/renderer.gyp
+++ b/src/cobalt/renderer/renderer.gyp
@@ -22,8 +22,6 @@
'target_name': 'renderer',
'type': 'static_library',
'sources': [
- 'frame_rate_throttler.cc',
- 'frame_rate_throttler.h',
'pipeline.cc',
'pipeline.h',
'renderer_module.cc',
@@ -49,6 +47,7 @@
'<(DEPTH)/cobalt/renderer/backend/backend.gyp:renderer_backend',
'<(DEPTH)/cobalt/renderer/rasterizer/rasterizer.gyp:rasterizer',
'<(DEPTH)/cobalt/system_window/system_window.gyp:system_window',
+ '<(DEPTH)/nb/nb.gyp:nb',
],
'conditions': [
['OS=="starboard"', {
diff --git a/src/cobalt/renderer/renderer_module.cc b/src/cobalt/renderer/renderer_module.cc
index 241707b..04d18b9 100644
--- a/src/cobalt/renderer/renderer_module.cc
+++ b/src/cobalt/renderer/renderer_module.cc
@@ -15,6 +15,7 @@
#include "cobalt/renderer/renderer_module.h"
#include "base/debug/trace_event.h"
+#include "cobalt/browser/memory_settings/calculations.h"
#include "cobalt/renderer/backend/default_graphics_system.h"
#include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
#include "cobalt/renderer/rasterizer/skia/software_rasterizer.h"
@@ -22,7 +23,8 @@
namespace cobalt {
namespace renderer {
-RendererModule::Options::Options() {
+RendererModule::Options::Options()
+ : skia_texture_atlas_dimensions(2048, 2048) {
// Call into platform-specific code for setting up render module options.
SetPerPlatformDefaultOptions();
}
@@ -31,7 +33,6 @@
const Options& options)
: system_window_(system_window), options_(options) {
TRACE_EVENT0("cobalt::renderer", "RendererModule::RendererModule()");
-
Resume();
}
diff --git a/src/cobalt/renderer/renderer_module.h b/src/cobalt/renderer/renderer_module.h
index 65eeafd..8446117 100644
--- a/src/cobalt/renderer/renderer_module.h
+++ b/src/cobalt/renderer/renderer_module.h
@@ -46,6 +46,10 @@
// hardware-accelerated Skia rasterizer.
int scratch_surface_cache_size_in_bytes;
+ // Represents the dimensions of the skia texture atlas which holds all of
+ // the glyph textures used during rendering.
+ math::Size skia_texture_atlas_dimensions;
+
// Determines the capacity of the skia cache. The Skia cache is maintained
// within Skia and is used to cache the results of complicated effects such
// as shadows, so that Skia draw calls that are used repeatedly across
diff --git a/src/cobalt/renderer/renderer_module_default_options_starboard.cc b/src/cobalt/renderer/renderer_module_default_options_starboard.cc
index 2e09f6f..443c212 100644
--- a/src/cobalt/renderer/renderer_module_default_options_starboard.cc
+++ b/src/cobalt/renderer/renderer_module_default_options_starboard.cc
@@ -39,16 +39,20 @@
return scoped_ptr<rasterizer::Rasterizer>(
new rasterizer::egl::SoftwareRasterizer(
graphics_context, options.surface_cache_size_in_bytes));
-#elif defined(COBALT_FORCE_CUSTOM_RASTERIZER)
+#elif defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
return scoped_ptr<rasterizer::Rasterizer>(
new rasterizer::egl::HardwareRasterizer(
- graphics_context, options.skia_cache_size_in_bytes,
+ graphics_context, options.skia_texture_atlas_dimensions.width(),
+ options.skia_texture_atlas_dimensions.height(),
+ options.skia_cache_size_in_bytes,
options.scratch_surface_cache_size_in_bytes,
options.surface_cache_size_in_bytes));
#else
return scoped_ptr<rasterizer::Rasterizer>(
new rasterizer::skia::HardwareRasterizer(
- graphics_context, options.skia_cache_size_in_bytes,
+ graphics_context, options.skia_texture_atlas_dimensions.width(),
+ options.skia_texture_atlas_dimensions.height(),
+ options.skia_cache_size_in_bytes,
options.scratch_surface_cache_size_in_bytes,
options.surface_cache_size_in_bytes));
#endif // COBALT_FORCE_SOFTWARE_RASTERIZER
@@ -60,7 +64,9 @@
#else
return scoped_ptr<rasterizer::Rasterizer>(
new rasterizer::blitter::HardwareRasterizer(
- graphics_context, options.scratch_surface_cache_size_in_bytes,
+ graphics_context, options.skia_texture_atlas_dimensions.width(),
+ options.skia_texture_atlas_dimensions.height(),
+ options.scratch_surface_cache_size_in_bytes,
options.surface_cache_size_in_bytes,
options.software_surface_cache_size_in_bytes));
#endif // COBALT_FORCE_SOFTWARE_RASTERIZER
@@ -75,11 +81,12 @@
void RendererModule::Options::SetPerPlatformDefaultOptions() {
// Set default options from the current build's Starboard configuration.
surface_cache_size_in_bytes = COBALT_SURFACE_CACHE_SIZE_IN_BYTES;
- skia_cache_size_in_bytes = COBALT_SKIA_CACHE_SIZE_IN_BYTES;
+ // Default to 4MB, but this may be modified externally.
+ skia_cache_size_in_bytes = 4 * 1024 * 1024;
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;
+ // 8MB default for software_surface_cache.
+ software_surface_cache_size_in_bytes = 8 * 1024 * 1024;
// 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 99d0e28..ce438b5 100644
--- a/src/cobalt/renderer/renderer_parameters_setup.gypi
+++ b/src/cobalt/renderer/renderer_parameters_setup.gypi
@@ -14,10 +14,8 @@
{
'defines': [
- '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"', {
@@ -30,9 +28,10 @@
'COBALT_FORCE_STUB_RASTERIZER',
],
}],
- ['rasterizer_type == "custom"', {
+ ['rasterizer_type == "direct-gles"', {
'defines': [
- 'COBALT_FORCE_CUSTOM_RASTERIZER',
+ 'COBALT_FORCE_DIRECT_GLES_RASTERIZER',
+ 'COBALT_RASTERIZER_USES_DEPTH_BUFFER',
],
}],
],
diff --git a/src/cobalt/script/exception_message.cc b/src/cobalt/script/exception_message.cc
index 25b9b87..6a619c4 100644
--- a/src/cobalt/script/exception_message.cc
+++ b/src/cobalt/script/exception_message.cc
@@ -17,6 +17,16 @@
namespace cobalt {
namespace script {
+// Exception message contains an exception information. It includes a
+// |message_type| which is the index of each exception message, a
+// |exception_type| which is based on the spec. of simple exception, and a
+// message |format|.
+struct ExceptionMessage {
+ MessageType message_type;
+ SimpleExceptionType exception_type;
+ const char* message;
+};
+
ExceptionMessage kMessages[kNumMessageTypes] = {
{kSimpleError, kError, " "},
{kSimpleTypeError, kTypeError, " "},
@@ -44,17 +54,12 @@
{kOutsideBounds, kRangeError, "Offset is outside the object's bounds."},
{kInvalidLength, kRangeError, "Invalid length."},
{kNotAnArrayBuffer, kTypeError, "Value is not an ArrayBuffer."},
- {kWrongByteOffsetMultiple, kRangeError,
- "Byte offset should be a multiple of %d."},
- {kWrongByteLengthMultiple, kRangeError,
- "Byte length should be a multiple of %d."},
- {kPropertySyntaxError, kSyntaxError, "%s."},
};
-const char* GetExceptionMessageFormat(MessageType message_type) {
+const char* GetExceptionMessage(MessageType message_type) {
DCHECK_GT(message_type, kNoError);
DCHECK_LT(message_type, kNumMessageTypes);
- return kMessages[message_type].format;
+ return kMessages[message_type].message;
}
SimpleExceptionType GetSimpleExceptionType(MessageType message_type) {
diff --git a/src/cobalt/script/exception_message.h b/src/cobalt/script/exception_message.h
index cd9ce9d..8bdafe1 100644
--- a/src/cobalt/script/exception_message.h
+++ b/src/cobalt/script/exception_message.h
@@ -60,24 +60,11 @@
kOutsideBounds,
kInvalidLength,
kNotAnArrayBuffer,
- kWrongByteOffsetMultiple,
- kWrongByteLengthMultiple,
- kPropertySyntaxError,
kNumMessageTypes,
};
-// Exception message contains an exception information. It includes a
-// |message_type| which is the index of each exception message, a
-// |exception_type| which is based on the spec. of simple exception, and a
-// message |format|.
-struct ExceptionMessage {
- MessageType message_type;
- SimpleExceptionType exception_type;
- const char* format;
-};
-
-const char* GetExceptionMessageFormat(MessageType message_type);
+const char* GetExceptionMessage(MessageType message_type);
SimpleExceptionType GetSimpleExceptionType(MessageType message_type);
} // namespace script
diff --git a/src/cobalt/script/exception_state.h b/src/cobalt/script/exception_state.h
index 93eedb8..d478086 100644
--- a/src/cobalt/script/exception_state.h
+++ b/src/cobalt/script/exception_state.h
@@ -14,6 +14,8 @@
#ifndef COBALT_SCRIPT_EXCEPTION_STATE_H_
#define COBALT_SCRIPT_EXCEPTION_STATE_H_
+#include <stdarg.h>
+
#include <string>
#include "cobalt/script/exception_message.h"
@@ -28,16 +30,24 @@
virtual void SetException(
const scoped_refptr<ScriptException>& exception) = 0;
- // The 'dummy' parameter avoids MessageType being the last (only) parameter
- // before varadic arguments to avoid promoting the enum to an int, which is an
- // error in newer clang because passing an object that undergoes default
- // argument promotion to 'va_start' has undefined behavior.
- virtual void SetSimpleExceptionWithArgs(MessageType message_type,
- int dummy, ...) = 0;
-
+ // Use MessageType for commonly used exceptions.
void SetSimpleException(MessageType message_type) {
- SetSimpleExceptionWithArgs(message_type, 0);
+ const char* message = GetExceptionMessage(message_type);
+ SimpleExceptionType type = GetSimpleExceptionType(message_type);
+ SetSimpleException(type, message);
}
+
+ // Set a simple exception with the specified error message.
+ void SetSimpleException(SimpleExceptionType type, const char* format, ...) {
+ va_list arguments;
+ va_start(arguments, format);
+ SetSimpleExceptionVA(type, format, arguments);
+ va_end(arguments);
+ }
+
+ protected:
+ virtual void SetSimpleExceptionVA(SimpleExceptionType, const char* format,
+ va_list args) = 0;
};
} // namespace script
diff --git a/src/cobalt/script/fake_script_runner.h b/src/cobalt/script/fake_script_runner.h
index 9e3b764..1a6bf9e 100644
--- a/src/cobalt/script/fake_script_runner.h
+++ b/src/cobalt/script/fake_script_runner.h
@@ -29,7 +29,11 @@
FakeScriptRunner() : fake_global_environment_(new FakeGlobalEnvironment()) {}
std::string Execute(
const std::string& /*script_utf8*/,
- const base::SourceLocation& /*script_location*/) OVERRIDE {
+ const base::SourceLocation& /*script_location*/,
+ bool* out_succeeded) OVERRIDE {
+ if (out_succeeded) {
+ *out_succeeded = true;
+ }
return "";
}
GlobalEnvironment* GetGlobalEnvironment() const OVERRIDE {
diff --git a/src/cobalt/script/javascript_engine.h b/src/cobalt/script/javascript_engine.h
index 9d4e49e..39f9c0d 100644
--- a/src/cobalt/script/javascript_engine.h
+++ b/src/cobalt/script/javascript_engine.h
@@ -29,10 +29,16 @@
class JavaScriptEngine {
public:
struct Options {
- Options() : disable_jit(false) {}
+ Options() : disable_jit(false), gc_threshold_bytes(1024*1024) {}
+
// Default false. When set to true then the javascript engine should
// disable the just-in-time compiler.
bool disable_jit;
+
+ // Determines the size of garbage collection threshold. After this many
+ // bytes have been allocated, the garbage collector will run.
+ // Default is 1MB (see default constructor).
+ size_t gc_threshold_bytes;
};
typedef base::Callback<void(const base::SourceLocation& location,
@@ -41,11 +47,11 @@
// Initialize a JavaScript engine. The JavaScript engine should only be
// accessed from the thread that called CreateEngine.
// This function is defined per-implementation.
- static scoped_ptr<JavaScriptEngine> CreateEngine();
+ static scoped_ptr<JavaScriptEngine> CreateEngine(
+ const Options& options = Options());
// Create a new JavaScript global object proxy.
- virtual scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment(
- const Options& options) = 0;
+ virtual scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() = 0;
// Kick off the engine's garbage collection synchronously.
virtual void CollectGarbage() = 0;
diff --git a/src/cobalt/script/logging_exception_state.h b/src/cobalt/script/logging_exception_state.h
index c9ac0dc..0cf0a70 100644
--- a/src/cobalt/script/logging_exception_state.h
+++ b/src/cobalt/script/logging_exception_state.h
@@ -29,16 +29,10 @@
void SetException(const scoped_refptr<ScriptException>& exception) OVERRIDE {
LogException(exception->name(), exception->message());
}
-
- void SetSimpleExceptionWithArgs(MessageType message_type,
- int dummy, ...) OVERRIDE {
- va_list arguments;
- va_start(arguments, dummy);
- LogException(
- SimpleExceptionToString(GetSimpleExceptionType(message_type)),
- base::StringPrintV(GetExceptionMessageFormat(message_type),
- arguments));
- va_end(arguments);
+ void SetSimpleExceptionVA(SimpleExceptionType type, const char* format,
+ va_list arguments) OVERRIDE {
+ LogException(SimpleExceptionToString(type),
+ base::StringPrintV(format, arguments));
}
bool is_exception_set() const { return is_exception_set_; }
diff --git a/src/cobalt/script/mozjs-45/conversion_helpers.cc b/src/cobalt/script/mozjs-45/conversion_helpers.cc
index 3331508..1870b1a 100644
--- a/src/cobalt/script/mozjs-45/conversion_helpers.cc
+++ b/src/cobalt/script/mozjs-45/conversion_helpers.cc
@@ -108,6 +108,35 @@
global_environment->wrapper_factory());
}
+// ValueHandle -> JSValue
+void ToJSValue(JSContext* context, const ValueHandleHolder* value_handle_holder,
+ JS::MutableHandleValue out_value) {
+ TRACK_MEMORY_SCOPE("Javascript");
+ JS::RootedValue js_value(context);
+ if (value_handle_holder) {
+ // Downcast to MozjsValueHandleHolder so we can get the JS object.
+ const MozjsValueHandleHolder* mozjs_value_handle_holder =
+ base::polymorphic_downcast<const MozjsValueHandleHolder*>(
+ value_handle_holder);
+ js_value = mozjs_value_handle_holder->js_value();
+ }
+
+ out_value.set(js_value);
+}
+
+// JSValue -> ValueHandle
+void FromJSValue(JSContext* context, JS::HandleValue value,
+ int conversion_flags, ExceptionState* exception_state,
+ MozjsValueHandleHolder* out_holder) {
+ TRACK_MEMORY_SCOPE("Javascript");
+ DCHECK_EQ(conversion_flags & ~kConversionFlagsObject, 0)
+ << "Unexpected conversion flags found.";
+ MozjsGlobalEnvironment* global_environment =
+ static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
+ *out_holder = MozjsValueHandleHolder(value, context,
+ global_environment->wrapper_factory());
+}
+
} // namespace mozjs
} // namespace script
} // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/conversion_helpers.h b/src/cobalt/script/mozjs-45/conversion_helpers.h
index d0f8df2..95a752e 100644
--- a/src/cobalt/script/mozjs-45/conversion_helpers.h
+++ b/src/cobalt/script/mozjs-45/conversion_helpers.h
@@ -29,10 +29,12 @@
#include "cobalt/script/mozjs-45/mozjs_global_environment.h"
#include "cobalt/script/mozjs-45/mozjs_object_handle.h"
#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/mozjs_value_handle.h"
#include "cobalt/script/mozjs-45/type_traits.h"
#include "cobalt/script/mozjs-45/union_type_conversion_forward.h"
#include "cobalt/script/mozjs-45/util/algorithm_helpers.h"
#include "cobalt/script/sequence.h"
+#include "cobalt/script/value_handle.h"
#include "nb/memory_scope.h"
#include "third_party/mozjs-45/js/public/CharacterEncoding.h"
#include "third_party/mozjs-45/js/public/Conversions.h"
@@ -53,9 +55,10 @@
kConversionFlagNullable = 1 << 1,
kConversionFlagTreatNullAsEmptyString = 1 << 2,
kConversionFlagTreatUndefinedAsEmptyString = 1 << 3,
+ kConversionFlagClamped = 1 << 4,
// Valid conversion flags for numeric values.
- kConversionFlagsNumeric = kConversionFlagRestricted,
+ kConversionFlagsNumeric = kConversionFlagRestricted | kConversionFlagClamped,
// Valid conversion flags for string types.
kConversionFlagsString = kConversionFlagTreatNullAsEmptyString |
@@ -135,6 +138,52 @@
out_value.setInt32(in_number);
}
+template <typename T>
+inline const double UpperBound() {
+ return std::numeric_limits<T>::max();
+}
+
+template <typename T>
+inline const double LowerBound() {
+ return std::numeric_limits<T>::min();
+}
+
+// The below specializations of UpperBound<T> and LowerBound<T> for 64
+// bit integers use the (2^(53) - 1) and similar bounds specified in
+// step 1 of ConvertToInt, see:
+// https://heycam.github.io/webidl/#abstract-opdef-converttoint
+template <>
+inline const double UpperBound<int64_t>() {
+ const double kInt64UpperBound = static_cast<double>((1ull << 53) - 1);
+ return kInt64UpperBound;
+}
+
+template <>
+inline const double LowerBound<int64_t>() {
+ const double kInt64LowerBound = static_cast<double>(-(1ull << 53) + 1);
+ return kInt64LowerBound;
+}
+
+template <>
+inline const double UpperBound<uint64_t>() {
+ const double kUInt64UpperBound = static_cast<double>((1ull << 53) - 1);
+ return kUInt64UpperBound;
+}
+
+template <typename T>
+void ClampedValue(JSContext* context, JS::HandleValue value,
+ JS::MutableHandleValue clamped_value) {
+ double value_double;
+ JS::ToNumber(context, value, &value_double);
+ if (value_double > UpperBound<T>()) {
+ clamped_value.setDouble(UpperBound<T>());
+ } else if (value_double < LowerBound<T>()) {
+ clamped_value.setDouble(LowerBound<T>());
+ } else {
+ clamped_value.set(value);
+ }
+}
+
// JSValue -> signed integers <= 4 bytes
template <typename T>
inline void FromJSValue(
@@ -146,14 +195,21 @@
(sizeof(T) <= 4),
T>::type* = NULL) {
TRACK_MEMORY_SCOPE("Javascript");
- DCHECK_EQ(conversion_flags, kNoConversionFlags)
- << "No conversion flags supported.";
DCHECK(out_number);
int32_t out;
// Convert a JavaScript value to an integer type as specified by the
// ECMAScript standard.
- bool success = JS::ToInt32(context, value, &out);
+ // TODO: Consider only creating |value_to_convert| if the conversion flag is
+ // set.
+ JS::RootedValue value_to_convert(context);
+ if (conversion_flags & kConversionFlagClamped) {
+ ClampedValue<T>(context, value, &value_to_convert);
+ } else {
+ value_to_convert.set(value);
+ }
+
+ bool success = JS::ToInt32(context, value_to_convert, &out);
DCHECK(success);
*out_number = static_cast<T>(out);
@@ -180,7 +236,15 @@
DCHECK(out_number);
int64_t out;
// This produces an IDL long long.
- bool success = JS::ToInt64(context, value, &out);
+ // TODO: Consider only creating |value_to_convert| if the conversion flag is
+ // set.
+ JS::RootedValue value_to_convert(context);
+ if (conversion_flags & kConversionFlagClamped) {
+ ClampedValue<T>(context, value, &value_to_convert);
+ } else {
+ value_to_convert.set(value);
+ }
+ bool success = JS::ToInt64(context, value_to_convert, &out);
DCHECK(success);
if (!success) {
exception_state->SetSimpleException(kNotInt64Type);
@@ -228,16 +292,25 @@
(sizeof(T) <= 4),
T>::type* = NULL) {
TRACK_MEMORY_SCOPE("Javascript");
- DCHECK_EQ(conversion_flags, kNoConversionFlags)
- << "No conversion flags supported.";
DCHECK(out_number);
uint32_t out;
// Convert a JavaScript value to an integer type as specified by the
// ECMAScript standard.
- bool success = JS::ToUint32(context, value, &out);
+ // TODO: Consider only creating |value_to_convert| if the conversion flag is
+ // set.
+ JS::RootedValue value_to_convert(context);
+ if (conversion_flags & kConversionFlagClamped) {
+ ClampedValue<T>(context, value, &value_to_convert);
+ } else {
+ value_to_convert.set(value);
+ }
+ bool success = JS::ToUint32(context, value_to_convert, &out);
DCHECK(success);
-
+ if (!success) {
+ exception_state->SetSimpleException(kNotUint64Type);
+ return;
+ }
*out_number = static_cast<T>(out);
}
@@ -252,13 +325,19 @@
(sizeof(T) > 4),
T>::type* = NULL) {
TRACK_MEMORY_SCOPE("Javascript");
- DCHECK_EQ(conversion_flags, kNoConversionFlags)
- << "No conversion flags supported.";
DCHECK(out_number);
uint64_t out;
// This produces and IDL unsigned long long.
- bool success = JS::ToUint64(context, value, &out);
+ // TODO: Consider only creating |value_to_convert| if the conversion flag is
+ // set.
+ JS::RootedValue value_to_convert(context);
+ if (conversion_flags & kConversionFlagClamped) {
+ ClampedValue<T>(context, value, &value_to_convert);
+ } else {
+ value_to_convert.set(value);
+ }
+ bool success = JS::ToUint64(context, value_to_convert, &out);
DCHECK(success);
if (!success) {
exception_state->SetSimpleException(kNotUint64Type);
@@ -377,6 +456,15 @@
int conversion_flags, ExceptionState* exception_state,
MozjsObjectHandleHolder* out_holder);
+// ValueHandle -> JSValue
+void ToJSValue(JSContext* context, const ValueHandleHolder* value_handle_holder,
+ JS::MutableHandleValue out_value);
+
+// JSValue -> ValueHandle
+void FromJSValue(JSContext* context, JS::HandleValue value,
+ int conversion_flags, ExceptionState* exception_state,
+ MozjsValueHandleHolder* out_holder);
+
// object -> JSValue
template <typename T>
inline void ToJSValue(JSContext* context, const scoped_refptr<T>& in_object,
diff --git a/src/cobalt/script/mozjs-45/mozjs-45.gyp b/src/cobalt/script/mozjs-45/mozjs-45.gyp
index 52f085b..d0f416c 100644
--- a/src/cobalt/script/mozjs-45/mozjs-45.gyp
+++ b/src/cobalt/script/mozjs-45/mozjs-45.gyp
@@ -25,8 +25,10 @@
'mozjs_exception_state.cc',
'mozjs_global_environment.cc',
'mozjs_property_enumerator.cc',
+ 'mozjs_script_value_factory.cc',
'mozjs_source_code.cc',
'opaque_root_tracker.cc',
+ 'promise_wrapper.cc',
'proxy_handler.cc',
'referenced_object_map.cc',
'util/algorithm_helpers.cc',
@@ -41,6 +43,7 @@
'dependencies': [
'<(DEPTH)/cobalt/script/script.gyp:script',
'<(DEPTH)/third_party/mozjs-45/mozjs-45.gyp:mozjs-45_lib',
+ 'embed_mozjs_resources_as_header_files',
],
'defines': [ 'ENGINE_SUPPORTS_INT64', ],
'all_dependent_settings': {
@@ -74,5 +77,42 @@
'<(DEPTH)/third_party/mozjs-45/mozjs-45.gyp:mozjs-45_lib',
],
},
+
+ {
+ # This target takes specified files and embeds them as header files for
+ # inclusion into the binary. The script currently requires all resources
+ # to be embedded to live in the same directory.
+ 'target_name': 'embed_mozjs_resources_as_header_files',
+ 'type': 'none',
+ # Because we generate a header, we must set the hard_dependency flag.
+ 'hard_dependency': 1,
+ 'variables': {
+ 'script_path': '<(DEPTH)/cobalt/build/generate_data_header.py',
+ 'output_path': '<(SHARED_INTERMEDIATE_DIR)/cobalt/script/mozjs-45/embedded_resources.h',
+ },
+ 'sources': [
+ '<(DEPTH)/third_party/promise-polyfill/promise.min.js',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'embed_mozjs_resources_as_header_files',
+ 'inputs': [
+ '<(script_path)',
+ '<@(_sources)',
+ ],
+ 'outputs': [
+ '<(output_path)',
+ ],
+ 'action': ['python', '<(script_path)', 'MozjsEmbeddedResources', '<(output_path)', '<@(_sources)' ],
+ 'message': 'Embedding mozjs resources in into header file, "<(output_path)".',
+ },
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ },
+ },
+
],
}
diff --git a/src/cobalt/script/mozjs-45/mozjs-45_variables.gypi b/src/cobalt/script/mozjs-45/mozjs-45_variables.gypi
index 2da6860..e40bbc5 100644
--- a/src/cobalt/script/mozjs-45/mozjs-45_variables.gypi
+++ b/src/cobalt/script/mozjs-45/mozjs-45_variables.gypi
@@ -28,7 +28,9 @@
'engine_template_files': [
'<(DEPTH)/cobalt/bindings/mozjs45/templates/callback-interface.cc.template',
'<(DEPTH)/cobalt/bindings/mozjs45/templates/callback-interface.h.template',
- '<(DEPTH)/cobalt/bindings/mozjs45/templates/dictionary-conversion.h.template',
+ '<(DEPTH)/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template',
+ '<(DEPTH)/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template',
+ '<(DEPTH)/cobalt/bindings/mozjs45/templates/generated-types.h.template',
'<(DEPTH)/cobalt/bindings/mozjs45/templates/interface.cc.template',
'<(DEPTH)/cobalt/bindings/mozjs45/templates/interface.h.template',
'<(DEPTH)/cobalt/bindings/mozjs45/templates/macros.cc.template',
@@ -36,8 +38,12 @@
'engine_bindings_scripts': [
'<(DEPTH)/cobalt/bindings/mozjs45/code_generator_mozjs45.py',
'<(DEPTH)/cobalt/bindings/mozjs45/idl_compiler_mozjs45.py',
+ '<(DEPTH)/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py',
],
- 'engine_idl_compiler': '<(DEPTH)/cobalt/bindings/mozjs45/idl_compiler_mozjs45.py',
+ 'engine_idl_compiler':
+ '<(DEPTH)/cobalt/bindings/mozjs45/idl_compiler_mozjs45.py',
+ 'engine_conversion_header_generator_script':
+ '<(DEPTH)/cobalt/bindings/mozjs45/generate_conversion_header_mozjs45.py',
}],
],
},
diff --git a/src/cobalt/script/mozjs-45/mozjs.cc b/src/cobalt/script/mozjs-45/mozjs.cc
index eefa9fe..53edb1f 100644
--- a/src/cobalt/script/mozjs-45/mozjs.cc
+++ b/src/cobalt/script/mozjs-45/mozjs.cc
@@ -60,7 +60,8 @@
}
int MozjsMain(int argc, char** argv) {
- cobalt::script::StandaloneJavascriptRunner standalone_runner;
+ JavaScriptEngine::Options js_options;
+ cobalt::script::StandaloneJavascriptRunner standalone_runner(js_options);
MozjsGlobalEnvironment* global_environment =
static_cast<MozjsGlobalEnvironment*>(
standalone_runner.global_environment().get());
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.cc b/src/cobalt/script/mozjs-45/mozjs_engine.cc
index dca2c22..e1d482d 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.cc
@@ -141,10 +141,11 @@
} // namespace
-MozjsEngine::MozjsEngine() : accumulated_extra_memory_cost_(0) {
+MozjsEngine::MozjsEngine(const Options& options)
+ : accumulated_extra_memory_cost_(0), options_(options) {
TRACE_EVENT0("cobalt::script", "MozjsEngine::MozjsEngine()");
SbOnce(&g_js_init_once_control, CallInitAndRegisterShutDownOnce);
- runtime_ = JS_NewRuntime(kGarbageCollectionThresholdBytes);
+ runtime_ = JS_NewRuntime(options_.js_options.gc_threshold_bytes);
CHECK(runtime_);
// Sets the size of the native stack that should not be exceeded.
@@ -209,7 +210,7 @@
scoped_refptr<GlobalEnvironment> MozjsEngine::CreateGlobalEnvironment() {
TRACE_EVENT0("cobalt::script", "MozjsEngine::CreateGlobalEnvironment()");
DCHECK(thread_checker_.CalledOnValidThread());
- return new MozjsGlobalEnvironment(runtime_);
+ return new MozjsGlobalEnvironment(runtime_, options_.js_options);
}
void MozjsEngine::CollectGarbage() {
@@ -221,7 +222,10 @@
void MozjsEngine::ReportExtraMemoryCost(size_t bytes) {
DCHECK(thread_checker_.CalledOnValidThread());
accumulated_extra_memory_cost_ += bytes;
- if (accumulated_extra_memory_cost_ > kGarbageCollectionThresholdBytes) {
+
+ const bool do_collect_garbage =
+ accumulated_extra_memory_cost_ > options_.js_options.gc_threshold_bytes;
+ if (do_collect_garbage) {
accumulated_extra_memory_cost_ = 0;
CollectGarbage();
}
@@ -233,9 +237,6 @@
bool MozjsEngine::RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) {
error_handler_ = handler;
- JSDebugErrorHook hook = ErrorHookCallback;
- void* closure = this;
- JS_SetDebugErrorHook(runtime_, hook, closure);
return true;
}
@@ -294,14 +295,8 @@
}
}
-JSBool MozjsEngine::ErrorHookCallback(JSContext* context, const char* message,
- JSErrorReport* report, void* closure) {
- MozjsEngine* this_ptr = static_cast<MozjsEngine*>(closure);
- return this_ptr->ReportJSError(context, message, report);
-}
-
-JSBool MozjsEngine::ReportJSError(JSContext* context, const char* message,
- JSErrorReport* report) {
+bool MozjsEngine::ReportJSError(JSContext* context, const char* message,
+ JSErrorReport* report) {
const bool is_invalid =
error_handler_.is_null() || !report || !report->filename;
if (is_invalid) {
@@ -329,9 +324,11 @@
} // namespace mozjs
-scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine() {
+scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine(
+ const JavaScriptEngine::Options& options) {
TRACE_EVENT0("cobalt::script", "JavaScriptEngine::CreateEngine()");
- return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine());
+ mozjs::MozjsEngine::Options moz_options(options);
+ return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine(moz_options));
}
} // namespace script
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.h b/src/cobalt/script/mozjs-45/mozjs_engine.h
index a8ee7e4..e4a3765 100644
--- a/src/cobalt/script/mozjs-45/mozjs_engine.h
+++ b/src/cobalt/script/mozjs-45/mozjs_engine.h
@@ -27,7 +27,13 @@
class MozjsEngine : public JavaScriptEngine {
public:
- MozjsEngine();
+ struct Options {
+ explicit Options(const JavaScriptEngine::Options& js_options)
+ : js_options(js_options) {}
+ JavaScriptEngine::Options js_options; // Generic settings.
+ };
+
+ explicit MozjsEngine(const Options& options);
~MozjsEngine() OVERRIDE;
scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() OVERRIDE;
@@ -43,10 +49,8 @@
static void GCCallback(JSRuntime* runtime, JSGCStatus status, void* data);
static void FinalizeCallback(JSFreeOp* free_op, JSFinalizeStatus status,
bool is_compartment, void* data);
- static JSBool ErrorHookCallback(JSContext* context, const char* message,
- JSErrorReport* report, void* closure);
- JSBool ReportJSError(JSContext* context, const char* message,
- JSErrorReport* report);
+ bool ReportJSError(JSContext* context, const char* message,
+ JSErrorReport* report);
base::ThreadChecker thread_checker_;
@@ -66,6 +70,8 @@
// Used to handle javascript errors.
ErrorHandler error_handler_;
+
+ Options options_;
};
} // namespace mozjs
} // namespace script
diff --git a/src/cobalt/script/mozjs-45/mozjs_exception_state.cc b/src/cobalt/script/mozjs-45/mozjs_exception_state.cc
index e35a8cb..a722e5d 100644
--- a/src/cobalt/script/mozjs-45/mozjs_exception_state.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_exception_state.cc
@@ -20,6 +20,7 @@
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "cobalt/script/mozjs-45/conversion_helpers.h"
+#include "third_party/mozjs-45/js/src/jsexn.h"
namespace cobalt {
namespace script {
@@ -72,32 +73,40 @@
is_exception_set_ = true;
}
-void MozjsExceptionState::SetSimpleException(MessageType message_type,
- int dummy, ...) {
+void MozjsExceptionState::SetSimpleExceptionVA(SimpleExceptionType type,
+ const char* format,
+ va_list arguments) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!is_exception_set_);
- va_list arguments;
- va_start(arguments, dummy);
- std::string error_message =
- base::StringPrintV(GetExceptionMessageFormat(message_type), arguments);
+ std::string error_message = base::StringPrintV(format, arguments);
JSErrorFormatString format_string;
format_string.format = error_message.c_str();
// Already fed arguments for format.
format_string.argCount = 0;
- format_string.exnType =
- ConvertToMozjsExceptionType(GetSimpleExceptionType(message_type));
+ format_string.exnType = ConvertToMozjsExceptionType(type);
// This function creates a JSErrorReport, populate it with an error message
// obtained from the given JSErrorCallback. The resulting error message is
// passed to the context's JSErrorReporter callback.
JS_ReportErrorNumber(context_, GetErrorMessage,
- static_cast<void*>(&format_string), message_type);
- va_end(arguments);
-
+ static_cast<void*>(&format_string), type);
is_exception_set_ = true;
}
+// static
+JSObject* MozjsExceptionState::CreateErrorObject(JSContext* context,
+ SimpleExceptionType type) {
+ JSExnType mozjs_type = ConvertToMozjsExceptionType(type);
+ JS::RootedObject error_prototype(context);
+ if (!JS_GetClassPrototype(context, js::GetExceptionProtoKey(mozjs_type),
+ &error_prototype)) {
+ DLOG(ERROR) << "Failed to get Error prototype.";
+ return NULL;
+ }
+ return JS_NewObjectWithGivenProto(context, NULL, error_prototype);
+}
+
} // namespace mozjs
} // namespace script
} // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs_exception_state.h b/src/cobalt/script/mozjs-45/mozjs_exception_state.h
index 10120bd..02b5082 100644
--- a/src/cobalt/script/mozjs-45/mozjs_exception_state.h
+++ b/src/cobalt/script/mozjs-45/mozjs_exception_state.h
@@ -30,11 +30,14 @@
: is_exception_set_(false), context_(context) {}
// ExceptionState interface
void SetException(const scoped_refptr<ScriptException>& exception) OVERRIDE;
- void SetSimpleExceptionWithArgs(MessageType message_type,
- int dummy, ...) OVERRIDE;
+ void SetSimpleExceptionVA(SimpleExceptionType type, const char* format,
+ va_list arguments) OVERRIDE;
bool is_exception_set() const { return is_exception_set_; }
+ static JSObject* CreateErrorObject(JSContext* context,
+ SimpleExceptionType type);
+
private:
bool is_exception_set_;
JSContext* context_;
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index 0448baf..624d591 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -20,7 +20,9 @@
#include "base/stringprintf.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/script/mozjs-45/conversion_helpers.h"
+#include "cobalt/script/mozjs-45/embedded_resources.h"
#include "cobalt/script/mozjs-45/mozjs_exception_state.h"
+#include "cobalt/script/mozjs-45/mozjs_script_value_factory.h"
#include "cobalt/script/mozjs-45/mozjs_source_code.h"
#include "cobalt/script/mozjs-45/mozjs_wrapper_handle.h"
#include "cobalt/script/mozjs-45/proxy_handler.h"
@@ -117,7 +119,8 @@
static base::LazyInstance<MozjsStubHandler> proxy_handler;
} // namespace
-MozjsGlobalEnvironment::MozjsGlobalEnvironment(JSRuntime* runtime)
+MozjsGlobalEnvironment::MozjsGlobalEnvironment(
+ JSRuntime* runtime, const JavaScriptEngine::Options& options)
: context_(NULL),
garbage_collection_count_(0),
cached_interface_data_deleter_(&cached_interface_data_),
@@ -135,6 +138,7 @@
kMaxCodeCacheBytes);
wrapper_factory_.reset(new WrapperFactory(context_));
+ script_value_factory_.reset(new MozjsScriptValueFactory(this));
referenced_objects_.reset(new ReferencedObjectMap(context_));
opaque_root_tracker_.reset(new OpaqueRootTracker(
context_, referenced_objects_.get(), wrapper_factory_.get()));
@@ -173,6 +177,8 @@
context_, ProxyHandler::NewProxy(context_, proxy_handler.Pointer(),
global_object, NULL));
global_object_proxy_ = proxy;
+
+ EvaluateAutomatics();
}
bool MozjsGlobalEnvironment::EvaluateScript(
@@ -339,6 +345,25 @@
DCHECK(success);
}
+ScriptValueFactory* MozjsGlobalEnvironment::script_value_factory() {
+ DCHECK(script_value_factory_);
+ return script_value_factory_.get();
+}
+
+void MozjsGlobalEnvironment::EvaluateAutomatics() {
+ TRACK_MEMORY_SCOPE("Javascript");
+ std::string source(
+ reinterpret_cast<const char*>(MozjsEmbeddedResources::promise_min_js),
+ sizeof(MozjsEmbeddedResources::promise_min_js));
+ scoped_refptr<SourceCode> source_code =
+ new MozjsSourceCode(source, base::SourceLocation("promise.min.js", 1, 1));
+ std::string result;
+ bool success = EvaluateScript(source_code, &result);
+ if (!success) {
+ DLOG(FATAL) << result;
+ }
+}
+
InterfaceData* MozjsGlobalEnvironment::GetInterfaceData(intptr_t key) {
CachedInterfaceData::iterator it = cached_interface_data_.find(key);
if (it != cached_interface_data_.end()) {
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
index 9762530..e23f1bb 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -23,6 +23,7 @@
#include "base/stl_util.h"
#include "base/threading/thread_checker.h"
#include "cobalt/script/global_environment.h"
+#include "cobalt/script/javascript_engine.h"
#include "cobalt/script/mozjs-45/interface_data.h"
#include "cobalt/script/mozjs-45/opaque_root_tracker.h"
#include "cobalt/script/mozjs-45/util/exception_helpers.h"
@@ -36,6 +37,7 @@
namespace script {
namespace mozjs {
+class MozjsScriptValueFactory;
class ReferencedObjectMap;
class WeakHandle;
@@ -44,7 +46,8 @@
class MozjsGlobalEnvironment : public GlobalEnvironment,
public Wrappable::CachedWrapperAccessor {
public:
- explicit MozjsGlobalEnvironment(JSRuntime* runtime);
+ MozjsGlobalEnvironment(JSRuntime* runtime,
+ const JavaScriptEngine::Options& options);
~MozjsGlobalEnvironment() OVERRIDE;
void CreateGlobalObject() OVERRIDE;
@@ -76,6 +79,11 @@
void Bind(const std::string& identifier,
const scoped_refptr<Wrappable>& impl) OVERRIDE;
+ ScriptValueFactory* script_value_factory() OVERRIDE;
+
+ // Evaluates any automatically included Javascript for the environment.
+ void EvaluateAutomatics();
+
JSContext* context() const { return context_; }
JSObject* global_object_proxy() const { return global_object_proxy_; }
@@ -162,6 +170,7 @@
STLValueDeleter<CachedInterfaceData> cached_interface_data_deleter_;
ContextDestructor context_destructor_;
scoped_ptr<WrapperFactory> wrapper_factory_;
+ scoped_ptr<MozjsScriptValueFactory> script_value_factory_;
scoped_ptr<OpaqueRootTracker::OpaqueRootState> opaque_root_state_;
JS::Heap<JSObject*> global_object_proxy_;
EnvironmentSettings* environment_settings_;
diff --git a/src/cobalt/script/mozjs-45/mozjs_script_value_factory.cc b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.cc
new file mode 100644
index 0000000..181b729
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/script/mozjs-45/mozjs_script_value_factory.h"
+
+#include "cobalt/base/polymorphic_downcast.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+MozjsScriptValueFactory::MozjsScriptValueFactory(
+ MozjsGlobalEnvironment* global_environment)
+ : global_environment_(global_environment) {}
+} // namespace mozjs
+
+// Implementation of template function declared in the base class.
+template <typename T>
+scoped_ptr<ScriptValue<Promise<T> > > ScriptValueFactory::CreatePromise() {
+ mozjs::MozjsScriptValueFactory* mozjs_this =
+ base::polymorphic_downcast<mozjs::MozjsScriptValueFactory*>(this);
+ return mozjs_this->CreatePromise<T>();
+}
+
+} // namespace script
+} // namespace cobalt
+
+// Explicit template instantiations must go after the template function
+// implementation.
+#include "cobalt/script/script_value_factory_instantiations.h"
diff --git a/src/cobalt/script/mozjs-45/mozjs_script_value_factory.h b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.h
new file mode 100644
index 0000000..c2612cf
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_MOZJS_45_MOZJS_SCRIPT_VALUE_FACTORY_H_
+#define COBALT_SCRIPT_MOZJS_45_MOZJS_SCRIPT_VALUE_FACTORY_H_
+
+#include "cobalt/script/mozjs-45/mozjs_global_environment.h"
+#include "cobalt/script/mozjs-45/native_promise.h"
+#include "cobalt/script/mozjs-45/promise_wrapper.h"
+#include "cobalt/script/script_value_factory.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+class MozjsScriptValueFactory : public ScriptValueFactory {
+ public:
+ explicit MozjsScriptValueFactory(MozjsGlobalEnvironment* global_environment);
+ ~MozjsScriptValueFactory() OVERRIDE {}
+
+ template <typename T>
+ scoped_ptr<ScriptValue<Promise<T> > > CreatePromise() {
+ typedef ScriptValue<Promise<T> > ScriptPromiseType;
+ typedef MozjsUserObjectHolder<NativePromise<T> > MozjsPromiseHolderType;
+
+ JSContext* context = global_environment_->context();
+ JS::RootedObject global_object(context,
+ global_environment_->global_object());
+ JSAutoRequest auto_request(context);
+ JSAutoCompartment auto_compartment(context, global_object);
+
+ JS::RootedObject promise_wrapper(
+ context, PromiseWrapper::Create(context, global_object));
+ DCHECK(promise_wrapper);
+ scoped_ptr<ScriptPromiseType> promise(new MozjsPromiseHolderType(
+ promise_wrapper, context, global_environment_->wrapper_factory()));
+ return promise.Pass();
+ }
+
+ private:
+ MozjsGlobalEnvironment* global_environment_;
+};
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
+#endif // COBALT_SCRIPT_MOZJS_45_MOZJS_SCRIPT_VALUE_FACTORY_H_
diff --git a/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h b/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h
index 952b4cc..3796ad3 100644
--- a/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h
+++ b/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h
@@ -86,6 +86,20 @@
}
}
+ void PreventGarbageCollection() OVERRIDE {
+ if (prevent_garbage_collection_count_++ == 0 && handle_) {
+ JSAutoRequest auto_request(context_);
+ persistent_root_ = JS::PersistentRootedValue(context_, handle_->value());
+ }
+ }
+
+ void AllowGarbageCollection() {
+ if (prevent_garbage_collection_count_++ == 0 && handle_) {
+ JSAutoRequest auto_request(context_);
+ persistent_root_ = base::nullopt;
+ }
+ }
+
const typename MozjsUserObjectType::BaseType* GetScriptValue()
const OVERRIDE {
return handle_ ? &handle_.value() : NULL;
@@ -134,6 +148,8 @@
JSContext* context_;
base::optional<MozjsUserObjectType> handle_;
WrapperFactory* wrapper_factory_;
+ int prevent_garbage_collection_count_;
+ base::optional<JS::Value> persistent_root_;
};
} // namespace mozjs
diff --git a/src/cobalt/script/mozjs-45/mozjs_value_handle.h b/src/cobalt/script/mozjs-45/mozjs_value_handle.h
new file mode 100644
index 0000000..fae1ae7
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/mozjs_value_handle.h
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SCRIPT_MOZJS_45_MOZJS_VALUE_HANDLE_H_
+#define COBALT_SCRIPT_MOZJS_45_MOZJS_VALUE_HANDLE_H_
+
+#include "base/optional.h"
+#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/type_traits.h"
+#include "cobalt/script/mozjs-45/weak_heap_object.h"
+#include "cobalt/script/value_handle.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+// A wrapper around a JS::Value that can be passed into Cobalt as an script
+// value object.
+//
+// An ValueHandle is never passed into Cobalt as-is, but only when wrapped as a
+// ScriptValue<ValueHandle>.
+class MozjsValueHandle : public ValueHandle {
+ public:
+ typedef ValueHandle BaseType;
+ JSObject* handle() const { return handle_.GetObject(); }
+ const JS::Value& value() const { return handle_.GetValue(); }
+ bool WasCollected() const { return handle_.WasCollected(); }
+
+ private:
+ MozjsValueHandle(JSContext* context, JS::HandleValue object)
+ : handle_(context, object) {}
+ ~MozjsValueHandle() {}
+
+ WeakHeapObject handle_;
+
+ friend class MozjsUserObjectHolder<MozjsValueHandle>;
+ friend class base::optional<MozjsValueHandle>;
+};
+
+typedef MozjsUserObjectHolder<MozjsValueHandle> MozjsValueHandleHolder;
+
+template <>
+struct TypeTraits<ValueHandle> {
+ typedef MozjsValueHandleHolder ConversionType;
+ typedef const ScriptValue<ValueHandle>* ReturnType;
+};
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
+
+#endif // COBALT_SCRIPT_MOZJS_45_MOZJS_VALUE_HANDLE_H_
diff --git a/src/cobalt/script/mozjs-45/native_promise.h b/src/cobalt/script/mozjs-45/native_promise.h
new file mode 100644
index 0000000..1dfccb5
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/native_promise.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_MOZJS_45_NATIVE_PROMISE_H_
+#define COBALT_SCRIPT_MOZJS_45_NATIVE_PROMISE_H_
+
+#include "cobalt/script/mozjs-45/conversion_helpers.h"
+#include "cobalt/script/mozjs-45/mozjs_exception_state.h"
+#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h"
+#include "cobalt/script/mozjs-45/promise_wrapper.h"
+#include "cobalt/script/mozjs-45/type_traits.h"
+#include "cobalt/script/mozjs-45/weak_heap_object.h"
+#include "cobalt/script/promise.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+// Shared functionality for NativePromise<T>. Does not implement the Resolve
+// function, since that needs to be specialized for Promise<T>.
+template <typename T>
+class NativePromiseBase : public Promise<T> {
+ public:
+ // ScriptObject boilerplate.
+ typedef Promise<T> BaseType;
+ JSObject* handle() const { return promise_resolver_->get().GetObject(); }
+ const JS::Value& value() const { return promise_resolver_->get().GetValue(); }
+ bool WasCollected() const { return promise_resolver_->get().WasCollected(); }
+
+ // The Promise JS object (not the resolver).
+ JSObject* promise() const { return promise_resolver_->GetPromise(); }
+
+ void Reject() const OVERRIDE {
+ JS::RootedObject promise_resolver(context_,
+ promise_resolver_->get().GetObject());
+ if (promise_resolver) {
+ JSAutoRequest auto_request(context_);
+ JSAutoCompartment auto_compartment(context_, promise_resolver);
+ promise_resolver_->Reject(JS::UndefinedHandleValue);
+ }
+ }
+
+ void Reject(SimpleExceptionType exception) const OVERRIDE {
+ JS::RootedObject promise_resolver(context_,
+ promise_resolver_->get().GetObject());
+ if (promise_resolver) {
+ JSAutoRequest auto_request(context_);
+ JSAutoCompartment auto_compartment(context_, promise_resolver);
+ JS::RootedValue error_result(context_);
+ error_result.setObject(
+ *MozjsExceptionState::CreateErrorObject(context_, exception));
+ promise_resolver_->Reject(error_result);
+ }
+ }
+
+ void Reject(const scoped_refptr<ScriptException>& result) const OVERRIDE {
+ JS::RootedObject promise_resolver(context_,
+ promise_resolver_->get().GetObject());
+ if (promise_resolver) {
+ JSAutoRequest auto_request(context_);
+ JSAutoCompartment auto_compartment(context_, promise_resolver);
+ JS::RootedValue converted_result(context_);
+ ToJSValue(context_, result, &converted_result);
+ promise_resolver_->Reject(converted_result);
+ }
+ }
+
+ protected:
+ NativePromiseBase(JSContext* context, JS::HandleObject resolver_object)
+ : context_(context) {
+ promise_resolver_.emplace(context, resolver_object);
+ }
+
+ NativePromiseBase(JSContext* context, JS::HandleValue resolver_value)
+ : context_(context) {
+ DCHECK(resolver_value.isObject());
+ JS::RootedObject resolver_object(context, &resolver_value.toObject());
+ promise_resolver_.emplace(context, resolver_object);
+ }
+
+ JSContext* context_;
+ base::optional<PromiseWrapper> promise_resolver_;
+};
+
+// Implements the Resolve() function for T != void.
+template <typename T>
+class NativePromise : public NativePromiseBase<T> {
+ public:
+ NativePromise(JSContext* context, JS::HandleObject resolver_object)
+ : NativePromiseBase<T>(context, resolver_object) {}
+
+ NativePromise(JSContext* context, JS::HandleValue resolver_value)
+ : NativePromiseBase<T>(context, resolver_value) {}
+
+ void Resolve(const T& value) const OVERRIDE {
+ JS::RootedObject promise_wrapper(
+ this->context_, this->promise_resolver_->get().GetObject());
+ if (promise_wrapper) {
+ JSAutoRequest auto_request(this->context_);
+ JSAutoCompartment auto_compartment(this->context_, promise_wrapper);
+ JS::RootedValue converted_value(this->context_);
+ ToJSValue(this->context_, value, &converted_value);
+ this->promise_resolver_->Resolve(converted_value);
+ }
+ }
+};
+
+// Implements the Resolve() function for T == void.
+template <>
+class NativePromise<void> : public NativePromiseBase<void> {
+ public:
+ NativePromise(JSContext* context, JS::HandleObject resolver_object)
+ : NativePromiseBase<void>(context, resolver_object) {}
+
+ NativePromise(JSContext* context, JS::HandleValue resolver_value)
+ : NativePromiseBase<void>(context, resolver_value) {}
+
+ void Resolve() const OVERRIDE {
+ JS::RootedObject promise_wrapper(context_,
+ promise_resolver_->get().GetObject());
+ if (promise_wrapper) {
+ JSAutoRequest auto_request(context_);
+ JSAutoCompartment auto_compartment(context_, promise_wrapper);
+ promise_resolver_->Resolve(JS::UndefinedHandleValue);
+ }
+ }
+};
+
+template <typename T>
+struct TypeTraits<NativePromise<T> > {
+ typedef MozjsUserObjectHolder<NativePromise<T> > ConversionType;
+ typedef const ScriptValue<Promise<T> >* ReturnType;
+};
+
+// Promise<T> -> JSValue
+// Note that JSValue -> Promise<T> is not yet supported.
+template <typename T>
+inline void ToJSValue(JSContext* context,
+ const ScriptValue<Promise<T> >* promise_holder,
+ JS::MutableHandleValue out_value) {
+ TRACK_MEMORY_SCOPE("Javascript");
+ if (!promise_holder) {
+ out_value.set(JS::NullValue());
+ return;
+ }
+ const MozjsUserObjectHolder<NativePromise<T> >* user_object_holder =
+ base::polymorphic_downcast<
+ const MozjsUserObjectHolder<NativePromise<T> >*>(promise_holder);
+
+ const NativePromise<T>* native_promise =
+ base::polymorphic_downcast<const NativePromise<T>*>(
+ user_object_holder->GetScriptValue());
+
+ DCHECK(native_promise);
+ out_value.setObjectOrNull(native_promise->promise());
+}
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
+#endif // COBALT_SCRIPT_MOZJS_45_NATIVE_PROMISE_H_
diff --git a/src/cobalt/script/mozjs-45/promise_wrapper.cc b/src/cobalt/script/mozjs-45/promise_wrapper.cc
new file mode 100644
index 0000000..46af70f
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/promise_wrapper.cc
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/script/mozjs-45/promise_wrapper.h"
+
+#include "base/logging.h"
+#include "third_party/mozjs-45/js/src/jsfun.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+namespace {
+enum ReservedSlots {
+ kResolveFunction,
+ kRejectFunction,
+ kPromiseObject,
+ kNumReservedSlots,
+};
+
+JSClass native_promise_class = {
+ "NativePromise", // name
+ JSCLASS_HAS_RESERVED_SLOTS(kNumReservedSlots), // flags
+ NULL, // addProperty
+ NULL, // delProperty
+ NULL, // getProperty
+ NULL, // setProperty
+ NULL, // enumerate
+ NULL, // resolve
+ NULL, // mayResolve
+ NULL, // finalize
+ NULL, // call
+};
+
+bool NativeExecutor(JSContext* context, unsigned argc, JS::Value* vp) {
+ // Get the resolve/reject functions from the call args.
+ JS::CallArgs call_args = CallArgsFromVp(argc, vp);
+ DCHECK_EQ(call_args.length(), 2);
+
+ // Get the this object. Should be the native_promise object.
+ JS::RootedValue this_value(context, call_args.computeThis(context));
+ DCHECK(this_value.isObject());
+ JS::RootedObject this_object(context, &this_value.toObject());
+ DCHECK_EQ(JS_GetClass(this_object), &native_promise_class);
+
+ // First argument is the resolve function. Second is the reject function.
+ // Stash these in the reserved slots. Reserved slots get visited so there is
+ // no need to define a special trace function.
+ JS::RootedValue resolve_function_value(context, call_args.get(0));
+ JS::RootedValue reject_function_value(context, call_args.get(1));
+ DCHECK(resolve_function_value.isObject());
+ DCHECK(JS_ObjectIsFunction(context, &resolve_function_value.toObject()));
+ DCHECK(reject_function_value.isObject());
+ DCHECK(JS_ObjectIsFunction(context, &reject_function_value.toObject()));
+
+ JS_SetReservedSlot(this_object, kResolveFunction, resolve_function_value);
+ JS_SetReservedSlot(this_object, kRejectFunction, reject_function_value);
+ return true;
+}
+
+// Creates a new NativePromise object and initializes its reserved slots.
+JSObject* CreateNativePromise(JSContext* context) {
+ JS::RootedObject native_promise(context,
+ JS_NewObject(context, &native_promise_class));
+ DCHECK(native_promise);
+ for (uint32_t i = 0; i < kNumReservedSlots; ++i) {
+ JS_SetReservedSlot(native_promise, i, JS::NullHandleValue);
+ }
+ return native_promise;
+}
+
+JSObject* BindCallable(JSContext* context, JSObject* target_arg,
+ JSObject* new_this) {
+ JS::RootedObject target(context, target_arg);
+ JS::AutoValueArray<1> args(context);
+ args[0].setObjectOrNull(new_this);
+ JS::RootedValue result(context);
+ bool call_bind_result =
+ JS_CallFunctionName(context, target, "bind", args, &result);
+ DCHECK(call_bind_result);
+ return &result.toObject();
+}
+
+// Create a new native function with the |native_promise| bound as |this|.
+JSObject* CreateExecutorArgument(JSContext* context,
+ JS::HandleObject native_promise) {
+ JS::RootedObject executor_function(context);
+ executor_function =
+ JS_NewFunction(context, &NativeExecutor, 2, 0, "NativeExecutor");
+ DCHECK(executor_function);
+
+ JS::RootedObject bound_executor(context);
+ bound_executor = BindCallable(context, executor_function, native_promise);
+ DCHECK(bound_executor);
+ return bound_executor;
+}
+
+// Get the Promise constructor from the global object.
+JSObject* GetPromiseConstructor(JSContext* context,
+ JS::HandleObject global_object) {
+ JS::RootedValue promise_constructor_property(context);
+ bool result = JS_GetProperty(context, global_object, "Promise",
+ &promise_constructor_property);
+ DCHECK(result);
+ if (!promise_constructor_property.isObject() ||
+ !JS_ObjectIsFunction(context, &promise_constructor_property.toObject())) {
+ DLOG(ERROR) << "\"Promise\" property is not a function.";
+ return NULL;
+ }
+
+ return &promise_constructor_property.toObject();
+}
+
+void Settle(JSContext* context, JS::HandleValue result,
+ JS::HandleObject resolver, ReservedSlots slot) {
+ JS::RootedValue slot_value(context, JS_GetReservedSlot(resolver, slot));
+ DCHECK(slot_value.isObject());
+ DCHECK(JS_ObjectIsFunction(context, &slot_value.toObject()));
+
+ JS::RootedValue return_value(context);
+ const size_t kNumArguments = result.isUndefined() ? 0 : 1;
+ JS::AutoValueArray<1> args(context);
+ args[0].set(result);
+ bool call_result =
+ JS_CallFunctionValue(context, resolver, slot_value, args, &return_value);
+ if (!call_result) {
+ DLOG(ERROR) << "Exception calling Promise function.";
+ JS_ClearPendingException(context);
+ }
+}
+} // namespace
+
+JSObject* PromiseWrapper::Create(JSContext* context,
+ JS::HandleObject global_object) {
+ // Get the Promise constructor.
+ JS::RootedObject constructor(context,
+ GetPromiseConstructor(context, global_object));
+ if (!constructor) {
+ DLOG(ERROR) << "Failed to find Promise constructor.";
+ return NULL;
+ }
+ // Create a new NativePromise JS object, and bind it to the NativeExecutor
+ // function.
+ JS::RootedObject promise_wrapper(context, CreateNativePromise(context));
+ DCHECK(promise_wrapper);
+ JS::RootedObject executor(context,
+ CreateExecutorArgument(context, promise_wrapper));
+ DCHECK(executor);
+
+ // Invoke the Promise constructor with the native executor function.
+ const size_t kNumArguments = 1;
+ JS::AutoValueArray<kNumArguments> args(context);
+ args[0].set(JS::ObjectOrNullValue(executor));
+ JS::RootedObject promise_object(context);
+ promise_object = JS_New(context, constructor, args);
+ if (!promise_object) {
+ DLOG(ERROR) << "Failed to create a new Promise.";
+ return NULL;
+ }
+ // Maintain a handle to the promise object on the NativePromise.
+ JS::RootedValue promise_value(context);
+ promise_value.setObject(*promise_object);
+ JS_SetReservedSlot(promise_wrapper, kPromiseObject, promise_value);
+
+ return promise_wrapper;
+}
+
+JSObject* PromiseWrapper::GetPromise() const {
+ JS::RootedObject promise(context_);
+ JS::RootedObject promise_wrapper(context_, weak_promise_wrapper_.GetObject());
+ if (promise_wrapper) {
+ JS::RootedValue slot_value(
+ context_, JS_GetReservedSlot(promise_wrapper, kPromiseObject));
+ DCHECK(slot_value.isObject());
+ promise.set(&slot_value.toObject());
+ }
+ return promise;
+}
+
+void PromiseWrapper::Resolve(JS::HandleValue value) const {
+ JS::RootedObject promise_wrapper(context_, weak_promise_wrapper_.GetObject());
+ if (promise_wrapper) {
+ Settle(context_, value, promise_wrapper, kResolveFunction);
+ }
+}
+
+void PromiseWrapper::Reject(JS::HandleValue value) const {
+ JS::RootedObject promise_wrapper(context_, weak_promise_wrapper_.GetObject());
+ if (promise_wrapper) {
+ Settle(context_, value, promise_wrapper, kRejectFunction);
+ }
+}
+
+PromiseWrapper::PromiseWrapper(JSContext* context,
+ JS::HandleObject promise_wrapper)
+ : context_(context), weak_promise_wrapper_(context, promise_wrapper) {
+ DCHECK_EQ(JS_GetClass(promise_wrapper), &native_promise_class);
+}
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/promise_wrapper.h b/src/cobalt/script/mozjs-45/promise_wrapper.h
new file mode 100644
index 0000000..d77d6a9
--- /dev/null
+++ b/src/cobalt/script/mozjs-45/promise_wrapper.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_SCRIPT_MOZJS_45_PROMISE_WRAPPER_H_
+#define COBALT_SCRIPT_MOZJS_45_PROMISE_WRAPPER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/script/mozjs-45/weak_heap_object.h"
+#include "third_party/mozjs-45/js/src/jsapi.h"
+
+namespace cobalt {
+namespace script {
+namespace mozjs {
+
+// Native class that maintains a weak handle to a JS object that is a proxy to
+// a JS Promise object. The wrapper object maintains references to the reject
+// and resolve functions that are passed to the Promise executor function.
+class PromiseWrapper {
+ public:
+ // Creates a new JS object that wraps a new Promise, created using the
+ // Promise constructor on |global_object|. Returns NULL on failure.
+ static JSObject* Create(JSContext* context, JS::HandleObject global_object);
+
+ PromiseWrapper(JSContext* context, JS::HandleObject promise_wrapper);
+
+ const WeakHeapObject& get() const { return weak_promise_wrapper_; }
+ JSObject* GetPromise() const;
+ void Resolve(JS::HandleValue value) const;
+ void Reject(JS::HandleValue value) const;
+
+ private:
+ JSContext* context_;
+ WeakHeapObject weak_promise_wrapper_;
+};
+
+} // namespace mozjs
+} // namespace script
+} // namespace cobalt
+
+#endif // COBALT_SCRIPT_MOZJS_45_PROMISE_WRAPPER_H_
diff --git a/src/cobalt/script/mozjs/mozjs.cc b/src/cobalt/script/mozjs/mozjs.cc
index 3129dd6..36bb288 100644
--- a/src/cobalt/script/mozjs/mozjs.cc
+++ b/src/cobalt/script/mozjs/mozjs.cc
@@ -59,8 +59,7 @@
}
int MozjsMain(int argc, char** argv) {
- JavaScriptEngine::Options js_options;
- cobalt::script::StandaloneJavascriptRunner standalone_runner(js_options);
+ cobalt::script::StandaloneJavascriptRunner standalone_runner;
MozjsGlobalEnvironment* global_environment =
static_cast<MozjsGlobalEnvironment*>(
standalone_runner.global_environment().get());
diff --git a/src/cobalt/script/mozjs/mozjs.gyp b/src/cobalt/script/mozjs/mozjs.gyp
index aa3d0c0..ce2763c 100644
--- a/src/cobalt/script/mozjs/mozjs.gyp
+++ b/src/cobalt/script/mozjs/mozjs.gyp
@@ -49,7 +49,6 @@
'embed_mozjs_resources_as_header_files',
],
'defines': [
- 'MOZJS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES=<(mozjs_garbage_collection_threshold_in_bytes)',
'ENGINE_SUPPORTS_INT64',
],
'all_dependent_settings': {
diff --git a/src/cobalt/script/mozjs/mozjs_engine.cc b/src/cobalt/script/mozjs/mozjs_engine.cc
index 2de9e14..7040fb3 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs/mozjs_engine.cc
@@ -103,11 +103,13 @@
}
} // namespace
-MozjsEngine::MozjsEngine() : accumulated_extra_memory_cost_(0) {
+MozjsEngine::MozjsEngine(const Options& options)
+ : accumulated_extra_memory_cost_(0),
+ moz_options_(options) {
TRACE_EVENT0("cobalt::script", "MozjsEngine::MozjsEngine()");
// TODO: Investigate the benefit of helper threads and things like
// parallel compilation.
- runtime_ = JS_NewRuntime(MOZJS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES,
+ runtime_ = JS_NewRuntime(moz_options_.js_options.gc_threshold_bytes,
JS_NO_HELPER_THREADS);
CHECK(runtime_);
@@ -156,11 +158,10 @@
JS_DestroyRuntime(runtime_);
}
-scoped_refptr<GlobalEnvironment> MozjsEngine::CreateGlobalEnvironment(
- const JavaScriptEngine::Options& options) {
+scoped_refptr<GlobalEnvironment> MozjsEngine::CreateGlobalEnvironment() {
TRACE_EVENT0("cobalt::script", "MozjsEngine::CreateGlobalEnvironment()");
DCHECK(thread_checker_.CalledOnValidThread());
- return new MozjsGlobalEnvironment(runtime_, options);
+ return new MozjsGlobalEnvironment(runtime_, moz_options_.js_options);
}
void MozjsEngine::CollectGarbage() {
@@ -172,8 +173,10 @@
void MozjsEngine::ReportExtraMemoryCost(size_t bytes) {
DCHECK(thread_checker_.CalledOnValidThread());
accumulated_extra_memory_cost_ += bytes;
- if (accumulated_extra_memory_cost_ >
- MOZJS_GARBAGE_COLLECTION_THRESHOLD_IN_BYTES) {
+
+ const bool do_collect_garbage = accumulated_extra_memory_cost_ >
+ moz_options_.js_options.gc_threshold_bytes;
+ if (do_collect_garbage) {
accumulated_extra_memory_cost_ = 0;
CollectGarbage();
}
@@ -292,9 +295,12 @@
} // namespace mozjs
-scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine() {
+scoped_ptr<JavaScriptEngine> JavaScriptEngine::CreateEngine(
+ const JavaScriptEngine::Options& options) {
TRACE_EVENT0("cobalt::script", "JavaScriptEngine::CreateEngine()");
- return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine());
+ mozjs::MozjsEngine::Options moz_options(options);
+ return make_scoped_ptr<JavaScriptEngine>(
+ new mozjs::MozjsEngine(moz_options));
}
} // namespace script
diff --git a/src/cobalt/script/mozjs/mozjs_engine.h b/src/cobalt/script/mozjs/mozjs_engine.h
index 447d6d4..3bd5235 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.h
+++ b/src/cobalt/script/mozjs/mozjs_engine.h
@@ -28,11 +28,16 @@
class MozjsEngine : public JavaScriptEngine {
public:
- MozjsEngine();
+ struct Options {
+ Options() {}
+ explicit Options(const JavaScriptEngine::Options& opt) : js_options(opt) {}
+ JavaScriptEngine::Options js_options; // Generic settings.
+ };
+
+ explicit MozjsEngine(const Options& options);
~MozjsEngine() OVERRIDE;
- scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment(
- const JavaScriptEngine::Options& options) OVERRIDE;
+ scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() OVERRIDE;
void CollectGarbage() OVERRIDE;
void ReportExtraMemoryCost(size_t bytes) OVERRIDE;
@@ -68,6 +73,8 @@
// Used to handle javascript errors.
ErrorHandler error_handler_;
+
+ Options moz_options_;
};
} // namespace mozjs
} // namespace script
diff --git a/src/cobalt/script/mozjs/mozjs_exception_state.cc b/src/cobalt/script/mozjs/mozjs_exception_state.cc
index e2fefbc..ab6387d 100644
--- a/src/cobalt/script/mozjs/mozjs_exception_state.cc
+++ b/src/cobalt/script/mozjs/mozjs_exception_state.cc
@@ -72,30 +72,24 @@
is_exception_set_ = true;
}
-void MozjsExceptionState::SetSimpleExceptionWithArgs(MessageType message_type,
- int dummy, ...) {
+void MozjsExceptionState::SetSimpleExceptionVA(SimpleExceptionType type,
+ const char* format,
+ va_list arguments) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!is_exception_set_);
- va_list arguments;
- va_start(arguments, dummy);
-
- std::string error_message = base::StringPrintV(
- GetExceptionMessageFormat(message_type), arguments);
+ std::string error_message = base::StringPrintV(format, arguments);
JSErrorFormatString format_string;
format_string.format = error_message.c_str();
// Already fed arguments for format.
format_string.argCount = 0;
- format_string.exnType =
- ConvertToMozjsExceptionType(GetSimpleExceptionType(message_type));
+ format_string.exnType = ConvertToMozjsExceptionType(type);
// This function creates a JSErrorReport, populate it with an error message
// obtained from the given JSErrorCallback. The resulting error message is
// passed to the context's JSErrorReporter callback.
JS_ReportErrorNumber(context_, GetErrorMessage,
- static_cast<void*>(&format_string), message_type);
- va_end(arguments);
-
+ static_cast<void*>(&format_string), type);
is_exception_set_ = true;
}
diff --git a/src/cobalt/script/mozjs/mozjs_exception_state.h b/src/cobalt/script/mozjs/mozjs_exception_state.h
index 86303a9..440c6b7 100644
--- a/src/cobalt/script/mozjs/mozjs_exception_state.h
+++ b/src/cobalt/script/mozjs/mozjs_exception_state.h
@@ -30,8 +30,8 @@
: is_exception_set_(false), context_(context) {}
// ExceptionState interface
void SetException(const scoped_refptr<ScriptException>& exception) OVERRIDE;
- void SetSimpleExceptionWithArgs(MessageType message_type,
- int dummy, ...) OVERRIDE;
+ void SetSimpleExceptionVA(SimpleExceptionType type, const char* format,
+ va_list arguments) OVERRIDE;
bool is_exception_set() const { return is_exception_set_; }
diff --git a/src/cobalt/script/mozjs/mozjs_variables.gypi b/src/cobalt/script/mozjs/mozjs_variables.gypi
index 857dfc7..f6ec89d 100644
--- a/src/cobalt/script/mozjs/mozjs_variables.gypi
+++ b/src/cobalt/script/mozjs/mozjs_variables.gypi
@@ -28,7 +28,9 @@
'engine_template_files': [
'<(DEPTH)/cobalt/bindings/mozjs/templates/callback-interface.cc.template',
'<(DEPTH)/cobalt/bindings/mozjs/templates/callback-interface.h.template',
- '<(DEPTH)/cobalt/bindings/mozjs/templates/dictionary-conversion.h.template',
+ '<(DEPTH)/cobalt/bindings/mozjs/templates/dictionary-conversion.cc.template',
+ '<(DEPTH)/cobalt/bindings/mozjs/templates/enumeration-conversion.cc.template',
+ '<(DEPTH)/cobalt/bindings/mozjs/templates/generated-types.h.template',
'<(DEPTH)/cobalt/bindings/mozjs/templates/interface.cc.template',
'<(DEPTH)/cobalt/bindings/mozjs/templates/interface.h.template',
'<(DEPTH)/cobalt/bindings/mozjs/templates/macros.cc.template',
@@ -36,8 +38,12 @@
'engine_bindings_scripts': [
'<(DEPTH)/cobalt/bindings/mozjs/code_generator_mozjs.py',
'<(DEPTH)/cobalt/bindings/mozjs/idl_compiler_mozjs.py',
+ '<(DEPTH)/cobalt/bindings/mozjs/generate_conversion_header_mozjs.py',
],
- 'engine_idl_compiler': '<(DEPTH)/cobalt/bindings/mozjs/idl_compiler_mozjs.py',
+ 'engine_idl_compiler':
+ '<(DEPTH)/cobalt/bindings/mozjs/idl_compiler_mozjs.py',
+ 'engine_conversion_header_generator_script':
+ '<(DEPTH)/cobalt/bindings/mozjs/generate_conversion_header_mozjs.py',
}],
],
},
diff --git a/src/cobalt/script/mozjs/native_promise_test.cc b/src/cobalt/script/mozjs/native_promise_test.cc
index e287f73..9194b9c 100644
--- a/src/cobalt/script/mozjs/native_promise_test.cc
+++ b/src/cobalt/script/mozjs/native_promise_test.cc
@@ -34,8 +34,7 @@
NativePromiseTest()
: environment_settings_(new script::EnvironmentSettings),
engine_(script::JavaScriptEngine::CreateEngine()),
- global_environment_(engine_->CreateGlobalEnvironment(
- script::JavaScriptEngine::Options())) {
+ global_environment_(engine_->CreateGlobalEnvironment()) {
global_environment_->CreateGlobalObject();
}
diff --git a/src/cobalt/script/script_runner.cc b/src/cobalt/script/script_runner.cc
index cc98f3f..0768705 100644
--- a/src/cobalt/script/script_runner.cc
+++ b/src/cobalt/script/script_runner.cc
@@ -69,7 +69,8 @@
: global_environment_(global_environment) {}
std::string Execute(const std::string& script_utf8,
- const base::SourceLocation& script_location) OVERRIDE;
+ const base::SourceLocation& script_location,
+ bool* out_succeeded) OVERRIDE;
GlobalEnvironment* GetGlobalEnvironment() const OVERRIDE {
return global_environment_;
}
@@ -80,9 +81,13 @@
std::string ScriptRunnerImpl::Execute(
const std::string& script_utf8,
- const base::SourceLocation& script_location) {
+ const base::SourceLocation& script_location,
+ bool* out_succeeded) {
scoped_refptr<SourceCode> source_code =
SourceCode::CreateSourceCode(script_utf8, script_location);
+ if (out_succeeded) {
+ *out_succeeded = false;
+ }
if (source_code == NULL) {
NOTREACHED() << "Failed to pre-process JavaScript source.";
return "";
@@ -98,6 +103,9 @@
#if defined(HANDLE_CORE_DUMP)
script_runner_log.Get().IncrementSuccessCount();
#endif
+ if (out_succeeded) {
+ *out_succeeded = true;
+ }
return result;
}
diff --git a/src/cobalt/script/script_runner.h b/src/cobalt/script/script_runner.h
index a040e3b..142c136 100644
--- a/src/cobalt/script/script_runner.h
+++ b/src/cobalt/script/script_runner.h
@@ -32,8 +32,11 @@
static scoped_ptr<ScriptRunner> CreateScriptRunner(
const scoped_refptr<GlobalEnvironment>& global_environment);
+ // |out_succeeded| is an optional parameter which reports whether the
+ // script executed without errors.
virtual std::string Execute(const std::string& script_utf8,
- const base::SourceLocation& script_location) = 0;
+ const base::SourceLocation& script_location,
+ bool* out_succeeded) = 0;
virtual GlobalEnvironment* GetGlobalEnvironment() const { return NULL; }
virtual ~ScriptRunner() {}
};
diff --git a/src/cobalt/script/standalone_javascript_runner.cc b/src/cobalt/script/standalone_javascript_runner.cc
index f2f0933..528f379 100644
--- a/src/cobalt/script/standalone_javascript_runner.cc
+++ b/src/cobalt/script/standalone_javascript_runner.cc
@@ -55,8 +55,8 @@
void StandaloneJavascriptRunner::CommonInitialization(
const JavaScriptEngine::Options& options) {
- engine_ = JavaScriptEngine::CreateEngine();
- global_environment_ = engine_->CreateGlobalEnvironment(options);
+ engine_ = JavaScriptEngine::CreateEngine(options);
+ global_environment_ = engine_->CreateGlobalEnvironment();
environment_settings_.reset(new EnvironmentSettings());
}
diff --git a/src/cobalt/script/standalone_javascript_runner.h b/src/cobalt/script/standalone_javascript_runner.h
index 600653b..16526df 100644
--- a/src/cobalt/script/standalone_javascript_runner.h
+++ b/src/cobalt/script/standalone_javascript_runner.h
@@ -29,10 +29,11 @@
// execute JavaScript.
class StandaloneJavascriptRunner {
public:
- explicit StandaloneJavascriptRunner(const JavaScriptEngine::Options& options);
+ StandaloneJavascriptRunner(
+ const JavaScriptEngine::Options& options = JavaScriptEngine::Options());
template <typename GlobalInterface>
- explicit StandaloneJavascriptRunner(
+ StandaloneJavascriptRunner(
const JavaScriptEngine::Options& options,
const scoped_refptr<GlobalInterface>& global_object) {
CommonInitialization(options);
diff --git a/src/cobalt/script/testing/fake_script_value.h b/src/cobalt/script/testing/fake_script_value.h
new file mode 100644
index 0000000..8e4a707
--- /dev/null
+++ b/src/cobalt/script/testing/fake_script_value.h
@@ -0,0 +1,57 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/script/script_value.h"
+
+#ifndef COBALT_SCRIPT_TESTING_FAKE_SCRIPT_VALUE_H_
+#define COBALT_SCRIPT_TESTING_FAKE_SCRIPT_VALUE_H_
+
+namespace cobalt {
+namespace script {
+namespace testing {
+
+template <class T>
+class FakeScriptValue : public cobalt::script::ScriptValue<T> {
+ public:
+ typedef cobalt::script::ScriptValue<T> BaseClass;
+ explicit FakeScriptValue(const T* listener)
+ : value_(listener) {}
+
+ void RegisterOwner(script::Wrappable*) OVERRIDE {}
+ void DeregisterOwner(script::Wrappable*) OVERRIDE {}
+ void PreventGarbageCollection() OVERRIDE {}
+ void AllowGarbageCollection() OVERRIDE {}
+ const T* GetScriptValue(void) const OVERRIDE {
+ return value_;
+ }
+ scoped_ptr<BaseClass> MakeCopy() const OVERRIDE {
+ return make_scoped_ptr<BaseClass>(new FakeScriptValue(value_));
+ }
+
+ bool EqualTo(const BaseClass& other) const OVERRIDE {
+ const FakeScriptValue* other_script_object =
+ base::polymorphic_downcast<const FakeScriptValue*>(&other);
+ return value_ == other_script_object->value_;
+ }
+
+ private:
+ const T* value_;
+};
+
+} // namespace testing
+} // namespace script
+} // namespace cobalt
+
+#endif // COBALT_SCRIPT_TESTING_FAKE_SCRIPT_VALUE_H_
diff --git a/src/cobalt/script/testing/mock_exception_state.h b/src/cobalt/script/testing/mock_exception_state.h
index 4613935..e1b008b 100644
--- a/src/cobalt/script/testing/mock_exception_state.h
+++ b/src/cobalt/script/testing/mock_exception_state.h
@@ -27,14 +27,8 @@
class MockExceptionState : public ExceptionState {
public:
MOCK_METHOD1(SetException, void(const scoped_refptr<ScriptException>&));
- MOCK_METHOD2(SetSimpleExceptionVA, void(MessageType, va_list));
-
- void SetSimpleExceptionWithArgs(MessageType message_type, int dummy, ...) {
- va_list arguments;
- va_start(arguments, dummy);
- SetSimpleExceptionVA(message_type, arguments);
- va_end(arguments);
- }
+ MOCK_METHOD3(SetSimpleExceptionVA,
+ void(SimpleExceptionType, const char*, va_list));
};
} // namespace testing
diff --git a/src/cobalt/speech/cobalt_speech_recognizer.cc b/src/cobalt/speech/cobalt_speech_recognizer.cc
new file mode 100644
index 0000000..0a8c161
--- /dev/null
+++ b/src/cobalt/speech/cobalt_speech_recognizer.cc
@@ -0,0 +1,154 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/speech/cobalt_speech_recognizer.h"
+
+#include "base/bind.h"
+#include "cobalt/base/tokens.h"
+#if defined(ENABLE_FAKE_MICROPHONE)
+#include "cobalt/speech/microphone_fake.h"
+#include "cobalt/speech/url_fetcher_fake.h"
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+#include "cobalt/speech/microphone_manager.h"
+#if defined(SB_USE_SB_MICROPHONE)
+#include "cobalt/speech/microphone_starboard.h"
+#endif // defined(SB_USE_SB_MICROPHONE)
+#include "net/url_request/url_fetcher.h"
+
+namespace cobalt {
+namespace speech {
+
+namespace {
+const int kSampleRate = 16000;
+const float kAudioPacketDurationInSeconds = 0.1f;
+
+scoped_ptr<net::URLFetcher> CreateURLFetcher(
+ const GURL& url, net::URLFetcher::RequestType request_type,
+ net::URLFetcherDelegate* delegate) {
+ return make_scoped_ptr<net::URLFetcher>(
+ net::URLFetcher::Create(url, request_type, delegate));
+}
+
+scoped_ptr<Microphone> CreateMicrophone(int buffer_size_bytes) {
+#if defined(SB_USE_SB_MICROPHONE)
+ return make_scoped_ptr<Microphone>(
+ new MicrophoneStarboard(kSampleRate, buffer_size_bytes));
+#else
+ UNREFERENCED_PARAMETER(buffer_size_bytes);
+ return scoped_ptr<Microphone>();
+#endif // defined(SB_USE_SB_MICROPHONE)
+}
+
+#if defined(SB_USE_SB_MICROPHONE)
+#if defined(ENABLE_FAKE_MICROPHONE)
+scoped_ptr<net::URLFetcher> CreateFakeURLFetcher(
+ const GURL& url, net::URLFetcher::RequestType request_type,
+ net::URLFetcherDelegate* delegate) {
+ return make_scoped_ptr<net::URLFetcher>(
+ new URLFetcherFake(url, request_type, delegate));
+}
+
+scoped_ptr<Microphone> CreateFakeMicrophone(const Microphone::Options& options,
+ int /*buffer_size_bytes*/) {
+ return make_scoped_ptr<Microphone>(new MicrophoneFake(options));
+}
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+#endif // defined(SB_USE_SB_MICROPHONE)
+} // namespace
+
+CobaltSpeechRecognizer::CobaltSpeechRecognizer(
+ network::NetworkModule* network_module,
+ const Microphone::Options& microphone_options,
+ const EventCallback& event_callback)
+ : SpeechRecognizer(event_callback), endpointer_delegate_(kSampleRate) {
+ UNREFERENCED_PARAMETER(microphone_options);
+
+ GoogleSpeechService::URLFetcherCreator url_fetcher_creator =
+ base::Bind(&CreateURLFetcher);
+ MicrophoneManager::MicrophoneCreator microphone_creator =
+ base::Bind(&CreateMicrophone);
+
+#if defined(SB_USE_SB_MICROPHONE)
+#if defined(ENABLE_FAKE_MICROPHONE)
+ if (microphone_options.enable_fake_microphone) {
+ // If fake microphone is enabled, fake URL fetchers should be enabled as
+ // well.
+ url_fetcher_creator = base::Bind(&CreateFakeURLFetcher);
+ microphone_creator = base::Bind(&CreateFakeMicrophone, microphone_options);
+ }
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+#endif // defined(SB_USE_SB_MICROPHONE)
+
+ service_.reset(new GoogleSpeechService(
+ network_module, base::Bind(&CobaltSpeechRecognizer::OnRecognizerEvent,
+ base::Unretained(this)),
+ url_fetcher_creator));
+ microphone_manager_.reset(new MicrophoneManager(
+ base::Bind(&CobaltSpeechRecognizer::OnDataReceived,
+ base::Unretained(this)),
+ base::Bind(&CobaltSpeechRecognizer::OnDataCompletion,
+ base::Unretained(this)),
+ base::Bind(&CobaltSpeechRecognizer::OnMicError, base::Unretained(this)),
+ microphone_creator));
+}
+
+CobaltSpeechRecognizer::~CobaltSpeechRecognizer() {}
+
+void CobaltSpeechRecognizer::Start(const SpeechRecognitionConfig& config) {
+ service_->Start(config, kSampleRate);
+ microphone_manager_->Open();
+ endpointer_delegate_.Start();
+}
+
+void CobaltSpeechRecognizer::Stop() {
+ endpointer_delegate_.Stop();
+ microphone_manager_->Close();
+ service_->Stop();
+ RunEventCallback(new dom::Event(base::Tokens::soundend()));
+}
+
+void CobaltSpeechRecognizer::OnDataReceived(
+ scoped_ptr<ShellAudioBus> audio_bus) {
+ if (endpointer_delegate_.IsFirstTimeSoundStarted(*audio_bus)) {
+ RunEventCallback(new dom::Event(base::Tokens::soundstart()));
+ }
+ service_->RecognizeAudio(audio_bus.Pass(), false);
+}
+
+void CobaltSpeechRecognizer::OnDataCompletion() {
+ // The encoder requires a non-empty final buffer, so encoding a packet of
+ // silence at the end in case encoder had no data already.
+ size_t dummy_frames =
+ static_cast<size_t>(kSampleRate * kAudioPacketDurationInSeconds);
+ scoped_ptr<ShellAudioBus> dummy_audio_bus(new ShellAudioBus(
+ 1, dummy_frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+ dummy_audio_bus->ZeroAllFrames();
+ service_->RecognizeAudio(dummy_audio_bus.Pass(), true);
+}
+
+void CobaltSpeechRecognizer::OnRecognizerEvent(
+ const scoped_refptr<dom::Event>& event) {
+ RunEventCallback(event);
+}
+
+void CobaltSpeechRecognizer::OnMicError(
+ const scoped_refptr<dom::Event>& event) {
+ // An error is occured in Mic, so stop the energy endpointer and recognizer.
+ endpointer_delegate_.Stop();
+ service_->Stop();
+ RunEventCallback(event);
+}
+
+} // namespace speech
+} // namespace cobalt
diff --git a/src/cobalt/speech/cobalt_speech_recognizer.h b/src/cobalt/speech/cobalt_speech_recognizer.h
new file mode 100644
index 0000000..5c9729d
--- /dev/null
+++ b/src/cobalt/speech/cobalt_speech_recognizer.h
@@ -0,0 +1,76 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_COBALT_SPEECH_RECOGNIZER_H_
+#define COBALT_SPEECH_COBALT_SPEECH_RECOGNIZER_H_
+
+#include "cobalt/network/network_module.h"
+#include "cobalt/speech/endpointer_delegate.h"
+#include "cobalt/speech/google_speech_service.h"
+#include "cobalt/speech/microphone.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"
+#include "cobalt/speech/speech_recognizer.h"
+#if defined(COBALT_MEDIA_SOURCE_2016)
+#include "cobalt/media/base/shell_audio_bus.h"
+#else // defined(COBALT_MEDIA_SOURCE_2016)
+#include "media/base/shell_audio_bus.h"
+#endif // defined(COBALT_MEDIA_SOURCE_2016)
+
+namespace cobalt {
+namespace speech {
+
+// Cobalt's implementation of speech recognizer which interacts with Google
+// speech service and device's microphone. It collects the microphone PCM audio
+// data and sends it to Google speech service, then gets the recognition results
+// from there.
+class CobaltSpeechRecognizer : public SpeechRecognizer {
+ public:
+#if defined(COBALT_MEDIA_SOURCE_2016)
+ typedef media::ShellAudioBus ShellAudioBus;
+#else // defined(COBALT_MEDIA_SOURCE_2016)
+ typedef ::media::ShellAudioBus ShellAudioBus;
+#endif // defined(COBALT_MEDIA_SOURCE_2016)
+
+ CobaltSpeechRecognizer(network::NetworkModule* network_module,
+ const Microphone::Options& microphone_options,
+ const EventCallback& event_callback);
+ ~CobaltSpeechRecognizer();
+
+ void Start(const SpeechRecognitionConfig& config) SB_OVERRIDE;
+ void Stop() SB_OVERRIDE;
+
+ private:
+ // Callbacks from mic.
+ void OnDataReceived(scoped_ptr<ShellAudioBus> audio_bus);
+ void OnDataCompletion();
+ void OnMicError(const scoped_refptr<dom::Event>& event);
+
+ // Callbacks from recognizer.
+ void OnRecognizerEvent(const scoped_refptr<dom::Event>& event);
+
+ EventCallback event_callback_;
+ scoped_ptr<GoogleSpeechService> service_;
+ scoped_ptr<MicrophoneManager> microphone_manager_;
+ // Delegate of endpointer which is used for detecting sound energy.
+ EndPointerDelegate endpointer_delegate_;
+};
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // COBALT_SPEECH_COBALT_SPEECH_RECOGNIZER_H_
diff --git a/src/cobalt/speech/google_speech_service.cc b/src/cobalt/speech/google_speech_service.cc
new file mode 100644
index 0000000..1b49b80
--- /dev/null
+++ b/src/cobalt/speech/google_speech_service.cc
@@ -0,0 +1,398 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Note to Cobalt porters: To use the v1 speech-api API, you must provide an API
+// key with v1 speech-api quota. The code is provided here, but not an API key.
+//
+// This is similar to how Chromium handles API keys:
+// https://www.chromium.org/developers/how-tos/api-keys
+//
+// The API key is provided by SbSystemGetProperty:
+// http://cobalt.foo/reference/starboard/modules/system.html#sbsystemgetproperty
+//
+// Talk with your Google representative about how to get speech-api quota.
+
+#include "cobalt/speech/google_speech_service.h"
+
+#include "base/bind.h"
+#include "base/rand_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "cobalt/base/language.h"
+#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/network/network_module.h"
+#include "cobalt/speech/google_streaming_api.pb.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/system.h"
+#endif // defined(SB_USE_SB_MICROPHONE)
+
+namespace cobalt {
+namespace speech {
+
+namespace {
+const char kBaseStreamURL[] =
+ "https://www.google.com/speech-api/full-duplex/v1";
+const char kUp[] = "up";
+const char kDown[] = "down";
+const char kClient[] = "com.speech.tv";
+
+GURL AppendPath(const GURL& url, const std::string& value) {
+ std::string path(url.path());
+
+ if (!path.empty()) path += "/";
+
+ path += net::EscapePath(value);
+ GURL::Replacements replacements;
+ replacements.SetPathStr(path);
+ return url.ReplaceComponents(replacements);
+}
+
+GURL AppendQueryParameter(const GURL& url, const std::string& new_query,
+ const std::string& value) {
+ std::string query(url.query());
+
+ if (!query.empty()) query += "&";
+
+ query += net::EscapeQueryParamValue(new_query, true);
+
+ if (!value.empty()) {
+ query += "=" + net::EscapeQueryParamValue(value, true);
+ }
+
+ GURL::Replacements replacements;
+ replacements.SetQueryStr(query);
+ return url.ReplaceComponents(replacements);
+}
+
+SpeechRecognitionResultList::SpeechRecognitionResults
+ProcessProtoSuccessResults(const proto::SpeechRecognitionEvent& event) {
+ DCHECK_EQ(event.status(), proto::SpeechRecognitionEvent::STATUS_SUCCESS);
+
+ SpeechRecognitionResultList::SpeechRecognitionResults results;
+ for (int i = 0; i < event.result_size(); ++i) {
+ SpeechRecognitionResult::SpeechRecognitionAlternatives alternatives;
+ 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(proto_alternative.transcript(),
+ confidence));
+ alternatives.push_back(alternative);
+ }
+
+ bool final = proto_result.has_final() && proto_result.final();
+ scoped_refptr<SpeechRecognitionResult> recognition_result(
+ new SpeechRecognitionResult(alternatives, final));
+ results.push_back(recognition_result);
+ }
+ return results;
+}
+
+// TODO: Feed error messages when creating SpeechRecognitionError.
+void ProcessAndFireErrorEvent(
+ const proto::SpeechRecognitionEvent& event,
+ const GoogleSpeechService::EventCallback& event_callback) {
+ scoped_refptr<dom::Event> error_event;
+ switch (event.status()) {
+ case proto::SpeechRecognitionEvent::STATUS_SUCCESS:
+ NOTREACHED();
+ return;
+ case proto::SpeechRecognitionEvent::STATUS_NO_SPEECH:
+ error_event =
+ new SpeechRecognitionError(kSpeechRecognitionErrorCodeNoSpeech, "");
+ break;
+ case proto::SpeechRecognitionEvent::STATUS_ABORTED:
+ error_event =
+ new SpeechRecognitionError(kSpeechRecognitionErrorCodeAborted, "");
+ break;
+ case proto::SpeechRecognitionEvent::STATUS_AUDIO_CAPTURE:
+ error_event = new SpeechRecognitionError(
+ kSpeechRecognitionErrorCodeAudioCapture, "");
+ break;
+ case proto::SpeechRecognitionEvent::STATUS_NETWORK:
+ error_event =
+ new SpeechRecognitionError(kSpeechRecognitionErrorCodeNetwork, "");
+ break;
+ case proto::SpeechRecognitionEvent::STATUS_NOT_ALLOWED:
+ error_event =
+ new SpeechRecognitionError(kSpeechRecognitionErrorCodeNotAllowed, "");
+ break;
+ case proto::SpeechRecognitionEvent::STATUS_SERVICE_NOT_ALLOWED:
+ error_event = new SpeechRecognitionError(
+ kSpeechRecognitionErrorCodeServiceNotAllowed, "");
+ break;
+ case proto::SpeechRecognitionEvent::STATUS_BAD_GRAMMAR:
+ error_event =
+ new SpeechRecognitionError(kSpeechRecognitionErrorCodeBadGrammar, "");
+ break;
+ case proto::SpeechRecognitionEvent::STATUS_LANGUAGE_NOT_SUPPORTED:
+ error_event = new SpeechRecognitionError(
+ kSpeechRecognitionErrorCodeLanguageNotSupported, "");
+ break;
+ }
+
+ DCHECK(error_event);
+ 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
+
+GoogleSpeechService::GoogleSpeechService(
+ network::NetworkModule* network_module, const EventCallback& event_callback,
+ const URLFetcherCreator& fetcher_creator)
+ : network_module_(network_module),
+ started_(false),
+ event_callback_(event_callback),
+ fetcher_creator_(fetcher_creator),
+ thread_("speech_recognizer") {
+ thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0));
+}
+
+GoogleSpeechService::~GoogleSpeechService() {
+ Stop();
+ // Stopping the thread here to ensure that StopInternal has completed before
+ // we finish running the destructor.
+ thread_.Stop();
+}
+
+void GoogleSpeechService::Start(const SpeechRecognitionConfig& config,
+ int sample_rate) {
+ // Called by the speech recognition manager thread.
+ thread_.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&GoogleSpeechService::StartInternal,
+ base::Unretained(this), config, sample_rate));
+}
+
+void GoogleSpeechService::Stop() {
+ // Called by the speech recognition manager thread.
+ thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&GoogleSpeechService::StopInternal, base::Unretained(this)));
+}
+
+void GoogleSpeechService::RecognizeAudio(scoped_ptr<ShellAudioBus> audio_bus,
+ bool is_last_chunk) {
+ // Called by the speech recognition manager thread.
+ thread_.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&GoogleSpeechService::UploadAudioDataInternal,
+ base::Unretained(this), base::Passed(&audio_bus),
+ is_last_chunk));
+}
+
+void GoogleSpeechService::OnURLFetchDownloadData(
+ 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()) {
+ 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;
+ }
+
+ if (event.status() == proto::SpeechRecognitionEvent::STATUS_SUCCESS) {
+ ProcessAndFireSuccessEvent(ProcessProtoSuccessResults(event));
+ } else {
+ ProcessAndFireErrorEvent(event, event_callback_);
+ }
+ }
+ } else {
+ event_callback_.Run(new SpeechRecognitionError(
+ kSpeechRecognitionErrorCodeNetwork, "Network response failure."));
+ }
+ }
+}
+
+void GoogleSpeechService::OnURLFetchComplete(const net::URLFetcher* source) {
+ DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+ UNREFERENCED_PARAMETER(source);
+ // no-op.
+}
+
+void GoogleSpeechService::StartInternal(const SpeechRecognitionConfig& config,
+ int sample_rate) {
+ DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+
+ if (started_) {
+ // Recognizer is already started.
+ return;
+ }
+ started_ = true;
+
+ encoder_.reset(new AudioEncoderFlac(sample_rate));
+
+ // Required for streaming on both up and down connections.
+ std::string pair = base::Uint64ToString(base::RandUint64());
+
+ // Set up down stream first.
+ GURL down_url(kBaseStreamURL);
+ down_url = AppendPath(down_url, kDown);
+ down_url = AppendQueryParameter(down_url, "pair", pair);
+ // Use protobuffer as the output format.
+ down_url = AppendQueryParameter(down_url, "output", "pb");
+
+ downstream_fetcher_ =
+ fetcher_creator_.Run(down_url, net::URLFetcher::GET, this);
+ downstream_fetcher_->SetRequestContext(
+ network_module_->url_request_context_getter());
+ downstream_fetcher_->Start();
+
+ // Up stream.
+ GURL up_url(kBaseStreamURL);
+ up_url = AppendPath(up_url, kUp);
+ up_url = AppendQueryParameter(up_url, "client", kClient);
+ up_url = AppendQueryParameter(up_url, "pair", pair);
+ up_url = AppendQueryParameter(up_url, "output", "pb");
+
+ const char* speech_api_key = "";
+#if defined(OS_STARBOARD)
+#if SB_VERSION(2)
+ const int kSpeechApiKeyLength = 100;
+ char buffer[kSpeechApiKeyLength] = {0};
+ bool result = SbSystemGetProperty(kSbSystemPropertySpeechApiKey, buffer,
+ SB_ARRAY_SIZE_INT(buffer));
+ SB_DCHECK(result);
+ speech_api_key = result ? buffer : "";
+#endif // SB_VERSION(2)
+#endif // defined(OS_STARBOARD)
+
+ 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()) {
+ up_url = AppendQueryParameter(up_url, "lang", config.lang);
+ } else {
+ up_url = AppendQueryParameter(up_url, "lang", base::GetSystemLanguage());
+ }
+
+ if (config.max_alternatives) {
+ up_url = AppendQueryParameter(up_url, "maxAlternatives",
+ base::UintToString(config.max_alternatives));
+ }
+
+ if (config.continuous) {
+ up_url = AppendQueryParameter(up_url, "continuous", "");
+ }
+ if (config.interim_results) {
+ up_url = AppendQueryParameter(up_url, "interim", "");
+ }
+
+ upstream_fetcher_ = fetcher_creator_.Run(up_url, net::URLFetcher::POST, this);
+ upstream_fetcher_->SetRequestContext(
+ network_module_->url_request_context_getter());
+ upstream_fetcher_->SetChunkedUpload(encoder_->GetMimeType());
+ upstream_fetcher_->Start();
+}
+
+void GoogleSpeechService::StopInternal() {
+ DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+
+ if (!started_) {
+ // Recognizer is not started.
+ return;
+ }
+ started_ = false;
+
+ upstream_fetcher_.reset();
+ downstream_fetcher_.reset();
+ encoder_.reset();
+
+ // Clear the final results.
+ final_results_.clear();
+ // Clear any remaining audio data.
+ chunked_byte_buffer_.Clear();
+}
+
+void GoogleSpeechService::UploadAudioDataInternal(
+ scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) {
+ DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+ DCHECK(audio_bus);
+
+ std::string encoded_audio_data;
+ if (encoder_) {
+ encoder_->Encode(audio_bus.get());
+ if (is_last_chunk) {
+ encoder_->Finish();
+ }
+ encoded_audio_data = encoder_->GetAndClearAvailableEncodedData();
+ }
+
+ if (upstream_fetcher_ && !encoded_audio_data.empty()) {
+ upstream_fetcher_->AppendChunkToUpload(encoded_audio_data, is_last_chunk);
+ }
+}
+
+void GoogleSpeechService::ProcessAndFireSuccessEvent(
+ const SpeechRecognitionResults& new_results) {
+ DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
+
+ SpeechRecognitionResults success_results;
+ size_t total_size = final_results_.size() + new_results.size();
+ success_results.reserve(total_size);
+ success_results = final_results_;
+ success_results.insert(success_results.end(), new_results.begin(),
+ new_results.end());
+
+ size_t result_index = final_results_.size();
+ // Update final results list.
+ for (size_t i = 0; i < new_results.size(); ++i) {
+ if (new_results[i]->is_final()) {
+ final_results_.push_back(new_results[i]);
+ }
+ }
+
+ scoped_refptr<SpeechRecognitionResultList> recognition_list(
+ new SpeechRecognitionResultList(success_results));
+ scoped_refptr<SpeechRecognitionEvent> recognition_event(
+ new SpeechRecognitionEvent(SpeechRecognitionEvent::kResult,
+ static_cast<uint32>(result_index),
+ recognition_list));
+ event_callback_.Run(recognition_event);
+}
+
+} // namespace speech
+} // namespace cobalt
diff --git a/src/cobalt/speech/google_speech_service.h b/src/cobalt/speech/google_speech_service.h
new file mode 100644
index 0000000..30d0113
--- /dev/null
+++ b/src/cobalt/speech/google_speech_service.h
@@ -0,0 +1,114 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_GOOGLE_SPEECH_SERVICE_H_
+#define COBALT_SPEECH_GOOGLE_SPEECH_SERVICE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/threading/thread.h"
+#include "cobalt/network/network_module.h"
+#include "cobalt/speech/audio_encoder_flac.h"
+#include "cobalt/speech/speech_recognition_config.h"
+#include "cobalt/speech/speech_recognition_event.h"
+#include "content/browser/speech/chunked_byte_buffer.h"
+#if defined(COBALT_MEDIA_SOURCE_2016)
+#include "cobalt/media/base/shell_audio_bus.h"
+#else // defined(COBALT_MEDIA_SOURCE_2016)
+#include "media/base/shell_audio_bus.h"
+#endif // defined(COBALT_MEDIA_SOURCE_2016)
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+namespace cobalt {
+namespace speech {
+
+// Interacts with Google speech recognition service, and then parses recognition
+// results and forms speech recognition event.
+// It creates an upstream fetcher to upload the encoded audio and a downstream
+// fetcher to fetch the speech recognition result. The fetched speech
+// recognition result is parsed by JSON parser and a SpeechRecognitionEvent,
+// is formed based on the parsed result, would be sent to speech recognition
+// manager.
+class GoogleSpeechService : public net::URLFetcherDelegate {
+ public:
+#if defined(COBALT_MEDIA_SOURCE_2016)
+ typedef media::ShellAudioBus ShellAudioBus;
+#else // defined(COBALT_MEDIA_SOURCE_2016)
+ typedef ::media::ShellAudioBus ShellAudioBus;
+#endif // defined(COBALT_MEDIA_SOURCE_2016)
+ typedef base::Callback<void(const scoped_refptr<dom::Event>&)> EventCallback;
+ typedef SpeechRecognitionResultList::SpeechRecognitionResults
+ SpeechRecognitionResults;
+ typedef base::Callback<scoped_ptr<net::URLFetcher>(
+ const GURL&, net::URLFetcher::RequestType, net::URLFetcherDelegate*)>
+ URLFetcherCreator;
+
+ GoogleSpeechService(network::NetworkModule* network_module,
+ const EventCallback& event_callback,
+ const URLFetcherCreator& fetcher_creator);
+ ~GoogleSpeechService() OVERRIDE;
+
+ // Multiple calls to Start/Stop are allowed, the implementation should take
+ // care of multiple calls.
+ // Start speech recognizer.
+ void Start(const SpeechRecognitionConfig& config, int sample_rate);
+ // Stop speech recognizer.
+ void Stop();
+ // An encoded audio data is available and ready to be recognized.
+ void RecognizeAudio(scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk);
+
+ // net::URLFetcherDelegate interface
+ void OnURLFetchDownloadData(const net::URLFetcher* source,
+ scoped_ptr<std::string> download_data) OVERRIDE;
+ void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+ bool ShouldSendDownloadData() OVERRIDE { return true; }
+ void OnURLFetchUploadProgress(const net::URLFetcher* /*source*/,
+ int64 /*current*/, int64 /*total*/) OVERRIDE {}
+
+ private:
+ void StartInternal(const SpeechRecognitionConfig& config, int sample_rate);
+ void StopInternal();
+ void UploadAudioDataInternal(scoped_ptr<ShellAudioBus> audio_bus,
+ bool is_last_chunk);
+ void ProcessAndFireSuccessEvent(const SpeechRecognitionResults& new_results);
+
+ // This is used for creating fetchers.
+ network::NetworkModule* network_module_;
+ // Track the start/stop state of speech recognizer.
+ bool started_;
+
+ // Encoder for encoding raw audio data to flac codec.
+ scoped_ptr<AudioEncoderFlac> encoder_;
+ // Fetcher for posting the audio data.
+ scoped_ptr<net::URLFetcher> upstream_fetcher_;
+ // Fetcher for receiving the streaming results.
+ scoped_ptr<net::URLFetcher> downstream_fetcher_;
+ // Used to send speech recognition event.
+ const EventCallback event_callback_;
+ // Used to create url fetcher.
+ const URLFetcherCreator fetcher_creator_;
+ // Used for processing proto buffer data.
+ content::ChunkedByteBuffer chunked_byte_buffer_;
+ // Used for accumulating final results.
+ SpeechRecognitionResults final_results_;
+ // Speech recognizer is operating in its own thread.
+ base::Thread thread_;
+};
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // COBALT_SPEECH_GOOGLE_SPEECH_SERVICE_H_
diff --git a/src/cobalt/speech/microphone_manager.cc b/src/cobalt/speech/microphone_manager.cc
index 2faf329..ac38787 100644
--- a/src/cobalt/speech/microphone_manager.cc
+++ b/src/cobalt/speech/microphone_manager.cc
@@ -73,7 +73,7 @@
microphone_.reset();
state_ = kError;
error_callback_.Run(new SpeechRecognitionError(
- SpeechRecognitionError::kAudioCapture, "No microphone available."));
+ kSpeechRecognitionErrorCodeAudioCapture, "No microphone available."));
return false;
}
}
@@ -90,7 +90,7 @@
if (!microphone_->Open()) {
state_ = kError;
error_callback_.Run(new SpeechRecognitionError(
- SpeechRecognitionError::kAborted, "Microphone open failed."));
+ kSpeechRecognitionErrorCodeAborted, "Microphone open failed."));
return;
}
@@ -118,7 +118,7 @@
if (!microphone_->Close()) {
state_ = kError;
error_callback_.Run(new SpeechRecognitionError(
- SpeechRecognitionError::kAborted, "Microphone close failed."));
+ kSpeechRecognitionErrorCodeAborted, "Microphone close failed."));
return;
}
completion_callback_.Run();
@@ -147,7 +147,7 @@
} else if (read_bytes != 0) {
state_ = kError;
error_callback_.Run(new SpeechRecognitionError(
- SpeechRecognitionError::kAborted, "Microphone read failed."));
+ kSpeechRecognitionErrorCodeAborted, "Microphone read failed."));
poll_mic_events_timer_->Stop();
}
}
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp
index 9f0c58e..c3b77f8 100644
--- a/src/cobalt/speech/speech.gyp
+++ b/src/cobalt/speech/speech.gyp
@@ -28,8 +28,12 @@
'sources': [
'audio_encoder_flac.cc',
'audio_encoder_flac.h',
+ 'cobalt_speech_recognizer.cc',
+ 'cobalt_speech_recognizer.h',
'endpointer_delegate.cc',
'endpointer_delegate.h',
+ 'google_speech_service.cc',
+ 'google_speech_service.h',
'google_streaming_api.pb.cc',
'google_streaming_api.pb.h',
'google_streaming_api.pb.proto',
@@ -62,14 +66,23 @@
'speech_synthesis_utterance.cc',
'speech_synthesis_utterance.h',
'speech_synthesis_voice.h',
+ 'starboard_speech_recognizer.cc',
+ 'starboard_speech_recognizer.h',
],
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
'<(DEPTH)/content/browser/speech/speech.gyp:speech',
'<(DEPTH)/third_party/flac/flac.gyp:libflac',
'<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
],
+ 'export_dependent_settings': [
+ # Additionally, ensure that the include directories for generated
+ # headers are put on the include directories for targets that depend
+ # on this one.
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+ ],
'include_dirs': [
# Get protobuf headers from the chromium tree.
'<(DEPTH)/third_party/protobuf/src',
diff --git a/src/cobalt/speech/speech_configuration.h b/src/cobalt/speech/speech_configuration.h
index 0750c04..0c421e3 100644
--- a/src/cobalt/speech/speech_configuration.h
+++ b/src/cobalt/speech/speech_configuration.h
@@ -22,6 +22,11 @@
#if SB_HAS(MICROPHONE) && SB_VERSION(2)
#define SB_USE_SB_MICROPHONE 1
#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+ SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#define SB_USE_SB_SPEECH_RECOGNIZER 1
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+ // SB_SPEECH_RECOGNIZER_API_VERSION
#endif // defined(OS_STARBOARD)
#endif // COBALT_SPEECH_SPEECH_CONFIGURATION_H_
diff --git a/src/cobalt/speech/speech_recognition_error.cc b/src/cobalt/speech/speech_recognition_error.cc
index 3d227bf..3fa0ee9 100644
--- a/src/cobalt/speech/speech_recognition_error.cc
+++ b/src/cobalt/speech/speech_recognition_error.cc
@@ -19,8 +19,8 @@
namespace cobalt {
namespace speech {
-SpeechRecognitionError::SpeechRecognitionError(ErrorCode error_code,
- const std::string& message)
+SpeechRecognitionError::SpeechRecognitionError(
+ SpeechRecognitionErrorCode error_code, const std::string& message)
: dom::Event(base::Tokens::error()),
error_code_(error_code),
message_(message) {}
diff --git a/src/cobalt/speech/speech_recognition_error.h b/src/cobalt/speech/speech_recognition_error.h
index 3791df4..4bd4fef 100644
--- a/src/cobalt/speech/speech_recognition_error.h
+++ b/src/cobalt/speech/speech_recognition_error.h
@@ -21,6 +21,7 @@
#include "base/memory/ref_counted.h"
#include "cobalt/dom/event.h"
#include "cobalt/script/wrappable.h"
+#include "cobalt/speech/speech_recognition_error_code.h"
namespace cobalt {
namespace speech {
@@ -29,20 +30,10 @@
// https://dvcs.w3.org/hg/speech-api/raw-file/9a0075d25326/speechapi.html#speechreco-error
class SpeechRecognitionError : public dom::Event {
public:
- enum ErrorCode {
- kNoSpeech,
- kAborted,
- kAudioCapture,
- kNetwork,
- kNotAllowed,
- kServiceNotAllowed,
- kBadGrammar,
- kLanguageNotSupported
- };
+ SpeechRecognitionError(SpeechRecognitionErrorCode error_code,
+ const std::string& message);
- SpeechRecognitionError(ErrorCode error_code, const std::string& message);
-
- ErrorCode error() const { return error_code_; }
+ SpeechRecognitionErrorCode error() const { return error_code_; }
const std::string& message() const { return message_; }
DEFINE_WRAPPABLE_TYPE(SpeechRecognitionError);
@@ -50,7 +41,7 @@
private:
~SpeechRecognitionError() OVERRIDE {}
- const ErrorCode error_code_;
+ const SpeechRecognitionErrorCode error_code_;
const std::string message_;
DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionError);
diff --git a/src/cobalt/speech/speech_recognition_error.idl b/src/cobalt/speech/speech_recognition_error.idl
index 298d38e..ede9832 100644
--- a/src/cobalt/speech/speech_recognition_error.idl
+++ b/src/cobalt/speech/speech_recognition_error.idl
@@ -14,18 +14,7 @@
// https://dvcs.w3.org/hg/speech-api/raw-file/9a0075d25326/speechapi.html#speechreco-section
-enum ErrorCode {
- "no-speech",
- "aborted",
- "audio-capture",
- "network",
- "not-allowed",
- "service-not-allowed",
- "bad-grammar",
- "language-not-supported"
-};
-
interface SpeechRecognitionError : Event {
- readonly attribute ErrorCode error;
+ readonly attribute SpeechRecognitionErrorCode error;
readonly attribute DOMString message;
};
diff --git a/src/cobalt/base/math.cc b/src/cobalt/speech/speech_recognition_error_code.idl
similarity index 62%
copy from src/cobalt/base/math.cc
copy to src/cobalt/speech/speech_recognition_error_code.idl
index d335495..1646a5a3 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/speech/speech_recognition_error_code.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 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.
@@ -12,8 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+// https://dvcs.w3.org/hg/speech-api/raw-file/9a0075d25326/speechapi.html#speechreco-section
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+enum SpeechRecognitionErrorCode {
+ "no-speech",
+ "aborted",
+ "audio-capture",
+ "network",
+ "not-allowed",
+ "service-not-allowed",
+ "bad-grammar",
+ "language-not-supported"
+};
diff --git a/src/cobalt/speech/speech_recognition_manager.cc b/src/cobalt/speech/speech_recognition_manager.cc
index 2ad31f2..ff5b480 100644
--- a/src/cobalt/speech/speech_recognition_manager.cc
+++ b/src/cobalt/speech/speech_recognition_manager.cc
@@ -15,60 +15,17 @@
#include "cobalt/speech/speech_recognition_manager.h"
#include "base/bind.h"
-#include "cobalt/base/tokens.h"
#include "cobalt/dom/dom_exception.h"
-#if defined(ENABLE_FAKE_MICROPHONE)
-#include "cobalt/speech/microphone_fake.h"
-#include "cobalt/speech/url_fetcher_fake.h"
-#endif // defined(ENABLE_FAKE_MICROPHONE)
-#include "cobalt/speech/microphone_manager.h"
-#if defined(SB_USE_SB_MICROPHONE)
-#include "cobalt/speech/microphone_starboard.h"
-#endif // defined(SB_USE_SB_MICROPHONE)
-#include "net/url_request/url_fetcher.h"
+#include "cobalt/speech/speech_configuration.h"
+#if defined(SB_USE_SB_SPEECH_RECOGNIZER)
+#include "cobalt/speech/starboard_speech_recognizer.h"
+#else
+#include "cobalt/speech/cobalt_speech_recognizer.h"
+#endif // defined(SB_USE_SB_SPEECH_RECOGNIZER)
namespace cobalt {
namespace speech {
-namespace {
-const int kSampleRate = 16000;
-const float kAudioPacketDurationInSeconds = 0.1f;
-
-scoped_ptr<net::URLFetcher> CreateURLFetcher(
- const GURL& url, net::URLFetcher::RequestType request_type,
- net::URLFetcherDelegate* delegate) {
- return make_scoped_ptr<net::URLFetcher>(
- net::URLFetcher::Create(url, request_type, delegate));
-}
-
-scoped_ptr<Microphone> CreateMicrophone(int buffer_size_bytes) {
-#if defined(SB_USE_SB_MICROPHONE)
- return make_scoped_ptr<Microphone>(
- new MicrophoneStarboard(kSampleRate, buffer_size_bytes));
-#else
- UNREFERENCED_PARAMETER(buffer_size_bytes);
- return scoped_ptr<Microphone>();
-#endif // defined(SB_USE_SB_MICROPHONE)
-}
-
-#if defined(SB_USE_SB_MICROPHONE)
-#if defined(ENABLE_FAKE_MICROPHONE)
-scoped_ptr<net::URLFetcher> CreateFakeURLFetcher(
- const GURL& url, net::URLFetcher::RequestType request_type,
- net::URLFetcherDelegate* delegate) {
- return make_scoped_ptr<net::URLFetcher>(
- new URLFetcherFake(url, request_type, delegate));
-}
-
-scoped_ptr<Microphone> CreateFakeMicrophone(const Microphone::Options& options,
- int /*buffer_size_bytes*/) {
- return make_scoped_ptr<Microphone>(new MicrophoneFake(options));
-}
-#endif // defined(ENABLE_FAKE_MICROPHONE)
-#endif // defined(SB_USE_SB_MICROPHONE)
-
-} // namespace
-
SpeechRecognitionManager::SpeechRecognitionManager(
network::NetworkModule* network_module, const EventCallback& event_callback,
const Microphone::Options& microphone_options)
@@ -76,40 +33,21 @@
weak_this_(weak_ptr_factory_.GetWeakPtr()),
main_message_loop_(base::MessageLoopProxy::current()),
event_callback_(event_callback),
- endpointer_delegate_(kSampleRate),
state_(kStopped) {
+#if defined(SB_USE_SB_SPEECH_RECOGNIZER)
+ UNREFERENCED_PARAMETER(network_module);
UNREFERENCED_PARAMETER(microphone_options);
-
- SpeechRecognizer::URLFetcherCreator url_fetcher_creator =
- base::Bind(&CreateURLFetcher);
- MicrophoneManager::MicrophoneCreator microphone_creator =
- base::Bind(&CreateMicrophone);
-
-#if defined(SB_USE_SB_MICROPHONE)
-#if defined(ENABLE_FAKE_MICROPHONE)
- if (microphone_options.enable_fake_microphone) {
- // If fake microphone is enabled, fake URL fetchers should be enabled as
- // well.
- url_fetcher_creator = base::Bind(&CreateFakeURLFetcher);
- microphone_creator = base::Bind(&CreateFakeMicrophone, microphone_options);
- }
-#endif // defined(ENABLE_FAKE_MICROPHONE)
-#endif // defined(SB_USE_SB_MICROPHONE)
-
- recognizer_.reset(new SpeechRecognizer(
- network_module, base::Bind(&SpeechRecognitionManager::OnRecognizerEvent,
- base::Unretained(this)),
- url_fetcher_creator));
- microphone_manager_.reset(new MicrophoneManager(
- base::Bind(&SpeechRecognitionManager::OnDataReceived,
- base::Unretained(this)),
- base::Bind(&SpeechRecognitionManager::OnDataCompletion,
- base::Unretained(this)),
- base::Bind(&SpeechRecognitionManager::OnMicError, base::Unretained(this)),
- microphone_creator));
+ recognizer_.reset(new StarboardSpeechRecognizer(base::Bind(
+ &SpeechRecognitionManager::OnEventAvailable, base::Unretained(this))));
+#else
+ recognizer_.reset(new CobaltSpeechRecognizer(
+ network_module, microphone_options,
+ base::Bind(&SpeechRecognitionManager::OnEventAvailable,
+ base::Unretained(this))));
+#endif // defined(SB_USE_SB_SPEECH_RECOGNIZER)
}
-SpeechRecognitionManager::~SpeechRecognitionManager() { Stop(false); }
+SpeechRecognitionManager::~SpeechRecognitionManager() { Abort(); }
void SpeechRecognitionManager::Start(const SpeechRecognitionConfig& config,
script::ExceptionState* exception_state) {
@@ -123,13 +61,11 @@
return;
}
- recognizer_->Start(config, kSampleRate);
- microphone_manager_->Open();
- endpointer_delegate_.Start();
+ recognizer_->Start(config);
state_ = kStarted;
}
-void SpeechRecognitionManager::Stop(bool run_callback) {
+void SpeechRecognitionManager::Stop() {
DCHECK(main_message_loop_->BelongsToCurrentThread());
// If the stop method is called on an object which is already stopped or being
@@ -138,13 +74,8 @@
return;
}
- endpointer_delegate_.Stop();
- microphone_manager_->Close();
recognizer_->Stop();
state_ = kStopped;
- if (run_callback) {
- event_callback_.Run(new dom::Event(base::Tokens::soundend()));
- }
}
void SpeechRecognitionManager::Abort() {
@@ -156,60 +87,17 @@
return;
}
- endpointer_delegate_.Stop();
- microphone_manager_->Close();
- recognizer_->Stop();
state_ = kAborted;
- event_callback_.Run(new dom::Event(base::Tokens::soundend()));
+ recognizer_->Stop();
}
-void SpeechRecognitionManager::OnDataReceived(
- scoped_ptr<ShellAudioBus> audio_bus) {
- if (!main_message_loop_->BelongsToCurrentThread()) {
- // Called from mic thread.
- main_message_loop_->PostTask(
- FROM_HERE, base::Bind(&SpeechRecognitionManager::OnDataReceived,
- weak_this_, base::Passed(&audio_bus)));
- return;
- }
-
- // 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);
- }
-}
-
-void SpeechRecognitionManager::OnDataCompletion() {
- if (!main_message_loop_->BelongsToCurrentThread()) {
- // Called from mic thread.
- main_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&SpeechRecognitionManager::OnDataCompletion, weak_this_));
- return;
- }
-
- // Stop recognizing if in the abort state.
- if (state_ != kAborted) {
- // The encoder requires a non-empty final buffer, so encoding a packet of
- // silence at the end in case encoder had no data already.
- size_t dummy_frames =
- static_cast<size_t>(kSampleRate * kAudioPacketDurationInSeconds);
- scoped_ptr<ShellAudioBus> dummy_audio_bus(new ShellAudioBus(
- 1, dummy_frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
- dummy_audio_bus->ZeroAllFrames();
- recognizer_->RecognizeAudio(dummy_audio_bus.Pass(), true);
- }
-}
-
-void SpeechRecognitionManager::OnRecognizerEvent(
+void SpeechRecognitionManager::OnEventAvailable(
const scoped_refptr<dom::Event>& event) {
if (!main_message_loop_->BelongsToCurrentThread()) {
- // Called from recognizer thread.
+ // Called from recognizer. |event_callback_| is required to be run on
+ // the |main_message_loop_|.
main_message_loop_->PostTask(
- FROM_HERE, base::Bind(&SpeechRecognitionManager::OnRecognizerEvent,
+ FROM_HERE, base::Bind(&SpeechRecognitionManager::OnEventAvailable,
weak_this_, event));
return;
}
@@ -220,24 +108,5 @@
}
}
-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_, event));
- return;
- }
-
- event_callback_.Run(event);
-
- // 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
} // namespace cobalt
diff --git a/src/cobalt/speech/speech_recognition_manager.h b/src/cobalt/speech/speech_recognition_manager.h
index b201c07..5efcf1b 100644
--- a/src/cobalt/speech/speech_recognition_manager.h
+++ b/src/cobalt/speech/speech_recognition_manager.h
@@ -19,24 +19,13 @@
#include "cobalt/network/network_module.h"
#include "cobalt/script/exception_state.h"
-#include "cobalt/speech/endpointer_delegate.h"
#include "cobalt/speech/microphone.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"
#include "cobalt/speech/speech_recognizer.h"
-#if defined(COBALT_MEDIA_SOURCE_2016)
-#include "cobalt/media/base/shell_audio_bus.h"
-#else // defined(COBALT_MEDIA_SOURCE_2016)
-#include "media/base/shell_audio_bus.h"
-#endif // defined(COBALT_MEDIA_SOURCE_2016)
namespace cobalt {
namespace speech {
-class MicrophoneManager;
-
// Owned by SpeechRecognition to manage major speech recognition logic.
// This class interacts with microphone, speech recognition service and audio
// encoder. It provides the interface to start/stop microphone and
@@ -44,11 +33,6 @@
// class would encode the audio data, then send it to recogniton service.
class SpeechRecognitionManager {
public:
-#if defined(COBALT_MEDIA_SOURCE_2016)
- typedef media::ShellAudioBus ShellAudioBus;
-#else // defined(COBALT_MEDIA_SOURCE_2016)
- typedef ::media::ShellAudioBus ShellAudioBus;
-#endif // defined(COBALT_MEDIA_SOURCE_2016)
typedef base::Callback<bool(const scoped_refptr<dom::Event>&)> EventCallback;
SpeechRecognitionManager(network::NetworkModule* network_module,
@@ -56,11 +40,10 @@
const Microphone::Options& microphone_options);
~SpeechRecognitionManager();
- // Start/Stop speech recognizer and microphone. Multiple calls would be
- // managed by their own class.
+ // Start/Stop speech recognizer.
void Start(const SpeechRecognitionConfig& config,
script::ExceptionState* exception_state);
- void Stop(bool run_callback = true);
+ void Stop();
void Abort();
private:
@@ -70,13 +53,7 @@
kAborted,
};
- // Callbacks from mic.
- void OnDataReceived(scoped_ptr<ShellAudioBus> audio_bus);
- void OnDataCompletion();
- void OnMicError(const scoped_refptr<dom::Event>& event);
-
- // Callbacks from recognizer.
- void OnRecognizerEvent(const scoped_refptr<dom::Event>& event);
+ void OnEventAvailable(const scoped_refptr<dom::Event>& event);
base::WeakPtrFactory<SpeechRecognitionManager> weak_ptr_factory_;
// We construct a WeakPtr upon SpeechRecognitionManager's construction in
@@ -88,11 +65,6 @@
EventCallback event_callback_;
scoped_ptr<SpeechRecognizer> recognizer_;
- scoped_ptr<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 9360688..0f3bec9 100644
--- a/src/cobalt/speech/speech_recognizer.cc
+++ b/src/cobalt/speech/speech_recognizer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,386 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Note to Cobalt porters: To use the v1 speech-api API, you must provide an API
-// key with v1 speech-api quota. The code is provided here, but not an API key.
-//
-// This is similar to how Chromium handles API keys:
-// https://www.chromium.org/developers/how-tos/api-keys
-//
-// The API key is provided by SbSystemGetProperty:
-// http://cobalt.foo/reference/starboard/modules/system.html#sbsystemgetproperty
-//
-// Talk with your Google representative about how to get speech-api quota.
-
#include "cobalt/speech/speech_recognizer.h"
-#include "base/bind.h"
-#include "base/rand_util.h"
-#include "base/string_number_conversions.h"
-#include "base/string_util.h"
-#include "base/utf_string_conversions.h"
-#include "cobalt/base/language.h"
-#include "cobalt/loader/fetcher_factory.h"
-#include "cobalt/network/network_module.h"
-#include "cobalt/speech/google_streaming_api.pb.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/system.h"
-#endif // defined(SB_USE_SB_MICROPHONE)
-
namespace cobalt {
namespace speech {
-namespace {
-const char kBaseStreamURL[] =
- "https://www.google.com/speech-api/full-duplex/v1";
-const char kUp[] = "up";
-const char kDown[] = "down";
-const char kClient[] = "com.speech.tv";
+SpeechRecognizer::SpeechRecognizer(const EventCallback& event_callback)
+ : event_callback_(event_callback) {}
-GURL AppendPath(const GURL& url, const std::string& value) {
- std::string path(url.path());
-
- if (!path.empty()) path += "/";
-
- path += net::EscapePath(value);
- GURL::Replacements replacements;
- replacements.SetPathStr(path);
- return url.ReplaceComponents(replacements);
-}
-
-GURL AppendQueryParameter(const GURL& url, const std::string& new_query,
- const std::string& value) {
- std::string query(url.query());
-
- if (!query.empty()) query += "&";
-
- query += net::EscapeQueryParamValue(new_query, true);
-
- if (!value.empty()) {
- query += "=" + net::EscapeQueryParamValue(value, true);
- }
-
- GURL::Replacements replacements;
- replacements.SetQueryStr(query);
- return url.ReplaceComponents(replacements);
-}
-
-SpeechRecognitionResultList::SpeechRecognitionResults
-ProcessProtoSuccessResults(const proto::SpeechRecognitionEvent& event) {
- DCHECK_EQ(event.status(), proto::SpeechRecognitionEvent::STATUS_SUCCESS);
-
- SpeechRecognitionResultList::SpeechRecognitionResults results;
- for (int i = 0; i < event.result_size(); ++i) {
- SpeechRecognitionResult::SpeechRecognitionAlternatives alternatives;
- 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(proto_alternative.transcript(),
- confidence));
- alternatives.push_back(alternative);
- }
-
- bool final = proto_result.has_final() && proto_result.final();
- scoped_refptr<SpeechRecognitionResult> recognition_result(
- new SpeechRecognitionResult(alternatives, final));
- results.push_back(recognition_result);
- }
- return results;
-}
-
-// TODO: Feed error messages when creating SpeechRecognitionError.
-void ProcessAndFireErrorEvent(
- const proto::SpeechRecognitionEvent& event,
- const SpeechRecognizer::EventCallback& event_callback) {
- scoped_refptr<dom::Event> error_event;
- switch (event.status()) {
- case proto::SpeechRecognitionEvent::STATUS_SUCCESS:
- NOTREACHED();
- return;
- case proto::SpeechRecognitionEvent::STATUS_NO_SPEECH:
- error_event =
- new SpeechRecognitionError(SpeechRecognitionError::kNoSpeech, "");
- break;
- case proto::SpeechRecognitionEvent::STATUS_ABORTED:
- error_event =
- new SpeechRecognitionError(SpeechRecognitionError::kAborted, "");
- break;
- case proto::SpeechRecognitionEvent::STATUS_AUDIO_CAPTURE:
- error_event =
- new SpeechRecognitionError(SpeechRecognitionError::kAudioCapture, "");
- break;
- case proto::SpeechRecognitionEvent::STATUS_NETWORK:
- error_event =
- new SpeechRecognitionError(SpeechRecognitionError::kNetwork, "");
- break;
- case proto::SpeechRecognitionEvent::STATUS_NOT_ALLOWED:
- error_event =
- new SpeechRecognitionError(SpeechRecognitionError::kNotAllowed, "");
- break;
- case proto::SpeechRecognitionEvent::STATUS_SERVICE_NOT_ALLOWED:
- error_event = new SpeechRecognitionError(
- SpeechRecognitionError::kServiceNotAllowed, "");
- break;
- case proto::SpeechRecognitionEvent::STATUS_BAD_GRAMMAR:
- error_event =
- new SpeechRecognitionError(SpeechRecognitionError::kBadGrammar, "");
- break;
- case proto::SpeechRecognitionEvent::STATUS_LANGUAGE_NOT_SUPPORTED:
- error_event = new SpeechRecognitionError(
- SpeechRecognitionError::kLanguageNotSupported, "");
- break;
- }
-
- DCHECK(error_event);
- 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,
- const EventCallback& event_callback,
- const URLFetcherCreator& fetcher_creator)
- : network_module_(network_module),
- started_(false),
- event_callback_(event_callback),
- fetcher_creator_(fetcher_creator),
- thread_("speech_recognizer") {
- thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0));
-}
-
-SpeechRecognizer::~SpeechRecognizer() {
- Stop();
- // Stopping the thread here to ensure that StopInternal has completed before
- // we finish running the destructor.
- thread_.Stop();
-}
-
-void SpeechRecognizer::Start(const SpeechRecognitionConfig& config,
- int sample_rate) {
- // Called by the speech recognition manager thread.
- thread_.message_loop()->PostTask(
- FROM_HERE, base::Bind(&SpeechRecognizer::StartInternal,
- base::Unretained(this), config, sample_rate));
-}
-
-void SpeechRecognizer::Stop() {
- // Called by the speech recognition manager thread.
- thread_.message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&SpeechRecognizer::StopInternal, base::Unretained(this)));
-}
-
-void SpeechRecognizer::RecognizeAudio(scoped_ptr<ShellAudioBus> audio_bus,
- bool is_last_chunk) {
- // Called by the speech recognition manager thread.
- thread_.message_loop()->PostTask(
- FROM_HERE, base::Bind(&SpeechRecognizer::UploadAudioDataInternal,
- base::Unretained(this), base::Passed(&audio_bus),
- is_last_chunk));
-}
-
-void SpeechRecognizer::OnURLFetchDownloadData(
- 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()) {
- 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;
- }
-
- 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."));
- }
- }
-}
-
-void SpeechRecognizer::OnURLFetchComplete(const net::URLFetcher* source) {
- DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
- UNREFERENCED_PARAMETER(source);
- // no-op.
-}
-
-void SpeechRecognizer::StartInternal(const SpeechRecognitionConfig& config,
- int sample_rate) {
- DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-
- if (started_) {
- // Recognizer is already started.
- return;
- }
- started_ = true;
-
- encoder_.reset(new AudioEncoderFlac(sample_rate));
-
- // Required for streaming on both up and down connections.
- std::string pair = base::Uint64ToString(base::RandUint64());
-
- // Set up down stream first.
- GURL down_url(kBaseStreamURL);
- down_url = AppendPath(down_url, kDown);
- down_url = AppendQueryParameter(down_url, "pair", pair);
- // Use protobuffer as the output format.
- down_url = AppendQueryParameter(down_url, "output", "pb");
-
- downstream_fetcher_ =
- fetcher_creator_.Run(down_url, net::URLFetcher::GET, this);
- downstream_fetcher_->SetRequestContext(
- network_module_->url_request_context_getter());
- downstream_fetcher_->Start();
-
- // Up stream.
- GURL up_url(kBaseStreamURL);
- up_url = AppendPath(up_url, kUp);
- up_url = AppendQueryParameter(up_url, "client", kClient);
- up_url = AppendQueryParameter(up_url, "pair", pair);
- up_url = AppendQueryParameter(up_url, "output", "pb");
-
- const char* speech_api_key = "";
-#if defined(OS_STARBOARD)
-#if SB_VERSION(2)
- const int kSpeechApiKeyLength = 100;
- char buffer[kSpeechApiKeyLength] = {0};
- bool result = SbSystemGetProperty(kSbSystemPropertySpeechApiKey, buffer,
- SB_ARRAY_SIZE_INT(buffer));
- SB_DCHECK(result);
- speech_api_key = result ? buffer : "";
-#endif // SB_VERSION(2)
-#endif // defined(OS_STARBOARD)
-
- 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()) {
- up_url = AppendQueryParameter(up_url, "lang", config.lang);
- } else {
- up_url = AppendQueryParameter(up_url, "lang", base::GetSystemLanguage());
- }
-
- if (config.max_alternatives) {
- up_url = AppendQueryParameter(up_url, "maxAlternatives",
- base::UintToString(config.max_alternatives));
- }
-
- if (config.continuous) {
- up_url = AppendQueryParameter(up_url, "continuous", "");
- }
- if (config.interim_results) {
- up_url = AppendQueryParameter(up_url, "interim", "");
- }
-
- upstream_fetcher_ = fetcher_creator_.Run(up_url, net::URLFetcher::POST, this);
- upstream_fetcher_->SetRequestContext(
- network_module_->url_request_context_getter());
- upstream_fetcher_->SetChunkedUpload(encoder_->GetMimeType());
- upstream_fetcher_->Start();
-}
-
-void SpeechRecognizer::StopInternal() {
- DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-
- if (!started_) {
- // Recognizer is not started.
- return;
- }
- started_ = false;
-
- upstream_fetcher_.reset();
- downstream_fetcher_.reset();
- encoder_.reset();
-
- // Clear the final results.
- final_results_.clear();
- // Clear any remaining audio data.
- chunked_byte_buffer_.Clear();
-}
-
-void SpeechRecognizer::UploadAudioDataInternal(
- scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) {
- DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
- DCHECK(audio_bus);
-
- std::string encoded_audio_data;
- if (encoder_) {
- encoder_->Encode(audio_bus.get());
- if (is_last_chunk) {
- encoder_->Finish();
- }
- encoded_audio_data = encoder_->GetAndClearAvailableEncodedData();
- }
-
- if (upstream_fetcher_ && !encoded_audio_data.empty()) {
- upstream_fetcher_->AppendChunkToUpload(encoded_audio_data, is_last_chunk);
- }
-}
-
-void SpeechRecognizer::ProcessAndFireSuccessEvent(
- const SpeechRecognitionResults& new_results) {
- DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
-
- SpeechRecognitionResults success_results;
- size_t total_size = final_results_.size() + new_results.size();
- success_results.reserve(total_size);
- success_results = final_results_;
- success_results.insert(success_results.end(), new_results.begin(),
- new_results.end());
-
- size_t result_index = final_results_.size();
- // Update final results list.
- for (size_t i = 0; i < new_results.size(); ++i) {
- if (new_results[i]->is_final()) {
- final_results_.push_back(new_results[i]);
- }
- }
-
- scoped_refptr<SpeechRecognitionResultList> recognition_list(
- new SpeechRecognitionResultList(success_results));
- scoped_refptr<SpeechRecognitionEvent> recognition_event(
- new SpeechRecognitionEvent(SpeechRecognitionEvent::kResult,
- static_cast<uint32>(result_index),
- recognition_list));
- event_callback_.Run(recognition_event);
+void SpeechRecognizer::RunEventCallback(
+ const scoped_refptr<dom::Event>& event) {
+ event_callback_.Run(event);
}
} // namespace speech
diff --git a/src/cobalt/speech/speech_recognizer.h b/src/cobalt/speech/speech_recognizer.h
index 339ed66..0622b7c 100644
--- a/src/cobalt/speech/speech_recognizer.h
+++ b/src/cobalt/speech/speech_recognizer.h
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,97 +15,32 @@
#ifndef COBALT_SPEECH_SPEECH_RECOGNIZER_H_
#define COBALT_SPEECH_SPEECH_RECOGNIZER_H_
-#include <string>
-#include <vector>
-
-#include "base/threading/thread.h"
-#include "cobalt/network/network_module.h"
-#include "cobalt/speech/audio_encoder_flac.h"
+#include "base/callback.h"
+#include "cobalt/dom/event.h"
#include "cobalt/speech/speech_recognition_config.h"
-#include "cobalt/speech/speech_recognition_event.h"
-#include "content/browser/speech/chunked_byte_buffer.h"
-#if defined(COBALT_MEDIA_SOURCE_2016)
-#include "cobalt/media/base/shell_audio_bus.h"
-#else // defined(COBALT_MEDIA_SOURCE_2016)
-#include "media/base/shell_audio_bus.h"
-#endif // defined(COBALT_MEDIA_SOURCE_2016)
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
namespace cobalt {
namespace speech {
-// Interacts with Google speech recognition service, and then parses recognition
-// results and forms speech recognition event.
-// It creates an upstream fetcher to upload the encoded audio and a downstream
-// fetcher to fetch the speech recognition result. The fetched speech
-// recognition result is parsed by JSON parser and a SpeechRecognitionEvent,
-// is formed based on the parsed result, would be sent to speech recognition
-// manager.
-class SpeechRecognizer : public net::URLFetcherDelegate {
+// An abstract class representing a speech recognizer and it is for a family of
+// classes to run event callback.
+class SpeechRecognizer {
public:
-#if defined(COBALT_MEDIA_SOURCE_2016)
- typedef media::ShellAudioBus ShellAudioBus;
-#else // defined(COBALT_MEDIA_SOURCE_2016)
- typedef ::media::ShellAudioBus ShellAudioBus;
-#endif // defined(COBALT_MEDIA_SOURCE_2016)
typedef base::Callback<void(const scoped_refptr<dom::Event>&)> EventCallback;
- typedef SpeechRecognitionResultList::SpeechRecognitionResults
- SpeechRecognitionResults;
- typedef base::Callback<scoped_ptr<net::URLFetcher>(
- const GURL&, net::URLFetcher::RequestType, net::URLFetcherDelegate*)>
- URLFetcherCreator;
- SpeechRecognizer(network::NetworkModule* network_module,
- const EventCallback& event_callback,
- const URLFetcherCreator& fetcher_creator);
- ~SpeechRecognizer() OVERRIDE;
+ explicit SpeechRecognizer(const EventCallback& event_callback);
- // Multiple calls to Start/Stop are allowed, the implementation should take
- // care of multiple calls.
- // Start speech recognizer.
- void Start(const SpeechRecognitionConfig& config, int sample_rate);
- // Stop speech recognizer.
- void Stop();
- // An encoded audio data is available and ready to be recognized.
- void RecognizeAudio(scoped_ptr<ShellAudioBus> audio_bus, bool is_last_chunk);
+ virtual ~SpeechRecognizer() {}
- // net::URLFetcherDelegate interface
- void OnURLFetchDownloadData(const net::URLFetcher* source,
- scoped_ptr<std::string> download_data) OVERRIDE;
- void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
- bool ShouldSendDownloadData() OVERRIDE { return true; }
- void OnURLFetchUploadProgress(const net::URLFetcher* /*source*/,
- int64 /*current*/, int64 /*total*/) OVERRIDE {}
+ virtual void Start(const SpeechRecognitionConfig& config) = 0;
+ virtual void Stop() = 0;
+
+ protected:
+ void RunEventCallback(const scoped_refptr<dom::Event>& event);
private:
- void StartInternal(const SpeechRecognitionConfig& config, int sample_rate);
- void StopInternal();
- void UploadAudioDataInternal(scoped_ptr<ShellAudioBus> audio_bus,
- bool is_last_chunk);
- void ProcessAndFireSuccessEvent(const SpeechRecognitionResults& new_results);
-
- // This is used for creating fetchers.
- network::NetworkModule* network_module_;
- // Track the start/stop state of speech recognizer.
- bool started_;
-
- // Encoder for encoding raw audio data to flac codec.
- scoped_ptr<AudioEncoderFlac> encoder_;
- // Fetcher for posting the audio data.
- scoped_ptr<net::URLFetcher> upstream_fetcher_;
- // Fetcher for receiving the streaming results.
- scoped_ptr<net::URLFetcher> downstream_fetcher_;
- // Used to send speech recognition event.
- const EventCallback event_callback_;
- // Used to create url fetcher.
- const URLFetcherCreator fetcher_creator_;
- // Used for processing proto buffer data.
- content::ChunkedByteBuffer chunked_byte_buffer_;
- // Used for accumulating final results.
- SpeechRecognitionResults final_results_;
- // Speech recognizer is operating in its own thread.
- base::Thread thread_;
+ // Callback for sending dom events if available.
+ EventCallback event_callback_;
};
} // namespace speech
diff --git a/src/cobalt/speech/speech_synthesis.cc b/src/cobalt/speech/speech_synthesis.cc
index ca0573f..3fd8db6 100644
--- a/src/cobalt/speech/speech_synthesis.cc
+++ b/src/cobalt/speech/speech_synthesis.cc
@@ -65,7 +65,7 @@
void SpeechSynthesis::DispatchErrorEvent(
const scoped_refptr<SpeechSynthesisUtterance>& utterance,
- SpeechSynthesisErrorEvent::SpeechErrorCode error_code) {
+ SpeechSynthesisErrorCode error_code) {
utterance->DispatchErrorEvent(error_code);
Cancel();
}
@@ -88,16 +88,16 @@
if (!utterance->lang().empty() &&
utterance->lang() != navigator_->language()) {
DispatchErrorEvent(utterance,
- SpeechSynthesisErrorEvent::kLanguageUnavailable);
+ kSpeechSynthesisErrorCodeLanguageUnavailable);
return;
}
if ((utterance->volume() != 1.0f) || (utterance->rate() != 1.0f) ||
(utterance->pitch() != 1.0f)) {
- DispatchErrorEvent(utterance, SpeechSynthesisErrorEvent::kInvalidArgument);
+ DispatchErrorEvent(utterance, kSpeechSynthesisErrorCodeInvalidArgument);
return;
}
-#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION < 4
// DEPRECATED IN API VERSION 4
std::string language =
utterance->lang().empty() ? navigator_->language() : utterance->lang();
@@ -109,8 +109,7 @@
utterance->DispatchStartEvent();
utterance->DispatchEndEvent();
#else
- DispatchErrorEvent(utterance,
- SpeechSynthesisErrorEvent::kSynthesisUnavailable);
+ DispatchErrorEvent(utterance, kSpeechSynthesisErrorCodeSynthesisUnavailable);
#endif
}
diff --git a/src/cobalt/speech/speech_synthesis.h b/src/cobalt/speech/speech_synthesis.h
index e067d36..7fca443 100644
--- a/src/cobalt/speech/speech_synthesis.h
+++ b/src/cobalt/speech/speech_synthesis.h
@@ -72,7 +72,7 @@
void DispatchErrorEvent(
const scoped_refptr<SpeechSynthesisUtterance>& utterance,
- SpeechSynthesisErrorEvent::SpeechErrorCode error_code);
+ SpeechSynthesisErrorCode error_code);
bool paused_;
typedef std::list<scoped_refptr<SpeechSynthesisUtterance> > UtterancesList;
diff --git a/src/cobalt/speech/speech_synthesis_error_code.idl b/src/cobalt/speech/speech_synthesis_error_code.idl
new file mode 100644
index 0000000..ecee5e3
--- /dev/null
+++ b/src/cobalt/speech/speech_synthesis_error_code.idl
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
+
+enum SpeechSynthesisErrorCode {
+ "canceled",
+ "interrupted",
+ "audio-busy",
+ "audio-hardware",
+ "network",
+ "synthesis-unavailable",
+ "synthesis-failed",
+ "language-unavailable",
+ "voice-unavailable",
+ "text-too-long",
+ "invalid-argument",
+};
diff --git a/src/cobalt/speech/speech_synthesis_error_event.h b/src/cobalt/speech/speech_synthesis_error_event.h
index 0e60750..052b018 100644
--- a/src/cobalt/speech/speech_synthesis_error_event.h
+++ b/src/cobalt/speech/speech_synthesis_error_event.h
@@ -18,6 +18,7 @@
#include "base/basictypes.h"
#include "cobalt/dom/event_target.h"
#include "cobalt/script/wrappable.h"
+#include "cobalt/speech/speech_synthesis_error_code.h"
#include "cobalt/speech/speech_synthesis_event.h"
namespace cobalt {
@@ -28,34 +29,20 @@
// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
class SpeechSynthesisErrorEvent : public SpeechSynthesisEvent {
public:
- enum SpeechErrorCode {
- kCanceled,
- kInterrupted,
- kAudioBusy,
- kAudioHardware,
- kNetwork,
- kSynthesisUnavailable,
- kSynthesisFailed,
- kLanguageUnavailable,
- kVoiceUnavailable,
- kTextTooLong,
- kInvalidArgument
- };
-
explicit SpeechSynthesisErrorEvent(
- SpeechErrorCode error_code,
+ SpeechSynthesisErrorCode error_code,
const scoped_refptr<SpeechSynthesisUtterance>& utterance)
: SpeechSynthesisEvent(base::Tokens::error(), utterance),
error_code_(error_code) {}
- SpeechErrorCode error() const { return error_code_; }
+ SpeechSynthesisErrorCode error() const { return error_code_; }
DEFINE_WRAPPABLE_TYPE(SpeechSynthesisErrorEvent);
private:
~SpeechSynthesisErrorEvent() OVERRIDE {}
- const SpeechErrorCode error_code_;
+ const SpeechSynthesisErrorCode error_code_;
DISALLOW_COPY_AND_ASSIGN(SpeechSynthesisErrorEvent);
};
diff --git a/src/cobalt/speech/speech_synthesis_error_event.idl b/src/cobalt/speech/speech_synthesis_error_event.idl
index 8cd554f..ff30d2f 100644
--- a/src/cobalt/speech/speech_synthesis_error_event.idl
+++ b/src/cobalt/speech/speech_synthesis_error_event.idl
@@ -14,20 +14,6 @@
// https://dvcs.w3.org/hg/speech-api/raw-file/4f41ea1126bb/webspeechapi.html#tts-section
-enum SpeechErrorCode {
- "canceled",
- "interrupted",
- "audio-busy",
- "audio-hardware",
- "network",
- "synthesis-unavailable",
- "synthesis-failed",
- "language-unavailable",
- "voice-unavailable",
- "text-too-long",
- "invalid-argument",
-};
-
interface SpeechSynthesisErrorEvent : SpeechSynthesisEvent {
- readonly attribute SpeechErrorCode error;
+ readonly attribute SpeechSynthesisErrorCode error;
};
diff --git a/src/cobalt/speech/speech_synthesis_utterance.cc b/src/cobalt/speech/speech_synthesis_utterance.cc
index ca96359..e6d67f5 100644
--- a/src/cobalt/speech/speech_synthesis_utterance.cc
+++ b/src/cobalt/speech/speech_synthesis_utterance.cc
@@ -55,7 +55,7 @@
void SpeechSynthesisUtterance::DispatchErrorCancelledEvent() {
if (pending_speak_) {
SB_DLOG(INFO) << "Utterance has a pending Speak()";
- DispatchErrorEvent(SpeechSynthesisErrorEvent::kCanceled);
+ DispatchErrorEvent(kSpeechSynthesisErrorCodeCanceled);
}
}
@@ -71,7 +71,7 @@
}
void SpeechSynthesisUtterance::DispatchErrorEvent(
- SpeechSynthesisErrorEvent::SpeechErrorCode error_code) {
+ SpeechSynthesisErrorCode error_code) {
pending_speak_ = false;
DispatchEvent(new SpeechSynthesisErrorEvent(error_code, this));
}
diff --git a/src/cobalt/speech/speech_synthesis_utterance.h b/src/cobalt/speech/speech_synthesis_utterance.h
index 9c476c3..8674e76 100644
--- a/src/cobalt/speech/speech_synthesis_utterance.h
+++ b/src/cobalt/speech/speech_synthesis_utterance.h
@@ -123,8 +123,7 @@
void DispatchStartEvent();
void DispatchEndEvent();
- void DispatchErrorEvent(
- SpeechSynthesisErrorEvent::SpeechErrorCode error_code);
+ void DispatchErrorEvent(SpeechSynthesisErrorCode error_code);
DEFINE_WRAPPABLE_TYPE(SpeechSynthesisUtterance);
diff --git a/src/cobalt/speech/speech_synthesis_voice.idl b/src/cobalt/speech/speech_synthesis_voice.idl
index 472552e..915e959 100644
--- a/src/cobalt/speech/speech_synthesis_voice.idl
+++ b/src/cobalt/speech/speech_synthesis_voice.idl
@@ -20,7 +20,5 @@
readonly attribute DOMString lang;
readonly attribute boolean localService;
- // Not supported by IDL compiler: Results in a member using a reserved
- // keyword, IDL compiler doesn't support ImplementedAs.
- // [ImplementedAs=attribute_default] readonly attribute boolean default;
+ [ImplementedAs=attribute_default] readonly attribute boolean default;
};
diff --git a/src/cobalt/speech/starboard_speech_recognizer.cc b/src/cobalt/speech/starboard_speech_recognizer.cc
new file mode 100644
index 0000000..14fdfd5
--- /dev/null
+++ b/src/cobalt/speech/starboard_speech_recognizer.cc
@@ -0,0 +1,183 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/speech/starboard_speech_recognizer.h"
+
+#if defined(SB_USE_SB_SPEECH_RECOGNIZER)
+
+#include "cobalt/base/tokens.h"
+#include "cobalt/speech/speech_recognition_error.h"
+#include "cobalt/speech/speech_recognition_event.h"
+#include "starboard/log.h"
+
+namespace cobalt {
+namespace speech {
+
+namespace {
+
+using cobalt::speech::StarboardSpeechRecognizer;
+
+void OnSpeechDetected(void* context, bool detected) {
+ StarboardSpeechRecognizer* recognizer =
+ static_cast<StarboardSpeechRecognizer*>(context);
+ recognizer->OnRecognizerSpeechDetected(detected);
+}
+
+void OnError(void* context, SbSpeechRecognizerError error) {
+ StarboardSpeechRecognizer* recognizer =
+ static_cast<StarboardSpeechRecognizer*>(context);
+ recognizer->OnRecognizerError(error);
+}
+
+void OnResults(void* context, SbSpeechResult* results, int results_size,
+ bool is_final) {
+ StarboardSpeechRecognizer* recognizer =
+ static_cast<StarboardSpeechRecognizer*>(context);
+ recognizer->OnRecognizerResults(results, results_size, is_final);
+}
+
+} // namespace
+
+StarboardSpeechRecognizer::StarboardSpeechRecognizer(
+ const EventCallback& event_callback)
+ : SpeechRecognizer(event_callback) {
+ SbSpeechRecognizerHandler handler = {&OnSpeechDetected, &OnError, &OnResults,
+ this};
+ speech_recognizer_ = SbSpeechRecognizerCreate(&handler);
+
+ if (!SbSpeechRecognizerIsValid(speech_recognizer_)) {
+ scoped_refptr<dom::Event> error_event(new SpeechRecognitionError(
+ kSpeechRecognitionErrorCodeServiceNotAllowed, ""));
+ RunEventCallback(error_event);
+ }
+}
+
+StarboardSpeechRecognizer::~StarboardSpeechRecognizer() {
+ if (SbSpeechRecognizerIsValid(speech_recognizer_)) {
+ SbSpeechRecognizerDestroy(speech_recognizer_);
+ }
+}
+
+void StarboardSpeechRecognizer::Start(const SpeechRecognitionConfig& config) {
+ SbSpeechConfiguration configuration = {
+ config.continuous, config.interim_results, config.max_alternatives};
+ if (SbSpeechRecognizerIsValid(speech_recognizer_)) {
+ SbSpeechRecognizerStart(speech_recognizer_, &configuration);
+ }
+}
+
+void StarboardSpeechRecognizer::Stop() {
+ if (SbSpeechRecognizerIsValid(speech_recognizer_)) {
+ SbSpeechRecognizerStop(speech_recognizer_);
+ }
+ // Clear the final results.
+ final_results_.clear();
+}
+
+void StarboardSpeechRecognizer::OnRecognizerSpeechDetected(bool detected) {
+ scoped_refptr<dom::Event> event(new dom::Event(
+ detected ? base::Tokens::soundstart() : base::Tokens::soundend()));
+ RunEventCallback(event);
+}
+
+void StarboardSpeechRecognizer::OnRecognizerError(
+ SbSpeechRecognizerError error) {
+ scoped_refptr<dom::Event> error_event;
+ switch (error) {
+ case kSbNoSpeechError:
+ error_event =
+ new SpeechRecognitionError(kSpeechRecognitionErrorCodeNoSpeech, "");
+ break;
+ case kSbAborted:
+ error_event =
+ new SpeechRecognitionError(kSpeechRecognitionErrorCodeAborted, "");
+ break;
+ case kSbAudioCaptureError:
+ error_event = new SpeechRecognitionError(
+ kSpeechRecognitionErrorCodeAudioCapture, "");
+ break;
+ case kSbNetworkError:
+ error_event =
+ new SpeechRecognitionError(kSpeechRecognitionErrorCodeNetwork, "");
+ break;
+ case kSbNotAllowed:
+ error_event =
+ new SpeechRecognitionError(kSpeechRecognitionErrorCodeNotAllowed, "");
+ break;
+ case kSbServiceNotAllowed:
+ error_event = new SpeechRecognitionError(
+ kSpeechRecognitionErrorCodeServiceNotAllowed, "");
+ break;
+ case kSbBadGrammar:
+ error_event =
+ new SpeechRecognitionError(kSpeechRecognitionErrorCodeBadGrammar, "");
+ break;
+ case kSbLanguageNotSupported:
+ error_event = new SpeechRecognitionError(
+ kSpeechRecognitionErrorCodeLanguageNotSupported, "");
+ break;
+ }
+ SB_DCHECK(error_event);
+ RunEventCallback(error_event);
+}
+
+void StarboardSpeechRecognizer::OnRecognizerResults(SbSpeechResult* results,
+ int results_size,
+ bool is_final) {
+ SpeechRecognitionResultList::SpeechRecognitionResults recognition_results;
+ SpeechRecognitionResult::SpeechRecognitionAlternatives alternatives;
+ for (int i = 0; i < results_size; ++i) {
+ scoped_refptr<SpeechRecognitionAlternative> alternative(
+ new SpeechRecognitionAlternative(results[i].transcript,
+ results[i].confidence));
+ alternatives.push_back(alternative);
+ // Platform implementation allocates memory of |transcript|.
+ SbMemoryDeallocate(results[i].transcript);
+ }
+ scoped_refptr<SpeechRecognitionResult> recognition_result(
+ new SpeechRecognitionResult(alternatives, is_final));
+ recognition_results.push_back(recognition_result);
+
+ // Gather all results for the SpeechRecognitionEvent, including all final
+ // results we've previously accumulated, plus all (final or not) results that
+ // we just received.
+ SpeechRecognitionResults success_results;
+ size_t total_size = final_results_.size() + recognition_results.size();
+ success_results.reserve(total_size);
+ success_results = final_results_;
+ success_results.insert(success_results.end(), recognition_results.begin(),
+ recognition_results.end());
+
+ size_t result_index = final_results_.size();
+ // Update final results list with final results that we just received, so we
+ // have them for the next event.
+ for (size_t i = 0; i < recognition_results.size(); ++i) {
+ if (recognition_results[i]->is_final()) {
+ final_results_.push_back(recognition_results[i]);
+ }
+ }
+
+ scoped_refptr<SpeechRecognitionResultList> recognition_list(
+ new SpeechRecognitionResultList(success_results));
+ scoped_refptr<SpeechRecognitionEvent> recognition_event(
+ new SpeechRecognitionEvent(SpeechRecognitionEvent::kResult,
+ static_cast<uint32>(result_index),
+ recognition_list));
+ RunEventCallback(recognition_event);
+}
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // defined(SB_USE_SB_SPEECH_RECOGNIZER)
diff --git a/src/cobalt/speech/starboard_speech_recognizer.h b/src/cobalt/speech/starboard_speech_recognizer.h
new file mode 100644
index 0000000..d7a8281
--- /dev/null
+++ b/src/cobalt/speech/starboard_speech_recognizer.h
@@ -0,0 +1,57 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_SPEECH_STARBOARD_SPEECH_RECOGNIZER_H_
+#define COBALT_SPEECH_STARBOARD_SPEECH_RECOGNIZER_H_
+
+#include "cobalt/speech/speech_configuration.h"
+#include "cobalt/speech/speech_recognition_result_list.h"
+#include "cobalt/speech/speech_recognizer.h"
+#include "starboard/speech_recognizer.h"
+
+#if defined(SB_USE_SB_SPEECH_RECOGNIZER)
+
+namespace cobalt {
+namespace speech {
+
+// Controls |SbSpeechRecognizer| to access the device's recognition service and
+// receives the speech recognition results from there.
+class StarboardSpeechRecognizer : public SpeechRecognizer {
+ public:
+ typedef SpeechRecognitionResultList::SpeechRecognitionResults
+ SpeechRecognitionResults;
+
+ explicit StarboardSpeechRecognizer(const EventCallback& event_callback);
+ ~StarboardSpeechRecognizer();
+
+ void Start(const SpeechRecognitionConfig& config) SB_OVERRIDE;
+ void Stop() SB_OVERRIDE;
+
+ void OnRecognizerSpeechDetected(bool detected);
+ void OnRecognizerError(SbSpeechRecognizerError error);
+ void OnRecognizerResults(SbSpeechResult* results, int results_size,
+ bool is_final);
+
+ private:
+ SbSpeechRecognizer speech_recognizer_;
+ // Used for accumulating final results.
+ SpeechRecognitionResults final_results_;
+};
+
+} // namespace speech
+} // namespace cobalt
+
+#endif // defined(SB_USE_SB_SPEECH_RECOGNIZER)
+
+#endif // COBALT_SPEECH_STARBOARD_SPEECH_RECOGNIZER_H_
diff --git a/src/cobalt/storage/savegame_thread.cc b/src/cobalt/storage/savegame_thread.cc
index 517ad4b..e659ec7 100644
--- a/src/cobalt/storage/savegame_thread.cc
+++ b/src/cobalt/storage/savegame_thread.cc
@@ -63,6 +63,9 @@
// because we won't be able to reload it. Something must have
// gone wrong, and it's far better to leave any existing readable
// data that's persisted than it is to risk making it unreadable.
+ if (!on_flush_complete.is_null()) {
+ on_flush_complete.Run();
+ }
return;
}
thread_->message_loop()->PostTask(
@@ -107,7 +110,6 @@
const int kMaxConsecutiveFlushFailures = 2;
DCHECK_LT(++num_consecutive_flush_failures_,
kMaxConsecutiveFlushFailures);
- return;
}
}
diff --git a/src/cobalt/storage/upgrade/storage_upgrade_test.cc b/src/cobalt/storage/upgrade/storage_upgrade_test.cc
index efc22e3..56710a2 100644
--- a/src/cobalt/storage/upgrade/storage_upgrade_test.cc
+++ b/src/cobalt/storage/upgrade/storage_upgrade_test.cc
@@ -32,6 +32,13 @@
namespace {
+using ::testing::StrictMock;
+
+class MockUpgradeReader : public UpgradeReader {
+ public:
+ MOCK_METHOD0(OnNullExpiration, void());
+};
+
void ReadFileToString(const char* pathname, std::string* string_out) {
EXPECT_TRUE(pathname);
EXPECT_TRUE(string_out);
@@ -71,7 +78,7 @@
const std::string domain = host.empty() ? "" : host.substr(host.find("."));
const std::string path = "/";
const base::Time creation = base::Time::Now();
- const base::Time expiration = creation;
+ const base::Time expiration = base::Time();
const bool http_only = false;
ValidateCookie(cookie, url, name, value, domain, path, creation, expiration,
http_only);
@@ -91,8 +98,13 @@
std::string file_contents;
ReadFileToString("cobalt/storage/upgrade/testdata/minimal_cookie_v1.json",
&file_contents);
- UpgradeReader upgrade_reader(file_contents.c_str(),
- static_cast<int>(file_contents.length()));
+ StrictMock<MockUpgradeReader> upgrade_reader;
+
+ // No expiration
+ EXPECT_CALL(upgrade_reader, OnNullExpiration());
+
+ upgrade_reader.Parse(file_contents.c_str(),
+ static_cast<int>(file_contents.length()));
// 1 cookie.
EXPECT_EQ(upgrade_reader.GetNumCookies(), 1);
@@ -111,8 +123,9 @@
ReadFileToString(
"cobalt/storage/upgrade/testdata/minimal_local_storage_entry_v1.json",
&file_contents);
- UpgradeReader upgrade_reader(file_contents.c_str(),
- static_cast<int>(file_contents.length()));
+ UpgradeReader upgrade_reader;
+ upgrade_reader.Parse(file_contents.c_str(),
+ static_cast<int>(file_contents.length()));
// 0 cookies.
EXPECT_EQ(upgrade_reader.GetNumCookies(), 0);
@@ -130,8 +143,9 @@
std::string file_contents;
ReadFileToString("cobalt/storage/upgrade/testdata/full_data_v1.json",
&file_contents);
- UpgradeReader upgrade_reader(file_contents.c_str(),
- static_cast<int>(file_contents.length()));
+ UpgradeReader upgrade_reader;
+ upgrade_reader.Parse(file_contents.c_str(),
+ static_cast<int>(file_contents.length()));
// 2 cookies.
EXPECT_EQ(upgrade_reader.GetNumCookies(), 2);
@@ -163,8 +177,9 @@
std::string file_contents;
ReadFileToString("cobalt/storage/upgrade/testdata/missing_fields_v1.json",
&file_contents);
- UpgradeReader upgrade_reader(file_contents.c_str(),
- static_cast<int>(file_contents.length()));
+ UpgradeReader upgrade_reader;
+ upgrade_reader.Parse(file_contents.c_str(),
+ static_cast<int>(file_contents.length()));
// 1 cookie with missing fields, 2 local storage entries with missing fields,
// 1 valid local storage entry.
@@ -181,8 +196,9 @@
std::string file_contents;
ReadFileToString("cobalt/storage/upgrade/testdata/malformed_v1.json",
&file_contents);
- UpgradeReader upgrade_reader(file_contents.c_str(),
- static_cast<int>(file_contents.length()));
+ UpgradeReader upgrade_reader;
+ upgrade_reader.Parse(file_contents.c_str(),
+ static_cast<int>(file_contents.length()));
// No cookies or local storage entries available in malformed data.
EXPECT_EQ(upgrade_reader.GetNumCookies(), 0);
@@ -195,8 +211,13 @@
std::string file_contents;
ReadFileToString("cobalt/storage/upgrade/testdata/extra_fields_v1.json",
&file_contents);
- UpgradeReader upgrade_reader(file_contents.c_str(),
- static_cast<int>(file_contents.length()));
+ StrictMock<MockUpgradeReader> upgrade_reader;
+
+ // No expiration
+ EXPECT_CALL(upgrade_reader, OnNullExpiration());
+
+ upgrade_reader.Parse(file_contents.c_str(),
+ static_cast<int>(file_contents.length()));
// 1 cookie, extra fields should be ignored.
EXPECT_EQ(upgrade_reader.GetNumCookies(), 1);
diff --git a/src/cobalt/storage/upgrade/upgrade_reader.cc b/src/cobalt/storage/upgrade/upgrade_reader.cc
index 25ceff4..6e5908a 100644
--- a/src/cobalt/storage/upgrade/upgrade_reader.cc
+++ b/src/cobalt/storage/upgrade/upgrade_reader.cc
@@ -16,6 +16,7 @@
#include <string>
+#include "base/i18n/time_formatting.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
@@ -36,7 +37,8 @@
const int kMaxUpgradeDataSize = 10 * 1024 * 1024;
// Deseralize a time value encoded as a possibly empty ASCII decimal string.
-base::Time StringToTime(const std::string& time_as_string) {
+base::Time StringToTime(const std::string& time_as_string,
+ const base::Time& default_time) {
if (!time_as_string.empty()) {
int64 time_as_int64;
if (base::StringToInt64(time_as_string, &time_as_int64)) {
@@ -44,15 +46,15 @@
}
}
- // Default value.
- return base::Time::Now();
+ return default_time;
}
base::Time GetSerializedTime(const base::DictionaryValue* dictionary,
- const std::string& field_name) {
+ const std::string& field_name,
+ const base::Time& default_time) {
std::string time_string;
dictionary->GetString(field_name, &time_string);
- return StringToTime(time_string);
+ return StringToTime(time_string, default_time);
}
// Get a list contained in a dictionary.
@@ -104,7 +106,7 @@
} // namespace
-UpgradeReader::UpgradeReader(const char* data, int size) {
+void UpgradeReader::Parse(const char* data, int size) {
DCHECK(data);
DCHECK_GE(size, kHeaderSize);
DCHECK_LE(size, kMaxUpgradeDataSize);
@@ -168,9 +170,12 @@
cookie->GetString("domain", &domain);
std::string path = "/";
cookie->GetString("path", &path);
- base::Time creation = GetSerializedTime(cookie, "creation");
- base::Time expiration = GetSerializedTime(cookie, "expiration");
- base::Time last_access = GetSerializedTime(cookie, "last_access");
+ base::Time creation =
+ GetSerializedTime(cookie, "creation", base::Time::Now());
+ base::Time expiration =
+ GetSerializedTime(cookie, "expiration", base::Time());
+ base::Time last_access =
+ GetSerializedTime(cookie, "last_access", base::Time::Now());
bool http_only = false;
cookie->GetBoolean("http_only", &http_only);
@@ -179,6 +184,15 @@
const std::string mac_algorithm;
const bool secure = true;
+ if (expiration.is_null()) {
+ LOG(ERROR) << "\"" << name
+ << "\" cookie can not be upgraded due to null expiration.";
+ OnNullExpiration();
+ }
+ LOG_IF(ERROR, expiration < base::Time::UnixEpoch())
+ << "\"" << name << "\" upgrade cookie expiration is "
+ << base::TimeFormatFriendlyDateAndTime(expiration);
+
cookies_.push_back(net::CanonicalCookie(
gurl, name, value, domain, path, mac_key, mac_algorithm, creation,
expiration, last_access, secure, http_only));
diff --git a/src/cobalt/storage/upgrade/upgrade_reader.h b/src/cobalt/storage/upgrade/upgrade_reader.h
index f59c7ba..a9961ad 100644
--- a/src/cobalt/storage/upgrade/upgrade_reader.h
+++ b/src/cobalt/storage/upgrade/upgrade_reader.h
@@ -18,6 +18,7 @@
#include <string>
#include <vector>
+#include "base/logging.h"
#include "base/values.h"
#include "net/cookies/canonical_cookie.h"
@@ -35,8 +36,11 @@
std::string value;
};
+ UpgradeReader() {}
+ virtual ~UpgradeReader() {}
+
// Parse |data| in JSON-encoded legacy save data upgrade format.
- UpgradeReader(const char* data, int size);
+ void Parse(const char* data, int size);
// The number of valid cookies found in the parsed data. May be zero.
int GetNumCookies() const;
@@ -70,6 +74,12 @@
void AddLocalStorageEntryIfValid(
const base::DictionaryValue* local_storage_entry);
+ // Alerts porters that expiration is required.
+ virtual void OnNullExpiration() {
+ NOTREACHED() << "Cookies must have a specified expiration, "
+ "as there is no reasonable default.";
+ }
+
std::vector<net::CanonicalCookie> cookies_;
std::vector<LocalStorageEntry> local_storage_entries_;
};
diff --git a/src/cobalt/test/document_loader.h b/src/cobalt/test/document_loader.h
index f97f4fb..df0ecdd 100644
--- a/src/cobalt/test/document_loader.h
+++ b/src/cobalt/test/document_loader.h
@@ -48,9 +48,9 @@
css_parser_(css_parser::Parser::Create()),
dom_parser_(new dom_parser::Parser()),
resource_provider_stub_(new render_tree::ResourceProviderStub()),
- loader_factory_(new loader::LoaderFactory(
- &fetcher_factory_, resource_provider_stub_.get(),
- base::kThreadPriority_Low)),
+ loader_factory_(new loader::LoaderFactory(&fetcher_factory_,
+ resource_provider_stub_.get(),
+ base::kThreadPriority_Low)),
image_cache_(loader::image::CreateImageCache(
"Test.ImageCache", 32U * 1024 * 1024, loader_factory_.get())),
dom_stat_tracker_(new dom::DomStatTracker("IsDisplayedTest")),
@@ -59,7 +59,8 @@
&fetcher_factory_, css_parser_.get(), dom_parser_.get(),
NULL /* can_play_type_handler */,
NULL /* web_media_player_factory */, &script_runner_,
- NULL /* media_source_registry */, &resource_provider_,
+ NULL /* script_value_factory */, NULL /* media_source_registry */,
+ &resource_provider_, NULL /* animated_image_tracker */,
image_cache_.get(), NULL /* reduced_image_cache_capacity_manager */,
NULL /* remote_font_cache */, NULL /* mesh_cache */,
dom_stat_tracker_.get(), "" /* language */) {}
diff --git a/src/cobalt/test/empty_document.h b/src/cobalt/test/empty_document.h
index b6ffccf..9b6ca37 100644
--- a/src/cobalt/test/empty_document.h
+++ b/src/cobalt/test/empty_document.h
@@ -31,7 +31,7 @@
: css_parser_(css_parser::Parser::Create()),
dom_stat_tracker_(new dom::DomStatTracker("EmptyDocument")),
html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
dom_stat_tracker_.get(), ""),
document_(new dom::Document(&html_element_context_)) {}
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index 9be98af..8cd1366 100644
--- a/src/cobalt/version.h
+++ b/src/cobalt/version.h
@@ -15,6 +15,6 @@
#define COBALT_VERSION_H_
// Cobalt release number.
-#define COBALT_VERSION "9"
+#define COBALT_VERSION "10"
#endif // COBALT_VERSION_H_
diff --git a/src/cobalt/web_animations/animation_effect_timing_read_only.idl b/src/cobalt/web_animations/animation_effect_timing_read_only.idl
index 789bbe5..550f269 100644
--- a/src/cobalt/web_animations/animation_effect_timing_read_only.idl
+++ b/src/cobalt/web_animations/animation_effect_timing_read_only.idl
@@ -14,17 +14,11 @@
// https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-animationeffecttimingreadonly-interface
-// https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-fillmode-enumeration
-enum FillMode { "none", "forwards", "backwards", "both" };
-
-// https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-playbackdirection-enumeration
-enum PlaybackDirection { "normal", "reverse", "alternate", "alternate-reverse" };
-
interface AnimationEffectTimingReadOnly {
readonly attribute double delay;
readonly attribute double endDelay;
- readonly attribute FillMode fill;
- readonly attribute PlaybackDirection direction;
+ readonly attribute AnimationFillMode fill;
+ readonly attribute AnimationPlaybackDirection direction;
readonly attribute double iterationStart;
readonly attribute unrestricted double iterations;
readonly attribute (unrestricted double or DOMString) duration;
diff --git a/src/cobalt/base/math.cc b/src/cobalt/web_animations/animation_fill_mode.idl
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/cobalt/web_animations/animation_fill_mode.idl
index d335495..6e71b7d 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/web_animations/animation_fill_mode.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,5 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
-
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+// https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-fillmode-enumeration
+enum AnimationFillMode { "none", "forwards", "backwards", "both" };
diff --git a/src/cobalt/base/math.cc b/src/cobalt/web_animations/animation_playback_direction.idl
similarity index 69%
copy from src/cobalt/base/math.cc
copy to src/cobalt/web_animations/animation_playback_direction.idl
index d335495..00a3506 100644
--- a/src/cobalt/base/math.cc
+++ b/src/cobalt/web_animations/animation_playback_direction.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,5 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
-
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+// https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-playbackdirection-enumeration
+enum AnimationPlaybackDirection { "normal", "reverse", "alternate", "alternate-reverse" };
diff --git a/src/cobalt/webdriver/algorithms.cc b/src/cobalt/webdriver/algorithms.cc
index 4d4c59e..8fbce6e 100644
--- a/src/cobalt/webdriver/algorithms.cc
+++ b/src/cobalt/webdriver/algorithms.cc
@@ -71,8 +71,9 @@
return strchr(kZeroWidthSpacesAndFeeds, c) != NULL;
}
-bool IsInHeadElement(const dom::Element* element) {
- if (element->tag_name() == dom::HTMLHeadElement::kTagName) {
+bool IsInHeadElement(dom::Element* element) {
+ if (element->AsHTMLElement() &&
+ element->AsHTMLElement()->AsHTMLHeadElement()) {
return true;
}
scoped_refptr<dom::Element> parent = element->parent_element();
@@ -215,7 +216,7 @@
void GetElementTextInternal(dom::Element* element,
std::vector<std::string>* lines) {
// If the element is a: BR element: Push '' to lines and continue.
- if (element->tag_name() == dom::HTMLBRElement::kTagName) {
+ if (element->AsHTMLElement() && element->AsHTMLElement()->AsHTMLBRElement()) {
lines->push_back("");
return;
}
@@ -437,7 +438,8 @@
// By convention, BODY element is always shown: BODY represents the document
// and even if there's nothing rendered in there, user can always see there's
// the document.
- if (element->tag_name() == dom::HTMLBodyElement::kTagName) {
+ if (element->AsHTMLElement() &&
+ element->AsHTMLElement()->AsHTMLBodyElement()) {
return true;
}
diff --git a/src/cobalt/webdriver/get_element_text_test.cc b/src/cobalt/webdriver/get_element_text_test.cc
index cecd563..29d70f3 100644
--- a/src/cobalt/webdriver/get_element_text_test.cc
+++ b/src/cobalt/webdriver/get_element_text_test.cc
@@ -41,7 +41,7 @@
: css_parser_(css_parser::Parser::Create()),
dom_stat_tracker_(new dom::DomStatTracker("GetElementTextTest")),
html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
dom_stat_tracker_.get(), "") {}
void SetUp() OVERRIDE {
diff --git a/src/cobalt/webdriver/testing/stub_window.h b/src/cobalt/webdriver/testing/stub_window.h
index 9a7fdca..e2751dc 100644
--- a/src/cobalt/webdriver/testing/stub_window.h
+++ b/src/cobalt/webdriver/testing/stub_window.h
@@ -26,6 +26,7 @@
#include "cobalt/dom_parser/parser.h"
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/media/media_module_stub.h"
+#include "cobalt/media_session/media_session.h"
#include "cobalt/network/network_module.h"
#include "googleurl/src/gurl.h"
@@ -47,19 +48,21 @@
url_("about:blank"),
dom_stat_tracker_(new dom::DomStatTracker("StubWindow")) {
engine_ = script::JavaScriptEngine::CreateEngine();
- global_environment_ = engine_->CreateGlobalEnvironment(
- script::JavaScriptEngine::Options());
+ global_environment_ = engine_->CreateGlobalEnvironment();
window_ = new dom::Window(
1920, 1080, css_parser_.get(), dom_parser_.get(),
- fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL,
+ fetcher_factory_.get(), NULL, NULL, 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(),
+ stub_media_module_.get(), NULL, 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() /* ran_animation_frame_callbacks */,
- base::Closure() /* window_close */, NULL, NULL);
+ base::Closure() /* window_close */,
+ base::Closure() /* window_minimize */,
+ NULL, NULL, NULL);
global_environment_->CreateGlobalObject(window_, &environment_settings_);
}
diff --git a/src/cobalt/webdriver/web_driver_module.cc b/src/cobalt/webdriver/web_driver_module.cc
index 1614fb2..daec972 100644
--- a/src/cobalt/webdriver/web_driver_module.cc
+++ b/src/cobalt/webdriver/web_driver_module.cc
@@ -157,7 +157,11 @@
} // namespace
+#if SB_HAS(IPV6)
+const char WebDriverModule::kDefaultListenIp[] = "::";
+#else
const char WebDriverModule::kDefaultListenIp[] = "0.0.0.0";
+#endif
WebDriverModule::WebDriverModule(
int server_port, const std::string& listen_ip,
diff --git a/src/cobalt/webdriver_benchmarks/c_val_names.py b/src/cobalt/webdriver_benchmarks/c_val_names.py
index 9e2a1aa..3256e48 100644
--- a/src/cobalt/webdriver_benchmarks/c_val_names.py
+++ b/src/cobalt/webdriver_benchmarks/c_val_names.py
@@ -13,10 +13,6 @@
return "Count.MainWebModule.ImageCache.LoadingResources"
-def count_font_files_loaded():
- return "Count.Font.FilesLoaded"
-
-
def event_duration_dom_video_start_delay():
return "Event.Duration.MainWebModule.DOM.VideoStartDelay"
diff --git a/src/cobalt/webdriver_benchmarks/tests/README.md b/src/cobalt/webdriver_benchmarks/tests/README.md
index 032c41c..752f1a0 100644
--- a/src/cobalt/webdriver_benchmarks/tests/README.md
+++ b/src/cobalt/webdriver_benchmarks/tests/README.md
@@ -1,26 +1,38 @@
-Cobalt Webdriver-driven Benchmarks
----------------------
+# Cobalt Webdriver-driven Benchmarks
This directory contains a set of webdriver-driven benchmarks
for Cobalt.
Each file should contain a set of tests in Python "unittest" format.
-All tests in all of the files included within "all.py" will be run on the
-build system. Results can be recorded in the build results database.
+All tests in all of the files included within [all.py](all.py) will be run on
+the build system. Results can be recorded in the build results database.
-To run an individual test, simply execute a script directly (or run
-all of them via "all.py"). Platform configuration will be inferred from
-the environment if set. Otherwise, it must be specified via commandline
-parameters.
+## Running the tests
-To make a new test:
+In most cases, you will want to run all tests, and you can do so by executing
+the script [all.py](all.py). You can call `python all.py --help` to see a list
+of commandline parameters to call it with. For example, to run tests on the
+raspi-2 QA build, you should run the following command:
+
+```
+python all.py -p raspi-2 -c qa -d $RASPI_ADDR
+```
+
+Where `RASPI_ADDR` is set to the IP of the target Raspberry Pi device.
+
+To run individual tests, simply execute the script directly. For all tests,
+platform configuration will be inferred from the environment if set. Otherwise,
+it must be specified via commandline parameters.
+
+## Creating a new test
1. If appropriate, create a new file borrowing the boilerplate from
- an existing simple file, such as "browse_horizontal.py".
+ an existing simple file, such as
+ [browse_horizontal.py](browse_horizontal.py).
- 2. Add the file name to the tests added within "all.py", causing it run
- when "all.py" is run.
+ 2. Add the file name to the tests added within [all.py](all.py), causing it run
+ when [all.py](all.py) is run.
3. If this file contains internal names or details, consider adding it
to the "EXCLUDE.FILES" list.
@@ -29,4 +41,105 @@
appropriate.
5. Results must be added to the build results database schema. See
- the internal "README-Updating-Result-Schema.md" file
+ the internal
+ [README-Updating-Result-Schema.md](README-Updating-Result-Schema.md) file.
+
+## Testing arbitrary loaders
+
+Unfortunately we haven't yet had the time to implement an easy way to adjust
+the test URL query parameters. In order to adjust the query parameters to test,
+you should do the following:
+
+Open [../tv_testcase_util.py](../tv_testcase_util.py) and in the function
+`get_url()`, replace the line
+
+```
+query_dict = BASE_PARAMS.copy()
+```
+
+with
+
+```
+query_dict = {}
+```
+
+and then append the following else-clause to the `if query_params:` statement,
+
+```
+else:
+ query_dict = BASE_PARAMS.copy()
+```
+
+and then finally, near the top of the file, modify `BASE_PARAMS` to include all
+query parameters you would like to test. For example,
+
+```
+BASE_PARAMS = {"loader": "airc"}
+```
+
+## Benchmark Results
+
+The results will be printed to stdout. You should redirect output to a file
+if you would like to store the results. Each line of the benchmark output
+prefixed with `webdriver_benchmark TEST_RESULT:` provides the result of one
+measurment. Those lines have the following format:
+
+```
+webdriver_benchmark TEST_RESULT: result_name result_value
+```
+
+where `result_name` is the name of the result and `result_value` is a number
+providing the measured result for that metric. For example,
+
+```
+webdriver_benchmark TEST RESULT: wbBrowseHorizontalDurLayoutBoxTreeUpdateUsedSizesUsPct50 3061.5
+```
+
+gives the 50th percentile of the duration Cobalt took to update the box tree's
+used sizes, on a horizontal scroll event, in microseconds.
+
+Note that most time-based measurements are in microseconds.
+
+### Interesting benchmarks
+
+Some particularly interesting benchmark results are:
+
+ - `wbStartupDurBlankToBrowseUs*`: Measures the startup time, until all images
+ finish loading.
+ - `wbBrowseToWatchDurVideoStartDelay*`: Measures the browse-to-watch time.
+ - `wbBrowseVerticalDurTotalUs*`: Measures the input latency (i.e. JavaScript
+ execution time + layout time) during vertical scroll events.
+ - `wbBrowseVerticalDurRasterizeAnimationsUs*`: Measures the time it takes to
+ render each frame of the animation triggered by a vertical scroll event.
+ The inverse of this number is the framerate.
+ - `wbBrowseHorizontalDurTotalUs*`: Same as `wbBrowseVerticalDurTotalUs*` except
+ for horizontal scroll events.
+ - `wbBrowseHorizontalDurRasterizeAnimationsUs*`: Same as
+ `wbBrowseVerticalDurRasterizeAnimationsUs*` except for horizontal scroll
+ events.
+
+In each case above, the `*` symbol can be one of either `Mean`, `Pct25`,
+`Pct50`, `Pct75` or `Pct95`. For example, `wbStartupDurBlankToBrowseUsMean` or
+`wbStartupDurBlankToBrowseUsPct95` are both valid measurements. The
+webdriver benchmarks runs its tests many times in order to obtain multiple
+samples, so you can drill into the data by exploring either the mean, or the
+various percentiles.
+
+### Filtering results
+
+The webdriver benchmarks output many metrics, but you may only be interested
+in a few. You will have to manually filter only the metrics that you are
+interested in. You can do so with `grep`, for example:
+
+```
+python all.py -p raspi-2 -c qa -d $RASPI_ADDR > results.txt
+echo "" > filtered_results.txt
+grep -o "wbStartupDurBlankToBrowseUs.*$" results.txt >> filtered_results.txt
+grep -o "wbBrowseToWatchDurVideoStartDelay.*$" results.txt >> filtered_results.txt
+grep -o "wbBrowseVerticalDurTotalUs.*$" results.txt >> filtered_results.txt
+grep -o "wbBrowseVerticalDurRasterizeAnimationsUs.*$" results.txt >> filtered_results.txt
+grep -o "wbBrowseHorizontalDurTotalUs.*$" results.txt >> filtered_results.txt
+grep -o "wbBrowseHorizontalDurRasterizeAnimationsUs.*$" results.txt >> filtered_results.txt
+cat filtered_results.txt
+```
+
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py b/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
index 3abcd5d..cb49624 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
@@ -26,7 +26,6 @@
self.record_rasterize_animations = True
self.record_video_start_delay = False
- self.skip_font_file_load_events = True
class EventRecorder(object):
@@ -40,9 +39,6 @@
Both rasterize animations and video start delay data can potentially be
recorded, depending on the |record_rasterize_animations| and
|record_video_start_delay| option flags.
-
- Additionally, events containing font file loads can be skipped if the
- |skip_font_file_load_events| option flag is set to True.
"""
def __init__(self, options):
@@ -57,12 +53,8 @@
self.test = options.test
self.event_name = options.event_name
self.event_type = options.event_type
- self.skip_font_file_load_events = options.skip_font_file_load_events
-
- self.font_files_loaded_count = None
self.render_tree_failure_count = 0
- self.font_file_load_skip_count = 0
# Each entry in the list contains a tuple with a key and value recorder.
self.value_dictionary_recorders = []
@@ -151,30 +143,11 @@
def on_start_event(self):
"""Handles logic related to the start of the event instance."""
-
- # If the recorder is set to skip events with font file loads and the font
- # file loaded count hasn't been initialized yet, then do that now.
- if self.skip_font_file_load_events and self.font_files_loaded_count is None:
- self.font_files_loaded_count = self.test.get_cval(
- c_val_names.count_font_files_loaded())
+ pass
def on_end_event(self):
"""Handles logic related to the end of the event instance."""
- # If the recorder is set to skip events with font file loads and a font file
- # loaded during the event, then its data is not collected. Log that it was
- # skipped and return.
- if self.skip_font_file_load_events:
- current_font_files_loaded_count = self.test.get_cval(
- c_val_names.count_font_files_loaded())
- if self.font_files_loaded_count != current_font_files_loaded_count:
- self.font_file_load_skip_count += 1
- print("{} event skipped because a font file loaded during it! {} "
- "events skipped.".format(self.event_name,
- self.font_file_load_skip_count))
- self.font_files_loaded_count = current_font_files_loaded_count
- return
-
value_dictionary = self.test.get_cval(
c_val_names.event_value_dictionary(self.event_type))
diff --git a/src/cobalt/websocket/buffered_amount_tracker.cc b/src/cobalt/websocket/buffered_amount_tracker.cc
new file mode 100644
index 0000000..708efc0
--- /dev/null
+++ b/src/cobalt/websocket/buffered_amount_tracker.cc
@@ -0,0 +1,58 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/websocket/buffered_amount_tracker.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+namespace cobalt {
+namespace websocket {
+
+std::size_t BufferedAmountTracker::Pop(const std::size_t number_bytes_to_pop) {
+ std::size_t size_left_pop = number_bytes_to_pop;
+ std::size_t payload_amount = 0;
+ while (!entries_.empty() && (size_left_pop > 0)) {
+ Entry& entry(entries_[0]);
+
+ std::size_t potential_payload_delta = 0;
+ if (entry.message_size_ > size_left_pop) {
+ potential_payload_delta = size_left_pop;
+ entry.message_size_ -= size_left_pop;
+ } else { // entry.message_size <= size_left
+ potential_payload_delta += entry.message_size_;
+ entries_.pop_front();
+ }
+
+ if (entry.is_user_payload_) {
+ payload_amount += potential_payload_delta;
+ }
+
+ size_left_pop -= potential_payload_delta;
+ }
+
+ // std::min prevents an underflow due to a bug.
+ DCHECK_LE(payload_amount, total_payload_inflight_);
+
+ total_payload_inflight_ -= std::min(total_payload_inflight_, payload_amount);
+
+ // Sort of an overflow check...
+ DCHECK_LE(size_left_pop, number_bytes_to_pop);
+
+ return payload_amount;
+}
+
+} // namespace websocket
+} // namespace cobalt
diff --git a/src/cobalt/websocket/buffered_amount_tracker.h b/src/cobalt/websocket/buffered_amount_tracker.h
new file mode 100644
index 0000000..ea35084
--- /dev/null
+++ b/src/cobalt/websocket/buffered_amount_tracker.h
@@ -0,0 +1,64 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_WEBSOCKET_BUFFERED_AMOUNT_TRACKER_H_
+#define COBALT_WEBSOCKET_BUFFERED_AMOUNT_TRACKER_H_
+
+#include <deque>
+
+namespace cobalt {
+namespace websocket {
+
+// This class is helper class for implementing the |bufferedAmount| attribute in
+// the Websockets API at:
+// https://www.w3.org/TR/websockets/#dom-websocket-bufferedamount.
+class BufferedAmountTracker {
+ public:
+ BufferedAmountTracker() : total_payload_inflight_(0) {}
+
+ // The payload in this context only applies to user messages.
+ // So even though a close message might have a "payload", for the purposes
+ // of the tracker, the |is_user_payload| will be false.
+ void Add(const bool is_user_payload, const std::size_t message_size) {
+ if (is_user_payload) {
+ total_payload_inflight_ += message_size;
+ }
+ entries_.push_back(Entry(is_user_payload, message_size));
+ }
+
+ // Returns the number of user payload bytes popped, these are bytes that were
+ // added with |is_user_payload| set to true.
+ std::size_t Pop(const std::size_t number_bytes_to_pop);
+
+ std::size_t GetTotalBytesInflight() const { return total_payload_inflight_; }
+
+ private:
+ struct Entry {
+ Entry(const bool is_user_payload, const std::size_t message_size)
+ : is_user_payload_(is_user_payload), message_size_(message_size) {}
+
+ bool is_user_payload_;
+ std::size_t message_size_;
+ };
+
+ typedef std::deque<Entry> Entries;
+
+ std::size_t total_payload_inflight_;
+ Entries entries_;
+};
+
+} // namespace websocket
+} // namespace cobalt
+
+#endif // COBALT_WEBSOCKET_BUFFERED_AMOUNT_TRACKER_H_
diff --git a/src/cobalt/websocket/buffered_amount_tracker_test.cc b/src/cobalt/websocket/buffered_amount_tracker_test.cc
new file mode 100644
index 0000000..435dc53
--- /dev/null
+++ b/src/cobalt/websocket/buffered_amount_tracker_test.cc
@@ -0,0 +1,144 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/websocket/buffered_amount_tracker.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace websocket {
+
+class BufferedAmountTrackerTest : public ::testing::Test {
+ public:
+ BufferedAmountTracker tracker_;
+};
+
+TEST_F(BufferedAmountTrackerTest, Construct) {
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, AddNonPayload) {
+ tracker_.Add(false, 5);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, AddPayload) {
+ tracker_.Add(true, 5);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 5ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, AddCombination) {
+ tracker_.Add(true, 5);
+ tracker_.Add(false, 3);
+ tracker_.Add(true, 11);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 16ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, AddCombination2) {
+ tracker_.Add(false, 2);
+ tracker_.Add(false, 3);
+ tracker_.Add(true, 11);
+ tracker_.Add(true, 1);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 12ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, AddZero) {
+ tracker_.Add(true, 0);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+
+ tracker_.Add(false, 0);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, PopZero) {
+ std::size_t amount_poped = 0;
+ tracker_.Add(false, 5);
+ amount_poped = tracker_.Pop(0);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+ EXPECT_EQ(amount_poped, 0);
+
+ tracker_.Add(false, 0);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+ EXPECT_EQ(amount_poped, 0);
+}
+
+TEST_F(BufferedAmountTrackerTest, PopPayload) {
+ std::size_t amount_poped = 0;
+ tracker_.Add(true, 5);
+ amount_poped = tracker_.Pop(4);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 1ul);
+ EXPECT_EQ(amount_poped, 4);
+}
+
+TEST_F(BufferedAmountTrackerTest, PopNonPayload) {
+ std::size_t amount_poped = 0;
+ tracker_.Add(false, 5);
+ amount_poped = tracker_.Pop(3);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+ EXPECT_EQ(amount_poped, 0);
+}
+
+TEST_F(BufferedAmountTrackerTest, PopCombined) {
+ std::size_t amount_poped = 0;
+ tracker_.Add(false, 5);
+ tracker_.Add(true, 3);
+ amount_poped = tracker_.Pop(7);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 1ul);
+ EXPECT_EQ(amount_poped, 2);
+}
+
+TEST_F(BufferedAmountTrackerTest, PopOverflow) {
+ std::size_t amount_poped = 0;
+ tracker_.Add(false, 2);
+ tracker_.Add(true, 2);
+ amount_poped = tracker_.Pop(16);
+ EXPECT_EQ(amount_poped, 2);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 0ul);
+}
+
+TEST_F(BufferedAmountTrackerTest, MultipleOperations) {
+ tracker_.Add(false, 2);
+ tracker_.Add(true, 3);
+ tracker_.Add(false, 1);
+ tracker_.Add(false, 6);
+ tracker_.Add(true, 14);
+ tracker_.Add(false, 3);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 17ul);
+
+ std::size_t amount_poped = 0;
+ tracker_.Add(false, 3);
+ amount_poped = tracker_.Pop(4);
+ EXPECT_EQ(amount_poped, 2);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 15ul);
+ tracker_.Add(false, 4);
+ amount_poped = tracker_.Pop(2);
+ EXPECT_EQ(amount_poped, 1);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 14ul);
+ tracker_.Add(true, 5);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 19ul);
+ amount_poped = tracker_.Pop(4);
+ EXPECT_EQ(amount_poped, 0);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 19ul);
+ tracker_.Add(false, 6);
+ amount_poped = tracker_.Pop(4);
+ EXPECT_EQ(amount_poped, 2);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 17ul);
+ tracker_.Add(true, 7);
+ amount_poped = tracker_.Pop(4);
+ EXPECT_EQ(amount_poped, 4);
+ EXPECT_EQ(tracker_.GetTotalBytesInflight(), 20ul);
+}
+
+} // namespace websocket
+} // namespace cobalt
diff --git a/src/cobalt/websocket/close_event.h b/src/cobalt/websocket/close_event.h
new file mode 100644
index 0000000..2a23cad
--- /dev/null
+++ b/src/cobalt/websocket/close_event.h
@@ -0,0 +1,73 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef COBALT_WEBSOCKET_CLOSE_EVENT_H_
+#define COBALT_WEBSOCKET_CLOSE_EVENT_H_
+
+#include <string>
+
+#include "cobalt/base/tokens.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/websocket/close_event_init.h"
+#include "net/websockets/websocket_errors.h"
+
+namespace cobalt {
+namespace websocket {
+
+class CloseEvent : public dom::Event {
+ public:
+ explicit CloseEvent(const base::Token type)
+ : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {}
+ explicit CloseEvent(const std::string& type)
+ : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {}
+ CloseEvent(const base::Token type,
+ const cobalt::websocket::CloseEventInit& eventInitDict)
+ : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {
+ InitializeFromCloseEventInit(eventInitDict);
+ }
+ CloseEvent(const std::string& type,
+ const cobalt::websocket::CloseEventInit& eventInitDict)
+ : Event(type), was_clean_(true), code_(net::kWebSocketNormalClosure) {
+ InitializeFromCloseEventInit(eventInitDict);
+ }
+
+ // Readonly Attributes.
+ bool was_clean() const { return was_clean_; }
+ uint16 code() const { return code_; }
+ std::string reason() const { return reason_; }
+
+ DEFINE_WRAPPABLE_TYPE(CloseEvent)
+
+ private:
+ void InitializeFromCloseEventInit(const CloseEventInit& eventInitDict) {
+ if (eventInitDict.has_was_clean()) {
+ was_clean_ = eventInitDict.was_clean();
+ }
+ if (eventInitDict.has_code()) {
+ code_ = eventInitDict.code();
+ }
+ if (eventInitDict.has_reason()) {
+ reason_ = eventInitDict.reason();
+ }
+ }
+ bool was_clean_;
+ uint16 code_;
+ std::string reason_;
+
+ DISALLOW_COPY_AND_ASSIGN(CloseEvent);
+};
+
+} // namespace websocket
+} // namespace cobalt
+
+#endif // COBALT_WEBSOCKET_CLOSE_EVENT_H_
diff --git a/src/cobalt/websocket/close_event.idl b/src/cobalt/websocket/close_event.idl
new file mode 100644
index 0000000..28023c1
--- /dev/null
+++ b/src/cobalt/websocket/close_event.idl
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang).
+// This software or document includes material copied from or derived from
+// The WebSocket API https://www.w3.org/TR/websockets/#event-definitions
+
+[Constructor(DOMString type, optional CloseEventInit eventInitDict)]
+interface CloseEvent : Event {
+ readonly attribute boolean wasClean;
+ readonly attribute unsigned short code;
+ readonly attribute DOMString reason;
+};
+
diff --git a/src/cobalt/websocket/close_event_init.idl b/src/cobalt/websocket/close_event_init.idl
new file mode 100644
index 0000000..ccd1237
--- /dev/null
+++ b/src/cobalt/websocket/close_event_init.idl
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang).
+// This software or document includes material copied from or derived from
+// The WebSocket API https://www.w3.org/TR/websockets/#event-definitions
+
+// From:
+// https://html.spec.whatwg.org/multipage/comms.html#the-closeevent-interfaces
+dictionary CloseEventInit : EventInit {
+ boolean wasClean = false;
+ unsigned short code = 0;
+ DOMString reason = "";
+};
diff --git a/src/cobalt/websocket/web_socket.cc b/src/cobalt/websocket/web_socket.cc
index 4c25991..fd291bf 100644
--- a/src/cobalt/websocket/web_socket.cc
+++ b/src/cobalt/websocket/web_socket.cc
@@ -21,8 +21,13 @@
#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "base/string_piece.h"
+#include "base/string_util.h"
#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/dom/document.h"
#include "cobalt/dom/dom_settings.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/websocket/close_event.h"
#include "googleurl/src/gurl.h"
#include "googleurl/src/url_canon.h"
#include "googleurl/src/url_constants.h"
@@ -33,6 +38,8 @@
typedef uint16 SerializedCloseStatusCodeType;
+static const std::string kComma = ",";
+
bool IsURLAbsolute(cobalt::dom::DOMSettings* dom_settings,
const std::string& url) {
// This is a requirement for calling spec()
@@ -160,31 +167,23 @@
WebSocket::WebSocket(script::EnvironmentSettings* settings,
const std::string& url,
script::ExceptionState* exception_state)
- : buffered_amount_(0),
- ready_state_(kConnecting),
- binary_type_(dom::MessageEvent::kBlob),
- is_secure_(false),
- port_(-1),
- settings_(base::polymorphic_downcast<dom::DOMSettings*>(settings)) {
+ : require_network_module_(true) {
const std::vector<std::string> empty;
- dom::DOMSettings* dom_settings =
- base::polymorphic_downcast<dom::DOMSettings*>(settings);
- Initialize(dom_settings, url, empty, exception_state);
+ Initialize(settings, url, empty, exception_state);
}
WebSocket::WebSocket(script::EnvironmentSettings* settings,
const std::string& url,
const std::vector<std::string>& sub_protocols,
script::ExceptionState* exception_state)
- : buffered_amount_(0),
- ready_state_(kConnecting),
- binary_type_(dom::MessageEvent::kBlob),
- is_secure_(false),
- port_(-1),
- settings_(base::polymorphic_downcast<dom::DOMSettings*>(settings)) {
- dom::DOMSettings* dom_settings =
- base::polymorphic_downcast<dom::DOMSettings*>(settings);
- Initialize(dom_settings, url, sub_protocols, exception_state);
+ : require_network_module_(true) {
+ Initialize(settings, url, sub_protocols, exception_state);
+}
+
+WebSocket::~WebSocket() {
+ if (impl_) {
+ impl_->SetWebSocketEventDelegate(NULL);
+ }
}
std::string WebSocket::binary_type(script::ExceptionState* exception_state) {
@@ -198,19 +197,13 @@
// Implements spec at https://www.w3.org/TR/websockets/#dom-websocket.
WebSocket::WebSocket(script::EnvironmentSettings* settings,
- const std::string& url, const std::string& sub_protocol,
+ const std::string& url,
+ const std::string& sub_protocol_list,
script::ExceptionState* exception_state)
- : buffered_amount_(0),
- ready_state_(kConnecting),
- binary_type_(dom::MessageEvent::kBlob),
- is_secure_(false),
- port_(-1),
- settings_(base::polymorphic_downcast<dom::DOMSettings*>(settings)) {
+ : require_network_module_(true) {
std::vector<std::string> sub_protocols;
- sub_protocols.push_back(sub_protocol);
- dom::DOMSettings* dom_settings =
- base::polymorphic_downcast<dom::DOMSettings*>(settings);
- Initialize(dom_settings, url, sub_protocols, exception_state);
+ Tokenize(sub_protocol_list, kComma, &sub_protocols);
+ Initialize(settings, url, sub_protocols, exception_state);
}
void WebSocket::set_binary_type(const std::string& binary_type,
@@ -269,12 +262,14 @@
return;
}
- switch (ready_state_) {
+ DLOG(INFO) << "Websocket close code " << code;
+
+ switch (ready_state()) {
case kOpen:
case kConnecting:
DCHECK(impl_);
impl_->Close(net::WebSocketError(code), reason);
- ready_state_ = kClosing;
+ SetReadyState(kClosing);
break;
case kClosing:
case kClosed:
@@ -291,7 +286,7 @@
// Per Websockets API spec:
// "If the readyState attribute is CONNECTING, it must throw an
// InvalidStateError exception"
- if (ready_state_ == kConnecting) {
+ if (ready_state() == kConnecting) {
dom::DOMException::Raise(dom::DOMException::kInvalidStateErr,
"readyState is not in CONNECTING state",
exception_state);
@@ -402,10 +397,20 @@
DLOG(INFO) << "Websockets selected subprotocol: [" << selected_subprotocol
<< "]";
protocol_ = selected_subprotocol;
- ready_state_ = kOpen;
+ SetReadyState(kOpen);
this->DispatchEvent(new dom::Event(base::Tokens::open()));
}
+void WebSocket::OnDisconnected(bool was_clean, uint16 code,
+ const std::string& reason) {
+ SetReadyState(kClosed);
+ CloseEventInit close_event_init;
+ close_event_init.set_was_clean(was_clean);
+ close_event_init.set_code(code);
+ close_event_init.set_reason(reason);
+ this->DispatchEvent(new CloseEvent(base::Tokens::close(), close_event_init));
+}
+
void WebSocket::OnReceivedData(bool is_text_frame,
scoped_refptr<net::IOBufferWithSize> data) {
dom::MessageEvent::ResponseTypeCode response_type_code = binary_type_;
@@ -416,13 +421,20 @@
response_type_code, data));
}
-void WebSocket::Initialize(dom::DOMSettings* dom_settings,
+void WebSocket::Initialize(script::EnvironmentSettings* settings,
const std::string& url,
const std::vector<std::string>& sub_protocols,
script::ExceptionState* exception_state) {
DCHECK(thread_checker_.CalledOnValidThread());
+ buffered_amount_ = 0;
+ binary_type_ = dom::MessageEvent::kBlob;
+ is_secure_ = false;
+ port_ = -1;
+ preventing_gc_ = false;
+ SetReadyState(kConnecting);
- if (!dom_settings) {
+ settings_ = base::polymorphic_downcast<dom::DOMSettings*>(settings);
+ if (!settings_) {
dom::DOMException::Raise(dom::DOMException::kNone,
"Internal error: Unable to get DOM settings.",
exception_state);
@@ -430,7 +442,15 @@
return;
}
- if (!dom_settings->base_url().is_valid()) {
+ if (require_network_module_ && !settings_->network_module()) {
+ dom::DOMException::Raise(dom::DOMException::kNone,
+ "Internal error: Unable to get network module.",
+ exception_state);
+ NOTREACHED() << "Unable to get network module.";
+ return;
+ }
+
+ if (!settings_->base_url().is_valid()) {
dom::DOMException::Raise(
dom::DOMException::kNone,
"Internal error: base_url (the url of the entry script) must be valid.",
@@ -440,7 +460,7 @@
// GetOrigin() can only be called on valid urls.
// Since origin does not contain fragments, spec() is guaranteed
// to return an ASCII encoded string.
- entry_script_origin_ = dom_settings->base_url().GetOrigin().spec();
+ entry_script_origin_ = settings_->base_url().GetOrigin().spec();
}
// Per spec:
@@ -448,7 +468,7 @@
// port, resource name, and secure. If this fails, throw a SyntaxError
// exception and abort these steps. [WSP]"
- resolved_url_ = dom_settings->base_url().Resolve(url);
+ resolved_url_ = settings_->base_url().Resolve(url);
if (resolved_url_.is_empty()) {
dom::DOMException::Raise(dom::DOMException::kSyntaxErr, "url is empty",
exception_state);
@@ -461,7 +481,7 @@
return;
}
- bool is_absolute = IsURLAbsolute(dom_settings, url);
+ bool is_absolute = IsURLAbsolute(settings_, url);
if (!is_absolute) {
std::string error_message = "Only relative URLs are supported. [" + url +
@@ -498,6 +518,13 @@
return;
}
+ dom::CspDelegate* csp = csp_delegate();
+ if (csp &&
+ !csp->CanLoad(dom::CspDelegate::kWebSocket, resolved_url_, false)) {
+ dom::DOMException::Raise(dom::DOMException::kSecurityErr, exception_state);
+ return;
+ }
+
if (!net::IsPortAllowedByDefault(GetPort())) {
std::string error_message = "Connecting to port " + GetPortAsString() +
" using websockets is not allowed.";
@@ -527,22 +554,136 @@
Connect(resolved_url_, sub_protocols);
}
+dom::CspDelegate* WebSocket::csp_delegate() const {
+ DCHECK(settings_);
+ if (!settings_) {
+ return NULL;
+ }
+ if (settings_->window() && settings_->window()->document()) {
+ return settings_->window()->document()->csp_delegate();
+ } else {
+ return NULL;
+ }
+}
+
void WebSocket::Connect(const GURL& url,
const std::vector<std::string>& sub_protocols) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(settings_);
- DCHECK(settings_->network_module());
GURL origin_gurl = settings_->base_url().GetOrigin();
const std::string& origin = origin_gurl.possibly_invalid_spec();
- impl_ = make_scoped_refptr<WebSocketImpl>(
- new WebSocketImpl(settings_->network_module(), this));
+ impl_ =
+ make_scoped_refptr(new WebSocketImpl(settings_->network_module(), this));
- UNREFERENCED_PARAMETER(origin);
- UNREFERENCED_PARAMETER(url);
- UNREFERENCED_PARAMETER(sub_protocols);
- // impl_->Connect(origin, url, sub_protocols);
+ impl_->Connect(origin, url, sub_protocols);
+}
+
+WebSocket::WebSocket(script::EnvironmentSettings* settings,
+ const std::string& url,
+ script::ExceptionState* exception_state,
+ const bool require_network_module)
+ : require_network_module_(require_network_module) {
+ const std::vector<std::string> empty;
+ Initialize(settings, url, empty, exception_state);
+}
+
+WebSocket::WebSocket(script::EnvironmentSettings* settings,
+ const std::string& url, const std::string& sub_protocol,
+ script::ExceptionState* exception_state,
+ const bool require_network_module)
+ : require_network_module_(require_network_module) {
+ std::vector<std::string> sub_protocols;
+ sub_protocols.push_back(sub_protocol);
+ Initialize(settings, url, sub_protocols, exception_state);
+}
+
+WebSocket::WebSocket(script::EnvironmentSettings* settings,
+ const std::string& url,
+ const std::vector<std::string>& sub_protocols,
+ script::ExceptionState* exception_state,
+ const bool require_network_module)
+ : require_network_module_(require_network_module) {
+ Initialize(settings, url, sub_protocols, exception_state);
+}
+
+void WebSocket::PotentiallyAllowGarbageCollection() {
+ bool prevent_gc = false;
+ switch (ready_state()) {
+ case kOpen:
+ // Per spec, "A WebSocket object whose readyState attribute's value was
+ // set to OPEN (1) as of the last time the event loop started executing a
+ // task must not be garbage collected if there are any event listeners
+ // registered for message events, error, or close events..
+ // A WebSocket object with an established connection that has data queued
+ // to be transmitted to the network must not be garbage collected."
+ prevent_gc = HasOnMessageListener() || HasOnErrorListener() ||
+ HasOnCloseListener() || HasOutstandingData();
+ break;
+ case kConnecting:
+ // Per spec, "WebSocket object whose readyState attribute's value was set
+ // to CONNECTING (0) as of the last time the event loop started executing
+ // a task must not be garbage collected if there are any event listeners
+ // registered for open events, message events, error events, or close
+ // events."
+ prevent_gc = HasOnOpenListener() || HasOnMessageListener() ||
+ HasOnErrorListener() || HasOnCloseListener() ||
+ HasOutstandingData();
+ break;
+ case kClosing:
+ // Per spec, "A WebSocket object whose readyState attribute's value was
+ // set to CLOSING (2) as of the last time the event loop started
+ // executing a task must not be garbage collected if there are any event
+ // listeners registered for error or close events."
+ prevent_gc =
+ HasOnErrorListener() || HasOnCloseListener() || HasOutstandingData();
+ break;
+ case kClosed:
+ prevent_gc = false;
+ break;
+ default:
+ NOTREACHED() << "Invalid ready_state: " << ready_state();
+ }
+
+ if (prevent_gc != preventing_gc_) {
+ if (prevent_gc) {
+ PreventGarbageCollection();
+ } else {
+ AllowGarbageCollection();
+ }
+
+ // The above function calls should change |preventing_gc_|.
+ DCHECK_EQ(prevent_gc, preventing_gc_);
+ }
+}
+
+void WebSocket::PreventGarbageCollection() {
+ settings_->global_environment()->PreventGarbageCollection(
+ make_scoped_refptr(this));
+ DCHECK(!preventing_gc_);
+ preventing_gc_ = true;
+}
+
+void WebSocket::AllowGarbageCollection() {
+ DCHECK(preventing_gc_);
+
+ // Note: the fall through in this switch statement is on purpose.
+ switch (ready_state_) {
+ case kConnecting:
+ DCHECK(!HasOnOpenListener());
+ case kOpen:
+ DCHECK(!HasOnMessageListener());
+ case kClosing:
+ DCHECK(!HasOnErrorListener());
+ DCHECK(!HasOnCloseListener());
+ default:
+ break;
+ }
+
+ preventing_gc_ = false;
+ settings_->global_environment()->AllowGarbageCollection(
+ make_scoped_refptr(this));
}
} // namespace websocket
diff --git a/src/cobalt/websocket/web_socket.h b/src/cobalt/websocket/web_socket.h
index b99990b..f81b0a1 100644
--- a/src/cobalt/websocket/web_socket.h
+++ b/src/cobalt/websocket/web_socket.h
@@ -18,6 +18,7 @@
#include <vector>
#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/optional.h"
#include "cobalt/base/compiler.h"
@@ -25,6 +26,7 @@
#include "cobalt/dom/array_buffer.h"
#include "cobalt/dom/array_buffer_view.h"
#include "cobalt/dom/blob.h"
+#include "cobalt/dom/csp_delegate.h"
#include "cobalt/dom/dom_exception.h"
#include "cobalt/dom/dom_settings.h"
#include "cobalt/dom/event_target.h"
@@ -36,7 +38,7 @@
namespace cobalt {
namespace websocket {
-// This class represents a WebSocket. It will abide by RFC 6455 "The WebSocket
+// This class represents a WebSocket. It abides by RFC 6455 "The WebSocket
// Protocol", and implements the The WebSocket API spec at
// https://www.w3.org/TR/websockets/ (as of Jan 2017).
class WebSocket : public dom::EventTarget, public WebsocketEventInterface {
@@ -56,6 +58,7 @@
WebSocket(script::EnvironmentSettings* settings, const std::string& url,
const std::vector<std::string>& sub_protocols,
script::ExceptionState* exception_state);
+ ~WebSocket();
// Readonly Attributes.
uint32 buffered_amount() const { return buffered_amount_; }
@@ -93,6 +96,7 @@
void set_onclose(const EventListenerScriptValue& event_listener) {
SetAttributeEventListener(base::Tokens::close(), event_listener);
+ PotentiallyAllowGarbageCollection();
}
const EventListenerScriptValue* onerror() const {
@@ -101,6 +105,7 @@
void set_onerror(const EventListenerScriptValue& event_listener) {
SetAttributeEventListener(base::Tokens::error(), event_listener);
+ PotentiallyAllowGarbageCollection();
}
const EventListenerScriptValue* onmessage() const {
@@ -109,6 +114,7 @@
void set_onmessage(const EventListenerScriptValue& event_listener) {
SetAttributeEventListener(base::Tokens::message(), event_listener);
+ PotentiallyAllowGarbageCollection();
}
const EventListenerScriptValue* onopen() const {
@@ -117,6 +123,7 @@
void set_onopen(const EventListenerScriptValue& event_listener) {
SetAttributeEventListener(base::Tokens::open(), event_listener);
+ PotentiallyAllowGarbageCollection();
}
std::string GetHost() const { return resolved_url_.host(); }
@@ -125,16 +132,35 @@
int GetPort() const { return resolved_url_.EffectiveIntPort(); }
std::string GetPortAsString() const;
+ dom::CspDelegate* csp_delegate() const;
+
DEFINE_WRAPPABLE_TYPE(WebSocket)
private:
+ // The following constructors are used for testing only.
+ WebSocket(script::EnvironmentSettings* settings, const std::string& url,
+ script::ExceptionState* exception_state,
+ const bool require_network_module);
+
+ WebSocket(script::EnvironmentSettings* settings, const std::string& url,
+ const std::string& sub_protocol_list,
+ script::ExceptionState* exception_state,
+ const bool require_network_module);
+
+ WebSocket(script::EnvironmentSettings* settings, const std::string& url,
+ const std::vector<std::string>& sub_protocols,
+ script::ExceptionState* exception_state,
+ const bool require_network_module);
+
void OnConnected(const std::string& selected_subprotocol) OVERRIDE;
- void OnDisconnected() OVERRIDE {
- this->DispatchEvent(new dom::Event(base::Tokens::close()));
- }
+ void OnDisconnected(bool was_clean, uint16 code,
+ const std::string& reason) OVERRIDE;
+
void OnSentData(int amount_sent) OVERRIDE {
- UNREFERENCED_PARAMETER(amount_sent);
+ DCHECK_GE(buffered_amount_, amount_sent);
+ buffered_amount_ -= amount_sent;
+ PotentiallyAllowGarbageCollection();
}
void OnReceivedData(bool is_text_frame,
scoped_refptr<net::IOBufferWithSize> data) OVERRIDE;
@@ -142,15 +168,36 @@
this->DispatchEvent(new dom::Event(base::Tokens::error()));
}
- void Initialize(dom::DOMSettings* dom_settings, const std::string& url,
+ void Initialize(script::EnvironmentSettings* settings, const std::string& url,
const std::vector<std::string>& sub_protocols,
script::ExceptionState* exception_state);
void Connect(const GURL& url, const std::vector<std::string>& sub_protocols);
+ void SetReadyState(const uint16 ready_state) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ready_state_ = ready_state;
+ PotentiallyAllowGarbageCollection();
+ }
+
+ // TODO: Investigate if these are worth optimizing.
+ bool HasOnOpenListener() const { return (onopen() != NULL); }
+ bool HasOnMessageListener() const { return (onmessage() != NULL); }
+ bool HasOnErrorListener() const { return (onerror() != NULL); }
+ bool HasOnCloseListener() const { return (onclose() != NULL); }
+ bool HasOutstandingData() const { return (buffered_amount_ > 0); }
+
+ void PotentiallyAllowGarbageCollection();
+
// Returns false if the check fails.
bool CheckReadyState(script::ExceptionState* exception_state);
+ // Per https://www.w3.org/TR/websockets/#garbage-collection, prevent garbage
+ // collection of this object while connection is live and event listeners are
+ // registered.
+ void AllowGarbageCollection();
+ void PreventGarbageCollection();
+
// https://www.w3.org/TR/websockets/#dom-websocket-bufferedamount
int32 buffered_amount_;
// https://www.w3.org/TR/websockets/#dom-websocket-readystate
@@ -172,10 +219,30 @@
std::string resource_name_; // The path of the URL.
std::string entry_script_origin_;
+ bool require_network_module_;
dom::DOMSettings* settings_;
scoped_refptr<WebSocketImpl> impl_;
- FRIEND_TEST(WebSocketTest, GoodOrigin);
+ bool preventing_gc_;
+
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, BadOrigin);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, GoodOrigin);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, TestInitialReadyState);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, SyntaxErrorWhenBadScheme);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, ParseWsAndWssCorrectly);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, CheckSecure);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, SyntaxErrorWhenRelativeUrl);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, URLHasFragments);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, URLHasPort);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, URLHasNoPort);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, ParseHost);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, ParseResourceName);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, ParseEmptyResourceName);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, ParseResourceNameWithQuery);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, FailUnsecurePort);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, FailInvalidSubProtocols);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, SubProtocols);
+ FRIEND_TEST_ALL_PREFIXES(WebSocketTest, DuplicatedSubProtocols);
DISALLOW_COPY_AND_ASSIGN(WebSocket);
};
diff --git a/src/cobalt/websocket/web_socket.idl b/src/cobalt/websocket/web_socket.idl
index a968129..b0d2127 100644
--- a/src/cobalt/websocket/web_socket.idl
+++ b/src/cobalt/websocket/web_socket.idl
@@ -47,9 +47,12 @@
attribute EventHandler onmessage;
[RaisesException] attribute DOMString binaryType;
[RaisesException] void send(DOMString data);
- [RaisesException] void send(Blob data);
[RaisesException] void send(ArrayBuffer data);
- [RaisesException] void send(ArrayBufferView data);
+
+ // Blob is not currently supported in Cobalt.
+ // [RaisesException] void send(Blob data);
+ // ArrayBufferViews translation is not well supported in Cobalt.
+ // [RaisesException] void send(ArrayBufferView data);
};
typedef EventListener? EventHandler;
diff --git a/src/cobalt/websocket/web_socket_event_interface.h b/src/cobalt/websocket/web_socket_event_interface.h
index 9a16c23..2a786a4 100644
--- a/src/cobalt/websocket/web_socket_event_interface.h
+++ b/src/cobalt/websocket/web_socket_event_interface.h
@@ -27,7 +27,8 @@
public:
virtual ~WebsocketEventInterface() {}
virtual void OnConnected(const std::string& selected_subprotocol) = 0;
- virtual void OnDisconnected() = 0;
+ virtual void OnDisconnected(bool was_clean, uint16 code,
+ const std::string& reason) = 0;
virtual void OnSentData(int amount_sent) = 0;
virtual void OnReceivedData(bool is_text_frame,
scoped_refptr<net::IOBufferWithSize> data) = 0;
diff --git a/src/cobalt/websocket/web_socket_impl.cc b/src/cobalt/websocket/web_socket_impl.cc
index 608e598..ed6995f 100644
--- a/src/cobalt/websocket/web_socket_impl.cc
+++ b/src/cobalt/websocket/web_socket_impl.cc
@@ -57,7 +57,7 @@
WebsocketEventInterface *delegate)
: network_module_(network_module),
delegate_(delegate),
- handshake_helper_(network_module->GetUserAgent()),
+ handshake_helper_(network_module ? network_module->GetUserAgent() : ""),
handshake_completed_(false) {
DCHECK(delegate_);
DCHECK(MessageLoop::current());
@@ -65,8 +65,17 @@
owner_task_runner_ = MessageLoop::current()->message_loop_proxy();
}
+void WebSocketImpl::SetWebSocketEventDelegate(
+ WebsocketEventInterface *delegate) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ delegate_ = delegate;
+}
+
void WebSocketImpl::Connect(const std::string &origin, const GURL &url,
const std::vector<std::string> &sub_protocols) {
+ if (!network_module_) {
+ return;
+ }
DCHECK(network_module_->url_request_context_getter());
thread_checker_.CalledOnValidThread();
origin_ = origin;
@@ -108,6 +117,7 @@
job_created_event->Signal(); // Signal that this->job_ has been assigned.
job_->set_context(context->GetURLRequestContext());
+ job_->set_network_task_runner(context->GetNetworkTaskRunner());
job_->Connect();
DCHECK_EQ(GetCurrentState(), net::WebSocketJob::CONNECTING);
@@ -116,25 +126,48 @@
void WebSocketImpl::Close(const net::WebSocketError code,
const std::string &reason) {
DCHECK(job_);
+ CloseInfo close_info(code, reason);
delegate_task_runner_->PostTask(
- FROM_HERE, base::Bind(&WebSocketImpl::DoClose, this, code, reason));
+ FROM_HERE, base::Bind(&WebSocketImpl::DoClose, this, close_info));
}
-void WebSocketImpl::DoClose(const net::WebSocketError code,
- const std::string &reason) {
+void WebSocketImpl::DoClose(const CloseInfo &close_info) {
DCHECK(job_);
DCHECK(delegate_task_runner_->BelongsToCurrentThread());
net::WebSocketJob::State current_state = GetCurrentState();
- if ((current_state != net::WebSocketJob::CLOSED) &&
- (current_state != net::WebSocketJob::CLOSING)) {
- // Write the close frame
- std::string error_message;
- if (!SendClose(code, reason, &error_message)) {
- DLOG(ERROR) << "Error while sending websocket close: " << error_message;
+ switch (current_state) {
+ case net::WebSocketJob::CONNECTING:
+ case net::WebSocketJob::OPEN: {
+ // Write the close frame
+ std::string error_message;
+ if (!SendClose(close_info.code, close_info.reason, &error_message)) {
+ DLOG(ERROR) << "Error while sending websocket close: " << error_message;
+ }
+ net::WebSocketJob::State new_state;
+ if (peer_close_info_) {
+ // We have received a Close frame from the peer.
+ new_state = net::WebSocketJob::RECV_CLOSED;
+ } else {
+ // We have not received a Close frame from the peer.
+ new_state = net::WebSocketJob::SEND_CLOSED;
+ }
+ job_->SetState(new_state);
+ job_->Close();
+ break;
}
+ case net::WebSocketJob::SEND_CLOSED:
+ if (peer_close_info_) {
+ // Closing handshake is now complete.
+ job_->SetState(net::WebSocketJob::CLOSE_WAIT);
+ job_->Close();
+ }
+ case net::WebSocketJob::CLOSE_WAIT:
+ case net::WebSocketJob::CLOSED:
+ case net::WebSocketJob::INITIALIZED:
+ case net::WebSocketJob::RECV_CLOSED:
+ break;
}
- job_->Close();
}
void WebSocketImpl::DoPong(const scoped_refptr<net::IOBufferWithSize> payload) {
@@ -143,8 +176,8 @@
net::WebSocketJob::State current_state = GetCurrentState();
- if ((current_state == net::WebSocketJob::CLOSED) ||
- (current_state == net::WebSocketJob::CLOSING)) {
+ if (!((current_state == net::WebSocketJob::CONNECTING) ||
+ (current_state == net::WebSocketJob::OPEN))) {
return;
}
@@ -225,8 +258,6 @@
return;
}
- DLOG(INFO) << "ReceivedData " << len << " bytes.";
-
std::size_t payload_offset = 0;
if (!ProcessHandshake(&payload_offset)) {
return; // Handshake is still in progress.
@@ -244,7 +275,6 @@
DLOG(INFO) << "Error while decoding websocket: " << websocket_error;
return;
}
- DLOG(INFO) << "Received " << frame_chunks.size() << " chunks.";
bool protocol_violation_occured = false;
@@ -273,14 +303,6 @@
}
}
- // if (header) {
- // DLOG(INFO) << "Got a chunk " << header->opcode << " "
- // << chunk->final_chunk;
- // } else {
- // DLOG(INFO) << "Got a chunk ? "
- // << " " << chunk->final_chunk;
- // }
-
// Note that |MoveInto| transfers ownership of the pointer.
WebSocketFrameContainer::ErrorCode move_into_retval =
current_frame_container_.Take(*iterator);
@@ -310,22 +332,57 @@
protocol_violation_occured |= (iterator != frame_chunks.end());
if (protocol_violation_occured) {
- TrampolineClose();
+ CloseInfo close_info(net::kWebSocketErrorProtocolError);
+ TrampolineClose(close_info);
frame_chunks.erase(iterator, frame_chunks.end());
}
frame_chunks.weak_clear();
}
-void WebSocketImpl::TrampolineClose(const net::WebSocketError error_code) {
- std::string empty;
+// The main reason to call TrampolineClose is to ensure messages that are posted
+// from this thread prior to this function call are processed before the
+// connection is closed.
+void WebSocketImpl::TrampolineClose(const CloseInfo &close_info) {
base::Closure no_op_closure(base::Bind(&base::DoNothing));
+
base::Closure do_close_closure(
- base::Bind(&WebSocketImpl::DoClose, this, error_code, empty));
+ base::Bind(&WebSocketImpl::DoClose, this, close_info));
owner_task_runner_->PostTaskAndReply(FROM_HERE, no_op_closure,
do_close_closure);
}
+void WebSocketImpl::OnWebSocketConnected(
+ const std::string &selected_subprotocol) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (delegate_) {
+ delegate_->OnConnected(selected_subprotocol);
+ }
+}
+
+void WebSocketImpl::OnWebSocketDisconnected(bool was_clean, uint16 code,
+ const std::string &reason) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (delegate_) {
+ delegate_->OnDisconnected(was_clean, code, reason);
+ }
+}
+
+void WebSocketImpl::OnWebSocketSentData(int amount_sent) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (delegate_) {
+ delegate_->OnSentData(amount_sent);
+ }
+}
+
+void WebSocketImpl::OnWebSocketReceivedData(
+ bool is_text_frame, scoped_refptr<net::IOBufferWithSize> data) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (delegate_) {
+ delegate_->OnReceivedData(is_text_frame, data);
+ }
+}
+
void WebSocketImpl::ProcessCompleteMessage(
const WebSocketMessageContainer &message_container) {
if (message_container.GetCurrentPayloadSizeBytes() >
@@ -354,27 +411,32 @@
if (is_text_message && buf && (buf->size() > 0)) {
base::StringPiece payload_string_piece(buf->data(), buf->size());
if (!IsStringUTF8(payload_string_piece)) {
- TrampolineClose();
+ CloseInfo close_info(net::kWebSocketErrorProtocolError);
+ TrampolineClose(close_info);
return;
}
}
owner_task_runner_->PostTask(
- FROM_HERE, base::Bind(&WebsocketEventInterface::OnReceivedData,
- base::Unretained(delegate_), is_text_message, buf));
+ FROM_HERE, base::Bind(&WebSocketImpl::OnWebSocketReceivedData, this,
+ is_text_message, buf));
}
void WebSocketImpl::ProcessControlMessage(
const WebSocketMessageContainer &message_container) {
if (message_container.empty()) {
- DoClose(net::kWebSocketErrorInternalServerError, "Message has no frames.");
+ CloseInfo close_info(net::kWebSocketErrorInternalServerError,
+ "Message has no frames.");
+ DoClose(close_info);
NOTREACHED();
return;
}
if (message_container.GetCurrentPayloadSizeBytes() >
kMaxControlPayloadSizeInBytes) {
- DoClose(net::kWebSocketErrorProtocolError, "Control Frame too large");
+ CloseInfo close_info(net::kWebSocketErrorProtocolError,
+ "Control Frame too large");
+ DoClose(close_info);
return;
}
@@ -385,8 +447,9 @@
if (number_of_frames > 0) {
const net::WebSocketFrameHeader *header = frames.begin()->GetHeader();
if ((number_of_frames > 1) || (header && !header->final)) {
- DoClose(net::kWebSocketErrorProtocolError,
- "Control messages must not be fragmented");
+ CloseInfo close_info(net::kWebSocketErrorProtocolError,
+ "Control messages must not be fragmented");
+ DoClose(close_info);
return;
}
}
@@ -414,7 +477,10 @@
case net::WebSocketFrameHeader::kOpCodeBinary:
default:
NOTREACHED() << "Invalid case " << header_pointer->opcode;
- DoClose(net::kWebSocketErrorInternalServerError, "Invalid op code.");
+
+ CloseInfo close_info(net::kWebSocketErrorInternalServerError,
+ "Invalid op code.");
+ DoClose(close_info);
break;
}
}
@@ -447,11 +513,15 @@
const scoped_refptr<net::IOBufferWithSize> &close_data) {
net::WebSocketError outgoing_status_code = net::kWebSocketNormalClosure;
std::size_t payload_length = 0;
+ std::string close_reason;
if (close_data) {
DCHECK_EQ(header.payload_length, close_data->size());
DCHECK(close_data->data());
payload_length = close_data->size();
}
+
+ peer_close_info_ = CloseInfo(net::kWebSocketErrorNoStatusReceived);
+
if (payload_length == 0) {
// default status code of normal is OK.
} else if (payload_length < 2) {
@@ -473,44 +543,65 @@
payload_length -= sizeof(status_code_on_wire);
if (net::IsValidCloseStatusCode(status_code_on_wire)) {
- DLOG(INFO) << "Received close status code in close "
- << outgoing_status_code;
+ DLOG(INFO) << "Websocket received close status code: "
+ << status_code_on_wire;
+ peer_close_info_->code = net::WebSocketError(status_code_on_wire);
} else {
- DLOG(ERROR) << "Received invalid status code in close "
+ DLOG(ERROR) << "Websocket received invalid close status code: "
<< outgoing_status_code;
outgoing_status_code = net::kWebSocketErrorProtocolError;
}
- std::string close_reason(payload_pointer, payload_length);
- if (IsStringUTF8(close_reason)) {
- DLOG(INFO) << "Websocket close reason [" << close_reason << "]";
- } else {
- outgoing_status_code = net::kWebSocketErrorProtocolError;
+ close_reason.assign(payload_pointer, payload_length);
+ if (payload_length > 0) {
+ if (IsStringUTF8(close_reason)) {
+ DLOG(INFO) << "Websocket close reason [" << close_reason << "]";
+ peer_close_info_->reason = close_reason;
+ } else {
+ outgoing_status_code = net::kWebSocketErrorProtocolError;
+ }
}
}
}
- TrampolineClose(outgoing_status_code);
+ CloseInfo outgoing_close_info(outgoing_status_code, close_reason);
+ TrampolineClose(outgoing_close_info);
}
void WebSocketImpl::OnSentData(net::SocketStream *socket, int amount_sent) {
UNREFERENCED_PARAMETER(socket);
- UNREFERENCED_PARAMETER(amount_sent);
DCHECK(delegate_task_runner_->BelongsToCurrentThread());
- DLOG(INFO) << "Websocket Sent " << amount_sent << " bytes.";
+ std::size_t payload_sent = buffered_amount_tracker_.Pop(amount_sent);
+ DCHECK_GE(payload_sent, 0ul);
+ DCHECK_LE(payload_sent, static_cast<unsigned int>(kint32max));
+
owner_task_runner_->PostTask(
- FROM_HERE, base::Bind(&WebsocketEventInterface::OnSentData,
- base::Unretained(delegate_), amount_sent));
+ FROM_HERE, base::Bind(&WebSocketImpl::OnWebSocketSentData, this,
+ static_cast<int>(payload_sent)));
}
void WebSocketImpl::OnClose(net::SocketStream *socket) {
- DLOG(INFO) << "Got a close yaar";
UNREFERENCED_PARAMETER(socket);
DCHECK(delegate_task_runner_->BelongsToCurrentThread());
+
+ bool close_was_clean = false;
+ net::WebSocketError close_code = net::kWebSocketErrorAbnormalClosure;
+ std::string close_reason;
+
+ if (peer_close_info_) {
+ close_was_clean = true;
+ close_code = peer_close_info_->code;
+ close_reason = peer_close_info_->reason;
+ }
+
+ DLOG(INFO) << "WebSocket is closing. was_clean[" << std::boolalpha
+ << close_was_clean << "] code[" << close_code << "] reason["
+ << close_reason << "]";
+
owner_task_runner_->PostTask(
- FROM_HERE, base::Bind(&WebsocketEventInterface::OnDisconnected,
- base::Unretained(delegate_)));
+ FROM_HERE, base::Bind(&WebSocketImpl::OnWebSocketDisconnected, this,
+ close_was_clean, close_code, close_reason));
}
// Currently only called in SocketStream::Finish(), so it is meant
@@ -536,14 +627,16 @@
handshake_helper_.GenerateHandshakeRequest(
connect_url_, origin_, desired_sub_protocols_, &header_string);
+ buffered_amount_tracker_.Add(false, header_string.size());
+
job_->SendData(header_string.data(), static_cast<int>(header_string.size()));
}
void WebSocketImpl::OnHandshakeComplete(
const std::string &selected_subprotocol) {
owner_task_runner_->PostTask(
- FROM_HERE, base::Bind(&WebsocketEventInterface::OnConnected,
- base::Unretained(delegate_), selected_subprotocol));
+ FROM_HERE, base::Bind(&WebSocketImpl::OnWebSocketConnected, this,
+ selected_subprotocol));
}
// Note that |payload_length| in |header| will define the payload length.
@@ -575,21 +668,28 @@
DCHECK_EQ(application_payload_offset + payload_length, frame_size);
if (payload_length != 0) {
char *payload_offset = data_ptr.get() + application_payload_offset;
- COMPILE_ASSERT(kMaxFramePayloadInBytes < kint32max,
- frame_payload_size_too_big);
+ COMPILE_ASSERT(
+ kMaxFramePayloadInBytes < static_cast<std::size_t>(kint32max),
+ frame_payload_size_too_big);
SbMemoryCopy(payload_offset, data, static_cast<int>(payload_length));
net::MaskWebSocketFramePayload(masking_key, 0, payload_offset,
static_cast<int>(payload_length));
}
+ int overhead_bytes = static_cast<int>(frame_size);
+ if ((header.opcode == net::WebSocketFrameHeader::kOpCodeText) ||
+ (header.opcode == net::WebSocketFrameHeader::kOpCodeBinary)) {
+ // Only consider text and binary frames as "payload".
+ overhead_bytes = application_payload_offset;
+ }
if (delegate_task_runner_->BelongsToCurrentThread()) {
// this behavior is not just an optimization, but required in case
// we are closing the connection
- SendFrame(data_ptr.Pass(), static_cast<int>(frame_size));
+ SendFrame(data_ptr.Pass(), static_cast<int>(frame_size), overhead_bytes);
} else {
- base::Closure do_send_closure(base::Bind(&WebSocketImpl::SendFrame, this,
- base::Passed(data_ptr.Pass()),
- static_cast<int>(frame_size)));
+ base::Closure do_send_closure(base::Bind(
+ &WebSocketImpl::SendFrame, this, base::Passed(data_ptr.Pass()),
+ static_cast<int>(frame_size), overhead_bytes));
delegate_task_runner_->PostTask(FROM_HERE, do_send_closure);
}
@@ -638,7 +738,8 @@
} else {
DLOG(ERROR) << "Handshake response is invalid: " << error_message;
// Something is wrong, let's shutdown.
- job_->Close();
+ CloseInfo close_info(net::kWebSocketErrorProtocolError);
+ DoClose(close_info);
}
return false;
@@ -672,6 +773,8 @@
header.masked = true;
header.payload_length = length;
+ *buffered_amount += length;
+
return SendHelper(header, data, error_message);
}
@@ -692,6 +795,8 @@
header.masked = true;
header.payload_length = length;
+ *buffered_amount += length;
+
return SendHelper(header, data, error_message);
}
@@ -763,10 +868,20 @@
return SendHelper(header, payload.data(), error_message);
}
-void WebSocketImpl::SendFrame(const scoped_array<char> data, const int length) {
+void WebSocketImpl::SendFrame(const scoped_array<char> data, const int length,
+ const int overhead_bytes) {
DCHECK(delegate_task_runner_->BelongsToCurrentThread());
+
DCHECK(data);
DCHECK_GE(length, 0);
+ DCHECK_GE(length, overhead_bytes);
+
+ buffered_amount_tracker_.Add(false, overhead_bytes);
+ int user_payload_bytes = length - overhead_bytes;
+ if (user_payload_bytes > 0) {
+ buffered_amount_tracker_.Add(true, user_payload_bytes);
+ }
+
job_->SendData(data.get(), length);
}
diff --git a/src/cobalt/websocket/web_socket_impl.h b/src/cobalt/websocket/web_socket_impl.h
index 812d4fb..2d89fc4 100644
--- a/src/cobalt/websocket/web_socket_impl.h
+++ b/src/cobalt/websocket/web_socket_impl.h
@@ -21,9 +21,11 @@
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_vector.h"
+#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_checker.h"
#include "cobalt/network/network_module.h"
+#include "cobalt/websocket/buffered_amount_tracker.h"
#include "cobalt/websocket/web_socket_event_interface.h"
#include "cobalt/websocket/web_socket_frame_container.h"
#include "cobalt/websocket/web_socket_handshake_helper.h"
@@ -53,6 +55,8 @@
explicit WebSocketImpl(cobalt::network::NetworkModule* network_module,
WebsocketEventInterface* delegate);
+ void SetWebSocketEventDelegate(WebsocketEventInterface* delegate);
+
// These functions are meant to be called from the Web Module thread.
void Connect(const std::string& origin, const GURL& url,
const std::vector<std::string>& sub_protocols);
@@ -81,13 +85,23 @@
void OnError(const net::SocketStream* socket, int error) OVERRIDE;
private:
+ struct CloseInfo {
+ CloseInfo(const net::WebSocketError code, const std::string& reason)
+ : code(code), reason(reason) {}
+ explicit CloseInfo(const net::WebSocketError code) : code(code) {}
+
+ net::WebSocketError code;
+ std::string reason;
+ };
+
void DoDetach(base::WaitableEvent* waitable_event);
- void DoClose(const net::WebSocketError code, const std::string& reason);
+ void DoClose(const CloseInfo& close_info);
void DoPong(const scoped_refptr<net::IOBufferWithSize> payload);
void DoConnect(
scoped_refptr<cobalt::network::URLRequestContextGetter> context,
const GURL& url, base::WaitableEvent* job_created_event);
- void SendFrame(const scoped_array<char> data, const int length);
+ void SendFrame(const scoped_array<char> data, const int length,
+ const int overhead_bytes);
void OnHandshakeComplete(const std::string& selected_subprotocol);
void ProcessCompleteMessage(
@@ -114,8 +128,15 @@
// Returns true if the handshake has been fully processed.
bool ProcessHandshake(std::size_t* payload_offset);
- void TrampolineClose(
- const net::WebSocketError error_code = net::kWebSocketErrorProtocolError);
+ void TrampolineClose(const CloseInfo& close_info);
+
+ void OnWebSocketConnected(const std::string& selected_subprotocol);
+ void OnWebSocketDisconnected(bool was_clean, uint16 code,
+ const std::string& reason);
+ void OnWebSocketSentData(int amount_sent);
+ void OnWebSocketReceivedData(bool is_text_frame,
+ scoped_refptr<net::IOBufferWithSize> data);
+ void OnWebSocketError();
base::ThreadChecker thread_checker_;
net::WebSocketJob::State GetCurrentState() const;
@@ -132,6 +153,9 @@
WebSocketFrameContainer current_frame_container_;
WebSocketMessageContainer current_message_container_;
+ base::optional<CloseInfo> peer_close_info_;
+ BufferedAmountTracker buffered_amount_tracker_;
+
scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner_;
diff --git a/src/cobalt/websocket/web_socket_test.cc b/src/cobalt/websocket/web_socket_test.cc
index 0595461..ebca33f 100644
--- a/src/cobalt/websocket/web_socket_test.cc
+++ b/src/cobalt/websocket/web_socket_test.cc
@@ -41,13 +41,12 @@
: dom::DOMSettings(0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL),
base_("https://example.com") {
- this->set_network_module(&network_module_);
+ this->set_network_module(NULL);
}
const GURL& base_url() const OVERRIDE { return base_; }
// public members, so that they're easier for testing.
GURL base_;
- cobalt::network::NetworkModule network_module_;
};
class WebSocketTest : public ::testing::Test {
@@ -73,7 +72,7 @@
.WillOnce(SaveArg<0>(&exception));
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com", &exception_state_));
+ new WebSocket(settings(), "ws://example.com", &exception_state_, false));
ASSERT_TRUE(exception.get());
dom::DOMException& dom_exception(
@@ -100,8 +99,8 @@
base::polymorphic_downcast<FakeSettings*>(settings())->base_ =
GURL(new_base);
- scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com", &exception_state_));
+ scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
+ &exception_state_, false));
ASSERT_FALSE(exception.get());
EXPECT_EQ(ws->entry_script_origin_, test_case.expected_output);
@@ -111,7 +110,7 @@
TEST_F(WebSocketTest, TestInitialReadyState) {
scoped_ptr<FakeSettings> settings_;
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com", &exception_state_));
+ new WebSocket(settings(), "ws://example.com", &exception_state_, false));
EXPECT_EQ(ws->ready_state(), WebSocket::kConnecting);
}
@@ -121,8 +120,8 @@
EXPECT_CALL(exception_state_, SetException(_))
.WillOnce(SaveArg<0>(&exception));
- scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "badscheme://example.com", &exception_state_));
+ scoped_refptr<WebSocket> ws(new WebSocket(
+ settings(), "badscheme://example.com", &exception_state_, false));
dom::DOMException& dom_exception(
*base::polymorphic_downcast<dom::DOMException*>(exception.get()));
@@ -137,22 +136,22 @@
TEST_F(WebSocketTest, ParseWsAndWssCorrectly) {
EXPECT_CALL(exception_state_, SetException(_)).Times(0);
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com/", &exception_state_));
+ new WebSocket(settings(), "ws://example.com/", &exception_state_, false));
EXPECT_CALL(exception_state_, SetException(_)).Times(0);
scoped_refptr<WebSocket> wss(
- new WebSocket(settings(), "wss://example.com", &exception_state_));
+ new WebSocket(settings(), "wss://example.com", &exception_state_, false));
}
TEST_F(WebSocketTest, CheckSecure) {
EXPECT_CALL(exception_state_, SetException(_)).Times(0);
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com/", &exception_state_));
+ new WebSocket(settings(), "ws://example.com/", &exception_state_, false));
EXPECT_FALSE(ws->IsSecure());
EXPECT_CALL(exception_state_, SetException(_)).Times(0);
scoped_refptr<WebSocket> wss(
- new WebSocket(settings(), "wss://example.com", &exception_state_));
+ new WebSocket(settings(), "wss://example.com", &exception_state_, false));
EXPECT_TRUE(wss->IsSecure());
}
@@ -165,7 +164,7 @@
.WillOnce(SaveArg<0>(&exception));
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "relative_url", &exception_state_));
+ new WebSocket(settings(), "relative_url", &exception_state_, false));
dom::DOMException& dom_exception(
*base::polymorphic_downcast<dom::DOMException*>(exception.get()));
@@ -183,7 +182,7 @@
.WillOnce(SaveArg<0>(&exception));
scoped_refptr<WebSocket> ws(new WebSocket(
- settings(), "wss://example.com/#fragment", &exception_state_));
+ settings(), "wss://example.com/#fragment", &exception_state_, false));
dom::DOMException& dom_exception(
*base::polymorphic_downcast<dom::DOMException*>(exception.get()));
@@ -195,8 +194,8 @@
}
TEST_F(WebSocketTest, URLHasPort) {
- scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "wss://example.com:789", &exception_state_));
+ scoped_refptr<WebSocket> ws(new WebSocket(settings(), "wss://example.com:789",
+ &exception_state_, false));
EXPECT_EQ(ws->GetPort(), 789);
EXPECT_EQ(ws->GetPortAsString(), "789");
@@ -208,13 +207,13 @@
// otherwise let port be 443.
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com", &exception_state_));
+ new WebSocket(settings(), "ws://example.com", &exception_state_, false));
EXPECT_EQ(ws->GetPort(), 80);
EXPECT_EQ(ws->GetPortAsString(), "80");
scoped_refptr<WebSocket> wss(
- new WebSocket(settings(), "wss://example.com", &exception_state_));
+ new WebSocket(settings(), "wss://example.com", &exception_state_, false));
EXPECT_EQ(wss->GetPort(), 443);
EXPECT_EQ(wss->GetPortAsString(), "443");
@@ -225,7 +224,7 @@
// Let host be the value of the <host> component of url, converted to ASCII
// lowercase.
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "wss://eXAmpLe.com", &exception_state_));
+ new WebSocket(settings(), "wss://eXAmpLe.com", &exception_state_, false));
std::string host(ws->GetHost());
EXPECT_EQ(host, "example.com");
@@ -237,7 +236,7 @@
// empty) of
// url.
scoped_refptr<WebSocket> ws(new WebSocket(
- settings(), "ws://eXAmpLe.com/resource_name", &exception_state_));
+ settings(), "ws://eXAmpLe.com/resource_name", &exception_state_, false));
std::string resource_name(ws->GetResourceName());
EXPECT_EQ(resource_name, "/resource_name");
@@ -248,7 +247,7 @@
// If resource name is the empty string, set it to a single character U+002F
// SOLIDUS (/).
scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com", &exception_state_));
+ new WebSocket(settings(), "ws://example.com", &exception_state_, false));
std::string resource_name(ws->GetResourceName());
EXPECT_EQ(resource_name, "/");
@@ -261,7 +260,7 @@
// component.
scoped_refptr<WebSocket> ws(
new WebSocket(settings(), "ws://example.com/resource_name?abc=xyz&j=3",
- &exception_state_));
+ &exception_state_, false));
std::string resource_name(ws->GetResourceName());
EXPECT_EQ(resource_name, "/resource_name?abc=xyz&j=3");
@@ -273,8 +272,8 @@
EXPECT_CALL(exception_state_, SetException(_))
.WillOnce(SaveArg<0>(&exception));
- scoped_refptr<WebSocket> ws(
- new WebSocket(settings(), "ws://example.com:22", &exception_state_));
+ scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com:22",
+ &exception_state_, false));
dom::DOMException& dom_exception(
*base::polymorphic_downcast<dom::DOMException*>(exception.get()));
@@ -317,7 +316,7 @@
scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
test_case.sub_protocol,
- &exception_state_));
+ &exception_state_, false));
if (test_case.exception_thrown) {
dom::DOMException& dom_exception(
@@ -335,8 +334,9 @@
std::vector<std::string> sub_protocols;
sub_protocols.push_back("chat.example.com");
sub_protocols.push_back("bicker.example.com");
- scoped_refptr<WebSocket> ws(new WebSocket(
- settings(), "ws://example.com", sub_protocols, &exception_state_));
+ scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
+ sub_protocols, &exception_state_,
+ false));
ASSERT_FALSE(exception.get());
}
@@ -344,7 +344,8 @@
scoped_refptr<script::ScriptException> exception;
std::string sub_protocol("chat");
scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
- sub_protocol, &exception_state_));
+ sub_protocol, &exception_state_,
+ false));
ASSERT_FALSE(exception.get());
}
@@ -358,8 +359,8 @@
std::vector<std::string> sub_protocols;
sub_protocols.push_back("chat");
sub_protocols.push_back("chat");
- scoped_refptr<WebSocket> ws(new WebSocket(settings(), "ws://example.com",
- sub_protocols, &exception_state_));
+ scoped_refptr<WebSocket> ws(new WebSocket(
+ settings(), "ws://example.com", sub_protocols, &exception_state_, false));
ASSERT_TRUE(exception.get());
diff --git a/src/cobalt/websocket/websocket.gyp b/src/cobalt/websocket/websocket.gyp
index d970680..d13c46d 100644
--- a/src/cobalt/websocket/websocket.gyp
+++ b/src/cobalt/websocket/websocket.gyp
@@ -21,21 +21,25 @@
'target_name': 'websocket',
'type': 'static_library',
'sources': [
+ 'buffered_amount_tracker.cc',
+ 'buffered_amount_tracker.h',
+ 'close_event.h',
'sec_web_socket_key.h',
'web_socket.cc',
'web_socket.h',
'web_socket_event_interface.h',
- 'web_socket_frame_container.h',
'web_socket_frame_container.cc',
- 'web_socket_handshake_helper.h',
+ 'web_socket_frame_container.h',
'web_socket_handshake_helper.cc',
- 'web_socket_impl.h',
+ 'web_socket_handshake_helper.h',
'web_socket_impl.cc',
- 'web_socket_message_container.h',
+ 'web_socket_impl.h',
'web_socket_message_container.cc',
+ 'web_socket_message_container.h',
],
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
'<(DEPTH)/cobalt/speech/speech.gyp:speech',
'<(DEPTH)/googleurl/googleurl.gyp:googleurl',
@@ -46,6 +50,7 @@
'target_name': 'websocket_test',
'type': '<(gtest_target_type)',
'sources': [
+ 'buffered_amount_tracker_test.cc',
'web_socket_frame_container_test.cc',
'web_socket_handshake_helper_test.cc',
'web_socket_message_container_test.cc',
@@ -57,6 +62,10 @@
'<(DEPTH)/googleurl/googleurl.gyp:googleurl',
'<(DEPTH)/testing/gmock.gyp:gmock',
'<(DEPTH)/testing/gtest.gyp:gtest',
+
+ # TODO: Remove the dependency below, it works around the fact that
+ # ScriptValueFactory has non-virtual method CreatePromise().
+ '<(DEPTH)/cobalt/script/engine.gyp:engine',
],
},
@@ -73,3 +82,4 @@
},
],
}
+
diff --git a/src/cobalt/xhr/xhr.gyp b/src/cobalt/xhr/xhr.gyp
index 1fbd0c0..8d4d574 100644
--- a/src/cobalt/xhr/xhr.gyp
+++ b/src/cobalt/xhr/xhr.gyp
@@ -50,6 +50,10 @@
'<(DEPTH)/testing/gtest.gyp:gtest',
'xhr',
'xhr_copy_test_data',
+
+ # TODO: Remove the dependency below, it works around the fact that
+ # ScriptValueFactory has non-virtual method CreatePromise().
+ '<(DEPTH)/cobalt/script/engine.gyp:engine',
],
},
{
diff --git a/src/cobalt/xhr/xml_http_request_test.cc b/src/cobalt/xhr/xml_http_request_test.cc
index 77aa859..5863f7f 100644
--- a/src/cobalt/xhr/xml_http_request_test.cc
+++ b/src/cobalt/xhr/xml_http_request_test.cc
@@ -19,6 +19,7 @@
#include "cobalt/dom/dom_settings.h"
#include "cobalt/dom/testing/mock_event_listener.h"
#include "cobalt/dom/window.h"
+#include "cobalt/script/testing/fake_script_value.h"
#include "cobalt/script/testing/mock_exception_state.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -30,8 +31,9 @@
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;
-using cobalt::dom::testing::FakeScriptValue;
+using cobalt::dom::EventListener;
using cobalt::dom::testing::MockEventListener;
+using cobalt::script::testing::FakeScriptValue;
using cobalt::script::testing::MockExceptionState;
namespace cobalt {
@@ -151,7 +153,7 @@
TEST_F(XhrTest, Open) {
scoped_ptr<MockEventListener> listener = MockEventListener::Create();
- FakeScriptValue script_object(listener.get());
+ FakeScriptValue<EventListener> script_object(listener.get());
xhr_->set_onreadystatechange(script_object);
EXPECT_CALL(
*listener,
diff --git a/src/glimp/egl/display.cc b/src/glimp/egl/display.cc
index 0d02a74..c4d7608 100644
--- a/src/glimp/egl/display.cc
+++ b/src/glimp/egl/display.cc
@@ -309,5 +309,9 @@
return true;
}
+bool Display::SwapInterval(EGLint interval) {
+ return impl_->SetSwapInterval(interval);
+}
+
} // namespace egl
} // namespace glimp
diff --git a/src/glimp/egl/display.h b/src/glimp/egl/display.h
index a61469a..8fc432d 100644
--- a/src/glimp/egl/display.h
+++ b/src/glimp/egl/display.h
@@ -64,6 +64,7 @@
bool MakeCurrent(EGLSurface draw, EGLSurface read, EGLContext ctx);
bool SwapBuffers(EGLSurface surface);
+ bool SwapInterval(EGLint interval);
DisplayImpl* impl() const { return impl_.get(); }
diff --git a/src/glimp/egl/display_impl.h b/src/glimp/egl/display_impl.h
index c430202..d98efb9 100644
--- a/src/glimp/egl/display_impl.h
+++ b/src/glimp/egl/display_impl.h
@@ -87,6 +87,11 @@
virtual nb::scoped_ptr<gles::ContextImpl> CreateContext(const Config* config,
int gles_version) = 0;
+ // Sets the swap behavior for this display. This will be called when
+ // eglSwapInterval() is called. Returns true on success and false on failure.
+ // https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglSwapInterval.xhtml
+ virtual bool SetSwapInterval(int interval) = 0;
+
private:
};
diff --git a/src/glimp/entry_points/egl.cc b/src/glimp/entry_points/egl.cc
index f1ecb62..da0ff5f 100644
--- a/src/glimp/entry_points/egl.cc
+++ b/src/glimp/entry_points/egl.cc
@@ -316,8 +316,12 @@
EGLBoolean EGLAPIENTRY eglSwapInterval(EGLDisplay dpy, EGLint interval) {
egl::ScopedEGLLock egl_lock;
- SB_NOTIMPLEMENTED();
- return false;
+ egl::Display* display = egl::GetDisplayOrSetError(dpy);
+ if (!display) {
+ return false;
+ }
+
+ return display->SwapInterval(interval);
}
EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api) {
diff --git a/src/glimp/gles/context.h b/src/glimp/gles/context.h
index 018a2f2..e0163cd 100644
--- a/src/glimp/gles/context.h
+++ b/src/glimp/gles/context.h
@@ -229,6 +229,15 @@
// Called when eglReleaseTexImage() is called.
bool ReleaseTextureFromEGLSurface(egl::Surface* surface);
+ // Utility functions for use by other modules.
+ nb::scoped_refptr<Texture> GetTexture(uint32_t id) {
+ return resource_manager_->GetTexture(id);
+ }
+
+ DrawStateDirtyFlags* GetDrawStateDirtyFlags() {
+ return &draw_state_dirty_flags_;
+ }
+
private:
void MakeCurrent(egl::Surface* draw, egl::Surface* read);
void ReleaseContext();
diff --git a/src/glimp/stub/egl/display_impl.h b/src/glimp/stub/egl/display_impl.h
index f766cce..9dbf897 100644
--- a/src/glimp/stub/egl/display_impl.h
+++ b/src/glimp/stub/egl/display_impl.h
@@ -50,6 +50,8 @@
nb::scoped_ptr<gles::ContextImpl> CreateContext(const Config* config,
int gles_version) SB_OVERRIDE;
+ bool SetSwapInterval(int interval) SB_OVERRIDE { return true; }
+
private:
void InitializeSupportedConfigs();
diff --git a/src/media/base/pipeline.h b/src/media/base/pipeline.h
index 4c3565b..d3d752a 100644
--- a/src/media/base/pipeline.h
+++ b/src/media/base/pipeline.h
@@ -105,8 +105,7 @@
const PipelineStatusCB& error_cb,
const PipelineStatusCB& seek_cb,
const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb,
- bool prefer_decode_to_texture) = 0;
+ const base::Closure& duration_change_cb) = 0;
// Asynchronously stops the pipeline, executing |stop_cb| when the pipeline
// teardown has completed.
@@ -181,8 +180,8 @@
// Get the SetBoundsCB used to set the bounds of the video frame.
virtual SetBoundsCB GetSetBoundsCB() { return SetBoundsCB(); }
- // Returns whether the player is configured for outputting in punch out mode.
- virtual bool IsPunchOutMode() { return false; }
+ // Updates the player's preference for decode-to-texture versus punch through.
+ virtual void SetDecodeToTextureOutputMode(bool /*enabled*/) {}
};
} // namespace media
diff --git a/src/media/base/pipeline_impl.cc b/src/media/base/pipeline_impl.cc
index 6d3c5fa..aff759d 100644
--- a/src/media/base/pipeline_impl.cc
+++ b/src/media/base/pipeline_impl.cc
@@ -138,9 +138,7 @@
const PipelineStatusCB& error_cb,
const PipelineStatusCB& seek_cb,
const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb,
- bool prefer_decode_to_texture) {
- UNREFERENCED_PARAMETER(prefer_decode_to_texture);
+ const base::Closure& duration_change_cb) {
DCHECK_EQ(collection->GetAudioDecoders()->size(), 1);
DCHECK_EQ(collection->GetVideoDecoders()->size(), 1);
diff --git a/src/media/base/pipeline_impl.h b/src/media/base/pipeline_impl.h
index 358694e..8b1983f 100644
--- a/src/media/base/pipeline_impl.h
+++ b/src/media/base/pipeline_impl.h
@@ -124,8 +124,7 @@
const PipelineStatusCB& error_cb,
const PipelineStatusCB& seek_cb,
const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb,
- bool prefer_decode_to_texture) OVERRIDE;
+ const base::Closure& duration_change_cb) OVERRIDE;
// Asynchronously stops the pipeline, executing |stop_cb| when the pipeline
// teardown has completed.
diff --git a/src/media/base/sbplayer_pipeline.cc b/src/media/base/sbplayer_pipeline.cc
index 22d4460..044871c 100644
--- a/src/media/base/sbplayer_pipeline.cc
+++ b/src/media/base/sbplayer_pipeline.cc
@@ -20,6 +20,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
+#include "base/optional.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/time.h"
@@ -61,7 +62,6 @@
PipelineStatusCB seek_cb;
Pipeline::BufferingStateCB buffering_state_cb;
base::Closure duration_change_cb;
- bool prefer_decode_to_texture;
};
// SbPlayerPipeline is a PipelineBase implementation that uses the SbPlayer
@@ -84,8 +84,7 @@
const PipelineStatusCB& error_cb,
const PipelineStatusCB& seek_cb,
const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb,
- bool prefer_decode_to_texture) OVERRIDE;
+ const base::Closure& duration_change_cb) OVERRIDE;
void Stop(const base::Closure& stop_cb) OVERRIDE;
void Seek(TimeDelta time, const PipelineStatusCB& seek_cb);
@@ -106,8 +105,7 @@
bool DidLoadingProgress() const OVERRIDE;
PipelineStatistics GetStatistics() const OVERRIDE;
SetBoundsCB GetSetBoundsCB() OVERRIDE;
-
- bool IsPunchOutMode() OVERRIDE;
+ void SetDecodeToTextureOutputMode(bool preference) OVERRIDE;
private:
void StartTask(const StartTaskParameters& parameters);
@@ -197,7 +195,7 @@
PipelineStatusCB error_cb_;
BufferingStateCB buffering_state_cb_;
base::Closure duration_change_cb_;
- bool prefer_decode_to_texture_;
+ base::optional<bool> decode_to_texture_output_mode_;
// Demuxer reference used for setting the preload value.
scoped_refptr<Demuxer> demuxer_;
@@ -237,8 +235,7 @@
audio_read_in_progress_(false),
video_read_in_progress_(false),
set_bounds_helper_(new SbPlayerSetBoundsHelper),
- suspended_(false),
- prefer_decode_to_texture_(false) {}
+ suspended_(false) {}
SbPlayerPipeline::~SbPlayerPipeline() {
DCHECK(!player_);
@@ -272,8 +269,7 @@
const PipelineStatusCB& error_cb,
const PipelineStatusCB& seek_cb,
const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb,
- bool prefer_decode_to_texture) {
+ const base::Closure& duration_change_cb) {
DCHECK(filter_collection);
StartTaskParameters parameters;
@@ -284,7 +280,6 @@
parameters.seek_cb = seek_cb;
parameters.buffering_state_cb = buffering_state_cb;
parameters.duration_change_cb = duration_change_cb;
- parameters.prefer_decode_to_texture = prefer_decode_to_texture;
message_loop_->PostTask(
FROM_HERE, base::Bind(&SbPlayerPipeline::StartTask, this, parameters));
@@ -459,17 +454,15 @@
return base::Bind(&SbPlayerSetBoundsHelper::SetBounds, set_bounds_helper_);
}
-bool SbPlayerPipeline::IsPunchOutMode() {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
- base::AutoLock auto_lock(lock_);
- if (player_) {
- return player_->GetSbPlayerOutputMode() == kSbPlayerOutputModePunchOut;
- } else {
- return true;
+void SbPlayerPipeline::SetDecodeToTextureOutputMode(bool enabled) {
+ if (!message_loop_->BelongsToCurrentThread()) {
+ message_loop_->PostTask(
+ FROM_HERE, base::Bind(&SbPlayerPipeline::SetDecodeToTextureOutputMode,
+ this, enabled));
+ return;
}
-#else // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
- return true;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+
+ decode_to_texture_output_mode_ = enabled;
}
void SbPlayerPipeline::StartTask(const StartTaskParameters& parameters) {
@@ -487,7 +480,6 @@
}
buffering_state_cb_ = parameters.buffering_state_cb;
duration_change_cb_ = parameters.duration_change_cb;
- prefer_decode_to_texture_ = parameters.prefer_decode_to_texture;
demuxer_->Initialize(
this, BindToCurrentLoop(
@@ -576,7 +568,7 @@
base::AutoLock auto_lock(lock_);
player_.reset(new StarboardPlayer(
message_loop_, audio_config, video_config, window_, drm_system, this,
- set_bounds_helper_.get(), prefer_decode_to_texture_));
+ set_bounds_helper_.get(), *decode_to_texture_output_mode_));
SetPlaybackRateTask(playback_rate_);
SetVolumeTask(volume_);
}
diff --git a/src/media/base/shell_data_source_reader.cc b/src/media/base/shell_data_source_reader.cc
index 07e1c8a..00e65f1 100644
--- a/src/media/base/shell_data_source_reader.cc
+++ b/src/media/base/shell_data_source_reader.cc
@@ -50,9 +50,15 @@
int total_bytes_read = 0;
while (size > 0 && !read_has_failed_) {
- data_source_->Read(
- position, size, data,
- base::Bind(&ShellDataSourceReader::BlockingReadCompleted, this));
+ {
+ base::AutoLock auto_lock(lock_);
+ if (!data_source_) {
+ break;
+ }
+ data_source_->Read(
+ position, size, data,
+ base::Bind(&ShellDataSourceReader::BlockingReadCompleted, this));
+ }
// wait for callback on read completion
blocking_read_event_.Wait();
@@ -90,6 +96,8 @@
if (data_source_) {
// stop the data source, it can call the callback
data_source_->Stop();
+
+ base::AutoLock auto_lock(lock_);
data_source_ = NULL;
}
callback.Run();
@@ -103,7 +111,8 @@
int64 ShellDataSourceReader::FileSize() {
if (file_size_ == -1) {
- if (!data_source_->GetSize(&file_size_)) {
+ base::AutoLock auto_lock(lock_);
+ if (data_source_ && !data_source_->GetSize(&file_size_)) {
file_size_ = -1;
}
}
diff --git a/src/media/base/shell_data_source_reader.h b/src/media/base/shell_data_source_reader.h
index 01c52bd..9e50be7 100644
--- a/src/media/base/shell_data_source_reader.h
+++ b/src/media/base/shell_data_source_reader.h
@@ -22,6 +22,7 @@
#include "base/message_loop.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "media/base/data_source.h"
@@ -59,6 +60,7 @@
// blocking read callback
virtual void BlockingReadCompleted(int bytes_read);
+ base::Lock lock_;
DataSource* data_source_;
base::WaitableEvent blocking_read_event_;
int64 file_size_;
diff --git a/src/media/base/shell_media_platform.h b/src/media/base/shell_media_platform.h
index 9c49fce..86f2d79 100644
--- a/src/media/base/shell_media_platform.h
+++ b/src/media/base/shell_media_platform.h
@@ -28,6 +28,12 @@
#include "media/base/shell_video_frame_provider.h"
#include "starboard/decode_target.h"
+namespace cobalt {
+namespace render_tree {
+class ResourceProvider;
+} // namespace render_tree
+} // namespace cobalt
+
namespace media {
// This class is meant to be the single point to attach platform specific media
@@ -49,7 +55,8 @@
// The following functions will be called when the application enters or
// leaves suspending status.
virtual void Suspend() {}
- virtual void Resume() {}
+ virtual void Resume(
+ cobalt::render_tree::ResourceProvider* /*resource_provider*/) {}
// Media stack buffer allocate/free functions currently only used by
// ShellBufferFactory.
@@ -65,9 +72,12 @@
return NULL;
}
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+ virtual SbDecodeTargetGraphicsContextProvider*
+ GetSbDecodeTargetGraphicsContextProvider() { return NULL; }
+#elif SB_API_VERSION >= 3
virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; }
-#endif // SB_API_VERSION >= 3
+#endif // SB_API_VERSION >= 4
// Total number of video frames which are populating in the pipeline when
// prerolling.
diff --git a/src/media/base/shell_video_frame_provider.cc b/src/media/base/shell_video_frame_provider.cc
index 104f64a..0cf2f98 100644
--- a/src/media/base/shell_video_frame_provider.cc
+++ b/src/media/base/shell_video_frame_provider.cc
@@ -17,6 +17,9 @@
#include "media/base/shell_video_frame_provider.h"
#include "base/logging.h"
+#if SB_API_VERSION >= 4
+#include "starboard/decode_target.h"
+#endif // #if SB_API_VERSION >= 4
namespace media {
@@ -99,6 +102,41 @@
return current_frame_;
}
+void ShellVideoFrameProvider::SetOutputMode(OutputMode output_mode) {
+ base::AutoLock auto_lock(frames_lock_);
+ output_mode_ = output_mode;
+}
+
+ShellVideoFrameProvider::OutputMode ShellVideoFrameProvider::GetOutputMode()
+ const {
+ base::AutoLock auto_lock(frames_lock_);
+ return output_mode_;
+}
+
+#if SB_API_VERSION >= 4
+
+void ShellVideoFrameProvider::SetGetCurrentSbDecodeTargetFunction(
+ GetCurrentSbDecodeTargetFunction function) {
+ base::AutoLock auto_lock(frames_lock_);
+ get_current_sb_decode_target_function_ = function;
+}
+
+void ShellVideoFrameProvider::ResetGetCurrentSbDecodeTargetFunction() {
+ base::AutoLock auto_lock(frames_lock_);
+ get_current_sb_decode_target_function_.Reset();
+}
+
+SbDecodeTarget ShellVideoFrameProvider::GetCurrentSbDecodeTarget() const {
+ base::AutoLock auto_lock(frames_lock_);
+ if (get_current_sb_decode_target_function_.is_null()) {
+ return kSbDecodeTargetInvalid;
+ } else {
+ return get_current_sb_decode_target_function_.Run();
+ }
+}
+
+#endif // #if SB_API_VERSION >= 4
+
void ShellVideoFrameProvider::AddFrame(const scoped_refptr<VideoFrame>& frame) {
base::AutoLock auto_lock(frames_lock_);
frames_.push_back(frame);
diff --git a/src/media/base/shell_video_frame_provider.h b/src/media/base/shell_video_frame_provider.h
index 66852c8..ae1e2d0 100644
--- a/src/media/base/shell_video_frame_provider.h
+++ b/src/media/base/shell_video_frame_provider.h
@@ -37,9 +37,9 @@
class ShellVideoFrameProvider
: public base::RefCountedThreadSafe<ShellVideoFrameProvider> {
public:
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
typedef base::Callback<SbDecodeTarget()> GetCurrentSbDecodeTargetFunction;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
enum OutputMode {
kOutputModePunchOut,
@@ -69,10 +69,10 @@
// returns NULL.
const scoped_refptr<VideoFrame>& GetCurrentFrame();
- void SetOutputMode(OutputMode output_mode) { output_mode_ = output_mode; }
- OutputMode GetOutputMode() const { return output_mode_; }
+ void SetOutputMode(OutputMode output_mode);
+ OutputMode GetOutputMode() const;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// For Starboard platforms that have a decode-to-texture player, we enable
// this ShellVideoFrameProvider to act as a bridge for Cobalt code to query
// for the current SbDecodeTarget. In effect, we bypass all of
@@ -80,22 +80,12 @@
// Starboard implementation to provide us with the current video frame when
// needed.
void SetGetCurrentSbDecodeTargetFunction(
- GetCurrentSbDecodeTargetFunction function) {
- get_current_sb_decode_target_function_ = function;
- }
+ GetCurrentSbDecodeTargetFunction function);
- void ResetGetCurrentSbDecodeTargetFunction() {
- get_current_sb_decode_target_function_.Reset();
- }
+ void ResetGetCurrentSbDecodeTargetFunction();
- SbDecodeTarget GetCurrentSbDecodeTarget() const {
- if (get_current_sb_decode_target_function_.is_null()) {
- return kSbDecodeTargetInvalid;
- } else {
- return get_current_sb_decode_target_function_.Run();
- }
- }
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+ SbDecodeTarget GetCurrentSbDecodeTarget() const;
+#endif // SB_API_VERSION >= 4
void AddFrame(const scoped_refptr<VideoFrame>& frame);
// Flush will clear all cached frames except the current frame. So the current
@@ -130,9 +120,9 @@
#endif // !defined(__LB_SHELL__FOR_RELEASE__)
OutputMode output_mode_;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
GetCurrentSbDecodeTargetFunction get_current_sb_decode_target_function_;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
DISALLOW_COPY_AND_ASSIGN(ShellVideoFrameProvider);
};
diff --git a/src/media/base/starboard_player.cc b/src/media/base/starboard_player.cc
index b74b39a..f8a838e 100644
--- a/src/media/base/starboard_player.cc
+++ b/src/media/base/starboard_player.cc
@@ -56,11 +56,11 @@
audio_config_.CopyFrom(audio_config);
video_config_.CopyFrom(video_config);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
output_mode_ = ComputeSbPlayerOutputMode(
MediaVideoCodecToSbMediaVideoCodec(video_config.codec()), drm_system,
prefer_decode_to_texture);
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
CreatePlayer();
message_loop->PostTask(
@@ -73,11 +73,13 @@
set_bounds_helper_->SetPlayer(NULL);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+ ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+ ShellVideoFrameProvider::kOutputModeInvalid);
+#if SB_API_VERSION >= 4
ShellMediaPlatform::Instance()
->GetVideoFrameProvider()
->ResetGetCurrentSbDecodeTargetFunction();
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
SbPlayerDestroy(player_);
}
@@ -131,7 +133,7 @@
video_info.frame_width = frame_width_;
video_info.frame_height = frame_height_;
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
SbMediaColorMetadata sb_media_color_metadata =
MediaToSbMediaColorMetadata(video_config_.webm_color_metadata());
video_info.color_metadata = &sb_media_color_metadata;
@@ -139,27 +141,43 @@
if (is_encrypted) {
FillDrmSampleInfo(buffer, &drm_info, &subsample_mapping);
}
+#if SB_API_VERSION >= 4
+ const void* sample_buffers[] = {buffer->GetData()};
+ int sample_buffer_sizes[] = {buffer->GetDataSize()};
+ SbPlayerWriteSample(player_, DemuxerStreamTypeToSbMediaType(type),
+ sample_buffers, sample_buffer_sizes, 1,
+ TimeDeltaToSbMediaTime(buffer->GetTimestamp()),
+ type == DemuxerStream::VIDEO ? &video_info : NULL,
+ drm_info.subsample_count > 0 ? &drm_info : NULL);
+#else // SB_API_VERSION >= 4
SbPlayerWriteSample(player_, DemuxerStreamTypeToSbMediaType(type),
buffer->GetData(), buffer->GetDataSize(),
TimeDeltaToSbMediaTime(buffer->GetTimestamp()),
type == DemuxerStream::VIDEO ? &video_info : NULL,
drm_info.subsample_count > 0 ? &drm_info : NULL);
+#endif // SB_API_VERSION >= 4
}
void StarboardPlayer::SetBounds(const gfx::Rect& rect) {
DCHECK(SbPlayerIsValid(player_));
+#if SB_API_VERSION >= 4
+ const int kZIndex = 0;
+ SbPlayerSetBounds(player_, kZIndex, rect.x(), rect.y(), rect.width(),
+ rect.height());
+#else // SB_API_VERSION >= 4
SbPlayerSetBounds(player_, rect.x(), rect.y(), rect.width(), rect.height());
+#endif // SB_API_VERSION >= 4
}
void StarboardPlayer::PrepareForSeek() {
DCHECK(message_loop_->BelongsToCurrentThread());
++ticket_;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
SbPlayerSetPause(player_, true);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, 0.f);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
seek_pending_ = true;
}
@@ -185,11 +203,11 @@
++ticket_;
SbPlayerSeek(player_, TimeDeltaToSbMediaTime(time), ticket_);
seek_pending_ = false;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
}
void StarboardPlayer::SetVolume(float volume) {
@@ -211,11 +229,11 @@
DCHECK(SbPlayerIsValid(player_));
playback_rate_ = playback_rate;
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
SbPlayerSetPause(player_, playback_rate == 0.0);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, playback_rate);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
}
void StarboardPlayer::GetInfo(uint32* video_frames_decoded,
@@ -265,11 +283,11 @@
DCHECK(SbPlayerIsValid(player_));
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
SbPlayerSetPause(player_, true);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, 0.0);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
base::AutoLock auto_lock(lock_);
@@ -306,6 +324,25 @@
state_ = kResuming;
}
+namespace {
+#if SB_API_VERSION >= 4
+ShellVideoFrameProvider::OutputMode ToVideoFrameProviderOutputMode(
+ SbPlayerOutputMode output_mode) {
+ switch (output_mode) {
+ case kSbPlayerOutputModeDecodeToTexture:
+ return ShellVideoFrameProvider::kOutputModeDecodeToTexture;
+ case kSbPlayerOutputModePunchOut:
+ return ShellVideoFrameProvider::kOutputModePunchOut;
+ case kSbPlayerOutputModeInvalid:
+ return ShellVideoFrameProvider::kOutputModeInvalid;
+ }
+
+ NOTREACHED();
+ return ShellVideoFrameProvider::kOutputModeInvalid;
+}
+#endif // #if SB_API_VERSION >= 4
+} // namespace
+
void StarboardPlayer::CreatePlayer() {
DCHECK(message_loop_->BelongsToCurrentThread());
@@ -331,25 +368,25 @@
SbMediaVideoCodec video_codec =
MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
player_ = SbPlayerCreate(
window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_,
&audio_header, &StarboardPlayer::DeallocateSampleCB,
&StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, this
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
,
- output_mode_
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+ output_mode_,
+ ShellMediaPlatform::Instance()->GetSbDecodeTargetGraphicsContextProvider()
+#elif SB_API_VERSION >= 3
,
ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider() // provider
#endif // SB_API_VERSION >= 3
- );
+ );
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
// If the player is setup to decode to texture, then provide Cobalt with
// a method of querying that texture.
@@ -359,19 +396,23 @@
base::Bind(&StarboardPlayer::GetCurrentSbDecodeTarget,
base::Unretained(this)));
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
+ ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+ ToVideoFrameProviderOutputMode(output_mode_));
+#else // SB_API_VERSION >= 4
+ ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
+ ShellVideoFrameProvider::kOutputModePunchOut);
+#endif // SB_API_VERSION >= 4
set_bounds_helper_->SetPlayer(this);
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget StarboardPlayer::GetCurrentSbDecodeTarget() {
return SbPlayerGetCurrentFrame(player_);
}
SbPlayerOutputMode StarboardPlayer::GetSbPlayerOutputMode() {
return output_mode_;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
void StarboardPlayer::ClearDecoderBufferCache() {
DCHECK(message_loop_->BelongsToCurrentThread());
@@ -446,11 +487,11 @@
}
SbPlayerSeek(player_, TimeDeltaToSbMediaTime(preroll_timestamp_), ticket_);
SetVolume(volume_);
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
return;
}
host_->OnPlayerStatus(state);
@@ -505,7 +546,7 @@
helper->weak_this_, sample_buffer));
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// static
SbPlayerOutputMode StarboardPlayer::ComputeSbPlayerOutputMode(
SbMediaVideoCodec codec,
@@ -520,7 +561,7 @@
output_mode = kSbPlayerOutputModePunchOut;
}
if ((prefer_decode_to_texture || output_mode == kSbPlayerOutputModeInvalid) &&
- SbPlayerOutputModeSupported(kSbPlayerOutputModePunchOut, codec,
+ SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture, codec,
drm_system)) {
output_mode = kSbPlayerOutputModeDecodeToTexture;
}
@@ -528,6 +569,6 @@
return output_mode;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
} // namespace media
diff --git a/src/media/base/starboard_player.h b/src/media/base/starboard_player.h
index 5d9ef46..521065a 100644
--- a/src/media/base/starboard_player.h
+++ b/src/media/base/starboard_player.h
@@ -75,10 +75,10 @@
void Suspend();
void Resume();
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget GetCurrentSbDecodeTarget();
SbPlayerOutputMode GetSbPlayerOutputMode();
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
private:
enum State {
@@ -119,14 +119,14 @@
void* context,
const void* sample_buffer);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// Returns the output mode that should be used for a video with the given
// specifications.
static SbPlayerOutputMode ComputeSbPlayerOutputMode(
SbMediaVideoCodec codec,
SbDrmSystem drm_system,
bool prefer_decode_to_texture);
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
// The following variables are initialized in the ctor and never changed.
const scoped_refptr<base::MessageLoopProxy> message_loop_;
@@ -158,10 +158,10 @@
uint32 cached_video_frames_dropped_;
base::TimeDelta preroll_timestamp_;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// Keep track of the output mode we are supposed to output to.
SbPlayerOutputMode output_mode_;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
};
} // namespace media
diff --git a/src/media/base/starboard_utils.cc b/src/media/base/starboard_utils.cc
index 4f60ec9..c66d3a6 100644
--- a/src/media/base/starboard_utils.cc
+++ b/src/media/base/starboard_utils.cc
@@ -125,7 +125,7 @@
}
}
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
// Ensure that the enums in starboard/media.h match enums in gfx::ColorSpace.
#define ENUM_EQ(a, b) \
COMPILE_ASSERT(static_cast<int>(a) == static_cast<int>(b), mismatching_enums)
@@ -276,6 +276,6 @@
return sb_media_color_metadata;
}
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif // SB_API_VERSION >= 4
} // namespace media
diff --git a/src/media/base/starboard_utils.h b/src/media/base/starboard_utils.h
index ef03e27..e5112d1 100644
--- a/src/media/base/starboard_utils.h
+++ b/src/media/base/starboard_utils.h
@@ -37,7 +37,7 @@
SbDrmSampleInfo* drm_info,
SbDrmSubSampleMapping* subsample_mapping);
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
SbMediaColorMetadata MediaToSbMediaColorMetadata(
const WebMColorMetadata& webm_color_metadata);
#endif
diff --git a/src/media/crypto/starboard_decryptor.cc b/src/media/crypto/starboard_decryptor.cc
index 48ee249..9ee01d0 100644
--- a/src/media/crypto/starboard_decryptor.cc
+++ b/src/media/crypto/starboard_decryptor.cc
@@ -53,8 +53,12 @@
key_system_ = key_system;
}
DCHECK_EQ(key_system_, key_system);
- SbDrmGenerateSessionUpdateRequest(drm_system_, type.c_str(), init_data,
- init_data_length);
+ SbDrmGenerateSessionUpdateRequest(drm_system_,
+#if SB_API_VERSION >= 4
+ 0, // No need to distinguish between
+ // callbacks in EME 0.1b implementation.
+#endif // SB_API_VERSION >= 4
+ type.c_str(), init_data, init_data_length);
return true;
}
@@ -156,6 +160,9 @@
// static
void StarboardDecryptor::KeyRequestFunc(SbDrmSystem drm_system,
void* context,
+#if SB_API_VERSION >= 4
+ int /*ticket*/,
+#endif // SB_API_VERSION >= 4
const void* session_id,
int session_id_size,
const void* content,
diff --git a/src/media/crypto/starboard_decryptor.h b/src/media/crypto/starboard_decryptor.h
index c594013..a2ffc17 100644
--- a/src/media/crypto/starboard_decryptor.h
+++ b/src/media/crypto/starboard_decryptor.h
@@ -86,6 +86,9 @@
static void KeyRequestFunc(SbDrmSystem drm_system,
void* context,
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
const void* session_id,
int session_id_size,
const void* content,
diff --git a/src/media/player/web_media_player_impl.cc b/src/media/player/web_media_player_impl.cc
index feb83a9..e1690a9 100644
--- a/src/media/player/web_media_player_impl.cc
+++ b/src/media/player/web_media_player_impl.cc
@@ -190,9 +190,6 @@
video_frame_provider_->UnregisterMediaTimeAndSeekingStateCB(
media_time_and_seeking_state_cb_);
media_time_and_seeking_state_cb_.Reset();
-
- video_frame_provider_->SetOutputMode(
- ShellVideoFrameProvider::kOutputModeInvalid);
}
#if defined(__LB_ANDROID__)
@@ -968,10 +965,6 @@
switch (buffering_state) {
case Pipeline::kHaveMetadata:
- video_frame_provider_->SetOutputMode(
- (pipeline_->IsPunchOutMode() ?
- ShellVideoFrameProvider::kOutputModePunchOut :
- ShellVideoFrameProvider::kOutputModeDecodeToTexture));
SetReadyState(WebMediaPlayer::kReadyStateHaveMetadata);
break;
case Pipeline::kPrerollCompleted:
@@ -982,6 +975,7 @@
void WebMediaPlayerImpl::OnDemuxerOpened() {
DCHECK_EQ(main_loop_, MessageLoop::current());
+
GetClient()->SourceOpened();
}
@@ -1089,14 +1083,14 @@
base::Unretained(decryptor_.get()));
}
+ pipeline_->SetDecodeToTextureOutputMode(client_->PreferDecodeToTexture());
pipeline_->Start(
filter_collection_.Pass(), set_decryptor_ready_cb,
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded),
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError),
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek),
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingState),
- BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged),
- client_->PreferDecodeToTexture());
+ BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged));
}
void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) {
diff --git a/src/nb/allocator.h b/src/nb/allocator.h
index 75ac4ed..07ee8e0 100644
--- a/src/nb/allocator.h
+++ b/src/nb/allocator.h
@@ -61,6 +61,11 @@
// Frees memory previously allocated via any call to Allocate().
virtual void Free(void* memory) = 0;
+ // Frees memory with a size. By default it will delegate to Free().
+ virtual void FreeWithSize(void* memory, std::size_t /*size*/) {
+ Free(memory);
+ }
+
// Returns the allocator's total capacity for allocations. It will always
// be true that GetSize() <= GetCapacity(), though it is possible for
// capacity to grow and change over time. It is also possible that due to,
diff --git a/src/nb/allocator_decorator.cc b/src/nb/allocator_decorator.cc
deleted file mode 100644
index 18578b1..0000000
--- a/src/nb/allocator_decorator.cc
+++ /dev/null
@@ -1,97 +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 "nb/allocator_decorator.h"
-
-#include "starboard/mutex.h"
-
-namespace nb {
-
-class AllocatorDecorator::Lock {
- public:
- void Acquire() { mutex_.Acquire(); }
- void Release() { mutex_.Release(); }
-
- private:
- starboard::Mutex mutex_;
-};
-
-class AllocatorDecorator::ScopedLock {
- public:
- ScopedLock(Lock* lock) : lock_(lock) {
- if (lock_) {
- lock_->Acquire();
- }
- }
- ~ScopedLock() {
- if (lock_) {
- lock_->Release();
- }
- }
-
- private:
- Lock* lock_;
-};
-
-AllocatorDecorator::AllocatorDecorator(scoped_ptr<Allocator> impl,
- bool thread_safe)
- : lock_(NULL), impl_(impl.Pass()) {
- if (thread_safe) {
- lock_ = new Lock;
- }
-}
-
-AllocatorDecorator::~AllocatorDecorator() {
- delete lock_;
-}
-
-void* AllocatorDecorator::Allocate(std::size_t size) {
- ScopedLock scoped_lock(lock_);
- return impl_->Allocate(size);
-}
-
-void* AllocatorDecorator::Allocate(std::size_t size, std::size_t alignment) {
- ScopedLock scoped_lock(lock_);
- return impl_->Allocate(size, alignment);
-}
-
-void* AllocatorDecorator::AllocateForAlignment(std::size_t* size,
- std::size_t alignment) {
- ScopedLock scoped_lock(lock_);
- return impl_->AllocateForAlignment(size, alignment);
-}
-
-void AllocatorDecorator::Free(void* memory) {
- ScopedLock scoped_lock(lock_);
- impl_->Free(memory);
-}
-
-std::size_t AllocatorDecorator::GetCapacity() const {
- ScopedLock scoped_lock(lock_);
- return impl_->GetCapacity();
-}
-
-std::size_t AllocatorDecorator::GetAllocated() const {
- ScopedLock scoped_lock(lock_);
- return impl_->GetAllocated();
-}
-
-void AllocatorDecorator::PrintAllocations() const {
- ScopedLock scoped_lock(lock_);
- impl_->PrintAllocations();
-}
-
-} // namespace nb
diff --git a/src/nb/allocator_decorator.h b/src/nb/allocator_decorator.h
deleted file mode 100644
index 78a8407..0000000
--- a/src/nb/allocator_decorator.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef NB_ALLOCATOR_DECORATOR_H_
-#define NB_ALLOCATOR_DECORATOR_H_
-
-#include "nb/allocator.h"
-#include "nb/scoped_ptr.h"
-
-namespace nb {
-
-// Class that can be configured with an allocator behavior and threading
-// behavior.
-// Dispatches Allocate and Free calls to the internal implementation.
-// Threadsafe or not, depending on the LockTraits.
-// In non-release builds, provides a cval tracking heap size.
-class AllocatorDecorator : public Allocator {
- public:
- AllocatorDecorator(scoped_ptr<Allocator> impl, bool thread_safe);
- ~AllocatorDecorator();
-
- void* Allocate(std::size_t size);
- void* Allocate(std::size_t size, std::size_t alignment);
- void* AllocateForAlignment(std::size_t* size, std::size_t alignment);
- void Free(void* memory);
- std::size_t GetCapacity() const;
- std::size_t GetAllocated() const;
- void PrintAllocations() const;
-
- private:
- class Lock;
- class ScopedLock;
-
- Lock* lock_;
- scoped_ptr<Allocator> impl_;
-};
-
-} // namespace nb
-
-#endif // NB_ALLOCATOR_DECORATOR_H_
diff --git a/src/nb/analytics/memory_tracker_helpers.h b/src/nb/analytics/memory_tracker_helpers.h
index 4d601d6..653a47f 100644
--- a/src/nb/analytics/memory_tracker_helpers.h
+++ b/src/nb/analytics/memory_tracker_helpers.h
@@ -23,16 +23,34 @@
#include "nb/analytics/memory_tracker.h"
#include "nb/atomic.h"
#include "nb/simple_thread.h"
+#include "nb/std_allocator.h"
#include "nb/thread_local_boolean.h"
#include "nb/thread_local_pointer.h"
#include "starboard/mutex.h"
#include "starboard/thread.h"
#include "starboard/types.h"
#include "starboard/log.h"
+#include "starboard/memory.h"
namespace nb {
namespace analytics {
+// This is used to build the std allocators for the internal data structures so
+// that they don't attempt to report memory allocations, while reporting
+// memory allocations.
+class NoReportAllocator {
+ public:
+ NoReportAllocator() {}
+ NoReportAllocator(const NoReportAllocator&) {}
+ static void* Allocate(size_t n) {
+ return SbMemoryAllocateNoReport(n);
+ }
+ // Second argument can be used for accounting, but is otherwise optional.
+ static void Deallocate(void* ptr, size_t /* not used*/) {
+ SbMemoryDeallocateNoReport(ptr);
+ }
+};
+
class AllocationGroup;
class AllocationRecord;
@@ -73,7 +91,14 @@
void GetAll(std::vector<const AllocationGroup*>* output) const;
private:
- typedef std::map<std::string, AllocationGroup*> Map;
+ // This map bypasses memory reporting of internal memory structures.
+ typedef std::map<
+ std::string,
+ AllocationGroup*,
+ std::less<std::string>,
+ nb::StdAllocator<
+ std::pair<const std::string, AllocationGroup*>,
+ NoReportAllocator> > Map;
Map group_map_;
AllocationGroup* unaccounted_group_;
mutable starboard::Mutex mutex_;
@@ -131,11 +156,19 @@
void Clear();
private:
- SB_DISALLOW_COPY_AND_ASSIGN(AtomicAllocationMap);
- typedef std::map<const void*, AllocationRecord> PointerMap;
+ // This map bypasses memory reporting of internal memory structures.
+ typedef std::map<
+ const void*,
+ AllocationRecord,
+ std::less<const void*>, // required, when specifying allocator.
+ nb::StdAllocator<
+ std::pair<const void*, AllocationRecord>,
+ NoReportAllocator> > PointerMap;
PointerMap pointer_map_;
mutable starboard::Mutex mutex_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(AtomicAllocationMap);
};
// A per-pointer map of allocations to AllocRecords. This is a hybrid data
diff --git a/src/nb/analytics/memory_tracker_impl.h b/src/nb/analytics/memory_tracker_impl.h
index 002fa28..b45b1a3 100644
--- a/src/nb/analytics/memory_tracker_impl.h
+++ b/src/nb/analytics/memory_tracker_impl.h
@@ -71,6 +71,7 @@
void RemoveGlobalTrackingHooks() SB_OVERRIDE {
SbMemorySetReporter(NULL);
NbSetMemoryScopeReporter(NULL);
+ global_hooks_installed_ = false;
}
bool AddMemoryTracking(const void* memory, size_t size) SB_OVERRIDE;
diff --git a/src/nb/analytics/memory_tracker_impl_test.cc b/src/nb/analytics/memory_tracker_impl_test.cc
index 26e3500..11f4f01 100644
--- a/src/nb/analytics/memory_tracker_impl_test.cc
+++ b/src/nb/analytics/memory_tracker_impl_test.cc
@@ -46,8 +46,9 @@
void* operator new(std::size_t size, CustomObject ignored) {
SB_UNREFERENCED_PARAMETER(ignored);
- // Volitile prevent optimization and elmination of operator new.
- volatile void* ptr = SbMemoryAllocate(size);
+ // Volatile prevents optimization and elimination of operator new by the
+ // optimizing compiler.
+ volatile void* ptr = ::operator new(size);
return const_cast<void*>(ptr);
}
@@ -254,18 +255,27 @@
static void SetUpTestCase() {
if (!s_memory_tracker_) {
s_memory_tracker_ = new MemoryTrackerImpl;
- s_memory_tracker_enabled_ =
- s_memory_tracker_->InstallGlobalTrackingHooks();
}
+ // There are obligatory background threads for nb_test suite. This filter
+ // makes sure that they don't intercept this test.
+ s_memory_tracker_->SetThreadFilter(SbThreadGetId());
+ s_memory_tracker_enabled_ =
+ s_memory_tracker_->InstallGlobalTrackingHooks();
}
- static void TearDownTestCase() { SbMemorySetReporter(NULL); }
+ static void TearDownTestCase() {
+ s_memory_tracker_->RemoveGlobalTrackingHooks();
+ // Give time for threads to sync. We don't use locks on the reporter
+ // for performance reasons.
+ SbThreadSleep(250 * kSbTimeMillisecond);
+ }
virtual void SetUp() {
memory_tracker()->Clear();
- memory_tracker()->SetThreadFilter(SbThreadGetId());
}
- virtual void TearDown() { memory_tracker()->Clear(); }
+ virtual void TearDown() {
+ memory_tracker()->Clear();
+ }
static bool s_memory_tracker_enabled_;
};
bool MemoryTrackerImplTest::s_memory_tracker_enabled_ = false;
@@ -447,6 +457,7 @@
EXPECT_EQ_NO_TRACKING(memory_scope.cached_handle_,
static_cast<void*>(group));
+ NbPopMemoryScope();
}
// Tests the expectation that the macro TRACK_MEMORY_SCOPE will capture the
diff --git a/src/nb/analytics/memory_tracker_test.cc b/src/nb/analytics/memory_tracker_test.cc
index 92cb8b3..8005802 100644
--- a/src/nb/analytics/memory_tracker_test.cc
+++ b/src/nb/analytics/memory_tracker_test.cc
@@ -102,6 +102,9 @@
}
static void TearDownTestCase() {
s_memory_tracker_->RemoveGlobalTrackingHooks();
+ // Give time for all threads to sync up to the fact that the memory tracker
+ // has been removed.
+ SbThreadSleep(50 * kSbTimeMillisecond);
}
static bool s_memory_tracker_enabled_;
};
diff --git a/src/nb/lexical_cast.h b/src/nb/lexical_cast.h
index 8b576c1..6ded009 100644
--- a/src/nb/lexical_cast.h
+++ b/src/nb/lexical_cast.h
@@ -46,7 +46,7 @@
// EXPECT_FALSE(ok);
// EXPECT_EQ(0, value);
template <typename T>
-T lexical_cast(const char* s, bool* cast_ok = NULL) {
+inline T lexical_cast(const char* s, bool* cast_ok = NULL) {
if (!s) { // Handle NULL case of input string.
if (cast_ok) {
*cast_ok = false;
@@ -66,12 +66,17 @@
return value;
}
+template <typename T>
+inline T lexical_cast(const std::string& s, bool* cast_ok = NULL) {
+ return lexical_cast<T>(s.c_str(), cast_ok);
+}
+
// int8_t and uint8_t will normally be interpreted as a char, which will
// result in only the first character being parsed. This is obviously not
// what we want. Therefore we provide specializations for lexical_cast for
// these types.
template <>
-int8_t lexical_cast<int8_t>(const char* s, bool* cast_ok) {
+inline int8_t lexical_cast<int8_t>(const char* s, bool* cast_ok) {
int16_t value_i16 = lexical_cast<int16_t>(s, cast_ok);
if (value_i16 < std::numeric_limits<int8_t>::min() ||
value_i16 > std::numeric_limits<int8_t>::max()) {
@@ -84,7 +89,7 @@
}
template <typename SmallInt>
-SmallInt NarrowingLexicalCast(const char* s, bool* cast_ok) {
+inline SmallInt NarrowingLexicalCast(const char* s, bool* cast_ok) {
int64_t value = lexical_cast<int64_t>(s, cast_ok);
if ((value > std::numeric_limits<SmallInt>::max()) ||
(value < std::numeric_limits<SmallInt>::min())) {
@@ -97,23 +102,23 @@
}
template <>
-uint8_t lexical_cast<uint8_t>(const char* s, bool* cast_ok) {
+inline uint8_t lexical_cast<uint8_t>(const char* s, bool* cast_ok) {
return NarrowingLexicalCast<uint8_t>(s, cast_ok);
}
template <>
-uint16_t lexical_cast<uint16_t>(const char* s, bool* cast_ok) {
+inline uint16_t lexical_cast<uint16_t>(const char* s, bool* cast_ok) {
return NarrowingLexicalCast<uint16_t>(s, cast_ok);
}
template <>
-uint32_t lexical_cast<uint32_t>(const char* s, bool* cast_ok) {
+inline uint32_t lexical_cast<uint32_t>(const char* s, bool* cast_ok) {
return NarrowingLexicalCast<uint32_t>(s, cast_ok);
}
// uint64_t types will have a max value of int64_t. But this is acceptable.
template <>
-uint64_t lexical_cast<uint64_t>(const char* s, bool* cast_ok) {
+inline uint64_t lexical_cast<uint64_t>(const char* s, bool* cast_ok) {
int64_t val_i64 = lexical_cast<int64_t>(s, cast_ok);
// Handle failure condition for negative values.
if (val_i64 < 0) {
@@ -127,4 +132,4 @@
} // namespace nb
-#endif // NB_LEXICAL_CAST_H_
\ No newline at end of file
+#endif // NB_LEXICAL_CAST_H_
diff --git a/src/nb/lexical_cast_test.cc b/src/nb/lexical_cast_test.cc
index fb7e12d..88b56d5 100644
--- a/src/nb/lexical_cast_test.cc
+++ b/src/nb/lexical_cast_test.cc
@@ -189,5 +189,11 @@
EXPECT_EQ(100000, value); //
}
+TEST(lexical_cast, StdString) {
+ bool cast_ok = false;
+ uint32_t value = lexical_cast<uint32_t>(std::string("100000"), &cast_ok);
+ EXPECT_EQ(100000, value);
+}
+
} // namespace
} // namespace nb
diff --git a/src/nb/memory_pool.cc b/src/nb/memory_pool.cc
index 4ae9a7e..6cdef60 100644
--- a/src/nb/memory_pool.cc
+++ b/src/nb/memory_pool.cc
@@ -22,15 +22,10 @@
MemoryPool::MemoryPool(void* buffer,
std::size_t size,
- bool thread_safe,
bool verify_full_capacity,
std::size_t small_allocation_threshold)
: no_free_allocator_(buffer, size),
- reuse_allocator_(
- scoped_ptr<Allocator>(new ReuseAllocator(&no_free_allocator_,
- size,
- small_allocation_threshold)),
- thread_safe) {
+ reuse_allocator_(&no_free_allocator_, size, small_allocation_threshold) {
SB_DCHECK(buffer);
SB_DCHECK(size != 0U);
diff --git a/src/nb/memory_pool.h b/src/nb/memory_pool.h
index b81e893..8dff09e 100644
--- a/src/nb/memory_pool.h
+++ b/src/nb/memory_pool.h
@@ -18,7 +18,6 @@
#define NB_MEMORY_POOL_H_
#include "nb/allocator.h"
-#include "nb/allocator_decorator.h"
#include "nb/fixed_no_free_allocator.h"
#include "nb/reuse_allocator.h"
@@ -31,7 +30,6 @@
public:
MemoryPool(void* buffer,
std::size_t size,
- bool thread_safe,
bool verify_full_capacity = false,
std::size_t small_allocation_threshold = 0);
@@ -56,7 +54,7 @@
// A reuse allocator that will be setup to fallback on the no free allocator
// to expand its pool as memory is required for which there is no re-usable
// space already.
- AllocatorDecorator reuse_allocator_;
+ ReuseAllocator reuse_allocator_;
};
} // namespace nb
diff --git a/src/nb/memory_scope.h b/src/nb/memory_scope.h
index 72e1ddc..5fd0034 100644
--- a/src/nb/memory_scope.h
+++ b/src/nb/memory_scope.h
@@ -58,13 +58,15 @@
static NbMemoryScopeInfo memory_scope_handle_##LineNum = \
{ NULL, Str, FileStr, LineNum, FuncStr, true }; \
NbPushMemoryScope(&memory_scope_handle_##LineNum); \
- NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;
+ NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum; \
+ (void)pop_on_scope_end_##LineNum;
#define TRACK_MEMORY_STATIC_NOT_CACHED_IMPL_2(Str, FileStr, LineNum, FuncStr) \
NbMemoryScopeInfo memory_scope_handle_##LineNum = { \
NULL, Str, FileStr, LineNum, FuncStr, false}; \
NbPushMemoryScope(&memory_scope_handle_##LineNum); \
- NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;
+ NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum; \
+ (void)pop_on_scope_end_##LineNum;
#ifdef __cplusplus
extern "C" {
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index 4c4b6c5..db80e43 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -24,8 +24,6 @@
['OS=="starboard" or (OS=="lb_shell" and target_arch == "ps3")', {
'sources': [
'allocator.h',
- 'allocator_decorator.cc',
- 'allocator_decorator.h',
'analytics/memory_tracker.cc',
'analytics/memory_tracker.h',
'analytics/memory_tracker_impl.cc',
diff --git a/src/nb/reuse_allocator_benchmark.cc b/src/nb/reuse_allocator_benchmark.cc
index ac53c08..299b7f7 100644
--- a/src/nb/reuse_allocator_benchmark.cc
+++ b/src/nb/reuse_allocator_benchmark.cc
@@ -20,7 +20,6 @@
#include <vector>
#include "nb/allocator.h"
-#include "nb/allocator_decorator.h"
#include "nb/fixed_no_free_allocator.h"
#include "nb/reuse_allocator.h"
#include "starboard/client_porting/wrap_main/wrap_main.h"
diff --git a/src/nb/std_allocator.h b/src/nb/std_allocator.h
index ce3a818..318a1b9 100644
--- a/src/nb/std_allocator.h
+++ b/src/nb/std_allocator.h
@@ -19,6 +19,8 @@
#include <memory>
+#include "nb/allocator.h"
+#include "starboard/configuration.h"
#include "starboard/types.h"
namespace nb {
@@ -60,10 +62,11 @@
// Constructor used for rebinding
template <typename U, typename V>
- StdAllocator(const StdAllocator<U, V>& x) {}
+ StdAllocator(const StdAllocator<U, V>&) {}
pointer allocate(size_type n,
std::allocator<void>::const_pointer hint = NULL) {
+ SB_UNREFERENCED_PARAMETER(hint);
void* ptr = AllocatorT::Allocate(n * sizeof(value_type));
return static_cast<pointer>(ptr);
}
@@ -77,6 +80,91 @@
};
};
+// A standard container compatible allocator that delegates allocations to a
+// custom allocator via a shared pointer. This differs from StdAllocator since
+// StdAllocator binds to static functions. This important difference allows
+// StdDynamicAllocator to keep memory accounting on a per-instance basis, but
+// otherwise is a tad slower, harder to instantiate and produces less readable
+// code.
+//
+// When in doubt, use StdAllocator over StdDynamicAllocator.
+//
+// Passed in nb::Allocator:
+// Even though nb::Allocator has many functions for alignment, only the two
+// are used within StdDynamicAllocator:
+// void* nb::Allocator::Allocate() -and-
+// void nb::FreeWithSize(void* ptr, size_t optional_size)
+//
+// Example of Allocator Definition:
+// class MyAllocator : public SimpleAllocator {
+// public:
+// void* Allocate(size_t size) SB_OVERRIDE {
+// return SbMemoryAllocate(size);
+// }
+//
+// // Second argument can be used for accounting, but is otherwise optional.
+// void FreeWithSize(void* ptr, size_t optional_size) SB_OVERRIDE {
+// SbMemoryDeallocate(ptr);
+// }
+//
+// // ... other functions
+// };
+//
+// Example of std::vector<int>:
+// typedef StdDynamicAllocator<int> IntAllocator;
+// typedef std::vector<int, IntAllocator> IntVector;
+//
+// MyAllocator my_allocator;
+// // Note that IntVector is not default constructible!
+// IntVector int_vector(IntAllocator(&my_allocator));
+//
+// Example of std::map<int, int>:
+// // Note that maps require std::less() instance to be passed in whenever
+// // a custom allocator is passed in.
+// typedef std::map<int, int, std::less<int>, MyAllocator> IntMap;
+// IntMap int_map(std::less<int>(), /* std::less<int> required pre-C++11 */
+// IntAllocator(&my_allocator));
+template <typename T>
+class StdDynamicAllocator : public std::allocator<T> {
+ public:
+ typedef typename std::allocator<T>::pointer pointer;
+ typedef typename std::allocator<T>::const_pointer const_pointer;
+ typedef typename std::allocator<T>::reference reference;
+ typedef typename std::allocator<T>::const_reference const_reference;
+ typedef typename std::allocator<T>::size_type size_type;
+ typedef typename std::allocator<T>::value_type value_type;
+ typedef typename std::allocator<T>::difference_type difference_type;
+
+ explicit StdDynamicAllocator(Allocator* allocator) : allocator_(allocator) {}
+ StdDynamicAllocator(StdDynamicAllocator& std_allocator)
+ : allocator_(std_allocator.allocator_) {}
+
+ // Constructor used for rebinding
+ template <typename U>
+ StdDynamicAllocator(const StdDynamicAllocator<U>& x)
+ : allocator_(x.allocator()) {}
+
+ pointer allocate(size_type n,
+ std::allocator<void>::const_pointer hint = NULL) {
+ SB_UNREFERENCED_PARAMETER(hint);
+ void* ptr = allocator_->Allocate(n * sizeof(value_type));
+ return static_cast<pointer>(ptr);
+ }
+
+ void deallocate(pointer ptr, size_type n) {
+ allocator_->FreeWithSize(ptr, n * sizeof(value_type));
+ }
+ template <typename U>
+ struct rebind {
+ typedef StdDynamicAllocator<U> other;
+ };
+
+ Allocator* allocator() const { return allocator_; }
+
+ private:
+ Allocator* allocator_;
+};
+
} // namespace nb
#endif // NB_STD_ALLOCATOR_H_
diff --git a/src/nb/std_allocator_test.cc b/src/nb/std_allocator_test.cc
index 06d634e..a866e2b 100644
--- a/src/nb/std_allocator_test.cc
+++ b/src/nb/std_allocator_test.cc
@@ -102,5 +102,179 @@
CountingAllocator::Reset();
}
+class CountingDynamicAllocator : public nb::Allocator {
+ public:
+ CountingDynamicAllocator() : bytes_allocated_(0), num_allocations_(0) {}
+ void* Allocate(size_t size) SB_OVERRIDE {
+ bytes_allocated_ += size;
+ ++num_allocations_;
+ return SbMemoryAllocate(size);
+ }
+
+ virtual void* Allocate(std::size_t size, std::size_t alignment) SB_OVERRIDE {
+ EXPECT_TRUE(false) << "Unexpected that aligned version is called.";
+ return Allocate(size);
+ }
+
+ virtual void Free(void* memory) SB_OVERRIDE {
+ FreeWithSize(memory, 0);
+ ASSERT_TRUE(false) << "Unexpected that free without size "
+ "version is called.";
+ }
+
+ void FreeWithSize(void* ptr, size_t optional_size) SB_OVERRIDE {
+ SbMemoryDeallocate(ptr);
+
+ bytes_allocated_ -= optional_size;
+ --num_allocations_;
+ }
+
+ std::size_t GetCapacity() const SB_OVERRIDE {
+ EXPECT_TRUE(false) << "Unexpected that GetCapacity().";
+ return 0;
+ }
+
+ std::size_t GetAllocated() const SB_OVERRIDE {
+ return static_cast<size_t>(bytes_allocated_);
+ }
+
+ void PrintAllocations() const SB_OVERRIDE {}
+
+ int64_t bytes_allocated_;
+ int64_t num_allocations_;
+};
+
+// Test the expectation that vector will go through the supplied allocator.
+TEST(StdDynamicAllocator, vector) {
+ typedef StdDynamicAllocator<int> IntAllocator;
+ typedef std::vector<int, IntAllocator> IntVector;
+
+ CountingDynamicAllocator counting_allocator;
+
+ EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+ EXPECT_EQ(0, counting_allocator.num_allocations_);
+
+ IntVector* int_vector = new IntVector(IntAllocator(&counting_allocator));
+ int_vector->push_back(0);
+
+ for (int i = 0; i < 10; ++i) {
+ int_vector->push_back(i);
+ }
+
+ // We aren't sure how much allocation is here, but we know it's more than 0.
+ EXPECT_LT(0, counting_allocator.bytes_allocated_);
+ EXPECT_LT(0, counting_allocator.num_allocations_);
+
+ delete int_vector;
+
+ // Expect that all allocations are destroyed now.
+ EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+ EXPECT_EQ(0, counting_allocator.num_allocations_);
+}
+
+// Test the expectation that vector will go through the supplied allocator.
+TEST(StdDynamicAllocator, map) {
+ typedef StdDynamicAllocator<int> IntAllocator;
+ typedef std::map<int, int, std::less<int>, IntAllocator> IntMap;
+
+ CountingDynamicAllocator counting_allocator;
+
+ EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+ EXPECT_EQ(0, counting_allocator.num_allocations_);
+
+ IntMap* int_map = new IntMap(std::less<int>(), // required.
+ IntAllocator(&counting_allocator));
+ for (int i = 0; i < 10; ++i) {
+ (*int_map)[i] = i;
+ }
+
+ // We aren't sure how much allocation is here, but we know it's more than 0.
+ EXPECT_LT(0, counting_allocator.bytes_allocated_);
+ EXPECT_LT(0, counting_allocator.num_allocations_);
+
+ delete int_map;
+
+ // Expect that all allocations are destroyed now.
+ EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+ EXPECT_EQ(0, counting_allocator.num_allocations_);
+}
+
+// Test the expectation that a copied vector will go through the allocator from
+// the first vector.
+TEST(StdDynamicAllocator, vector_copy) {
+ typedef StdDynamicAllocator<int> IntAllocator;
+ typedef std::vector<int, IntAllocator> IntVector;
+
+ CountingDynamicAllocator counting_allocator;
+
+ EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+ EXPECT_EQ(0, counting_allocator.num_allocations_);
+
+ IntVector* int_vector = new IntVector(IntAllocator(&counting_allocator));
+ int_vector->push_back(0);
+
+ for (int i = 0; i < 10; ++i) {
+ int_vector->push_back(i);
+ }
+
+ // We aren't sure how much allocation is here, but we know it's more than 0.
+ EXPECT_LT(0, counting_allocator.bytes_allocated_);
+ EXPECT_LT(0, counting_allocator.num_allocations_);
+
+ int64_t saved_bytes_allocated = counting_allocator.bytes_allocated_;
+ int64_t saved_num_allocations = counting_allocator.num_allocations_;
+
+ IntVector* int_vector_copy = new IntVector(*int_vector);
+ for (int i = 0; i < 10; ++i) {
+ int_vector_copy->push_back(i);
+ }
+
+ EXPECT_LT(saved_bytes_allocated, counting_allocator.bytes_allocated_);
+ EXPECT_LT(saved_num_allocations, counting_allocator.num_allocations_);
+
+ delete int_vector_copy;
+ delete int_vector;
+
+ // Expect that all allocations are destroyed now.
+ EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+ EXPECT_EQ(0, counting_allocator.num_allocations_);
+}
+
+// Test the expectation that vector will go through the supplied allocator.
+TEST(StdDynamicAllocator, map_copy) {
+ typedef StdDynamicAllocator<int> IntAllocator;
+ typedef std::map<int, int, std::less<int>, IntAllocator> IntMap;
+
+ CountingDynamicAllocator counting_allocator;
+
+ EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+ EXPECT_EQ(0, counting_allocator.num_allocations_);
+
+ IntMap* int_map = new IntMap(std::less<int>(), // required.
+ IntAllocator(&counting_allocator));
+ for (int i = 0; i < 10; ++i) {
+ (*int_map)[i] = i;
+ }
+
+ int64_t saved_bytes_allocated = counting_allocator.bytes_allocated_;
+ int64_t saved_num_allocations = counting_allocator.num_allocations_;
+
+ IntMap* int_map_copy = new IntMap(*int_map);
+
+ for (int i = 0; i < 10; ++i) {
+ (*int_map_copy)[i] = i;
+ }
+
+ EXPECT_LT(saved_bytes_allocated, counting_allocator.bytes_allocated_);
+ EXPECT_LT(saved_num_allocations, counting_allocator.num_allocations_);
+
+ delete int_map_copy;
+ delete int_map;
+
+ // Expect that all allocations are destroyed now.
+ EXPECT_EQ(0, counting_allocator.bytes_allocated_);
+ EXPECT_EQ(0, counting_allocator.num_allocations_);
+}
+
} // namespace
} // namespace nb
diff --git a/src/nb/thread_local_boolean.h b/src/nb/thread_local_boolean.h
index a70336d..d91a744 100644
--- a/src/nb/thread_local_boolean.h
+++ b/src/nb/thread_local_boolean.h
@@ -49,4 +49,4 @@
} // namespace nb.
-#endif // NB_THREAD_LOCAL_POINTER_H_
\ No newline at end of file
+#endif // NB_THREAD_LOCAL_POINTER_H_
diff --git a/src/nb/thread_local_pointer.h b/src/nb/thread_local_pointer.h
index 5c86208..75fea51 100644
--- a/src/nb/thread_local_pointer.h
+++ b/src/nb/thread_local_pointer.h
@@ -50,4 +50,4 @@
} // namespace nb.
-#endif // NB_THREAD_LOCAL_POINTER_H_
\ No newline at end of file
+#endif // NB_THREAD_LOCAL_POINTER_H_
diff --git a/src/net/base/address_list.cc b/src/net/base/address_list.cc
index 9fb4068..29c9946 100644
--- a/src/net/base/address_list.cc
+++ b/src/net/base/address_list.cc
@@ -5,6 +5,8 @@
#include "net/base/address_list.h"
#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/values.h"
#include "net/base/net_util.h"
@@ -58,6 +60,34 @@
}
#if defined(OS_STARBOARD)
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+
+namespace {
+const char kResolveOnlyIpv6[] = "resolve_only_ipv6";
+const char kResolveOnlyIpv4[] = "resolve_only_ipv4";
+
+struct ResolveFilterFlags {
+ ResolveFilterFlags();
+
+ bool resolve_only_ipv6;
+ bool resolve_only_ipv4;
+};
+
+base::LazyInstance<ResolveFilterFlags>::Leaky g_resolve_filter_flags =
+ LAZY_INSTANCE_INITIALIZER;
+
+ResolveFilterFlags::ResolveFilterFlags() {
+ resolve_only_ipv6 =
+ CommandLine::ForCurrentProcess()->HasSwitch(kResolveOnlyIpv6);
+ resolve_only_ipv4 =
+ CommandLine::ForCurrentProcess()->HasSwitch(kResolveOnlyIpv4);
+ DCHECK(!(resolve_only_ipv6 && resolve_only_ipv4));
+}
+
+} // namespace
+
+#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+
// static
AddressList AddressList::CreateFromSbSocketResolution(
const SbSocketResolution* resolution) {
@@ -65,6 +95,14 @@
AddressList list;
for (int i = 0; i < resolution->address_count; ++i) {
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
+ SbSocketAddressType address_type = resolution->addresses[i].type;
+ ResolveFilterFlags& flags = g_resolve_filter_flags.Get();
+ if ((flags.resolve_only_ipv6 && address_type != kSbSocketAddressTypeIpv6) ||
+ (flags.resolve_only_ipv4 && address_type != kSbSocketAddressTypeIpv4)) {
+ continue;
+ }
+#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
IPEndPoint end_point;
if (end_point.FromSbSocketAddress(&resolution->addresses[i])) {
list.push_back(end_point);
diff --git a/src/net/base/gzip_filter.cc b/src/net/base/gzip_filter.cc
index d4d72c3..bf0fb9b 100644
--- a/src/net/base/gzip_filter.cc
+++ b/src/net/base/gzip_filter.cc
@@ -4,6 +4,8 @@
#include "net/base/gzip_filter.h"
+#include <algorithm>
+
#if defined(USE_SYSTEM_ZLIB)
#include <zlib.h>
#else
diff --git a/src/net/base/gzip_header.cc b/src/net/base/gzip_header.cc
index 23a3dd3..5c4ef75 100644
--- a/src/net/base/gzip_header.cc
+++ b/src/net/base/gzip_header.cc
@@ -4,6 +4,8 @@
#include "net/base/gzip_header.h"
+#include <algorithm>
+
#if defined(USE_SYSTEM_ZLIB)
#include <zlib.h>
#else
diff --git a/src/net/base/net_util.cc b/src/net/base/net_util.cc
index b1473db..bfd3c72 100644
--- a/src/net/base/net_util.cc
+++ b/src/net/base/net_util.cc
@@ -2122,6 +2122,11 @@
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
}
+#if SB_HAS(IPV6)
+const unsigned char kIPv6Localhost[] = {0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1};
+#endif
+
IPAddressNumber ConvertIPv4NumberToIPv6Number(
const IPAddressNumber& ipv4_number) {
DCHECK(ipv4_number.size() == 4);
@@ -2260,6 +2265,13 @@
host == "localhost6.localdomain6")
return true;
+#if SB_HAS(IPV6)
+ IPAddressNumber ipv6_localhost;
+ ipv6_localhost.reserve(16);
+ ipv6_localhost.insert(ipv6_localhost.end(), kIPv6Localhost,
+ kIPv6Localhost + arraysize(kIPv6Localhost));
+#endif
+
IPAddressNumber ip_number;
if (ParseIPLiteralToNumber(host, &ip_number)) {
size_t size = ip_number.size();
@@ -2273,11 +2285,9 @@
return IPNumberMatchesPrefix(ip_number, localhost_prefix, 8);
}
-#if defined(IN6ADDR_ANY_INIT)
+#if SB_HAS(IPV6)
case kIPv6AddressSize: {
- struct in6_addr sin6_addr;
- memcpy(&sin6_addr, &ip_number[0], kIPv6AddressSize);
- return !!IN6_IS_ADDR_LOOPBACK(&sin6_addr);
+ return ip_number == ipv6_localhost;
}
#endif
diff --git a/src/net/base/tcp_listen_socket.cc b/src/net/base/tcp_listen_socket.cc
index 2a955cc..b0db082 100644
--- a/src/net/base/tcp_listen_socket.cc
+++ b/src/net/base/tcp_listen_socket.cc
@@ -66,18 +66,29 @@
SocketDescriptor TCPListenSocket::CreateAndBind(const string& ip, int port) {
#if defined(OS_STARBOARD)
+ base::optional<IPEndPoint> endpoint = ToIPEndPoint(ip, port);
+ if (!endpoint) {
+ return kSbSocketInvalid;
+ }
+ SbSocketAddressType socket_address_type;
+ switch (endpoint->GetFamily()) {
+ case ADDRESS_FAMILY_IPV4:
+ socket_address_type = kSbSocketAddressTypeIpv4;
+ break;
+ case ADDRESS_FAMILY_IPV6:
+ socket_address_type = kSbSocketAddressTypeIpv6;
+ break;
+ default:
+ return kSbSocketInvalid;
+ }
+
SocketDescriptor socket =
- SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
+ SbSocketCreate(socket_address_type, kSbSocketProtocolTcp);
if (SbSocketIsValid(socket)) {
SbSocketSetReuseAddress(socket, true);
- base::optional<IPEndPoint> endpoint = ToIPEndPoint(ip, port);
SbSocketAddress sb_address;
- bool groovy = endpoint ? true : false;
- if (groovy) {
- groovy &= endpoint->ToSbSocketAddress(&sb_address);
- }
-
+ bool groovy = endpoint->ToSbSocketAddress(&sb_address);
if (groovy) {
groovy &= (SbSocketBind(socket, &sb_address) == kSbSocketOk);
}
diff --git a/src/net/dial/dial_http_server.cc b/src/net/dial/dial_http_server.cc
index c4150c7..a1a44c4 100644
--- a/src/net/dial/dial_http_server.cc
+++ b/src/net/dial/dial_http_server.cc
@@ -77,11 +77,37 @@
int ret = http_server_->GetLocalAddress(addr);
#if defined(OS_STARBOARD)
+
+#if SB_API_VERSION >= 4
+ if (ret != 0) {
+ return ERR_FAILED;
+ }
+
+ SbSocketAddress local_ip = {0};
+
// Now get the IPAddress of the network card.
+ SbSocketAddress destination = {0};
+ SbSocketAddress netmask = {0};
+
+ // Dial only works with IPv4.
+ destination.type = kSbSocketAddressTypeIpv4;
+ if (!SbSocketGetInterfaceAddress(&destination, &local_ip, NULL)) {
+ return ERR_FAILED;
+ }
+ local_ip.port = addr->port();
+
+ if (addr->FromSbSocketAddress(&local_ip)) {
+ return OK;
+ }
+
+ return ERR_FAILED;
+#else
SbSocketAddress address;
ret |= SbSocketGetLocalInterfaceAddress(&address) ? 0 : -1;
address.port = addr->port();
return (ret == 0 && addr->FromSbSocketAddress(&address)) ? OK : ERR_FAILED;
+#endif // SB_API_VERSION >= 4
+
#else
// Now get the IPAddress of the network card.
SockaddrStorage sock_addr;
diff --git a/src/net/dial/dial_system_config_starboard.cc b/src/net/dial/dial_system_config_starboard.cc
index f25e9e6..c1f7add 100644
--- a/src/net/dial/dial_system_config_starboard.cc
+++ b/src/net/dial/dial_system_config_starboard.cc
@@ -21,30 +21,44 @@
// static
std::string DialSystemConfig::GetFriendlyName() {
char buffer[kMaxNameSize];
- SbSystemGetProperty(kSbSystemPropertyFriendlyName, buffer, sizeof(buffer));
- return std::string(buffer);
+ if (SbSystemGetProperty(kSbSystemPropertyFriendlyName, buffer,
+ sizeof(buffer))) {
+ return std::string(buffer);
+ } else {
+ return std::string();
+ }
}
// static
std::string DialSystemConfig::GetManufacturerName() {
char buffer[kMaxNameSize];
- SbSystemGetProperty(kSbSystemPropertyManufacturerName, buffer,
- sizeof(buffer));
- return std::string(buffer);
+ if (SbSystemGetProperty(kSbSystemPropertyManufacturerName, buffer,
+ sizeof(buffer))) {
+ return std::string(buffer);
+ } else {
+ return std::string();
+ }
}
// static
std::string DialSystemConfig::GetModelName() {
char buffer[kMaxNameSize];
- SbSystemGetProperty(kSbSystemPropertyModelName, buffer, sizeof(buffer));
- return std::string(buffer);
+ if (SbSystemGetProperty(kSbSystemPropertyModelName, buffer, sizeof(buffer))) {
+ return std::string(buffer);
+ } else {
+ return std::string();
+ }
}
// static
std::string DialSystemConfig::GeneratePlatformUuid() {
char buffer[kMaxNameSize];
- SbSystemGetProperty(kSbSystemPropertyPlatformUuid, buffer, sizeof(buffer));
- return std::string(buffer);
+ if (SbSystemGetProperty(kSbSystemPropertyPlatformUuid, buffer,
+ sizeof(buffer))) {
+ return std::string(buffer);
+ } else {
+ return std::string();
+ }
}
} // namespace net
diff --git a/src/net/dial/dial_udp_server.cc b/src/net/dial/dial_udp_server.cc
index bfeac10..53a336a 100644
--- a/src/net/dial/dial_udp_server.cc
+++ b/src/net/dial/dial_udp_server.cc
@@ -4,7 +4,11 @@
#include "net/dial/dial_udp_server.h"
+#if defined(OS_STARBOARD)
+#include "starboard/client_porting/poem/inet_poem.h"
+#else
#include <arpa/inet.h>
+#endif
#include <utility>
#include <vector>
diff --git a/src/net/dns/address_sorter_posix.cc b/src/net/dns/address_sorter_posix.cc
index 51ef35e..548745f 100644
--- a/src/net/dns/address_sorter_posix.cc
+++ b/src/net/dns/address_sorter_posix.cc
@@ -4,7 +4,11 @@
#include "net/dns/address_sorter_posix.h"
+#if defined(OS_STARBOARD)
+#include "starboard/client_porting/poem/inet_poem.h"
+#else
#include <netinet/in.h>
+#endif
#if defined(OS_MACOSX) || defined(OS_BSD)
#include <sys/socket.h> // Must be included before ifaddrs.h.
@@ -425,4 +429,3 @@
}
} // namespace net
-
diff --git a/src/net/quic/congestion_control/cubic.cc b/src/net/quic/congestion_control/cubic.cc
index 6ea46bd..b5e8cdf 100644
--- a/src/net/quic/congestion_control/cubic.cc
+++ b/src/net/quic/congestion_control/cubic.cc
@@ -4,6 +4,8 @@
#include "net/quic/congestion_control/cubic.h"
+#include <algorithm>
+
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/time.h"
diff --git a/src/net/server/http_server.cc b/src/net/server/http_server.cc
index 4c1ed8e..80372dc 100644
--- a/src/net/server/http_server.cc
+++ b/src/net/server/http_server.cc
@@ -180,7 +180,18 @@
base::StringToSizeT(
base::StringPiece(request.GetHeaderValue("Content-Length")),
&content_length);
- if (content_length > 0 && pos < len) {
+
+ if (pos > connection->recv_data_.size()) {
+ NOTREACHED() << "pos should not be outside of recv_data_";
+ break;
+ }
+
+ size_t payload_size = connection->recv_data_.size() - pos;
+ if ((content_length > 0) && (content_length > payload_size)) {
+ break;
+ }
+
+ if (content_length > 0 && pos < (connection->recv_data_.size())) {
request.data = connection->recv_data_.substr(pos);
}
pos += request.data.length();
diff --git a/src/net/socket_stream/socket_stream_job.h b/src/net/socket_stream/socket_stream_job.h
index 7c97aab..d3e9512 100644
--- a/src/net/socket_stream/socket_stream_job.h
+++ b/src/net/socket_stream/socket_stream_job.h
@@ -55,6 +55,14 @@
void set_context(const URLRequestContext* context) {
socket_->set_context(context);
}
+ void set_network_task_runner(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+ network_task_runner_ = task_runner;
+ }
+
+ base::SingleThreadTaskRunner* network_task_runner() {
+ return network_task_runner_.get();
+ }
virtual void Connect();
@@ -79,6 +87,7 @@
virtual ~SocketStreamJob();
scoped_refptr<SocketStream> socket_;
+ scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
DISALLOW_COPY_AND_ASSIGN(SocketStreamJob);
};
diff --git a/src/net/udp/udp_listen_socket.cc b/src/net/udp/udp_listen_socket.cc
index 82fdfd9..56d2c63 100644
--- a/src/net/udp/udp_listen_socket.cc
+++ b/src/net/udp/udp_listen_socket.cc
@@ -16,6 +16,8 @@
#include "net/udp/udp_listen_socket.h"
+#include <algorithm>
+
#if defined(OS_POSIX)
#include <errno.h>
#include <sys/types.h>
diff --git a/src/net/url_request/url_request_throttler_entry.cc b/src/net/url_request/url_request_throttler_entry.cc
index 0899ed4..0207f83 100644
--- a/src/net/url_request/url_request_throttler_entry.cc
+++ b/src/net/url_request/url_request_throttler_entry.cc
@@ -4,6 +4,7 @@
#include "net/url_request/url_request_throttler_entry.h"
+#include <algorithm>
#include <cmath>
#include "base/logging.h"
diff --git a/src/net/url_request/view_cache_helper.cc b/src/net/url_request/view_cache_helper.cc
index d468075..16e1a46 100644
--- a/src/net/url_request/view_cache_helper.cc
+++ b/src/net/url_request/view_cache_helper.cc
@@ -4,6 +4,8 @@
#include "net/url_request/view_cache_helper.h"
+#include <algorithm>
+
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/stringprintf.h"
diff --git a/src/net/websockets/websocket_job.cc b/src/net/websockets/websocket_job.cc
index 71e49b5..f2a9b3e 100644
--- a/src/net/websockets/websocket_job.cc
+++ b/src/net/websockets/websocket_job.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/string_tokenizer.h"
+#include "base/synchronization/waitable_event.h"
#include "googleurl/src/gurl.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
@@ -27,6 +28,12 @@
namespace {
+// RFC 6455 (https://tools.ietf.org/html/rfc6455#section-7.1.1) advises
+// to use 2 * Maximum Segment Life to wait for the close on the tcp connection.
+// However, Chromium team has found that some servers wait for the client to
+// close the connection, and this leads to unnecessary long wait times.
+const int kClosingHandshakeTimeoutSeconds = 60;
+
// lower-case header names.
const char* const kCookieHeaders[] = {
"cookie", "cookie2"
@@ -54,6 +61,22 @@
static base::LazyInstance<WebSocketJobInitSingleton> g_websocket_job_init =
LAZY_INSTANCE_INITIALIZER;
+bool IsWebSocketAlive(net::WebSocketJob::State state) {
+ switch (state) {
+ case net::WebSocketJob::OPEN:
+ case net::WebSocketJob::SEND_CLOSED:
+ case net::WebSocketJob::RECV_CLOSED:
+ case net::WebSocketJob::CLOSE_WAIT:
+ return true;
+ case net::WebSocketJob::INITIALIZED:
+ case net::WebSocketJob::CONNECTING:
+ case net::WebSocketJob::CLOSED:
+ return false;
+ }
+
+ return false;
+}
+
} // anonymous namespace
namespace net {
@@ -72,6 +95,8 @@
started_to_send_handshake_request_(false),
handshake_request_sent_(0),
response_cookies_save_index_(0),
+ closing_handshake_timeout_(
+ base::TimeDelta::FromSeconds(kClosingHandshakeTimeoutSeconds)),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_for_send_pending_(this)) {
}
@@ -80,6 +105,24 @@
DCHECK_EQ(CLOSED, state_);
DCHECK(!delegate_);
DCHECK(!socket_.get());
+
+ base::WaitableEvent timer_stop_event(false, false);
+
+ if (network_task_runner()) {
+ network_task_runner()->PostTask(
+ FROM_HERE, base::Bind(&WebSocketJob::StopTimer, base::Unretained(this),
+ &timer_stop_event));
+ timer_stop_event.Wait();
+ }
+}
+
+void WebSocketJob::StopTimer(base::WaitableEvent* timer_stop_event) {
+ if (close_timer_) {
+ close_timer_.reset();
+ }
+ if (timer_stop_event) {
+ timer_stop_event->Signal();
+ }
}
void WebSocketJob::Connect() {
@@ -97,8 +140,7 @@
case CONNECTING:
return SendHandshakeRequest(data, len);
- case OPEN:
- {
+ case OPEN: {
scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(len);
SbMemoryCopy(buffer->data(), data, len);
if (current_send_buffer_ || !send_buffer_queue_.empty()) {
@@ -108,26 +150,50 @@
current_send_buffer_ = new DrainableIOBuffer(buffer.get(), len);
return SendDataInternal(current_send_buffer_->data(),
current_send_buffer_->BytesRemaining());
+ break;
}
- case CLOSING:
+ case CLOSE_WAIT:
+ case SEND_CLOSED:
+ case RECV_CLOSED:
case CLOSED:
return false;
}
return false;
}
+void WebSocketJob::CloseTimeout() {
+ DLOG(INFO) << "Close timed out";
+ // DCHECK to make sure we've either:
+ // 1. Sent the close message, but timed out waiting to received a response
+ // 2. Closing handshake is complete, but the server has not closed the
+ // connection.
+ DCHECK((state_ == SEND_CLOSED) || (state_ == CLOSE_WAIT));
+ state_ = CLOSED;
+ CloseInternal();
+}
+
void WebSocketJob::Close() {
if (state_ == CLOSED)
return;
- state_ = CLOSING;
if (current_send_buffer_) {
// Will close in SendPending.
return;
}
- state_ = CLOSED;
- CloseInternal();
+
+ WaitForSocketClose();
+}
+
+void WebSocketJob::DelayedClose() {
+ if (!close_timer_) {
+ DLOG(INFO) << "Delaying close";
+ close_timer_ = make_scoped_ptr<base::OneShotTimer<WebSocketJob> >(
+ new base::OneShotTimer<WebSocketJob>());
+ close_timer_->Start(
+ FROM_HERE, closing_handshake_timeout_,
+ base::Bind(&WebSocketJob::CloseTimeout, base::Unretained(this)));
+ }
}
void WebSocketJob::RestartWithAuth(const AuthCredentials& credentials) {
@@ -194,7 +260,7 @@
return;
}
if (delegate_) {
- DCHECK(state_ == OPEN || state_ == CLOSING);
+ DCHECK(IsWebSocketAlive(state_));
if (!current_send_buffer_) {
VLOG(1) << "OnSentData current_send_buffer=NULL amount_sent="
<< amount_sent;
@@ -228,13 +294,16 @@
OnReceivedHandshakeResponse(socket, data, len);
return;
}
- DCHECK(state_ == OPEN || state_ == CLOSING);
+ DCHECK(IsWebSocketAlive(state_));
if (delegate_ && len > 0)
delegate_->OnReceivedData(socket, data, len);
}
void WebSocketJob::OnClose(SocketStream* socket) {
state_ = CLOSED;
+ if (close_timer_) {
+ close_timer_.reset();
+ }
WebSocketThrottle::GetInstance()->RemoveFromQueue(this);
WebSocketThrottle::GetInstance()->WakeupSocketIfNecessary();
@@ -333,11 +402,12 @@
if (handshake_request_sent_ >= handshake_request_->raw_length()) {
// handshake request has been sent.
// notify original size of handshake request to delegate.
+ std::size_t original_length = handshake_request_->original_length();
+ handshake_request_.reset();
if (delegate_)
delegate_->OnSentData(
socket,
- handshake_request_->original_length());
- handshake_request_.reset();
+ original_length);
}
}
@@ -494,6 +564,27 @@
socket_->Close();
}
+void WebSocketJob::WaitForSocketClose() {
+ switch (state_) {
+ case CLOSE_WAIT:
+ case CLOSED:
+ case OPEN:
+ break;
+ case INITIALIZED:
+ case CONNECTING:
+ NOTREACHED();
+ break;
+ case SEND_CLOSED:
+ // Close frame has been sent, so wait for peer to send a close reponse,
+ // and a socket close.
+ case RECV_CLOSED:
+ // Our reponse to a close frame has gone out (since |send_buffer_queue_|
+ // is empty. So wait for the peer to close the connection;
+ DelayedClose();
+ break;
+ }
+}
+
void WebSocketJob::SendPending() {
if (current_send_buffer_)
return;
@@ -501,8 +592,7 @@
// Current buffer has been sent. Try next if any.
if (send_buffer_queue_.empty()) {
// No more data to send.
- if (state_ == CLOSING)
- CloseInternal();
+ WaitForSocketClose();
return;
}
diff --git a/src/net/websockets/websocket_job.h b/src/net/websockets/websocket_job.h
index 99a9c93..32c5539 100644
--- a/src/net/websockets/websocket_job.h
+++ b/src/net/websockets/websocket_job.h
@@ -9,7 +9,10 @@
#include <string>
#include <vector>
+#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/time.h"
+#include "base/timer.h"
#include "net/base/address_list.h"
#include "net/base/completion_callback.h"
#include "net/socket_stream/socket_stream_job.h"
@@ -38,8 +41,14 @@
INITIALIZED = -1,
CONNECTING = 0,
OPEN = 1,
- CLOSING = 2,
- CLOSED = 3,
+ SEND_CLOSED = 2, // A Close frame has been sent but not received.
+ RECV_CLOSED = 3, // Used briefly between receiving a Close frame and
+ // sending the response. Once the response is sent, the
+ // state changes to CLOSED.
+ CLOSE_WAIT = 4, // The Closing Handshake has completed, but the remote
+ // server has not yet closed the connection.
+ CLOSED = 5, // The Closing Handshake has completed and the connection
+ // has been closed; or the connection is failed.
};
explicit WebSocketJob(SocketStream::Delegate* delegate);
@@ -74,6 +83,11 @@
return handshake_response_.get();
}
+ void SetState(const State new_state) {
+ DCHECK_GE(new_state, state_); // states should only move forward.
+ state_ = new_state;
+ }
+
private:
friend class WebSocketThrottle;
virtual ~WebSocketJob();
@@ -102,6 +116,11 @@
bool SendDataInternal(const char* data, int length);
void CloseInternal();
void SendPending();
+ void CloseTimeout();
+ void StopTimer(base::WaitableEvent* timer_stop_event);
+
+ void WaitForSocketClose();
+ void DelayedClose();
SocketStream::Delegate* delegate_;
State state_;
@@ -124,6 +143,9 @@
std::string challenge_;
+ base::TimeDelta closing_handshake_timeout_;
+ scoped_ptr<base::OneShotTimer<WebSocketJob> > close_timer_;
+
base::WeakPtrFactory<WebSocketJob> weak_ptr_factory_;
base::WeakPtrFactory<WebSocketJob> weak_ptr_factory_for_send_pending_;
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index 836ad1f..fbddb4a 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -7,17 +7,132 @@
version previous to it.
## Version 4
-(This is a temporary template, as this version is not actually released yet.)
-**Introduce feature X**
+### Decode-to-Texture Player Output Mode
+Feature introducing support for decode-to-texture player output mode, and
+runtime player output mode selection and detection.
+In `starboard/configuration.h`,
+ * `SB_IS_PLAYER_PUNCHED_OUT`, `SB_IS_PLAYER_PRODUCING_TEXTURE`, and
+ `SB_IS_PLAYER_COMPOSITED` now no longer need to be defined (and should not
+ be defined) by platforms. Instead, these capabilities are detected at
+ runtime via `SbPlayerOutputModeSupported()`.
+In `starboard/player.h`,
+ * The enum `SbPlayerOutputMode` is introduced.
+ * `SbPlayerOutputModeSupported()` is introduced to let applications query
+ for player output mode support.
+ * `SbPlayerCreate()` now takes an additional parameter that specifies the
+ desired output mode.
+ * The punch out specific function `SbPlayerSetBounds()` must now be
+ defined on all platforms, even if they don't support punch out (in which
+ case they can implement a stub).
+ * The function `SbPlayerGetCompositionHandle()` is removed.
+ * The function `SbPlayerGetTextureId()` is replaced by the new
+ `SbPlayerGetCurrentFrame()`, which returns a `SbDecodeTarget`.
+In `starboard/decode_target.h`,
+ * All get methods (`SbDecodeTargetGetPlane()` and `SbDecodeTargetGetFormat()`,
+ `SbDecodeTargetIsOpaque()`) are now replaced with `SbDecodeTargetGetInfo()`.
+ * The `SbDecodeTargetInfo` structure is introduced and is the return value
+ type of `SbDecodeTargetGetInfo()`.
+ * `SbDecdodeTargetCreate()` is now responsible for creating all its internal
+ planes, and so its `planes` parameter is replaced by `width` and
+ `height` parameters.
+ * The GLES2 version of `SbDecdodeTargetCreate()` has its EGL types
+ (`EGLDisplay`, `EGLContext`) replaced by `void*` types, so that
+ `decode_target.h` can avoid #including EGL/GLES2 headers.
+ * `SbDecodeTargetDestroy()` is renamed to `SbDecodeTargetRelease()`.
+In `starboard/player.h`, `starboard/image.h` and `starboard/decode_target.h`,
+ * Replace `SbDecodeTargetProvider` with
+ `SbDecodeTargetGraphicsContextProvider`.
+ * Instead of restricting Starboard implementations to only be able to run
+ `SbDecodeTarget` creation and destruction code on the application's
+ renderer's thread with the application's renderer's `EGLContext` current,
+ Starboard implementations can now run arbitrary code on the application's
+ renderer's thread with its `EGLContext` current.
+ * Remove `SbDecodeTargetCreate()`, `SbDecodeTarget` creation is now an
+ implementation detail to be dealt with in other Starboard API functions
+ that create `SbDecodeTargets`, like `SbImageDecode()` or `SbPlayerCreate()`.
-In starboard/foo.h,
+### Playback Rate
+Support for setting the playback rate on an SbPlayer. This allows for control
+of the playback speed of video at runtime.
- * Introduce SbFooBar().
- * Remove SbFooDom().
+### Floating Point Input Vector
+Change `input.h`'s `SbInputVector` structure to contain float members instead of
+ints.
-**Introduce feature Y**
+### Delete SbUserApplicationTokenResults
+Deleted the vestigal struct `SbUserApplicationTokenResults` from `user.h`.
-In starboard/hello.h,
+### Storage Options for Encoded Audio/Video Data
+Enables the SbPlayer implementation to provide instructions to its user on
+how to store audio/video data. Encoded audio/video data is cached once being
+demuxed and may occupy a significant amount of memory. Enabling this feature
+allows the SbPlayer implementation to have better control on where encoded
+audio/video data is stored.
- * Introduce SbHelloWorld().
+### Unified implementation of `SbMediaCanPlayMimeAndKeySystem()`
+Use a unified implementation of `SbMediaCanPlayMimeAndKeySystem()` based on
+`SbMediaIsSupported()`, `SbMediaIsAudioSupported()`, and
+`SbMediaIsVideoSupported()`.
+
+### Introduce `ticket` parameter to `SbDrmGenerateSessionUpdateRequest()`
+Introduce `ticket` parameter to `SbDrmGenerateSessionUpdateRequest()`
+and `SbDrmSessionUpdateRequestFunc` to allow distinguishing between callbacks
+from multiple concurrent calls.
+
+### Introduce `SbSocketGetInterfaceAddress()`
+`SbSocketGetInterfaceAddress()` is introduced to let applications find out
+which source IP address and the associated netmask will be used to connect to
+the destination. This is very important for multi-homed devices, and for
+certain conditions in IPv6.
+
+### Introduce `starboard/cryptography.h`
+In newly-introduced `starboard/cryptography.h`,
+ * Optional support for accelerated cryptography, which can, in
+ particular, be used for accelerating SSL.
+
+### Introduce z-index parameter to `SbPlayerSetBounds()`
+Allow `SbPlayerSetBounds` to use an extra parameter to indicate the z-index of
+the video so multiple overlapping videos can be rendered.
+
+### Media source buffer settings removed from `configuration.h`
+Media source buffer settings in Starboard.
+
+### Introduce `starboard/accessibility.h`
+In particular, the functions `SbAccessibilityGetDisplaySettings()` and
+`SbAccessibilityGetTextToSpeechSettings()` have now been introduced.
+
+Additionally, a new Starboard event, `kSbEventTypeAccessiblitySettingsChanged`,
+has been defined in `starboard/event.h`.
+
+### HDR decode support
+In `starboard/media.h`, `SbMediaColorMetadata` is now defined and it contains
+HDR metadata. The field `SbMediaColorMetadata color_metadata` is now added to
+`SbMediaVideoSampleInfo`.
+
+### Add `kSbSystemDeviceTypeAndroidTV` to `starboard/system.h`
+A new device type, `kSbSystemDeviceTypeAndroidTV`, is added to
+starboard/system.h.
+
+### Deprecate `SbSpeechSynthesisSetLanguage()`
+SbSpeechSynthesisSetLanguage() has been deprecated.
+
+### Request State Change Support
+Added `SbSystemRequestPause()`, `SbSystemRequestUnpause()`,
+`SbSystemRequestSuspend()`.
+
+`SbSystemRequestSuspend()` in particular can be hooked into a platform's "hide"
+or "minimize" window functionality.
+
+### Font Directory Path Support
+Added `kSbSystemPathFontDirectory` and `kSbSystemPathFontConfigurationDirectory`
+which can be optionally specified for platforms that want to provide system
+fonts to Starboard applications. The font and font configuration formats
+supported are application-specific.
+
+### Add `SB_NORETURN` to `starboard/configuration.h`.
+Added attribute macro `SB_NORETURN` to allow functions to be marked as noreturn.
+
+### Mark `SbSystemBreakIntoDebugger` `SB_NORETURN`.
+Add `SB_NORETURN` to declaration of `SbSystemBreakIntoDebugger`, to allow it to
+be used in a manner similar to `abort`.
diff --git a/src/starboard/accessibility.h b/src/starboard/accessibility.h
index b274da0..3e63bf1 100644
--- a/src/starboard/accessibility.h
+++ b/src/starboard/accessibility.h
@@ -26,7 +26,7 @@
extern "C" {
#endif
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
// A group of settings related to text-to-speech functionality, for platforms
// that expose system settings for text-to-speech.
@@ -65,7 +65,7 @@
SB_EXPORT bool SbAccessibilityGetDisplaySettings(
SbAccessibilityDisplaySettings* out_settings);
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif // SB_API_VERSION >= 4
#ifdef __cplusplus
} // extern "C"
diff --git a/src/starboard/atomic.h b/src/starboard/atomic.h
index 243c4ea..447958b 100644
--- a/src/starboard/atomic.h
+++ b/src/starboard/atomic.h
@@ -182,27 +182,27 @@
static SB_C_FORCE_INLINE void
SbAtomicNoBarrier_StorePtr(volatile SbAtomicPtr* ptr, SbAtomicPtr value) {
#if SB_HAS(64_BIT_POINTERS)
- return SbAtomicNoBarrier_Store64(ptr, value);
+ SbAtomicNoBarrier_Store64(ptr, value);
#else
- return SbAtomicNoBarrier_Store(ptr, value);
+ SbAtomicNoBarrier_Store(ptr, value);
#endif
}
static SB_C_FORCE_INLINE void
SbAtomicAcquire_StorePtr(volatile SbAtomicPtr* ptr, SbAtomicPtr value) {
#if SB_HAS(64_BIT_POINTERS)
- return SbAtomicAcquire_Store64(ptr, value);
+ SbAtomicAcquire_Store64(ptr, value);
#else
- return SbAtomicAcquire_Store(ptr, value);
+ SbAtomicAcquire_Store(ptr, value);
#endif
}
static SB_C_FORCE_INLINE void
SbAtomicRelease_StorePtr(volatile SbAtomicPtr* ptr, SbAtomicPtr value) {
#if SB_HAS(64_BIT_POINTERS)
- return SbAtomicRelease_Store64(ptr, value);
+ SbAtomicRelease_Store64(ptr, value);
#else
- return SbAtomicRelease_Store(ptr, value);
+ SbAtomicRelease_Store(ptr, value);
#endif
}
diff --git a/src/starboard/client_porting/wrap_main/wrap_main.h b/src/starboard/client_porting/wrap_main/wrap_main.h
index 7b51a6e..a12e779 100644
--- a/src/starboard/client_porting/wrap_main/wrap_main.h
+++ b/src/starboard/client_porting/wrap_main/wrap_main.h
@@ -20,7 +20,6 @@
#ifndef STARBOARD_CLIENT_PORTING_WRAP_MAIN_WRAP_MAIN_H_
#define STARBOARD_CLIENT_PORTING_WRAP_MAIN_WRAP_MAIN_H_
-#if defined(STARBOARD)
#include "starboard/event.h"
#include "starboard/system.h"
@@ -48,38 +47,10 @@
} // namespace client_porting
} // namespace starboard
-#if defined(_WIN32)
-// Today there is no Starboard win32. Make sure those who create it know
-// the _CrtSet* functions below should be moved to starboard win32 main.
-#error For starboard win32, please move _CrtSet* to main
-#endif
#define STARBOARD_WRAP_SIMPLE_MAIN(main_function) \
void SbEventHandle(const SbEvent* event) { \
::starboard::client_porting::wrap_main::SimpleEventHandler< \
main_function>(event); \
}
-#else
-#if defined(_WIN32)
-#include <windows.h>
-
-// TODO this case should be removed when win32 is starboardized
-#define STARBOARD_WRAP_SIMPLE_MAIN(main_function) \
- int main(int argc, char** argv) { \
- if (!IsDebuggerPresent()) { \
- _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); \
- _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); \
- _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); \
- _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); \
- } \
- return main_function(argc, argv); \
- }
-#else // defined(_WIN32)
-#define STARBOARD_WRAP_SIMPLE_MAIN(main_function) \
- int main(int argc, char** argv) { \
- return main_function(argc, argv); \
- }
-#endif
-
-#endif // STARBOARD
#endif // STARBOARD_CLIENT_PORTING_WRAP_MAIN_WRAP_MAIN_H_
diff --git a/src/starboard/common/common.cc b/src/starboard/common/common.cc
index 86c63c2..1200a32 100644
--- a/src/starboard/common/common.cc
+++ b/src/starboard/common/common.cc
@@ -16,6 +16,8 @@
// 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!"
+#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 228f48f..bfe17b0 100644
--- a/src/starboard/common/common.gyp
+++ b/src/starboard/common/common.gyp
@@ -25,10 +25,10 @@
},
'sources': [
'common.cc',
- 'decode_target_provider.cc',
'flat_map.h',
'memory.cc',
'move.h',
+ 'new.cc',
'ref_counted.cc',
'ref_counted.h',
'reset_and_return.h',
diff --git a/src/starboard/common/decode_target_provider.cc b/src/starboard/common/decode_target_provider.cc
deleted file mode 100644
index 673d091..0000000
--- a/src/starboard/common/decode_target_provider.cc
+++ /dev/null
@@ -1,97 +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/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/common/locked_ptr.h b/src/starboard/common/locked_ptr.h
new file mode 100644
index 0000000..6f308a0
--- /dev/null
+++ b/src/starboard/common/locked_ptr.h
@@ -0,0 +1,104 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_COMMON_LOCKED_PTR_H_
+#define STARBOARD_COMMON_LOCKED_PTR_H_
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/log.h"
+#include "starboard/mutex.h"
+
+#ifdef __cplusplus
+namespace starboard {
+
+// This class adds thread-safety to any class. Note when using LockedPtr<T>,
+// the caller should use the returned object directly as a pointer without
+// holding on it. This is enforced by making the ctors of Impl private.
+template <typename Type>
+class LockedPtr {
+ public:
+ template <typename ImplType>
+ class Impl {
+ public:
+ ~Impl() {
+ if (mutex_) {
+ mutex_->Release();
+ }
+ }
+
+ ImplType* operator->() { return ptr_; }
+
+ private:
+ friend class LockedPtr<Type>;
+
+ Impl(Mutex* mutex, ImplType* ptr) : mutex_(mutex), ptr_(ptr) {
+ SB_DCHECK(mutex);
+ SB_DCHECK(ptr);
+
+ mutex_->Acquire();
+ }
+
+ // The copy ctor transfers the ownership. So only the last one holds the
+ // lock. This is safe as long as the user won't hold a reference to the
+ // returned object.
+ Impl(Impl& that) {
+ mutex_ = that.mutex_;
+ ptr_ = that.ptr_;
+
+ that.mutex_ = NULL;
+ that.ptr_ = NULL;
+ }
+
+ Mutex* mutex_;
+ ImplType* ptr_;
+ };
+
+ LockedPtr() : ptr_(NULL) {}
+ explicit LockedPtr(scoped_ptr<Type> ptr) : ptr_(ptr) {
+ SB_DCHECK(ptr != NULL);
+ }
+
+ // This function is solely used to set the pointer to a valid value when it is
+ // not convenient to construct the object directly. So it checks if the
+ // existing |ptr_| is NULL.
+ void set(scoped_ptr<Type> ptr) {
+ starboard::ScopedLock scoped_lock(mutex_);
+ SB_DCHECK(ptr_ == NULL);
+ ptr_ = ptr.Pass();
+ }
+
+ bool is_valid() const {
+ starboard::ScopedLock scoped_lock(mutex_);
+ return ptr_ != NULL;
+ }
+
+ Impl<Type> operator->() {
+ Impl<Type> impl(&mutex_, ptr_.get());
+ return impl;
+ }
+
+ Impl<const Type> operator->() const {
+ Impl<const Type> impl(&mutex_, ptr_.get());
+ return impl;
+ }
+
+ private:
+ mutable Mutex mutex_;
+ scoped_ptr<Type> ptr_;
+};
+
+} // namespace starboard
+#endif // __cplusplus
+
+#endif // STARBOARD_COMMON_LOCKED_PTR_H_
diff --git a/src/starboard/common/new.cc b/src/starboard/common/new.cc
new file mode 100644
index 0000000..7e08ed0
--- /dev/null
+++ b/src/starboard/common/new.cc
@@ -0,0 +1,34 @@
+// 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.
+
+// TODO: I believe this code will have to be linked into all DLLs on
+// Windows. I think Starboard can do this through GYP when the time comes.
+
+#include "starboard/memory.h"
+
+void* operator new(size_t size) {
+ return SbMemoryAllocate(size);
+}
+
+void operator delete(void* pointer) {
+ SbMemoryDeallocate(pointer);
+}
+
+void* operator new[](size_t size) {
+ return SbMemoryAllocate(size);
+}
+
+void operator delete[](void* pointer) {
+ SbMemoryDeallocate(pointer);
+}
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index f9efb66..a549295 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -39,12 +39,12 @@
// The maximum API version allowed by this version of the Starboard headers,
// inclusive.
-#define SB_MAXIMUM_API_VERSION 4
+#define SB_MAXIMUM_API_VERSION 5
// 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 4
+#define SB_EXPERIMENTAL_API_VERSION 5
// --- Experimental Feature Defines ------------------------------------------
@@ -52,49 +52,20 @@
// starboard/CHANGELOG.md when released. Thus, you can find examples of the
// format your feature comments should take by checking that file.
-// Feature introducing support for decode-to-texture player output mode, and
-// runtime player output mode selection and detection.
-// In starboard/configuration.h,
-// * SB_IS_PLAYER_PUNCHED_OUT, SB_IS_PLAYER_PRODUCING_TEXTURE, and
-// SB_IS_PLAYER_COMPOSITED now no longer need to be defined (and should not
-// be defined) by platforms. Instead, these capabilities are detected at
-// runtime via SbPlayerOutputModeSupported().
-// In starboard/player.h,
-// * The enum SbPlayerOutputMode is introduced.
-// * SbPlayerOutputModeSupported() is introduced to let applications query
-// for player output mode support.
-// * SbPlayerCreate() now takes an additional parameter that specifies the
-// desired output mode.
-// * The punch out specific function SbPlayerSetBounds() must now be
-// defined on all platforms, even if they don't support punch out (in which
-// case they can implement a stub).
-// * The function SbPlayerGetCompositionHandle() is removed.
-// * The function SbPlayerGetTextureId() is replaced by the new
-// SbPlayerGetCurrentFrame(), which returns a SbDecodeTarget.
-// In starboard/decode_target.h,
-// * All get methods (SbDecodeTargetGetPlane() and SbDecodeTargetGetFormat(),
-// SbDecodeTargetIsOpaque()) are now replaced with SbDecodeTargetGetInfo().
-// * The SbDecodeTargetInfo structure is introduced and is the return value
-// type of SbDecodeTargetGetInfo().
-// * SbDecdodeTargetCreate() is now responsible for creating all its internal
-// planes, and so its |planes| parameter is replaced by |width| and
-// |height| parameters.
-// * The GLES2 version of SbDecdodeTargetCreate() has its EGL types
-// (EGLDisplay, EGLContext) replaced by void* types, so that decode_target.h
-// can avoid #including EGL/GLES2 headers.
-// * SbDecodeTargetDestroy() is renamed to SbDecodeTargetRelease().
-#define SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION SB_EXPERIMENTAL_API_VERSION
+// EXAMPLE:
+// // Introduce new experimental feature.
+// // Add a function, `SbMyNewFeature()` to `starboard/feature.h` which
+// // exposes functionality for my new feature.
+// #define SB_MY_EXPERIMENTAL_FEATURE VERSION SB_EXPERIMENTAL_API_VERSION
-// Support for setting the playback rate on an SbPlayer. This allows for
-// control of the playback speed of video at runtime.
-#define SB_PLAYER_SET_PLAYBACK_RATE_VERSION SB_EXPERIMENTAL_API_VERSION
+// Adds kSbSystemPropertyUserAgentAuxField to the SbSystemPropertyId
+// enum to allow platform-specific User-Agent suffix.
+#define SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION SB_EXPERIMENTAL_API_VERSION
-// Change input.h's SbInputVector structure to contain float members instead of
-// ints.
-#define SB_INPUT_FLOATING_POINT_INPUT_VECTOR_VERSION SB_EXPERIMENTAL_API_VERSION
-
-// Deleted the vestigal struct SbUserApplicationTokenResults from user.h.
-#define SB_DELETE_USER_APPLICATION_TOKEN_VERSION SB_EXPERIMENTAL_API_VERSION
+// Introduce 'starboard/speech_recognizer.h'
+// This newly-introduced 'starboard/speech_recognizer.h' adds the on-device
+// speech recognizer feature.
+#define SB_SPEECH_RECOGNIZER_API_VERSION SB_EXPERIMENTAL_API_VERSION
// --- Common Detected Features ----------------------------------------------
@@ -107,18 +78,20 @@
// --- Common Helper Macros --------------------------------------------------
// Determines a compile-time capability of the system.
-#define SB_CAN(SB_FEATURE) (defined(SB_CAN_##SB_FEATURE) && SB_CAN_##SB_FEATURE)
+#define SB_CAN(SB_FEATURE) \
+ ((defined SB_CAN_##SB_FEATURE) && SB_CAN_##SB_FEATURE)
// Determines at compile-time whether this platform has a standard feature or
// header available.
-#define SB_HAS(SB_FEATURE) (defined(SB_HAS_##SB_FEATURE) && SB_HAS_##SB_FEATURE)
+#define SB_HAS(SB_FEATURE) \
+ ((defined SB_HAS_##SB_FEATURE) && SB_HAS_##SB_FEATURE)
// Determines at compile-time an inherent aspect of this platform.
-#define SB_IS(SB_FEATURE) (defined(SB_IS_##SB_FEATURE) && SB_IS_##SB_FEATURE)
+#define SB_IS(SB_FEATURE) ((defined SB_IS_##SB_FEATURE) && SB_IS_##SB_FEATURE)
// Determines at compile-time whether this platform has a quirk.
#define SB_HAS_QUIRK(SB_FEATURE) \
- (defined(SB_HAS_QUIRK_##SB_FEATURE) && SB_HAS_QUIRK_##SB_FEATURE)
+ ((defined SB_HAS_QUIRK_##SB_FEATURE) && SB_HAS_QUIRK_##SB_FEATURE)
// Determines at compile-time if this platform implements a given Starboard API
// version number (or above).
@@ -195,7 +168,7 @@
#define SB_RESTRICT
#endif // COMPILER
#else // __cplusplus
-#define SB_RESTRICT restrict
+#define SB_RESTRICT __restrict
#endif // __cplusplus
#endif // SB_RESTRICT
@@ -311,6 +284,21 @@
#define SB_DEPRECATED_EXTERNAL(FUNC) SB_DEPRECATED(FUNC)
#endif
+#if SB_API_VERSION >= 4
+// Macro to annotate a function as noreturn, which signals to the compiler
+// that the function cannot return.
+#if !defined(SB_NORETURN)
+#if SB_IS(COMPILER_GCC)
+#define SB_NORETURN __attribute__((__noreturn__))
+#elif SB_IS(COMPILER_MSVC)
+#define SB_NORETURN __declspec(noreturn)
+#else
+// Empty definition for other compilers.
+#define SB_NORETURN
+#endif
+#endif // SB_NORETURN
+#endif // SB_API_VERSION >= 4
+
// --- Configuration Audits --------------------------------------------------
#if !defined(SB_API_VERSION)
@@ -448,7 +436,7 @@
#error "Your platform must define SB_HAS_TIME_THREAD_NOW in API 3 or later."
#endif
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
#if SB_HAS(PLAYER)
#if !SB_IS(PLAYER_COMPOSITED) && !SB_IS(PLAYER_PUNCHED_OUT) && \
!SB_IS(PLAYER_PRODUCING_TEXTURE)
@@ -467,12 +455,12 @@
#error "Your player can't have a composition method if it doesn't exist."
#endif
#endif // SB_HAS(PLAYER)
-#else // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // SB_API_VERSION < 4
#if defined(SB_IS_PLAYER_COMPOSITITED) || defined(SB_IS_PLAYER_PUNCHED_OUT) || \
defined(SB_IS_PLAYER_PRODUCING_TEXTURE)
#error "New versions of Starboard specify player output mode at runtime."
#endif
-#endif // // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // // SB_API_VERSION < 4
#if (SB_HAS(MANY_CORES) && (SB_HAS(1_CORE) || SB_HAS(2_CORES) || \
SB_HAS(4_CORES) || SB_HAS(6_CORES))) || \
@@ -513,6 +501,63 @@
#error "Your platform must define SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER."
#endif
+#if SB_API_VERSION >= 4
+
+#if !defined(SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND)
+#error \
+ "Your platform must define SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND."
+#endif // !defined(SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND)
+
+#if !defined(SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND)
+#error \
+ "Your platform must define SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND."
+#endif // !defined(SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND)
+
+#endif // SB_API_VERSION >= 4
+
+#if SB_API_VERSION >= 4
+
+#if defined(SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT)
+#error "SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT is deprecated."
+#error "Use gyp variable |cobalt_media_buffer_non_video_budget| instead."
+#endif // defined(SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT)
+
+#if defined(SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT)
+#error "SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT is deprecated."
+#error "Use gyp variable |cobalt_media_buffer_video_budget_1080p| instead."
+#error "Use gyp variable |cobalt_media_buffer_video_budget_4k| instead."
+#endif // defined(SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT)
+
+#if defined(SB_MEDIA_MAIN_BUFFER_BUDGET)
+#error "SB_MEDIA_MAIN_BUFFER_BUDGET is deprecated."
+#endif // defined(SB_MEDIA_MAIN_BUFFER_BUDGET)
+
+#if defined(SB_MEDIA_GPU_BUFFER_BUDGET)
+#error "SB_MEDIA_GPU_BUFFER_BUDGET is deprecated."
+#endif // defined(SB_MEDIA_GPU_BUFFER_BUDGET)
+
+#if COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET <= 0
+#error "cobalt_media_buffer_non_video_budget has to be greater than 0."
+#endif // COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET < 0
+
+#if COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P <= 0
+#error "cobalt_media_buffer_video_budget_1080p has to be greater than 0."
+#endif // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P < 0
+
+#if COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K < COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
+#error cobalt_media_buffer_video_budget_4k has to be greater than or equal to \
+ cobalt_media_buffer_video_budget_1080p.
+#endif // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K <
+ // COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P
+
+#endif // SB_API_VERSION >= 4
+
+#if SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+#if !defined(SB_HAS_SPEECH_RECOGNIZER)
+#error "Your platform must define SB_HAS_SPEECH_RECOGNIZER."
+#endif // !defined(SB_HAS_SPEECH_RECOGNIZER)
+#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+
// --- Derived Configuration -------------------------------------------------
// Whether the current platform is little endian.
diff --git a/src/starboard/creator/ci20directfb/starboard_platform.gyp b/src/starboard/creator/ci20directfb/starboard_platform.gyp
index d897ef9..e7ca37e 100644
--- a/src/starboard/creator/ci20directfb/starboard_platform.gyp
+++ b/src/starboard/creator/ci20directfb/starboard_platform.gyp
@@ -303,8 +303,17 @@
'<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
'<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
'<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
'<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
'<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
'<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
'<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
diff --git a/src/starboard/creator/ci20x11/starboard_platform.gyp b/src/starboard/creator/ci20x11/starboard_platform.gyp
index 8105b97..f412dec 100644
--- a/src/starboard/creator/ci20x11/starboard_platform.gyp
+++ b/src/starboard/creator/ci20x11/starboard_platform.gyp
@@ -266,8 +266,17 @@
'<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
'<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
'<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
'<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
'<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
'<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
'<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
diff --git a/src/starboard/creator/shared/configuration_public.h b/src/starboard/creator/shared/configuration_public.h
index 23a5afe..1a587d9 100644
--- a/src/starboard/creator/shared/configuration_public.h
+++ b/src/starboard/creator/shared/configuration_public.h
@@ -122,6 +122,9 @@
// Whether the current platform has microphone supported.
#define SB_HAS_MICROPHONE 0
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
// Whether the current platform has speech synthesis.
#define SB_HAS_SPEECH_SYNTHESIS 0
@@ -328,7 +331,7 @@
// supported composition methods below.
#define SB_HAS_PLAYER 1
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
// Specifies whether this platform's player will produce an OpenGL texture that
// the client must draw every frame with its graphics rendering. It may be that
// we get a texture handle, but cannot perform operations like GlReadPixels on
@@ -347,43 +350,7 @@
// this case, changing the video bounds must be tightly synchronized between the
// player and the graphics plane.
#define SB_IS_PLAYER_PUNCHED_OUT 1
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
-// Specifies the maximum amount of memory used by audio buffers of media source
-// before triggering a garbage collection. A large value will cause more memory
-// being used by audio buffers but will also make JavaScript app less likely to
-// re-download audio data. Note that the JavaScript app may experience
-// significant difficulty if this value is too low.
-#define SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT (3U * 1024U * 1024U)
-
-// Specifies the maximum amount of memory used by video buffers of media source
-// before triggering a garbage collection. A large value will cause more memory
-// being used by video buffers but will also make JavaScript app less likely to
-// re-download video data. Note that the JavaScript app may experience
-// significant difficulty if this value is too low.
-#define SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT (16U * 1024U * 1024U)
-
-// Specifies how much memory to reserve up-front for the main media buffer
-// (usually resides inside the CPU memory) used by media source and demuxers.
-// The main media buffer can work in one of the following two ways:
-// 1. If GPU buffer is used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is non-zero), the
-// main buffer will be used as a cache so a media buffer will be copied from
-// GPU memory to main memory before sending to the decoder for further
-// processing. In this case this macro should be set to a value that is
-// large enough to hold all media buffers being decoded.
-// 2. If GPU buffer is not used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is zero) all
-// media buffers will reside in the main memory buffer. In this case the
-// macro should be set to a value that is greater than the sum of the above
-// source buffer stream memory limits with extra room to take account of
-// fragmentations and memory used by demuxers.
-#define SB_MEDIA_MAIN_BUFFER_BUDGET (32U * 1024U * 1024U)
-
-// Specifies how much GPU memory to reserve up-front for media source buffers.
-// This should only be set to non-zero on system with limited CPU memory and
-// excess GPU memory so the app can store media buffer in GPU memory.
-// SB_MEDIA_MAIN_BUFFER_BUDGET has to be set to a non-zero value to avoid
-// media buffers being decoded when being stored in GPU.
-#define SB_MEDIA_GPU_BUFFER_BUDGET 0U
+#endif // SB_API_VERSION < 4
// Specifies whether this platform has webm/vp9 support. This should be set to
// non-zero on platforms with webm/vp9 support.
diff --git a/src/starboard/cryptography.h b/src/starboard/cryptography.h
new file mode 100644
index 0000000..e9e2cab
--- /dev/null
+++ b/src/starboard/cryptography.h
@@ -0,0 +1,213 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Module Overview: Starboard Cryptography module
+//
+// Hardware-accelerated cryptography. Platforms should **only** implement this
+// when there are **hardware-accelerated** cryptography facilities. Applications
+// must fall back to platform-independent CPU-based algorithms if the cipher
+// algorithm isn't supported in hardware.
+//
+// # Tips for Porters
+//
+// You should implement cipher algorithms in this descending order of priority
+// to maximize usage for SSL.
+//
+// 1. GCM - The preferred block cipher mode for OpenSSL, mainly due to speed.
+// 2. CBC - If GCM is disabled, then SSL will use the CBC stream cipher.
+// Normally this is less desirable because GCM is faster, but if CBC is
+// hardware-accelerated, it is likely to be better than software GCM. CBC
+// is also considered by some to be more secure.
+// 3. CTR - This can be used internally with GCM, as long as the CTR
+// implementation only uses the last 4 bytes of the IV for the counter.
+// (i.e. 96-bit IV, 32-bit counter)
+// 4. ECB - This is for if you only have core AES block encryption. It can be
+// used with any of the other cipher block modes to accelerate the core AES
+// algorithm if none of the streaming modes can be accelerated.
+//
+// Further reading on GCM vs CBC vs CTR:
+// https://crypto.stackexchange.com/questions/10775/practical-disadvantages-of-gcm-mode-encryption
+
+#ifndef STARBOARD_CRYPTOGRAPHY_H_
+#define STARBOARD_CRYPTOGRAPHY_H_
+
+#include "starboard/configuration.h"
+#include "starboard/export.h"
+#include "starboard/types.h"
+
+#if SB_API_VERSION >= 4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// String literal for the AES symmetric block cipher.
+// https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+#define kSbCryptographyAlgorithmAes "aes"
+
+// The direction of a cryptographic transformation.
+typedef enum SbCryptographyDirection {
+ // Cryptographic transformations that encode/encrypt data into a
+ // target format.
+ kSbCryptographyDirectionEncode,
+
+ // Cryptographic transformations that decode/decrypt data into
+ // its original form.
+ kSbCryptographyDirectionDecode,
+} SbCryptographyDirection;
+
+// The method of chaining encrypted blocks in a sequence.
+// https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+typedef enum SbCryptographyBlockCipherMode {
+ // Cipher Block Chaining mode.
+ kSbCryptographyBlockCipherModeCbc,
+
+ // Cipher Feedback mode.
+ kSbCryptographyBlockCipherModeCfb,
+
+ // Counter mode: A nonce is combined with an increasing counter.
+ kSbCryptographyBlockCipherModeCtr,
+
+ // Electronic Code Book mode: No chaining.
+ kSbCryptographyBlockCipherModeEcb,
+
+ // Output Feedback mode.
+ kSbCryptographyBlockCipherModeOfb,
+
+ // Galois/Counter Mode.
+ kSbCryptographyBlockCipherModeGcm,
+} SbCryptographyBlockCipherMode;
+
+// Private structure representing a cryptographic transformer
+// configuration and state.
+typedef struct SbCryptographyTransformerPrivate
+ SbCryptographyTransformerPrivate;
+
+// A handle to a cryptographic transformer.
+typedef SbCryptographyTransformerPrivate* SbCryptographyTransformer;
+
+// Well-defined value for an invalid transformer.
+#define kSbCryptographyInvalidTransformer ((SbCryptographyTransformer)NULL)
+
+// Returns whether the given transformer handle is valid.
+static SB_C_INLINE bool SbCryptographyIsTransformerValid(
+ SbCryptographyTransformer transformer) {
+ return transformer != kSbCryptographyInvalidTransformer;
+}
+
+// Creates an SbCryptographyTransformer with the given initialization
+// data. It can then be used to transform a series of data blocks.
+// Returns kSbCryptographyInvalidTransformer if the algorithm isn't
+// supported, or if the parameters are not compatible with the
+// algorithm.
+//
+// An SbCryptographyTransformer contains all state to start decrypting
+// a sequence of cipher blocks according to the cipher block mode. It
+// is not thread-safe, but implementations must allow different
+// SbCryptographyTransformer instances to operate on different threads.
+//
+// All parameters must not be assumed to live longer than the call to
+// this function. They must be copied by the implementation to be
+// retained.
+//
+// This function determines success mainly based on whether the combination of
+// |algorithm|, |direction|, |block_size_bits|, and |mode| is supported and
+// whether all the sizes passed in are sufficient for the selected
+// parameters. In particular, this function cannot verify that the key and IV
+// used were correct for the ciphertext, were it to be used in the decode
+// direction. The caller must make that verification.
+//
+// For example, to decrypt AES-128-CTR:
+// SbCryptographyCreateTransformer(kSbCryptographyAlgorithmAes, 128,
+// kSbCryptographyDirectionDecode,
+// kSbCryptographyBlockCipherModeCtr,
+// ...);
+//
+// |algorithm|: A string that represents the cipher algorithm.
+// |block_size_bits|: The block size variant of the algorithm to use, in bits.
+// |direction|: The direction in which to transform the data.
+// |mode|: The block cipher mode to use.
+// |initialization_vector|: The Initialization Vector (IV) to use. May be NULL
+// for block cipher modes that don't use it, or don't set it at init time.
+// |initialization_vector_size|: The size, in bytes, of the IV.
+// |key|: The key to use for this transformation.
+// |key_size|: The size, in bytes, of the key.
+SB_EXPORT SbCryptographyTransformer
+SbCryptographyCreateTransformer(const char* algorithm,
+ int block_size_bits,
+ SbCryptographyDirection direction,
+ SbCryptographyBlockCipherMode mode,
+ const void* initialization_vector,
+ int initialization_vector_size,
+ const void* key,
+ int key_size);
+
+// Destroys the given |transformer| instance.
+SB_EXPORT void SbCryptographyDestroyTransformer(
+ SbCryptographyTransformer transformer);
+
+// Transforms one or more |block_size_bits|-sized blocks of |in_data|, with the
+// given |transformer|, placing the result in |out_data|. Returns the number of
+// bytes that were written to |out_data|, unless there was an error, in which
+// case it will return a negative number.
+//
+// |transformer|: A transformer initialized with an algorithm, IV, cipherkey,
+// and so on.
+// |in_data|: The data to be transformed.
+// |in_data_size|: The size of the data to be transformed, in bytes. Must be a
+// multiple of the transformer's |block-size_bits|, or an error will be
+// returned.
+// |out_data|: A buffer where the transformed data should be placed. Must have
+// at least capacity for |in_data_size| bytes. May point to the same memory as
+// |in_data|.
+SB_EXPORT int SbCryptographyTransform(
+ SbCryptographyTransformer transformer,
+ const void* in_data,
+ int in_data_size,
+ void* out_data);
+
+// Sets the initialization vector (IV) for a transformer, replacing the
+// internally-set IV. The block cipher mode algorithm will update the IV
+// appropriately after every block, so this is not necessary unless the stream
+// is discontiguous in some way. This happens with AES-GCM in TLS.
+SB_EXPORT void SbCryptographySetInitializationVector(
+ SbCryptographyTransformer transformer,
+ const void* initialization_vector,
+ int initialization_vector_size);
+
+// Sets additional authenticated data (AAD) for a transformer, for chaining
+// modes that support it (GCM). Returns whether the data was successfully
+// set. This can fail if the chaining mode doesn't support AAD, if the
+// parameters are invalid, or if the internal state is invalid for setting AAD.
+SB_EXPORT bool SbCryptographySetAuthenticatedData(
+ SbCryptographyTransformer transformer,
+ const void* data,
+ int data_size);
+
+// Calculates the authenticator tag for a transformer and places up to
+// |out_tag_size| bytes of it in |out_tag|. Returns whether it was able to get
+// the tag, which mainly has to do with whether it is compatible with the
+// current block cipher mode.
+SB_EXPORT bool SbCryptographyGetTag(
+ SbCryptographyTransformer transformer,
+ void* out_tag,
+ int out_tag_size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // SB_API_VERSION >= 4
+
+#endif // STARBOARD_CRYPTOGRAPHY_H_
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h
index 48198f5..20e1f70 100644
--- a/src/starboard/decode_target.h
+++ b/src/starboard/decode_target.h
@@ -34,26 +34,29 @@
// an error. Each decoder provides a way to check if a given
// SbDecodeTargetFormat is supported by that decoder.
//
-// #### SbDecodeTargetProvider
+// #### SbDecodeTargetGraphicsContextProvider
//
// 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 a designated thread.
+// SbDecodeTargetGraphicsContextProvider are passed in to the Starboard
+// implementation from the application and provide information about the
+// rendering context that will be used to render the SbDecodeTarget objects.
+// For GLES renderers, it also provides functionality to enable the Starboard
+// implementation to run arbitrary code on the application's renderer thread
+// with the renderer's EGLContext held current. This may be useful if your
+// SbDecodeTarget creation code needs to execute GLES commands like, for
+// example, glGenTextures().
//
// 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:
+// Let's say that we are an application and we would like to use the interface
+// defined in starboard/image.h to decode an imaginary "image/foo" image type.
//
-// bool SbImageDecodeFooSupportsFormat(SbDecodeTargetFormat format);
-// SbDecodeTarget SbImageDecodeFoo(void* data, int data_size,
-// SbDecodeTargetFormat format);
-//
-// First, the client should enumerate which SbDecodeTargetFormats are supported
-// by that decoder.
+// First, the application should enumerate which SbDecodeTargetFormats are
+// supported by that decoder.
//
// SbDecodeTargetFormat kPreferredFormats[] = {
// kSbDecodeTargetFormat3PlaneYUVI420,
@@ -63,19 +66,20 @@
//
// SbDecodeTargetFormat format = kSbDecodeTargetFormatInvalid;
// for (int i = 0; i < SB_ARRAY_SIZE_INT(kPreferredFormats); ++i) {
-// if (SbImageDecodeFooSupportsFormat(kPreferredFormats[i])) {
+// if (SbImageIsDecodeSupported("image/foo", kPreferredFormats[i])) {
// format = kPreferredFormats[i];
// break;
// }
// }
//
-// Now that the client has a format, it can create a decode target that it
+// Now that the application 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, that we are on an EGL/GLES2 platform.
// Also, we won't do any error checking, to keep things even simpler.
//
-// SbDecodeTarget target = SbImageDecodeFoo(encoded_foo_data,
-// encoded_foo_data_size, format);
+// SbDecodeTarget target = SbImageDecode(
+// context_provider, encoded_foo_data, encoded_foo_data_size,
+// "image/foo", format);
//
// // If the decode works, you can get the texture out and render it.
// SbDecodeTargetInfo info;
@@ -96,10 +100,10 @@
#if SB_HAS(BLITTER)
#include "starboard/blitter.h"
#elif SB_HAS(GLES2) // SB_HAS(BLITTER)
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
#include <EGL/egl.h>
#include <GLES2/gl2.h>
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
#endif // SB_HAS(BLITTER)
#ifdef __cplusplus
@@ -162,6 +166,63 @@
kSbDecodeTargetPlaneV = 2,
} SbDecodeTargetPlane;
+#if SB_API_VERSION >= 4
+#if SB_HAS(GLES2)
+struct SbDecodeTargetGraphicsContextProvider;
+
+// Signature for a Starboard implementaion function that is to be run by a
+// SbDecodeTargetGlesContextRunner callback.
+typedef void (*SbDecodeTargetGlesContextRunnerTarget)(
+ void* gles_context_runner_target_context);
+
+// Signature for a function provided by the application to the Starboard
+// implementation that will let the Starboard implementation run arbitrary code
+// on the application's renderer thread with the application's EGLContext held
+// current.
+typedef void (*SbDecodeTargetGlesContextRunner)(
+ struct SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
+ SbDecodeTargetGlesContextRunnerTarget target_function,
+ void* target_function_context);
+#endif // SB_HAS(GLES2)
+
+// In general, the SbDecodeTargetGraphicsContextProvider structure provides
+// information about the graphics context that will be used to render
+// SbDecodeTargets. Some Starboard implementations may need to have references
+// to some graphics objects when creating/destroying resources used by
+// SbDecodeTarget. References to SbDecodeTargetGraphicsContextProvider objects
+// should be provided to all Starboard functions that might create
+// SbDecodeTargets (e.g. SbImageDecode()).
+typedef struct SbDecodeTargetGraphicsContextProvider {
+#if SB_HAS(BLITTER)
+ // The SbBlitterDevice object that will be used to render any produced
+ // SbDecodeTargets.
+ SbBlitterDevice device;
+#elif SB_HAS(GLES2)
+ // A reference to the EGLDisplay object that hosts the EGLContext that will
+ // be used to render any produced SbDecodeTargets. Note that it has the
+ // type |void*| in order to avoid #including the EGL header files here.
+ void* egl_display;
+ // The EGLContext object that will be used to render any produced
+ // SbDecodeTargets. Note that it has the
+ // type |void*| in order to avoid #including the EGL header files here.
+ void* egl_context;
+
+ // The |gles_context_runner| function pointer is passed in from the
+ // application into the Starboard implementation, and can be invoked by the
+ // Starboard implementation to allow running arbitrary code on the renderer's
+ // thread with the EGLContext above held current.
+ SbDecodeTargetGlesContextRunner gles_context_runner;
+
+ // Context data that is to be passed in to |gles_context_runner| when it is
+ // invoked.
+ void* gles_context_runner_context;
+#else // SB_HAS(BLITTER)
+ // Some compilers complain about empty structures, this is to appease them.
+ char dummy;
+#endif // SB_HAS(BLITTER)
+} SbDecodeTargetGraphicsContextProvider;
+
+#else // SB_API_VERSION >= 4
// A function that can produce an SbDecodeTarget of the given |format|, |width|,
// and |height|.
typedef SbDecodeTarget (*SbDecodeTargetAcquireFunction)(
@@ -186,8 +247,9 @@
// |context| will be passed into every call to |acquire| and |release|.
void* context;
} SbDecodeTargetProvider;
+#endif // SB_API_VERSION >= 4
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// Defines a rectangular content region within a SbDecodeTargetInfoPlane
// structure.
@@ -256,7 +318,7 @@
SbDecodeTargetInfoPlane planes[3];
} SbDecodeTargetInfo;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
// --- Constants -------------------------------------------------------------
@@ -276,9 +338,9 @@
return format != kSbDecodeTargetFormatInvalid;
}
-#if SB_HAS(BLITTER)
+#if SB_API_VERSION < 4
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_HAS(BLITTER)
// Creates a new SbBlitter-compatible SbDecodeTarget from one or more |planes|
// created from |display|.
@@ -295,25 +357,8 @@
SB_EXPORT SbBlitterSurface SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
SbDecodeTargetPlane plane);
-#else // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
-// Creates a new SbBlitter-compatible SbDecodeTarget and all internal
-// surfaces/planes associated with it, created from |device|.
-//
-// |device|: The device from which the internal surfaces should be created on.
-// |format|: The format of the decode target being created.
-// |width|: The width of the decode target being created.
-// |height|: The height of the decode target being created.
-SB_EXPORT SbDecodeTarget SbDecodeTargetCreate(SbBlitterDevice device,
- SbDecodeTargetFormat format,
- int width,
- int height);
-
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
#elif SB_HAS(GLES2) // SB_HAS(BLITTER)
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
// 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.
@@ -335,39 +380,6 @@
SB_EXPORT GLuint SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
SbDecodeTargetPlane plane);
-#else // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
-// SBCHANGELOG: SbDecodeTargetCreate() is now responsible for creating its
-// internal planes/textures/surfaces, instead of expecting them
-// to be externally created and passed into it. This allows the
-// platform to customize the process of plane creation. This
-// change applies to both Blitter API and GLES platforms.
-
-// Creates a new EGL/GLES2-compatible SbDecodeTarget including all internal
-// textures/planes associated with it, which will be owned by |context|, created
-// from |display|. Must be called from a thread where |context| is current.
-// Returns kSbDecodeTargetInvalid on failure.
-//
-// void* is used in place of the EGL types so as to avoid including EGL headers,
-// which may transitively include unexpected platform-specific headers. You must
-// call reinterpret_cast<void*>(my_egl_context).
-//
-// |display|: The platform-specific graphics display (e.g. EGLDisplay) being
-// targeted.
-// |context|: The platform-specific graphics context (e.g. EGLContext) that owns
-// the provided planes, or NULL if a context is not required.
-// |format|: The format of the decode target being created, which implies how
-// many |planes| are expected, and what format they are expected to be in.
-// |width|: The width of the decode target being created.
-// |height|: The height of the decode target being created.
-SB_EXPORT SbDecodeTarget SbDecodeTargetCreate(void* display,
- void* context,
- SbDecodeTargetFormat format,
- int width,
- int height);
-
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
#else // SB_HAS(BLITTER)
// Stub function for when graphics aren't enabled. Always creates
@@ -381,8 +393,6 @@
#endif // SB_HAS(BLITTER)
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
// Destroys the given SbDecodeTarget and all its associated surfaces.
SB_EXPORT void SbDecodeTargetDestroy(SbDecodeTarget decode_target);
@@ -400,7 +410,24 @@
// not have alpha values, then this function should return |true|.
SB_EXPORT bool SbDecodeTargetIsOpaque(SbDecodeTarget decode_target);
-#else // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+// Inline convenience function to acquire an SbDecodeTarget of type |format|,
+// |width|, and |height| from |provider|.
+static SB_C_INLINE SbDecodeTarget
+SbDecodeTargetAcquireFromProvider(SbDecodeTargetProvider* provider,
+ SbDecodeTargetFormat format,
+ int width,
+ int height) {
+ return provider->acquire(provider->context, format, width, height);
+}
+
+// Inline convenience function to release |decode_target| back to |provider|.
+static SB_C_INLINE void SbDecodeTargetReleaseToProvider(
+ SbDecodeTargetProvider* provider,
+ SbDecodeTarget decode_target) {
+ provider->release(provider->context, decode_target);
+}
+
+#else // SB_API_VERSION < 4
// SBCHANGELOG: Rename SbDecodeTargetDestroy() to SbDecodeTargetRelease() to
// more accurately reflect its potential semantics as a reference
@@ -422,25 +449,38 @@
SB_EXPORT bool SbDecodeTargetGetInfo(SbDecodeTarget decode_target,
SbDecodeTargetInfo* out_info);
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-
-// Inline convenience function to acquire an SbDecodeTarget of type |format|,
-// |width|, and |height| from |provider|.
-static SB_C_INLINE SbDecodeTarget
-SbDecodeTargetAcquireFromProvider(SbDecodeTargetProvider* provider,
- SbDecodeTargetFormat format,
- int width,
- int height) {
- return provider->acquire(provider->context, format, width, height);
+#if SB_HAS(GLES2)
+// Inline convenience function to run an arbitrary
+// SbDecodeTargetGlesContextRunnerTarget function through a
+// SbDecodeTargetGraphicsContextProvider. This is intended to be called by
+// Starboard implementations, if it is necessary.
+static SB_C_INLINE void SbDecodeTargetRunInGlesContext(
+ SbDecodeTargetGraphicsContextProvider* provider,
+ SbDecodeTargetGlesContextRunnerTarget target,
+ void* target_context) {
+ provider->gles_context_runner(provider, target, target_context);
}
-// Inline convenience function to release |decode_target| back to |provider|.
-static SB_C_INLINE void SbDecodeTargetReleaseToProvider(
- SbDecodeTargetProvider* provider,
+// This function is just an implementation detail of
+// SbDecodeTargetReleaseInGlesContext() and should not be called directly.
+static SB_C_INLINE void PrivateDecodeTargetReleaser(void* context) {
+ SbDecodeTarget decode_target = (SbDecodeTarget)context;
+ SbDecodeTargetRelease(decode_target);
+}
+
+// Helper function that is possibly useful to Starboard implementations that
+// will release a decode target on the thread with the GLES context current.
+static SB_C_INLINE void SbDecodeTargetReleaseInGlesContext(
+ SbDecodeTargetGraphicsContextProvider* provider,
SbDecodeTarget decode_target) {
- provider->release(provider->context, decode_target);
+ SbDecodeTargetRunInGlesContext(provider, &PrivateDecodeTargetReleaser,
+ (void*)decode_target);
}
+#endif // SB_HAS(GLES2)
+
+#endif // SB_API_VERSION < 4
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/src/starboard/doc/howto_decode_to_texture.md b/src/starboard/doc/howto_decode_to_texture.md
new file mode 100644
index 0000000..093deb4
--- /dev/null
+++ b/src/starboard/doc/howto_decode_to_texture.md
@@ -0,0 +1,137 @@
+# **HOWTO:** Decode to Texture
+
+Starboard declares the interfaces necessary to allow applications to query for
+video frames from the media player, and have them returned as texture objects
+(e.g. GLES textures). This is useful if the application would like to apply
+a geometrical transformation to the rendered video, in order to support 360
+spherical video playback for example. Additionally, if a Starboard platform
+implementation does not support punch-through video playback, then
+applications can choose to use decode-to-texture instead.
+
+## API Overview
+
+Decode-to-texture support involves multiple Starboard API functions spanning
+both the [`starboard/player.h`](../player.h) and
+[`starboard/decode_target.h`](../decode_target.h) Starboard interface header
+files. Support for decode-to-texture began in version 4 of the Starboard
+API.
+
+In particular, the following function implementations require consideration
+for decode-to-texture support:
+
+From [`starboard/player.h`](../player.h),
+
+* `SbPlayerCreate()`
+* `SbPlayerOutputModeSupported()`
+* `SbPlayerGetCurrentFrame()`
+
+From [`starboard/decode_target.h`](../decode_target.h),
+
+* `SbDecodeTargetCreate()`
+* `SbDecodeTargetRelease()`
+* `SbDecodeTargetGetInfo()`
+
+Note that it is possible that you may not need to use the
+`SbDecodeTargetGraphicsContextProvider` parameter of SbPlayerCreate(). More on
+this later.
+
+## Example Application Usage Pattern
+
+We now describe an example, and typical, sequence of steps that an
+application will take when it wishes to make use of decode-to-texture
+support.
+
+
+
+1. An application with the desire to make use of decode-to-texture will first
+ call `SbPlayerOutputModeSupported()`, passing in
+ `kSbPlayerOutputModeDecodeToTexture` for its `output_mode` parameter. If
+ the function returns false, the application learns that decode-to-texture
+ is not supported by the platform and it will not continue with a
+ decode-to-texture flow.
+
+2. If `SbPlayerOutputModeSupported()` returns true, the application will call
+ `SbPlayerCreate()`, passing in `kSbPlayerOutputModeDecodeToTexture` for
+ the `output_mode` parameter, and also providing a valid `provider`
+ parameter (more on this later). At this point, the Starboard platform is
+ expected to have created a player with the decode-to-texture output mode.
+
+3. Once the player is started and playback has begun, the application's
+ renderer thread (this may be a different thread than the one that called
+ `SbPlayerCreate()`) will repeatedly and frequently call
+ `SbPlayerGetCurrentFrame()`. Since this function will be called from the
+ application's renderer thread, it should be thread-safe. If the platform
+ uses a GLES renderer, it is guaranteed that this function will be called
+ with the GLES renderer context set as current. This function is expected
+ to return the video frame that is to be displayed at the time the function
+ is called as a `SbDecodeTarget` object. The `SbPlayerGetCurrentFrame()`
+ will be called at the renderer's frequency, i.e. the application render
+ loop's frame rate. If the application's frame rate is higher than the
+ video's frame rate, then the same video frame will sometimes be returned
+ in consecutive calls to `SbPlayerGetCurrentFrame()`. If the video's frame
+ rate is higher than the application's (this should be rare), then some
+ video frames will never be returned by calls to
+ `SbPlayerGetCurrentFrame()`; in other words, video frames will be
+ dropped.
+
+4. Once the application has acquired a valid SbDecodeTarget object through a
+ call to `SbPlayerGetCurrentFrame()`, it will call
+ `SbDecodeTargetGetInfo()` on it to extract information about the opaque
+ `SbDecodeTarget` object. The `SbDecodeTargetGetInfo()` function fills
+ out a `SbDecodeTargetInfo` structure which contains information about the
+ decoded frame and, most importantly, a reference to a GLES texture ID on
+ GLES platforms, or a reference to a `SbBlitterSurface` object on
+ Starboard Blitter API platforms. The application can then use this
+ texture/surface handle to render the video frame as it wishes.
+
+5. When the application is finished using the `SbDecodeTarget` that it has
+ aquired through the `SbPlayerGetCurrentFrame()` function, it will call
+ `SbDecodeTargetRelease()` on it. The Starboard platform implementation
+ should ensure that the `SbDecodeTarget` object returned by
+ `SbPlayerGetCurrentFrame()` remains valid until the corresponding call to
+ `SbDecodeTargetRelease()` is made. A call to `SbDecodeTargetRelease()`
+ will be made to match each call to `SbPlayerGetCurrentFrame()`.
+
+## The `SbDecodeTargetGraphicsContextProvider` object
+
+It is completely possible that a platform's Starboard implementation can
+properly implement decode-to-texture support without dealing with the
+`SbDecodeTargetGraphicsContextProvider` object (passed in to
+`SbPlayerCreate()`). The `SbDecodeTargetGraphicsContextProvider` reference
+gives platforms references to the graphics objects that will later be used to
+render the decoded frames. For example, on Blitter API platforms, a reference
+to the `SbBlitterDevice` object will be a mamber of
+`SbDecodeTargetGraphicsContextProvider`. For EGL platforms, a `EGLDisplay` and
+`EGLContext` will be available, but additionally a
+`SbDecodeTargetGlesContextRunner` function pointer will be provided that will
+allow you to run arbitrary code on the renderer thread with the `EGLContext`
+held current. This may be useful if your `SbDecodeTarget` creation code will
+required making GLES calls (e.g. `glGenTextures()`) in which a `EGLContext` must
+be held current.
+
+## Performance Considerations
+
+The decode-to-texture Starboard API is specifically designed to allow
+Starboard implementations to have the player decode directly to a texture,
+so that the application can then reference and render with that texture
+without at any point performing a pixel copy. The
+decode-to-texture path can therefore be highly performant.
+
+It is still recommended however that platforms support the punch-through
+player mode if possible. When using the decode-to-texture player output
+mode, the video may be rendered within the application's render loop, which
+means that non-video-related time complexity in the application's render
+loop can affect video playback's apparent frame rate, potentially resulting in
+dropped frames. The platform can likely configure punch-through video to
+refresh on its own loop, decoupling it from the application render loop.
+
+## Implementation Strategies
+
+### Working with "push" players
+
+If your player implementation is setup with a "push" framework where
+frames are pushed out as soon as they are decoded, then you will need
+to cache those frames (along with their timestamps) so that they can be
+passed on to the application when `SbPlayerGetCurrentFrame()` is called.
+This same strategy applies if the player pushes frames only when they are meant
+to be rendered.
\ No newline at end of file
diff --git a/src/starboard/doc/resources/decode_to_texture_sequence.png b/src/starboard/doc/resources/decode_to_texture_sequence.png
new file mode 100644
index 0000000..b2b4cb4
--- /dev/null
+++ b/src/starboard/doc/resources/decode_to_texture_sequence.png
Binary files differ
diff --git a/src/starboard/doc/resources/decode_to_texture_sequence.txt b/src/starboard/doc/resources/decode_to_texture_sequence.txt
new file mode 100644
index 0000000..5118f0b
--- /dev/null
+++ b/src/starboard/doc/resources/decode_to_texture_sequence.txt
@@ -0,0 +1,14 @@
+participant Application (e.g. Cobalt) as a [fillcolor="#ffd0d0"]
+participant Starboard as s [fillcolor="#d0d0ff"]
+
+a->s: SbPlayerOutputModeSupported(kSbPlayerOutputModeDecodeToTexture, ...)
+s-->a: returns bool
+Note over a: If SbPlayerOutputModeSupported()\nreturns true... [fillcolor="white"]
+a->s: SbPlayerCreate(..., kSbPlayerOutputModeDecodeToTexture, ...)
+Note over a: Start of render loop [fillcolor="#ffffd0"]
+a->s: SbPlayerGetCurrentFrame()
+s-->a: returns SbDecodeTarget
+Note over a: Extracts GLES texture(s) from the\nSbDecodeTarget object and\nrenders a scene with them. [fillcolor="white"]
+a->s: SbDecodeTargetRelease()
+Note over a: Goto: Start of render loop [fillcolor="#ffffd0"]
+
diff --git a/src/starboard/drm.h b/src/starboard/drm.h
index f7f4544..dad7d24 100644
--- a/src/starboard/drm.h
+++ b/src/starboard/drm.h
@@ -78,10 +78,18 @@
// A callback that will receive generated session update request when requested
// from a SbDrmSystem. |drm_system| will be the DRM system that
// SbDrmGenerateSessionUpdateRequest() was called on. |context| will be the same
-// context that was passed into the call to SbDrmCreateSystem(). |session_id|
-// can be NULL if there was an error generating the request.
+// context that was passed into the call to SbDrmCreateSystem().
+#if SB_API_VERSION >= 4
+// |ticket| will be the same ticket that was passed
+// to SbDrmGenerateSessionUpdateRequest() or |kSbDrmTicketInvalid| if
+// the update request was generated by the DRM system.
+#endif // SB_API_VERSION >= 4
+// |session_id| can be NULL if there was an error generating the request.
typedef void (*SbDrmSessionUpdateRequestFunc)(SbDrmSystem drm_system,
void* context,
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
const void* session_id,
int session_id_size,
const void* content,
@@ -91,10 +99,16 @@
// A callback for notifications that a session has been added, and subsequent
// encrypted samples are actively ready to be decoded. |drm_system| will be the
// DRM system that SbDrmUpdateSession() was called on. |context| will be the
-// same context passed into that call to SbDrmCreateSystem(). |succeeded| is
-// whether the session was successfully updated or not.
+// same context passed into that call to SbDrmCreateSystem().
+#if SB_API_VERSION >= 4
+// |ticket| will be the same ticket that was passed to SbDrmUpdateSession().
+#endif // SB_API_VERSION >= 4
+// |succeeded| is whether the session was successfully updated or not.
typedef void (*SbDrmSessionUpdatedFunc)(SbDrmSystem drm_system,
void* context,
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
const void* session_id,
int session_id_size,
bool succeeded);
@@ -104,6 +118,11 @@
// An invalid SbDrmSystem.
#define kSbDrmSystemInvalid ((SbDrmSystem)NULL)
+#if SB_API_VERSION >= 4
+// A ticket for callback invocations initiated by the DRM system.
+#define kSbDrmTicketInvalid kSbInvalidInt
+#endif // SB_API_VERSION >= 4
+
// --- Functions -------------------------------------------------------------
// Indicates whether |drm_system| is a valid SbDrmSystem.
@@ -111,6 +130,13 @@
return drm != kSbDrmSystemInvalid;
}
+#if SB_API_VERSION >= 4
+// Indicates whether |ticket| is a valid ticket.
+static SB_C_FORCE_INLINE bool SbDrmTicketIsValid(int ticket) {
+ return ticket != kSbDrmTicketInvalid;
+}
+#endif // SB_API_VERSION >= 4
+
// Creates a new DRM system that can be used when constructing an SbPlayer
// or an SbDecoder.
//
@@ -146,9 +172,8 @@
// |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.
+// |drm_system|'s |context| may be used 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.
@@ -156,22 +181,38 @@
// |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.
+#if SB_API_VERSION >= 4
+// |ticket|: The opaque ID that allows to distinguish callbacks from
+// multiple concurrent calls to SbDrmGenerateSessionUpdateRequest(),
+// which will be passed to |update_request_callback| as-is. It is
+// the responsibility of the caller to establish ticket uniqueness, issuing
+// multiple request with the same ticket may result in undefined behavior.
+// Value |kSbDrmTicketInvalid| must not be used.
+#endif // SB_API_VERSION >= 4
// |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,
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
const char* type,
const void* initialization_data,
int initialization_data_size);
// Update session with |key|, in |drm_system|'s key system, from the license
-// server response. |request| must be the corresponding returned request from
-// SbDrmGenerateSessionUpdateRequest(). Calls |session_updated_callback| with
-// |context| and whether the update succeeded. |context| may be used to
-// distinguish callbacks from multiple concurrent calls to SbDrmUpdateSession(),
-// and/or to route callbacks back to an object instance.
+// server response. Calls |session_updated_callback| with |context| and whether
+// the update succeeded. |context| may be used to route callbacks back to
+// an object instance.
+#if SB_API_VERSION >= 4
+// |ticket| is the opaque ID that allows to distinguish callbacks from
+// multiple concurrent calls to SbDrmUpdateSession(), which will be passed
+// to |session_updated_callback| as-is. It is the responsibility of the caller
+// to establish ticket uniqueness, issuing multiple calls with the same ticket
+// may result in undefined behavior.
+#endif // SB_API_VERSION >= 4
//
// Once the session is successfully updated, an SbPlayer or SbDecoder associated
// with that DRM key system will be able to decrypt encrypted samples.
@@ -179,6 +220,9 @@
// |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,
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
const void* key,
int key_size,
const void* session_id,
diff --git a/src/starboard/event.h b/src/starboard/event.h
index f7d9a68..9ab88c7 100644
--- a/src/starboard/event.h
+++ b/src/starboard/event.h
@@ -152,7 +152,7 @@
// directly. The data type is an internally-defined structure.
kSbEventTypeScheduled,
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
// The platform's accessibility settings have changed. The application should
// query the accessibility settings using the appropriate APIs to get the
// new settings.
diff --git a/src/starboard/examples/window/main.cc b/src/starboard/examples/window/main.cc
index cea9a13..fb47cdb 100644
--- a/src/starboard/examples/window/main.cc
+++ b/src/starboard/examples/window/main.cc
@@ -13,6 +13,8 @@
// limitations under the License.
#include <iomanip>
+#include <set>
+#include <sstream>
#include "starboard/event.h"
#include "starboard/input.h"
@@ -20,33 +22,109 @@
#include "starboard/system.h"
#include "starboard/window.h"
+namespace {
+// Helper set to keep track of which keys are currently pressed.
+typedef std::set<SbKey> KeySet;
+KeySet s_is_pressed;
+} // namespace
+
+SbWindow g_window;
+
void SbEventHandle(const SbEvent* event) {
switch (event->type) {
case kSbEventTypeStart: {
- SB_DLOG(INFO) << __FUNCTION__ << ": START";
+ SB_LOG(INFO) << "START";
SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
- SbWindow window = SbWindowCreate(NULL);
- SB_CHECK(SbWindowIsValid(window));
+ g_window = SbWindowCreate(NULL);
+ SB_CHECK(SbWindowIsValid(g_window));
+
+#if SB_API_VERSION >= 4
+ SB_LOG(INFO) << " F1 - Pause";
+ SB_LOG(INFO) << " F2 - Unpause";
+ SB_LOG(INFO) << " F3 - Suspend";
+#endif // SB_API_VERSION >= 4
+ SB_LOG(INFO) << " F5 - Stop";
break;
}
case kSbEventTypeInput: {
SbInputData* data = static_cast<SbInputData*>(event->data);
- SB_DLOG(INFO) << __FUNCTION__ << ": INPUT: type=" << data->type
- << ", window=" << data->window
- << ", device_type=" << data->device_type
- << ", device_id=" << data->device_id
- << ", key=0x" << std::hex << data->key
- << ", character=" << data->character
- << ", modifiers=0x" << std::hex << data->key_modifiers
- << ", location=" << std::dec << data->key_location
- << ", position="
- << "[ " << data->position.x << " , " << data->position.y
- << " ]";
+
+ SB_LOG(INFO) << "INPUT: type=" << data->type
+ << ", window=" << data->window
+ << ", device_type=" << data->device_type
+ << ", device_id=" << data->device_id
+ << ", key=0x" << std::hex << data->key
+ << ", character=" << data->character
+ << ", modifiers=0x" << std::hex << data->key_modifiers
+ << ", location=" << std::dec << data->key_location
+ << ", position="
+ << "[ " << data->position.x << " , " << data->position.y
+ << " ]";
+
+ // Track which keys are currently pressed, from our perspective outside
+ // of Starboard. Print out the current state after each key event.
+ if (data->type == kSbInputEventTypePress ||
+ data->type == kSbInputEventTypeUnpress) {
+ if (data->type == kSbInputEventTypePress) {
+ s_is_pressed.insert(data->key);
+ } else {
+ s_is_pressed.erase(data->key);
+ }
+ if (!s_is_pressed.empty()) {
+ std::stringstream keys;
+ keys << "Keys currently pressed:";
+ for (KeySet::const_iterator iter = s_is_pressed.begin();
+ iter != s_is_pressed.end(); ++iter) {
+ keys << " " << std::hex << *iter;
+ }
+ SB_LOG(INFO) << keys.str();
+ }
+ }
+
+ switch (data->key) {
+#if SB_API_VERSION >= 4
+ case kSbKeyF1:
+ SbSystemRequestPause();
+ break;
+ case kSbKeyF2:
+ SbSystemRequestUnpause();
+ break;
+ case kSbKeyF3:
+ SbSystemRequestSuspend();
+ break;
+#endif // SB_API_VERSION >= 4
+ case kSbKeyF5:
+ SbSystemRequestStop(0);
+ break;
+ default:
+ // Do nothing.
+ break;
+ }
+ break;
+ }
+ case kSbEventTypePause: {
+ SB_LOG(INFO) << "PAUSE";
+ break;
+ }
+ case kSbEventTypeResume: {
+ SB_LOG(INFO) << "RESUME";
+ break;
+ }
+ case kSbEventTypeStop: {
+ SB_LOG(INFO) << "STOP";
+ SbWindowDestroy(g_window);
+ break;
+ }
+ case kSbEventTypeSuspend: {
+ SB_LOG(INFO) << "SUSPEND";
+ break;
+ }
+ case kSbEventTypeUnpause: {
+ SB_LOG(INFO) << "UNPAUSE";
break;
}
default:
- SB_DLOG(INFO) << __FUNCTION__ << ": Event Type " << event->type
- << " not handled.";
+ SB_LOG(INFO) << "Event Type " << event->type << " not handled.";
break;
}
}
diff --git a/src/starboard/image.h b/src/starboard/image.h
index 5ff6e3f..82d93be 100644
--- a/src/starboard/image.h
+++ b/src/starboard/image.h
@@ -87,11 +87,16 @@
// requested format is not supported or the decode fails,
// kSbDecodeTargetInvalid will be returned, with any intermediate allocations
// being cleaned up in the implementation.
-SB_EXPORT SbDecodeTarget SbImageDecode(SbDecodeTargetProvider* provider,
- void* data,
- int data_size,
- const char* mime_type,
- SbDecodeTargetFormat format);
+SB_EXPORT SbDecodeTarget SbImageDecode(
+#if SB_API_VERSION >= 4
+ SbDecodeTargetGraphicsContextProvider* context_provider,
+#else
+ SbDecodeTargetProvider* provider,
+#endif
+ void* data,
+ int data_size,
+ const char* mime_type,
+ SbDecodeTargetFormat format);
#ifdef __cplusplus
} // extern "C"
diff --git a/src/starboard/input.h b/src/starboard/input.h
index aa43c6f..10c919a 100644
--- a/src/starboard/input.h
+++ b/src/starboard/input.h
@@ -117,7 +117,7 @@
} SbInputEventType;
// A 2-dimensional vector used to represent points and motion vectors.
-#if SB_API_VERSION >= SB_INPUT_FLOATING_POINT_INPUT_VECTOR_VERSION
+#if SB_API_VERSION >= 4
typedef struct SbInputVector {
float x;
float y;
diff --git a/src/starboard/linux/shared/compiler_flags.gypi b/src/starboard/linux/shared/compiler_flags.gypi
index 0218e92..931779b 100644
--- a/src/starboard/linux/shared/compiler_flags.gypi
+++ b/src/starboard/linux/shared/compiler_flags.gypi
@@ -42,6 +42,9 @@
],
'conditions': [
['clang==1', {
+ 'linker_flags': [
+ '-fuse-ld=lld',
+ ],
'common_clang_flags': [
'-Werror',
'-fcolor-diagnostics',
@@ -50,8 +53,7 @@
# Warn for implicit type conversions that may change a value.
'-Wconversion',
'-Wno-c++11-compat',
- # This (rightfully) complains about 'override', which we use
- # heavily.
+ # This complains about 'override', which we use heavily.
'-Wno-c++11-extensions',
# Warns on switches on enums that cover all enum values but
# also contain a default: branch. Chrome is full of that.
@@ -98,11 +100,7 @@
'-std=c99',
],
'cflags_cc': [
- # Limit to gnu++98. This allows Linux to be a canary build for any
- # C++11 features that are not supported on some platforms' compilers.
- # We do allow ourselves GNU extensions, which are assumed to exist
- # by Chromium code.
- '-std=gnu++98',
+ '-std=gnu++11',
],
'target_conditions': [
['cobalt_code==1', {
@@ -143,6 +141,13 @@
'defines': [
'ADDRESS_SANITIZER',
],
+ 'conditions': [
+ ['asan_symbolizer_path!=""', {
+ 'defines': [
+ 'ASAN_SYMBOLIZER_PATH="<@(asan_symbolizer_path)"',
+ ],
+ }],
+ ],
}],
['use_tsan==1', {
'cflags': [
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index 632e4e1..b4bed14 100644
--- a/src/starboard/linux/shared/configuration_public.h
+++ b/src/starboard/linux/shared/configuration_public.h
@@ -24,7 +24,7 @@
#define STARBOARD_LINUX_SHARED_CONFIGURATION_PUBLIC_H_
#ifndef SB_API_VERSION
-#define SB_API_VERSION 2
+#define SB_API_VERSION 4
#endif
// --- System Header Configuration -------------------------------------------
@@ -59,6 +59,9 @@
// Whether the current platform has microphone supported.
#define SB_HAS_MICROPHONE 0
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
// Whether the current platform has speech synthesis.
#define SB_HAS_SPEECH_SYNTHESIS 0
@@ -261,7 +264,7 @@
// supported composition methods below.
#define SB_HAS_PLAYER 1
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
// Specifies whether this platform's player will produce an OpenGL texture that
// the client must draw every frame with its graphics rendering. It may be that
// we get a texture handle, but cannot perform operations like GlReadPixels on
@@ -280,7 +283,9 @@
// this case, changing the video bounds must be tightly synchronized between the
// player and the graphics plane.
#define SB_IS_PLAYER_PUNCHED_OUT 1
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
+
+#if SB_API_VERSION < 4
// Specifies the maximum amount of memory used by audio buffers of media source
// before triggering a garbage collection. A large value will cause more memory
@@ -309,7 +314,7 @@
// macro should be set to a value that is greater than the sum of the above
// source buffer stream memory limits with extra room to take account of
// fragmentations and memory used by demuxers.
-#define SB_MEDIA_MAIN_BUFFER_BUDGET (32U * 1024U * 1024U)
+#define SB_MEDIA_MAIN_BUFFER_BUDGET (24U * 1024U * 1024U)
// Specifies how much GPU memory to reserve up-front for media source buffers.
// This should only be set to non-zero on system with limited CPU memory and
@@ -318,6 +323,22 @@
// media buffers being decoded when being stored in GPU.
#define SB_MEDIA_GPU_BUFFER_BUDGET 0U
+#endif // SB_API_VERSION < 4
+
+#if SB_API_VERSION >= 4
+
+// The maximum audio bitrate the platform can decode. The following value
+// equals to 5M bytes per seconds which is more than enough for compressed
+// audio.
+#define SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND (40 * 1024 * 1024)
+
+// The maximum video bitrate the platform can decode. The following value
+// equals to 25M bytes per seconds which is more than enough for compressed
+// video.
+#define SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND (200 * 1024 * 1024)
+
+#endif // SB_API_VERSION >= 4
+
// Specifies whether this platform has webm/vp9 support. This should be set to
// non-zero on platforms with webm/vp9 support.
#define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
diff --git a/src/starboard/linux/shared/gyp_configuration.gypi b/src/starboard/linux/shared/gyp_configuration.gypi
index f053843..01ca539 100644
--- a/src/starboard/linux/shared/gyp_configuration.gypi
+++ b/src/starboard/linux/shared/gyp_configuration.gypi
@@ -24,6 +24,10 @@
'in_app_dial%': 1,
'gl_type%': 'system_gles3',
+ # This should have a default value in cobalt/base.gypi. See the comment
+ # there for acceptable values for this variable.
+ 'cobalt_media_source_2016': 1,
+
'platform_libraries': [
'-lasound',
'-lavcodec',
@@ -40,12 +44,22 @@
'-U__linux__',
],
+ # Using an inner scope for 'variables' so that it can be made a default
+ # (and so overridden elsewhere), but yet still used immediately in this
+ # file.
+ 'variables': {
+ 'use_dlmalloc_allocator%': 0,
+ },
+
'conditions': [
- ['use_asan==0', {
+ ['use_dlmalloc_allocator==1 and use_asan==0', {
'linker_flags': [
- # We don't wrap these symbols, but this ensures that they aren't
- # linked in. We do have to allow them to linked in when using ASAN, as
- # it needs to use its own version of these allocators in the Starboard
+ # If we're not using the system allocator (e.g. we are using dlmalloc
+ # and ASAN is inactive) then we should never be making any calls to
+ # malloc() or free(). The following linker flags ensure that they
+ # are not linked in because we don't actually implement the wrapped
+ # version of them. We do link them in when using ASAN, as it needs to
+ # use its own version of these allocators in the Starboard
# implementation.
'-Wl,--wrap=malloc',
'-Wl,--wrap=calloc',
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index d2cb2e8..3750233 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -28,12 +28,7 @@
'<(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',
- '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc',
- '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc',
'<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h',
@@ -88,6 +83,7 @@
'<(DEPTH)/starboard/shared/linux/get_home_directory.cc',
'<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc',
'<(DEPTH)/starboard/shared/linux/page_internal.cc',
+ '<(DEPTH)/starboard/shared/linux/socket_get_interface_address.cc',
'<(DEPTH)/starboard/shared/linux/socket_get_local_interface_address.cc',
'<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
'<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
@@ -214,11 +210,17 @@
'<(DEPTH)/starboard/shared/starboard/log_message.cc',
'<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc',
'<(DEPTH)/starboard/shared/starboard/log_raw_format.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/codec_util.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/codec_util.h',
'<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_hfr_only.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_util.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_util.h',
'<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
'<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
'<(DEPTH)/starboard/shared/starboard/new.cc',
@@ -265,14 +267,29 @@
'<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
'<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
'<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
'<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
'<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
+ '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
+ '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
'<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
'<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
'<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
'<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
+ '<(DEPTH)/starboard/shared/stub/image_decode.cc',
+ '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
'<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
'<(DEPTH)/starboard/shared/stub/microphone_close.cc',
'<(DEPTH)/starboard/shared/stub/microphone_create.cc',
@@ -287,6 +304,25 @@
'<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
'<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc',
],
+ 'conditions': [
+ ['use_dlmalloc_allocator==1', {
+ 'starboard_platform_sources': [
+ '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc',
+ '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_unchecked.cc',
+ '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
+ '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc',
+ '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
+ ],
+ }, {
+ 'starboard_platform_sources': [
+ '<(DEPTH)/starboard/shared/iso/memory_allocate_unchecked.cc',
+ '<(DEPTH)/starboard/shared/iso/memory_free.cc',
+ '<(DEPTH)/starboard/shared/iso/memory_reallocate_unchecked.cc',
+ '<(DEPTH)/starboard/shared/posix/memory_allocate_aligned_unchecked.cc',
+ '<(DEPTH)/starboard/shared/posix/memory_free_aligned.cc',
+ ],
+ }]
+ ],
'starboard_platform_dependencies': [
'<(DEPTH)/starboard/common/common.gyp:common',
'<(DEPTH)/starboard/linux/shared/starboard_base_symbolize.gyp:starboard_base_symbolize',
diff --git a/src/starboard/linux/shared/system_get_path.cc b/src/starboard/linux/shared/system_get_path.cc
index b06571d..6621841 100644
--- a/src/starboard/linux/shared/system_get_path.cc
+++ b/src/starboard/linux/shared/system_get_path.cc
@@ -21,6 +21,7 @@
#include <cstring>
#include "starboard/directory.h"
+#include "starboard/log.h"
#include "starboard/string.h"
namespace {
@@ -58,7 +59,8 @@
return false;
}
- char* last_slash = strrchr(out_path, '/');
+ char* last_slash =
+ const_cast<char *>(SbStringFindLastCharacter(out_path, '/'));
if (!last_slash) {
return false;
}
@@ -73,7 +75,7 @@
return false;
}
- const int kPathSize = PATH_MAX;
+ const int kPathSize = SB_FILE_MAX_PATH;
char path[kPathSize];
path[0] = '\0';
@@ -127,9 +129,13 @@
path_size);
case kSbSystemPathExecutableFile:
return GetExecutablePath(out_path, path_size);
+
+ default:
+ SB_NOTIMPLEMENTED();
+ return false;
}
- int length = strlen(path);
+ int length = SbStringGetLength(path);
if (length < 1 || length > path_size) {
return false;
}
diff --git a/src/starboard/linux/x64directfb/future/configuration_public.h b/src/starboard/linux/x64directfb/future/configuration_public.h
index 2851606..ec08c92 100644
--- a/src/starboard/linux/x64directfb/future/configuration_public.h
+++ b/src/starboard/linux/x64directfb/future/configuration_public.h
@@ -18,9 +18,6 @@
#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
@@ -29,4 +26,7 @@
#undef SB_IS_PLAYER_COMPOSITED
#undef SB_IS_PLAYER_PUNCHED_OUT
+// Include the X64DIRECTFB Linux configuration.
+#include "starboard/linux/x64directfb/configuration_public.h"
+
#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
index fd813f6..44b920d 100644
--- a/src/starboard/linux/x64directfb/future/gyp_configuration.gypi
+++ b/src/starboard/linux/x64directfb/future/gyp_configuration.gypi
@@ -13,12 +13,6 @@
# limitations under the License.
{
- 'variables': {
- # This should have a default value in cobalt/base.gypi. See the comment
- # there for acceptable values for this variable.
- 'cobalt_media_source_2016': 1,
- },
-
'target_defaults': {
'default_configuration': 'linux-x64directfb-future_debug',
'configurations': {
diff --git a/src/starboard/linux/x64directfb/future/starboard_platform.gyp b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
index 9a7b68e..6fe0639 100644
--- a/src/starboard/linux/x64directfb/future/starboard_platform.gyp
+++ b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
@@ -17,19 +17,7 @@
'target_name': 'starboard_platform',
'product_name': 'starboard_platform_future',
'type': 'static_library',
- 'sources': [
- '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
- '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_create_blitter.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_get_plane_blitter.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_get_size.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_is_opaque.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
- '<(DEPTH)/starboard/shared/stub/image_decode.cc',
- '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
- ],
+ 'sources': [],
'defines': [
# This must be defined when building Starboard, and must not when
# building Starboard client code.
diff --git a/src/starboard/linux/x64directfb/sanitizer_options.cc b/src/starboard/linux/x64directfb/sanitizer_options.cc
index b5f72cd..6205ec9 100644
--- a/src/starboard/linux/x64directfb/sanitizer_options.cc
+++ b/src/starboard/linux/x64directfb/sanitizer_options.cc
@@ -38,7 +38,13 @@
SANITIZER_HOOK_ATTRIBUTE const char* __lsan_default_suppressions() {
return
// DirectFB leaks __strdup data at initialization time.
- "leak:__strdup\n"
+ "leak:__strdup\n";
}
+#if defined(ASAN_SYMBOLIZER_PATH)
+extern "C" const char *__asan_default_options() {
+ return "external_symbolizer_path=" ASAN_SYMBOLIZER_PATH;
+}
+#endif
+
#endif // defined(ADDRESS_SANITIZER)
diff --git a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
index 1f3cb42..67a704c 100644
--- a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
@@ -32,7 +32,7 @@
def __init__(self, platform, asan_enabled_by_default=True):
super(PlatformConfig, self).__init__(
- platform, asan_enabled_by_default, goma_supports_compiler=False)
+ platform, goma_supports_compiler=False)
def GetEnvironmentVariables(self):
env_variables = {
diff --git a/src/starboard/linux/x64x11/configuration_public.h b/src/starboard/linux/x64x11/configuration_public.h
index d76ffff..b1cff4c 100644
--- a/src/starboard/linux/x64x11/configuration_public.h
+++ b/src/starboard/linux/x64x11/configuration_public.h
@@ -22,11 +22,6 @@
#ifndef STARBOARD_LINUX_X64X11_CONFIGURATION_PUBLIC_H_
#define STARBOARD_LINUX_X64X11_CONFIGURATION_PUBLIC_H_
-// The API version implemented by this platform.
-#if !defined(SB_API_VERSION)
-#define SB_API_VERSION 2
-#endif
-
// --- Architecture Configuration --------------------------------------------
// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
diff --git a/src/cobalt/base/math.cc b/src/starboard/linux/x64x11/directgles/atomic_public.h
similarity index 65%
copy from src/cobalt/base/math.cc
copy to src/starboard/linux/x64x11/directgles/atomic_public.h
index d335495..7bd6e3b 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/linux/x64x11/directgles/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#ifndef STARBOARD_LINUX_X64X11_DIRECTGLES_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_DIRECTGLES_ATOMIC_PUBLIC_H_
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#include "starboard/linux/shared/atomic_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_DIRECTGLES_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/directgles/configuration_public.h b/src/starboard/linux/x64x11/directgles/configuration_public.h
new file mode 100644
index 0000000..0158690
--- /dev/null
+++ b/src/starboard/linux/x64x11/directgles/configuration_public.h
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The Starboard configuration for Desktop X86 Linux configured for the future
+// starboard API.
+
+#ifndef STARBOARD_LINUX_X64X11_DIRECTGLES_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_DIRECTGLES_CONFIGURATION_PUBLIC_H_
+
+// This is not a released configuration, so it should implement the
+// experimental API version to validate trunk's viability.
+#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// Include the X64X11 Linux configuration.
+#include "starboard/linux/x64x11/configuration_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_DIRECTGLES_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/directgles/gyp_configuration.gypi b/src/starboard/linux/x64x11/directgles/gyp_configuration.gypi
new file mode 100644
index 0000000..66e6d55
--- /dev/null
+++ b/src/starboard/linux/x64x11/directgles/gyp_configuration.gypi
@@ -0,0 +1,62 @@
+# 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': {
+ # Use the direct-to-GLES rasterizer.
+ # This rasterizer falls back to the hardware skia rasterizer in certain
+ # situations.
+ # NOTE: This rasterizer requires a 16-bit depth buffer and a full frame
+ # scratch surface (without depth buffer).
+ 'rasterizer_type': 'direct-gles',
+
+ # Accommodate the direct-to-GLES rasterizer's additional memory overhead
+ # by reducing the image cache size. This is only an issue when additional
+ # memory is required for video frames, so shrink the image cache size
+ # only during video playback.
+ 'image_cache_capacity_multiplier_when_playing_video': '0.5f',
+
+ # This dictates how much GPU memory will be used for the offscreen render
+ # target atlases. One will be used as a cache and the other used as a
+ # working scratch. It is recommended to allot enough memory for two
+ # atlases that are roughly a quarter of the framebuffer. (The render
+ # target atlases use power-of-2 dimenions.)
+ 'surface_cache_size_in_bytes': 2 * (1024 * 512 * 4),
+
+ # The rasterizer does not benefit much from rendering only the dirty
+ # region. Disable this option since it costs GPU time.
+ 'render_dirty_region_only': 0,
+ },
+ 'target_defaults': {
+ 'default_configuration': 'linux-x64x11-directgles_debug',
+ 'configurations': {
+ 'linux-x64x11-directgles_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'linux-x64x11-directgles_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'linux-x64x11-directgles_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'linux-x64x11-directgles_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ '../gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/linux/x64x11/directgles/gyp_configuration.py b/src/starboard/linux/x64x11/directgles/gyp_configuration.py
new file mode 100644
index 0000000..4fc899f
--- /dev/null
+++ b/src/starboard/linux/x64x11/directgles/gyp_configuration.py
@@ -0,0 +1,34 @@
+# 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')))
+# pylint: disable=import-self,g-import-not-at-top
+import gyp_configuration
+
+
+def CreatePlatformConfig():
+ try:
+ return gyp_configuration.PlatformConfig('linux-x64x11-directgles')
+ except RuntimeError as e:
+ logging.critical(e)
+ return None
diff --git a/src/starboard/linux/x64x11/directgles/starboard_platform.gyp b/src/starboard/linux/x64x11/directgles/starboard_platform.gyp
new file mode 100644
index 0000000..9a450cc
--- /dev/null
+++ b/src/starboard/linux/x64x11/directgles/starboard_platform.gyp
@@ -0,0 +1,36 @@
+# 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.
+{
+ 'includes': [
+ '../starboard_platform.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'starboard_platform',
+ 'product_name': 'starboard_platform_future',
+ 'type': 'static_library',
+ 'sources': [
+ '<@(starboard_platform_sources)',
+ ],
+ 'defines': [
+ # This must be defined when building Starboard, and must not when
+ # building Starboard client code.
+ 'STARBOARD_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '<@(starboard_platform_dependencies)',
+ ],
+ },
+ ],
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/linux/x64x11/directgles/thread_types_public.h
similarity index 64%
copy from src/cobalt/base/math.cc
copy to src/starboard/linux/x64x11/directgles/thread_types_public.h
index d335495..4809a57 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/linux/x64x11/directgles/thread_types_public.h
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#ifndef STARBOARD_LINUX_X64X11_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#include "starboard/linux/shared/thread_types_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/future/gyp_configuration.gypi b/src/starboard/linux/x64x11/future/gyp_configuration.gypi
index d7f3af6..0cb12cf 100644
--- a/src/starboard/linux/x64x11/future/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/future/gyp_configuration.gypi
@@ -13,12 +13,6 @@
# limitations under the License.
{
- 'variables': {
- # This should have a default value in cobalt/base.gypi. See the comment
- # there for acceptable values for this variable.
- 'cobalt_media_source_2016': 1,
- },
-
'target_defaults': {
'default_configuration': 'linux-x64x11-future_debug',
'configurations': {
diff --git a/src/starboard/linux/x64x11/future/starboard_platform.gyp b/src/starboard/linux/x64x11/future/starboard_platform.gyp
index 391210a..9a450cc 100644
--- a/src/starboard/linux/x64x11/future/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/future/starboard_platform.gyp
@@ -22,17 +22,6 @@
'type': 'static_library',
'sources': [
'<@(starboard_platform_sources)',
- '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
- '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_create_egl.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_get_info.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_get_plane_egl.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_get_size.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_is_opaque.cc',
- '<(DEPTH)/starboard/shared/stub/decode_target_release.cc',
- '<(DEPTH)/starboard/shared/stub/image_decode.cc',
- '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
],
'defines': [
# This must be defined when building Starboard, and must not when
diff --git a/src/starboard/linux/x64x11/sanitizer_options.cc b/src/starboard/linux/x64x11/sanitizer_options.cc
index 2ac2410..e8a4aea 100644
--- a/src/starboard/linux/x64x11/sanitizer_options.cc
+++ b/src/starboard/linux/x64x11/sanitizer_options.cc
@@ -36,7 +36,15 @@
// http://clang.llvm.org/docs/AddressSanitizer.html#issue-suppression
// http://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.cc
SANITIZER_HOOK_ATTRIBUTE const char* __lsan_default_suppressions() {
- return "leak:egl_gallium.so\n";
+ return
+ "leak:egl_gallium.so\n"
+ "leak:libspeechd.so\n";
}
+#if defined(ASAN_SYMBOLIZER_PATH)
+extern "C" const char *__asan_default_options() {
+ return "external_symbolizer_path=" ASAN_SYMBOLIZER_PATH;
+}
+#endif
+
#endif // defined(ADDRESS_SANITIZER)
diff --git a/src/starboard/media.h b/src/starboard/media.h
index 1b11b8f..3982d15 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -131,7 +131,7 @@
kSbMediaAudioFrameStorageTypePlanar,
} SbMediaAudioFrameStorageType;
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
// SMPTE 2086 mastering data
// http://ieeexplore.ieee.org/document/7291707/
// This standard specifies the metadata items to specify the color
@@ -384,7 +384,7 @@
// completed as (0, 0, 0, 1).
float custom_primary_matrix[12];
} SbMediaColorMetadata;
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif // SB_API_VERSION >= 4
// The set of information required by the decoder or player for each video
// sample.
@@ -403,7 +403,7 @@
// key frames, but may change on any key frame.
int frame_height;
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
// HDR metadata common for HDR10 and WebM/VP9-based HDR formats as
// well as the Color Space, and Color elements: MatrixCoefficients,
// BitsPerChannel, ChromaSubsamplingHorz, ChromaSubsamplingVert,
@@ -495,6 +495,8 @@
SbMediaAudioCodec audio_codec,
const char* key_system);
+#if SB_API_VERSION < 4
+
// Indicates whether a given combination of
// (|frame_width| x |frame_height|) frames at |bitrate| and |fps| is supported
// on this platform with |video_codec|. If |video_codec| is not supported under
@@ -523,6 +525,8 @@
SB_EXPORT bool SbMediaIsAudioSupported(SbMediaVideoCodec audio_codec,
int64_t bitrate);
+#endif // SB_API_VERSION < 4
+
// Returns information about whether the playback of the specific media
// described by |mime| and encrypted using |key_system| can be played.
//
@@ -531,12 +535,13 @@
//
// |mime|: The mime information of the media in the form of |video/webm|
// or |video/mp4; codecs="avc1.42001E"|. It may include arbitrary parameters
-// like "codecs", "channels", etc.
+// like "codecs", "channels", etc. Note that the "codecs" parameter may
+// contain more than one codec, delimited by comma.
// |key_system|: A lowercase value in fhe form of "com.example.somesystem"
-// as suggested by https://w3c.github.io/encrypted-media/#key-system
-// that can be matched exactly with known DRM key systems of the platform.
-// When |key_system| is an empty string, the return value is an indication for
-// non-encrypted media.
+// as suggested by https://w3c.github.io/encrypted-media/#key-system
+// that can be matched exactly with known DRM key systems of the platform.
+// When |key_system| is an empty string, the return value is an indication for
+// non-encrypted media.
SB_EXPORT SbMediaSupportType
SbMediaCanPlayMimeAndKeySystem(const char* mime, const char* key_system);
diff --git a/src/starboard/nplb/accessibility_get_setting_test.cc b/src/starboard/nplb/accessibility_get_setting_test.cc
index 3ffaa18..ca0662f 100644
--- a/src/starboard/nplb/accessibility_get_setting_test.cc
+++ b/src/starboard/nplb/accessibility_get_setting_test.cc
@@ -19,7 +19,7 @@
namespace nplb {
namespace {
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
TEST(SbAccessibilityGetSettingTest, CanCallGetTextToSpeechSettings) {
SbAccessibilityTextToSpeechSettings settings = {0};
diff --git a/src/starboard/nplb/cryptography_create_transformer_test.cc b/src/starboard/nplb/cryptography_create_transformer_test.cc
new file mode 100644
index 0000000..241d6cf
--- /dev/null
+++ b/src/starboard/nplb/cryptography_create_transformer_test.cc
@@ -0,0 +1,52 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/cryptography.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 4
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+const int kBlockSizeBits = 128;
+const int kBlockSizeBytes = kBlockSizeBits / 8;
+
+TEST(SbCryptographyCreateTransformer, SunnyDay) {
+ char initialization_vector[kBlockSizeBytes] = {0};
+ char key[kBlockSizeBytes] = {0};
+
+ // Try to create a transformer for the most likely algorithm to be supported:
+ // AES-128-CBC
+ SbCryptographyTransformer transformer = SbCryptographyCreateTransformer(
+ kSbCryptographyAlgorithmAes, kBlockSizeBits,
+ kSbCryptographyDirectionDecode, kSbCryptographyBlockCipherModeCbc,
+ initialization_vector, kBlockSizeBytes, key, kBlockSizeBytes);
+
+ if (!SbCryptographyIsTransformerValid(transformer)) {
+ // Test over if there's no implementation.
+ return;
+ }
+
+ // TODO: Validate implementation.
+ SbCryptographyDestroyTransformer(transformer);
+}
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
+
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/nplb/cryptography_transform_gcm_test.cc b/src/starboard/nplb/cryptography_transform_gcm_test.cc
new file mode 100644
index 0000000..819b63d
--- /dev/null
+++ b/src/starboard/nplb/cryptography_transform_gcm_test.cc
@@ -0,0 +1,466 @@
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+// Modifications Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This test is adapted from BoringSSL's crypto/fipsmodule/modes/gcm_test.cc
+
+#include "starboard/cryptography.h"
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/log.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 4
+
+using ::testing::NotNull;
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+const int kBlockSizeBits = 128;
+const int kBlockSizeBytes = kBlockSizeBits / 8;
+
+struct test_case {
+ const char *key;
+ const char *plaintext;
+ const char *additional_data;
+ const char *nonce;
+ const char *ciphertext;
+ const char *tag;
+};
+
+const struct test_case test_cases[] = {
+ {
+ "00000000000000000000000000000000",
+ NULL,
+ NULL,
+ "000000000000000000000000",
+ NULL,
+ "58e2fccefa7e3061367f1d57a4e7455a",
+ },
+ {
+ "00000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ NULL,
+ "000000000000000000000000",
+ "0388dace60b6a392f328c2b971b2fe78",
+ "ab6e47d42cec13bdf53a67b21257bddf",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+ NULL,
+ "cafebabefacedbaddecaf888",
+ "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985",
+ "4d5c2af327cd64a62cf35abd2ba6fab4",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbaddecaf888",
+ "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091",
+ "5bc94fbc3221a5db94fae95ae7121a47",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbad",
+ "61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598",
+ "3612d2e79e3b0785561be14aaca2fccb",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+ "8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5",
+ "619cc5aefffe0bfa462af43c1699d050",
+ },
+ {
+ "000000000000000000000000000000000000000000000000",
+ NULL,
+ NULL,
+ "000000000000000000000000",
+ NULL,
+ "cd33b28ac773f74ba00ed1f312572435",
+ },
+ {
+ "000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ NULL,
+ "000000000000000000000000",
+ "98e7247c07f0fe411c267e4384b0f600",
+ "2ff58d80033927ab8ef4d4587514f0fb",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+ NULL,
+ "cafebabefacedbaddecaf888",
+ "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256",
+ "9924a7c8587336bfb118024db8674a14",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbaddecaf888",
+ "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710",
+ "2519498e80f1478f37ba55bd6d27618c",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbad",
+ "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+ "65dcc57fcf623a24094fcca40d3533f8",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbad",
+ "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+ "65dcc57fcf623a24094fcca40d3533f8",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+ "d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b",
+ "dcf566ff291c25bbb8568fc3d376a6d9",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ NULL,
+ NULL,
+ "000000000000000000000000",
+ NULL,
+ "530f8afbc74536b9a963b4f1c4cb738b",
+ },
+ {
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ NULL,
+ "000000000000000000000000",
+ "cea7403d4d606b6e074ec5d3baf39d18",
+ "d0d1c8a799996bf0265b98b5d48ab919",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+ NULL,
+ "cafebabefacedbaddecaf888",
+ "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+ "b094dac5d93471bdec1a502270e3cc6c",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbaddecaf888",
+ "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662",
+ "76fc6ece0f4e1768cddf8853bb2d551b",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbad",
+ "c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f",
+ "3a337dbf46a792c45e454913fe2ea8f2",
+ },
+ {
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+ "5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f",
+ "a44a8266ee1c8eb0c8b5d4cf5ae9f19a",
+ },
+ {
+ "00000000000000000000000000000000",
+ NULL,
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+ "000000000000000000000000",
+ NULL,
+ "5fea793a2d6f974d37e68e0cb8ff9492",
+ },
+ {
+ "00000000000000000000000000000000",
+ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ NULL,
+ /* This nonce results in 0xfff in counter LSB. */
+ "ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "56b3373ca9ef6e4a2b64fe1e9a17b61425f10d47a75a5fce13efc6bc784af24f4141bdd48cf7c770887afd573cca5418a9aeffcd7c5ceddfc6a78397b9a85b499da558257267caab2ad0b23ca476a53cb17fb41c4b8b475cb4f3f7165094c229c9e8c4dc0a2a5ff1903e501511221376a1cdb8364c5061a20cae74bc4acd76ceb0abc9fd3217ef9f8c90be402ddf6d8697f4f880dff15bfb7a6b28241ec8fe183c2d59e3f9dfff653c7126f0acb9e64211f42bae12af462b1070bef1ab5e3606872ca10dee15b3249b1a1b958f23134c4bccb7d03200bce420a2f8eb66dcf3644d1423c1b5699003c13ecef4bf38a3b60eedc34033bac1902783dc6d89e2e774188a439c7ebcc0672dbda4ddcfb2794613b0be41315ef778708a70ee7d75165c",
+ "8b307f6b33286d0ab026a9ed3fe1e85f",
+ },
+};
+
+int from_hex(uint8_t *out, char in) {
+ if (in >= '0' && in <= '9') {
+ *out = in - '0';
+ return 1;
+ }
+ if (in >= 'a' && in <= 'f') {
+ *out = in - 'a' + 10;
+ return 1;
+ }
+ if (in >= 'A' && in <= 'F') {
+ *out = in - 'A' + 10;
+ return 1;
+ }
+
+ return 0;
+}
+
+void decode_hex(scoped_array<uint8_t> *out, int *out_len, const char *in,
+ int test_num, const char *description) {
+ if (in == NULL) {
+ out->reset();
+ *out_len = 0;
+ return;
+ }
+
+ size_t len = SbStringGetLength(in);
+ if (len & 1) {
+ ADD_FAILURE() << description << ": Odd length.";
+ return;
+ }
+
+ scoped_array<uint8_t> buf(new uint8_t[len / 2]);
+ if (!buf) {
+ ADD_FAILURE() << description << ": Memory fail.";
+ return;
+ }
+
+ for (size_t i = 0; i < len; i += 2) {
+ uint8_t v, v2;
+ bool result = from_hex(&v, in[i]) && from_hex(&v2, in[i + 1]);
+ if (!result) {
+ ADD_FAILURE() << description << ": Invalid character at " << i << ".";
+ continue;
+ }
+
+ buf[i / 2] = (v << 4) | v2;
+ }
+
+ *out = buf.Pass();
+ *out_len = len / 2;
+}
+
+std::string hexdump(const void *in, int len) {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(in);
+
+ std::string result;
+ for (size_t i = 0; i < len; i++) {
+ char hex[3] = {0};
+ SbStringFormatF(hex, 3, "%02x", data[i]);
+ result += hex;
+ }
+
+ return result;
+}
+
+class Gcm
+ : public ::testing::TestWithParam<int> {
+ public:
+ int GetTestCase() { return GetParam(); }
+};
+
+TEST_P(Gcm, TestCase) {
+ int test_num = GetTestCase();
+ const struct test_case *test = &test_cases[test_num];
+
+ int key_len;
+ scoped_array<uint8_t> key;
+ int plaintext_len;
+ scoped_array<uint8_t> plaintext;
+ int additional_data_len;
+ scoped_array<uint8_t> additional_data;
+ int nonce_len;
+ scoped_array<uint8_t> nonce;
+ int ciphertext_len;
+ scoped_array<uint8_t> ciphertext;
+ int tag_len;
+ scoped_array<uint8_t> tag;
+ scoped_array<uint8_t> out;
+
+ decode_hex(&key, &key_len, test->key, test_num, "key");
+ decode_hex(&plaintext, &plaintext_len, test->plaintext, test_num,
+ "plaintext");
+ decode_hex(&additional_data, &additional_data_len,
+ test->additional_data, test_num, "additional_data");
+ decode_hex(&nonce, &nonce_len, test->nonce, test_num, "nonce");
+ decode_hex(&ciphertext, &ciphertext_len, test->ciphertext, test_num,
+ "ciphertext");
+ decode_hex(&tag, &tag_len, test->tag, test_num, "tag");
+
+ if (plaintext_len != ciphertext_len) {
+ FAIL() << "Plaintext and ciphertext have differing lengths.";
+ }
+
+ if (key_len != 16 && key_len != 24 && key_len != 32) {
+ FAIL() << "Bad key length.";
+ }
+
+ if (tag_len != 16) {
+ FAIL() << "Bad tag length.";
+ }
+
+ out.reset(new uint8_t[plaintext_len]);
+ if (plaintext_len != 0 && out == NULL) {
+ FAIL() << "Out of memory.";
+ }
+
+ // Test encryption.
+
+ // Try to create a transformer for GCM.
+ SbCryptographyTransformer encrypter = SbCryptographyCreateTransformer(
+ kSbCryptographyAlgorithmAes, kBlockSizeBits,
+ kSbCryptographyDirectionEncode, kSbCryptographyBlockCipherModeGcm,
+ NULL, 0, key.get(), key_len);
+
+ if (!SbCryptographyIsTransformerValid(encrypter)) {
+ // Test over (but no failure) if there's no implementation.
+ return;
+ }
+
+ SbCryptographySetInitializationVector(encrypter, nonce.get(), nonce_len);
+ SbMemorySet(out.get(), 0, plaintext_len);
+ if (additional_data) {
+ SbCryptographySetAuthenticatedData(encrypter, additional_data.get(),
+ additional_data_len);
+ }
+
+ if (plaintext) {
+ EXPECT_EQ(plaintext_len,
+ SbCryptographyTransform(encrypter, plaintext.get(), plaintext_len,
+ out.get()));
+ }
+
+ scoped_array<uint8_t> actual_tag(new uint8_t[tag_len]);
+ SbMemorySet(actual_tag.get(), 0, tag_len);
+ SbCryptographyGetTag(encrypter, actual_tag.get(), tag_len);
+ if (tag) {
+ EXPECT_STREQ(hexdump(tag.get(), tag_len).c_str(),
+ hexdump(actual_tag.get(), tag_len).c_str());
+ }
+
+ if (ciphertext) {
+ EXPECT_STREQ(hexdump(ciphertext.get(), plaintext_len).c_str(),
+ hexdump(out.get(), plaintext_len).c_str());
+ }
+
+ SbCryptographyDestroyTransformer(encrypter);
+
+ // Test decryption.
+
+ SbCryptographyTransformer decrypter = SbCryptographyCreateTransformer(
+ kSbCryptographyAlgorithmAes, kBlockSizeBits,
+ kSbCryptographyDirectionDecode, kSbCryptographyBlockCipherModeGcm,
+ NULL, 0, key.get(), key_len);
+ ASSERT_THAT(decrypter, NotNull());
+
+ SbCryptographySetInitializationVector(decrypter, nonce.get(), nonce_len);
+ SbMemorySet(out.get(), 0, plaintext_len);
+ if (additional_data) {
+ SbCryptographySetAuthenticatedData(decrypter, additional_data.get(),
+ additional_data_len);
+ }
+
+ if (ciphertext) {
+ EXPECT_EQ(plaintext_len,
+ SbCryptographyTransform(decrypter, ciphertext.get(),
+ plaintext_len, out.get()));
+ }
+
+ actual_tag.reset(new uint8_t[tag_len]);
+ SbMemorySet(actual_tag.get(), 0, tag_len);
+ SbCryptographyGetTag(decrypter, actual_tag.get(), tag_len);
+ if (tag) {
+ EXPECT_STREQ(hexdump(tag.get(), tag_len).c_str(),
+ hexdump(actual_tag.get(), tag_len).c_str());
+ }
+
+ if (plaintext) {
+ EXPECT_STREQ(hexdump(plaintext.get(), plaintext_len).c_str(),
+ hexdump(out.get(), plaintext_len).c_str());
+ }
+ SbCryptographyDestroyTransformer(decrypter);
+}
+
+INSTANTIATE_TEST_CASE_P(
+ SbCryptographyTransform,
+ Gcm,
+ ::testing::Range(0, SB_ARRAY_SIZE_INT(test_cases)));
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
+
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/nplb/cryptography_transform_test.cc b/src/starboard/nplb/cryptography_transform_test.cc
new file mode 100644
index 0000000..ae916a7
--- /dev/null
+++ b/src/starboard/nplb/cryptography_transform_test.cc
@@ -0,0 +1,85 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/cryptography.h"
+
+#include "starboard/log.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_API_VERSION >= 4
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+const int kBlockSizeBits = 128;
+const int kBlockSizeBytes = kBlockSizeBits / 8;
+
+TEST(SbCryptographyTransform, SunnyDay) {
+ char initialization_vector[kBlockSizeBytes + 1] = "0123456789ABCDEF";
+ char key[kBlockSizeBytes + 1] = "RijndaelRijndael";
+ const char kClearText[] =
+ "This test text is designed to be a multiple of "
+ "128 bits, huzzah.";
+
+ // Try to create a transformer for the most likely algorithm to be supported:
+ // AES-128-CBC
+ SbCryptographyTransformer transformer = SbCryptographyCreateTransformer(
+ kSbCryptographyAlgorithmAes, kBlockSizeBits,
+ kSbCryptographyDirectionEncode, kSbCryptographyBlockCipherModeCbc,
+ initialization_vector, kBlockSizeBytes, key, kBlockSizeBytes);
+
+ if (!SbCryptographyIsTransformerValid(transformer)) {
+ // Test over if there's no implementation.
+ return;
+ }
+
+ const int kInputSize = SbStringGetLength(kClearText);
+ const int kBufferSize = sizeof(kClearText);
+ char* cipher_text = new char[kBufferSize];
+ SbMemorySet(cipher_text, 0, kBufferSize);
+ int count = SbCryptographyTransform(transformer, kClearText, kInputSize,
+ cipher_text);
+ EXPECT_EQ(kInputSize, count);
+ EXPECT_NE(0, SbStringCompare(kClearText, cipher_text, kBufferSize));
+
+ SbCryptographyTransformer decode_transformer =
+ SbCryptographyCreateTransformer(
+ kSbCryptographyAlgorithmAes, kBlockSizeBits,
+ kSbCryptographyDirectionDecode, kSbCryptographyBlockCipherModeCbc,
+ initialization_vector, kBlockSizeBytes, key, kBlockSizeBytes);
+
+ ASSERT_TRUE(SbCryptographyIsTransformerValid(decode_transformer));
+
+ char* decrypted_text = new char[kBufferSize];
+ SbMemorySet(decrypted_text, 0, kBufferSize);
+ count =
+ SbCryptographyTransform(decode_transformer, cipher_text, kInputSize,
+ decrypted_text);
+
+ EXPECT_EQ(kInputSize, count);
+ EXPECT_EQ(kInputSize, SbStringGetLength(decrypted_text));
+ EXPECT_STREQ(kClearText, decrypted_text);
+
+ delete[] decrypted_text;
+ delete[] cipher_text;
+ SbCryptographyDestroyTransformer(decode_transformer);
+ SbCryptographyDestroyTransformer(transformer);
+}
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
+
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/nplb/decode_target_create_test.cc b/src/starboard/nplb/decode_target_create_test.cc
index 33235e6..d6edb3a 100644
--- a/src/starboard/nplb/decode_target_create_test.cc
+++ b/src/starboard/nplb/decode_target_create_test.cc
@@ -19,7 +19,7 @@
// which will define None to be 0L, which conflicts with gtest.
#include "starboard/decode_target.h" // NOLINT(build/include_order)
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_API_VERSION < 4 && SB_HAS(GRAPHICS)
#if SB_HAS(BLITTER)
#include "starboard/blitter.h"
@@ -42,7 +42,7 @@
ASSERT_TRUE(SbBlitterSetRenderTarget(env.context(), env.render_target()));
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget target = SbDecodeTargetCreate(
env.device(), kSbDecodeTargetFormat1PlaneRGBA, kWidth, kHeight);
if (SbDecodeTargetIsValid(target)) {
@@ -59,7 +59,7 @@
SbBlitterIsSurfaceValid(info.planes[kSbDecodeTargetPlaneRGBA].surface));
}
SbDecodeTargetRelease(target);
-#else // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // SB_API_VERSION >= 4
SbBlitterSurface surface =
CreateArbitraryRenderTargetSurface(env.device(), kWidth, kHeight);
@@ -72,7 +72,7 @@
}
SbDecodeTargetDestroy(target);
EXPECT_TRUE(SbBlitterDestroySurface(surface));
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
}
#elif SB_HAS(GLES2) // SB_HAS(BLITTER)
// clang-format off
@@ -175,7 +175,7 @@
SbWindow window_;
};
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
TEST_F(SbDecodeTargetTest, SunnyDayCreate) {
const int kTextureWidth = 256;
const int kTextureHeight = 256;
@@ -200,7 +200,7 @@
SbDecodeTargetRelease(target);
}
}
-#else // #if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // #if SB_API_VERSION >= 4
TEST_F(SbDecodeTargetTest, SunnyDayCreate) {
// Generate a texture to put in the SbDecodeTarget.
const int kTextureWidth = 256;
@@ -224,7 +224,7 @@
}
glDeleteTextures(1, &texture_handle);
}
-#endif // #if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // #if SB_API_VERSION >= 4
#else // SB_HAS(BLITTER)
@@ -244,4 +244,6 @@
} // namespace nplb
} // namespace starboard
-#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif // SB_API_VERSION >= 3 && \
+ // SB_API_VERSION < 4 && \
+ // SB_HAS(GRAPHICS)
diff --git a/src/starboard/nplb/decode_target_provider_test.cc b/src/starboard/nplb/decode_target_provider_test.cc
index 78fc485..1da7057 100644
--- a/src/starboard/nplb/decode_target_provider_test.cc
+++ b/src/starboard/nplb/decode_target_provider_test.cc
@@ -18,10 +18,12 @@
// 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 {
+
+#if SB_HAS(GRAPHICS)
+#if SB_API_VERSION >= 3 && SB_API_VERSION < 4
+
namespace {
struct ProviderContext {
@@ -50,11 +52,11 @@
bool valid = SbDecodeTargetIsValid(target);
EXPECT_FALSE(valid);
if (valid) {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTargetRelease(target);
-#else // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // SB_API_VERSION >= 4
SbDecodeTargetDestroy(target);
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
}
}
}
@@ -152,7 +154,10 @@
}
} // namespace
+
+#endif // SB_API_VERSION >= 3 && \
+ // SB_API_VERSION < 4
+#endif // SB_HAS(GRAPHICS)
+
} // 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 69ce553..2b8a50d 100644
--- a/src/starboard/nplb/include_all.c
+++ b/src/starboard/nplb/include_all.c
@@ -22,6 +22,7 @@
#include "starboard/character.h"
#include "starboard/condition_variable.h"
#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
#include "starboard/decode_target.h"
#include "starboard/directory.h"
#include "starboard/double.h"
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 43576cd..92c4f2d 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -81,6 +81,9 @@
'condition_variable_wait_test.cc',
'condition_variable_wait_timed_test.cc',
'configuration_test.cc',
+ 'cryptography_create_transformer_test.cc',
+ 'cryptography_transform_test.cc',
+ 'cryptography_transform_gcm_test.cc',
'decode_target_create_test.cc',
'decode_target_provider_test.cc',
'directory_can_open_test.cc',
@@ -120,9 +123,9 @@
'memory_copy_test.cc',
'memory_deallocate_aligned_test.cc',
'memory_deallocate_test.cc',
+ 'memory_find_byte_test.cc',
'memory_get_stack_bounds_test.cc',
'memory_is_zero_test.cc',
- 'memory_find_byte_test.cc',
'memory_map_test.cc',
'memory_move_test.cc',
'memory_reallocate_test.cc',
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index f4247c1..3bff176 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "starboard/blitter.h"
#include "starboard/decode_target.h"
#include "starboard/player.h"
#include "starboard/window.h"
@@ -22,7 +23,18 @@
namespace starboard {
namespace nplb {
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+#if SB_HAS(GLES2)
+void GlesContextRunner(
+ SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
+ SbDecodeTargetGlesContextRunnerTarget target_function,
+ void* target_function_context) {
+ SB_UNREFERENCED_PARAMETER(graphics_context_provider);
+ SB_UNREFERENCED_PARAMETER(target_function);
+ SB_UNREFERENCED_PARAMETER(target_function_context);
+}
+#endif // SB_HAS(GLES2)
+#elif SB_API_VERSION >= 3
SbDecodeTarget SbDecodeTargetAcquireStub(void* /*context*/,
SbDecodeTargetFormat /*format*/,
int /*width*/,
@@ -55,7 +67,7 @@
SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
kSbPlayerOutputModePunchOut};
@@ -64,9 +76,21 @@
if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) {
continue;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
-#if SB_API_VERSION >= 3
+#if SB_API_VERSION >= 4
+ SbDecodeTargetGraphicsContextProvider
+ decode_target_graphics_context_provider;
+#if SB_HAS(BLITTER)
+ decode_target_graphics_context_provider.device = kSbBlitterInvalidDevice;
+#elif SB_HAS(GLES2)
+ decode_target_graphics_context_provider.egl_display = NULL;
+ decode_target_graphics_context_provider.egl_context = NULL;
+ decode_target_graphics_context_provider.gles_context_runner =
+ &GlesContextRunner;
+ decode_target_graphics_context_provider.gles_context_runner_context = NULL;
+#endif // SB_HAS(BLITTER)
+#elif SB_API_VERSION >= 3
SbDecodeTargetProvider decode_target_provider;
decode_target_provider.acquire = &SbDecodeTargetAcquireStub;
decode_target_provider.release = &SbDecodeTargetReleaseStub;
@@ -77,29 +101,30 @@
SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,
SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid,
&audio_header, NULL, NULL, NULL, NULL
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
,
- output_mode
-#endif
-#if SB_API_VERSION >= 3
+ output_mode,
+ &decode_target_graphics_context_provider
+#elif SB_API_VERSION >= 3
,
&decode_target_provider
#endif
); // NOLINT
EXPECT_TRUE(SbPlayerIsValid(player));
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
if (output_mode == kSbPlayerOutputModeDecodeToTexture) {
SbDecodeTarget current_frame = SbPlayerGetCurrentFrame(player);
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
SbPlayerDestroy(player);
- SbWindowDestroy(window);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
+
+ SbWindowDestroy(window);
}
} // namespace nplb
diff --git a/src/starboard/nplb/player_output_mode_supported_test.cc b/src/starboard/nplb/player_output_mode_supported_test.cc
index 4336204..ffffb84 100644
--- a/src/starboard/nplb/player_output_mode_supported_test.cc
+++ b/src/starboard/nplb/player_output_mode_supported_test.cc
@@ -17,7 +17,7 @@
#include "starboard/window.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if SB_HAS(PLAYER) && SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_HAS(PLAYER) && SB_API_VERSION >= 4
namespace starboard {
namespace nplb {
@@ -45,4 +45,4 @@
} // namespace starboard
#endif // SB_HAS(PLAYER) && \
- SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+ SB_API_VERSION >= 4
diff --git a/src/starboard/nplb/semaphore_test.cc b/src/starboard/nplb/semaphore_test.cc
index 709ab67..05de364 100644
--- a/src/starboard/nplb/semaphore_test.cc
+++ b/src/starboard/nplb/semaphore_test.cc
@@ -95,20 +95,37 @@
kSbTimeMillisecond * 10); // Error threshold
}
+double IsDoubleNear(double first, double second, double diff_threshold) {
+ double diff = first - second;
+ if (diff < 0) {
+ diff = -diff;
+ }
+ return diff < diff_threshold;
+}
+
TEST(Semaphore, ThreadTakesWait_TimeExpires) {
- SbTime wait_time = kSbTimeMillisecond * 20;
- ThreadTakesWaitSemaphore thread(wait_time);
- thread.Start();
+ const int attempts = 20; // Retest up to 20 times.
+ bool passed = false;
- SbThreadSleep(wait_time * 2);
- thread.semaphore_.Put();
+ const SbTime kTimeThreshold = kSbTimeMillisecond * 5;
- thread.Join();
+ for (int i = 0; i < attempts; ++i) {
+ SbTime wait_time = kSbTimeMillisecond * 20;
+ ThreadTakesWaitSemaphore thread(wait_time);
+ thread.Start();
- EXPECT_FALSE(thread.result_signaled_);
- EXPECT_NEAR(thread.result_wait_time_,
- wait_time,
- kSbTimeMillisecond * 5); // Error threshold
+ SbThreadSleep(wait_time * 2);
+ thread.semaphore_.Put();
+
+ thread.Join();
+ EXPECT_FALSE(thread.result_signaled_);
+
+ if (IsDoubleNear(wait_time, thread.result_wait_time_, kTimeThreshold)) {
+ return; // Test passed.
+ }
+ }
+
+ EXPECT_TRUE(false) << "Thread waited, but time exceeded expectations.";
}
class ThreadPutsSemaphore : public AbstractTestThread {
diff --git a/src/starboard/nplb/socket_accept_test.cc b/src/starboard/nplb/socket_accept_test.cc
index f9e9b9e..9c3357c 100644
--- a/src/starboard/nplb/socket_accept_test.cc
+++ b/src/starboard/nplb/socket_accept_test.cc
@@ -24,13 +24,17 @@
namespace nplb {
namespace {
-TEST(SbSocketAcceptTest, RainyDayNoConnection) {
+class SbSocketAcceptTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+TEST_P(SbSocketAcceptTest, RainyDayNoConnection) {
// Set up a socket to listen.
SbSocket server_socket =
- CreateListeningTcpIpv4Socket(GetPortNumberForTests());
- if (!SbSocketIsValid(server_socket)) {
- return;
- }
+ CreateListeningTcpSocket(GetAddressType(), GetPortNumberForTests());
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
// Don't create a socket to connect to it.
@@ -43,12 +47,10 @@
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
-TEST(SbSocketAcceptTest, RainyDayNotBound) {
+TEST_P(SbSocketAcceptTest, RainyDayNotBound) {
// Set up a socket, but don't Bind or Listen.
- SbSocket server_socket = CreateServerTcpIpv4Socket();
- if (!SbSocketIsValid(server_socket)) {
- return;
- }
+ SbSocket server_socket = CreateServerTcpSocket(GetAddressType());
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
// Accept should result in an error.
EXPECT_EQ(kSbSocketInvalid, SbSocketAccept(server_socket));
@@ -57,12 +59,11 @@
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
-TEST(SbSocketAcceptTest, RainyDayNotListening) {
+TEST_P(SbSocketAcceptTest, RainyDayNotListening) {
// Set up a socket, but don't Bind or Listen.
- SbSocket server_socket = CreateBoundTcpIpv4Socket(GetPortNumberForTests());
- if (!SbSocketIsValid(server_socket)) {
- return;
- }
+ SbSocket server_socket =
+ CreateBoundTcpSocket(GetAddressType(), GetPortNumberForTests());
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
// Accept should result in an error.
EXPECT_EQ(kSbSocketInvalid, SbSocketAccept(server_socket));
@@ -71,6 +72,17 @@
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketAcceptTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketAcceptTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_bind_test.cc b/src/starboard/nplb/socket_bind_test.cc
index 81cb4e8..04b9c5e 100644
--- a/src/starboard/nplb/socket_bind_test.cc
+++ b/src/starboard/nplb/socket_bind_test.cc
@@ -15,6 +15,8 @@
// The basic Sunny Day test is a subset of other Sunny Day tests, so it is not
// repeated here.
+#include <utility>
+
#include "starboard/nplb/socket_helpers.h"
#include "starboard/socket.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -23,62 +25,84 @@
namespace nplb {
namespace {
+class SbSocketBindTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketResolveFilter> > {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam().first; }
+ SbSocketResolveFilter GetFilterType() { return GetParam().second; }
+};
+
+#if SB_HAS(IPV6)
+class PairSbSocketBindTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+ SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+ SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+#endif
+
// This is to use NULL in asserts, which otherwise complain about long
// vs. pointer type.
const void* kNull = NULL;
-TEST(SbSocketBindTest, RainyDayNullSocket) {
- SbSocketAddress address = GetIpv4Unspecified(GetPortNumberForTests());
+TEST_P(SbSocketBindTest, RainyDayNullSocket) {
+ SbSocketAddress address =
+ GetUnspecifiedAddress(GetAddressType(), GetPortNumberForTests());
EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(kSbSocketInvalid, &address));
}
-TEST(SbSocketBindTest, RainyDayNullAddress) {
- SbSocket server_socket = CreateServerTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(SbSocketBindTest, RainyDayNullAddress) {
+ SbSocket server_socket = CreateServerTcpSocket(GetAddressType());
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
// Binding with a NULL address should fail.
EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(server_socket, NULL));
// Even though that failed, binding the same socket now with 0.0.0.0:2048
// should work.
- SbSocketAddress address = GetIpv4Unspecified(GetPortNumberForTests());
+ SbSocketAddress address =
+ GetUnspecifiedAddress(GetAddressType(), GetPortNumberForTests());
EXPECT_EQ(kSbSocketOk, SbSocketBind(server_socket, &address));
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
-TEST(SbSocketBindTest, RainyDayNullNull) {
+TEST_F(SbSocketBindTest, RainyDayNullNull) {
EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(kSbSocketInvalid, NULL));
}
#if SB_HAS(IPV6)
-TEST(SbSocketBindTest, RainyDayWrongAddressType) {
- SbSocket server_socket = CreateServerTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(PairSbSocketBindTest, RainyDayWrongAddressType) {
+ SbSocket server_socket = CreateServerTcpSocket(GetServerAddressType());
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
// Binding with the wrong address type should fail.
- SbSocketAddress address = GetIpv6Unspecified(GetPortNumberForTests());
+ SbSocketAddress address =
+ GetUnspecifiedAddress(GetClientAddressType(), GetPortNumberForTests());
EXPECT_EQ(kSbSocketErrorFailed, SbSocketBind(server_socket, &address));
- // Even though that failed, binding the same socket now with 0.0.0.0:2048
- // should work.
- address = GetIpv4Unspecified(GetPortNumberForTests());
+ // Even though that failed, binding the same socket now with the server
+ // address type should work.
+ address =
+ GetUnspecifiedAddress(GetServerAddressType(), GetPortNumberForTests());
EXPECT_EQ(kSbSocketOk, SbSocketBind(server_socket, &address));
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
#endif
-TEST(SbSocketBindTest, RainyDayBadInterface) {
- SbSocket server_socket = CreateServerTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(SbSocketBindTest, RainyDayBadInterface) {
+ SbSocket server_socket = CreateServerTcpSocket(GetAddressType());
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
// Binding with an interface that doesn't exist on this device should fail, so
// let's find an address of a well-known public website that we shouldn't be
// able to bind to.
const char* kTestHostName = "www.yahoo.com";
SbSocketResolution* resolution =
- SbSocketResolve(kTestHostName, kSbSocketResolveFilterIpv4);
+ SbSocketResolve(kTestHostName, GetFilterType());
ASSERT_NE(kNull, resolution);
EXPECT_LT(0, resolution->address_count);
EXPECT_EQ(kSbSocketErrorFailed,
@@ -88,17 +112,42 @@
SbSocketFreeResolution(resolution);
}
-TEST(SbSocketBindTest, SunnyDayLocalInterface) {
- SbSocket server_socket = CreateServerTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(server_socket));
-
+TEST_F(SbSocketBindTest, SunnyDayLocalInterface) {
SbSocketAddress address = {0};
+#if SB_API_VERSION < 4
EXPECT_TRUE(SbSocketGetLocalInterfaceAddress(&address));
+#else
+ EXPECT_TRUE(SbSocketGetInterfaceAddress(NULL, &address, NULL));
+#endif // SB_API_VERSION < 4
+ SbSocket server_socket = CreateServerTcpSocket(address.type);
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
+
EXPECT_EQ(kSbSocketOk, SbSocketBind(server_socket, &address));
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ SbSocketBindTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketResolveFilterIpv4),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketResolveFilterIpv6)));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketBindTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv6),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ SbSocketBindTest,
+ ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+ kSbSocketResolveFilterIpv4)));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_clear_last_error_test.cc b/src/starboard/nplb/socket_clear_last_error_test.cc
index dd54d13..bce6015 100644
--- a/src/starboard/nplb/socket_clear_last_error_test.cc
+++ b/src/starboard/nplb/socket_clear_last_error_test.cc
@@ -25,7 +25,7 @@
TEST(SbSocketClearLastErrorTest, SunnyDay) {
// Set up a socket, but don't Bind or Listen.
- SbSocket server_socket = CreateServerTcpIpv4Socket();
+ SbSocket server_socket = CreateServerTcpSocket(kSbSocketAddressTypeIpv4);
ASSERT_TRUE(SbSocketIsValid(server_socket));
// Accept on the unbound socket should result in an error.
diff --git a/src/starboard/nplb/socket_connect_test.cc b/src/starboard/nplb/socket_connect_test.cc
index 64b8dba..34378cf 100644
--- a/src/starboard/nplb/socket_connect_test.cc
+++ b/src/starboard/nplb/socket_connect_test.cc
@@ -23,22 +23,39 @@
namespace nplb {
namespace {
-TEST(SbSocketConnectTest, RainyDayNullSocket) {
- SbSocketAddress address = GetIpv4Unspecified(2048);
+class SbSocketConnectTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+TEST_P(SbSocketConnectTest, RainyDayNullSocket) {
+ SbSocketAddress address = GetUnspecifiedAddress(GetAddressType(), 2048);
EXPECT_EQ(kSbSocketErrorFailed, SbSocketConnect(kSbSocketInvalid, &address));
}
-TEST(SbSocketConnectTest, RainyDayNullAddress) {
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+TEST_P(SbSocketConnectTest, RainyDayNullAddress) {
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_EQ(kSbSocketErrorFailed, SbSocketConnect(socket, NULL));
EXPECT_TRUE(SbSocketDestroy(socket));
}
-TEST(SbSocketConnectTest, RainyDayNullNull) {
+TEST_F(SbSocketConnectTest, RainyDayNullNull) {
EXPECT_EQ(kSbSocketErrorFailed, SbSocketConnect(kSbSocketInvalid, NULL));
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketConnectTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketConnectTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_create_test.cc b/src/starboard/nplb/socket_create_test.cc
index fea779a..d379f26 100644
--- a/src/starboard/nplb/socket_create_test.cc
+++ b/src/starboard/nplb/socket_create_test.cc
@@ -19,59 +19,59 @@
namespace nplb {
namespace {
-TEST(SbSocketCreateTest, TcpIpv4) {
- SbSocket socket =
- SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
- EXPECT_TRUE(SbSocketIsValid(socket));
+class SbSocketCreateTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketCreateTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketProtocol> > {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam().first; }
+ SbSocketProtocol GetProtocol() { return GetParam().second; }
+};
+
+TEST_P(PairSbSocketCreateTest, Create) {
+ SbSocket socket = SbSocketCreate(GetAddressType(), GetProtocol());
+#if !SB_HAS(IPV6)
+ // It is allowed for a platform not to support IPv6 sockets, but we use this
+ // test to at least exercise the code path.
+ if (kSbSocketAddressTypeIpv6 == GetAddressType()) {
+ if (SbSocketIsValid(socket)) {
+ EXPECT_TRUE(SbSocketDestroy(socket));
+ }
+ return;
+ }
+#endif
+ if (kSbSocketProtocolUdp == GetProtocol()) {
+ // It is allowed for a platform not to support UDP sockets, but we use this
+ // test to at least exercise the code path.
+ if (SbSocketIsValid(socket)) {
+ EXPECT_TRUE(SbSocketDestroy(socket));
+ }
+ return;
+ }
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_TRUE(SbSocketDestroy(socket));
}
-TEST(SbSocketCreateTest, TcpIpv6) {
- // It is allowed for a platform not to support IPv6 sockets, but we use this
- // test to at least exercise the code path.
- SbSocket socket =
- SbSocketCreate(kSbSocketAddressTypeIpv6, kSbSocketProtocolTcp);
- if (SbSocketIsValid(socket)) {
- EXPECT_TRUE(SbSocketDestroy(socket));
- }
-}
-
-TEST(SbSocketCreateTest, UdpIpv4) {
- // It is allowed for a platform not to support UDP sockets, but we use this
- // test to at least exercise the code path.
- SbSocket socket =
- SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp);
- if (SbSocketIsValid(socket)) {
- EXPECT_TRUE(SbSocketDestroy(socket));
- }
-}
-
-TEST(SbSocketCreateTest, UdpIpv6) {
- // It is allowed for a platform not to support UDP and/or IPv6 sockets, but we
- // use this test to at least exercise the code path.
- SbSocket socket =
- SbSocketCreate(kSbSocketAddressTypeIpv6, kSbSocketProtocolUdp);
- if (SbSocketIsValid(socket)) {
- EXPECT_TRUE(SbSocketDestroy(socket));
- }
-}
-
-TEST(SbSocketCreateTest, ATonOfTcpIpv4) {
+TEST_P(SbSocketCreateTest, ATonOfTcp) {
const int kATon = 4096;
for (int i = 0; i < kATon; ++i) {
- SbSocket socket =
- SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
- EXPECT_TRUE(SbSocketIsValid(socket));
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_TRUE(SbSocketDestroy(socket));
}
}
-TEST(SbSocketCreateTest, ManyTcpIpv4AtOnce) {
+TEST_P(SbSocketCreateTest, ManyTcpAtOnce) {
const int kMany = 128;
SbSocket sockets[kMany] = {0};
for (int i = 0; i < kMany; ++i) {
- sockets[i] = SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
- EXPECT_TRUE(SbSocketIsValid(sockets[i]));
+ sockets[i] = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(sockets[i]));
}
for (int i = 0; i < kMany; ++i) {
@@ -79,6 +79,26 @@
}
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketCreateTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketCreateTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
+INSTANTIATE_TEST_CASE_P(
+ SbSocketTypes,
+ PairSbSocketCreateTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp),
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketProtocolTcp),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketProtocolUdp)));
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_get_local_address_test.cc b/src/starboard/nplb/socket_get_local_address_test.cc
index 112f9a4..2021446 100644
--- a/src/starboard/nplb/socket_get_local_address_test.cc
+++ b/src/starboard/nplb/socket_get_local_address_test.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <utility>
+
#include "starboard/nplb/socket_helpers.h"
#include "starboard/socket.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -20,40 +22,54 @@
namespace nplb {
namespace {
-TEST(SbSocketGetLocalAddressTest, RainyDayInvalidSocket) {
+class SbSocketGetLocalAddressTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketGetLocalAddressTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+ SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+ SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
+TEST_F(SbSocketGetLocalAddressTest, RainyDayInvalidSocket) {
SbSocketAddress address = {0};
EXPECT_FALSE(SbSocketGetLocalAddress(kSbSocketInvalid, &address));
}
-TEST(SbSocketGetLocalAddressTest, RainyDayNullAddress) {
- SbSocket server_socket = CreateBoundTcpIpv4Socket(0);
- EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(SbSocketGetLocalAddressTest, RainyDayNullAddress) {
+ SbSocket server_socket = CreateBoundTcpSocket(GetAddressType(), 0);
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
EXPECT_FALSE(SbSocketGetLocalAddress(server_socket, NULL));
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
-TEST(SbSocketGetLocalAddressTest, RainyDayNullNull) {
+TEST_F(SbSocketGetLocalAddressTest, RainyDayNullNull) {
EXPECT_FALSE(SbSocketGetLocalAddress(kSbSocketInvalid, NULL));
}
-TEST(SbSocketGetLocalAddressTest, SunnyDayUnbound) {
- SbSocket server_socket = CreateServerTcpIpv4Socket();
+TEST_P(SbSocketGetLocalAddressTest, SunnyDayUnbound) {
+ SbSocket server_socket = CreateServerTcpSocket(GetAddressType());
SbSocketAddress address = {0};
EXPECT_TRUE(SbSocketGetLocalAddress(server_socket, &address));
- EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type);
+ EXPECT_EQ(GetAddressType(), address.type);
EXPECT_EQ(0, address.port);
EXPECT_TRUE(IsUnspecified(&address));
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
-TEST(SbSocketGetLocalAddressTest, SunnyDayBoundUnspecified) {
- SbSocket server_socket = CreateBoundTcpIpv4Socket(0);
- EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(SbSocketGetLocalAddressTest, SunnyDayBoundUnspecified) {
+ SbSocket server_socket = CreateBoundTcpSocket(GetAddressType(), 0);
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
SbSocketAddress address = {0};
EXPECT_TRUE(SbSocketGetLocalAddress(server_socket, &address));
- EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type);
+ EXPECT_EQ(GetAddressType(), address.type);
EXPECT_NE(0, address.port);
// We bound to an unspecified address, so it should come back that way.
@@ -62,31 +78,31 @@
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
-TEST(SbSocketGetLocalAddressTest, SunnyDayBoundSpecified) {
- SbSocket server_socket = CreateServerTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_F(SbSocketGetLocalAddressTest, SunnyDayBoundSpecified) {
+ SbSocketAddress interface_address = {0};
+#if SB_API_VERSION < 4
+ EXPECT_TRUE(SbSocketGetLocalInterfaceAddress(&interface_address));
+#else
+ EXPECT_TRUE(SbSocketGetInterfaceAddress(NULL, &interface_address, NULL));
+#endif
+ SbSocket server_socket = CreateServerTcpSocket(interface_address.type);
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
- {
- SbSocketAddress address = {0};
- EXPECT_TRUE(SbSocketGetLocalInterfaceAddress(&address));
- EXPECT_EQ(kSbSocketOk, SbSocketBind(server_socket, &address));
- }
+ EXPECT_EQ(kSbSocketOk, SbSocketBind(server_socket, &interface_address));
- SbSocketAddress address = {0};
- EXPECT_TRUE(SbSocketGetLocalAddress(server_socket, &address));
- EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type);
- EXPECT_NE(0, address.port);
- EXPECT_FALSE(IsUnspecified(&address));
+ SbSocketAddress local_address = {0};
+ EXPECT_TRUE(SbSocketGetLocalAddress(server_socket, &local_address));
+ EXPECT_NE(0, local_address.port);
+ EXPECT_FALSE(IsUnspecified(&local_address));
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
-TEST(SbSocketGetLocalAddressTest, SunnyDayConnected) {
+TEST_P(PairSbSocketGetLocalAddressTest, SunnyDayConnected) {
const int kPort = GetPortNumberForTests();
- ConnectedTrio trio = CreateAndConnect(kPort, kSocketTimeout);
- if (!SbSocketIsValid(trio.server_socket)) {
- return;
- }
+ ConnectedTrio trio = CreateAndConnect(
+ GetServerAddressType(), GetClientAddressType(), kPort, kSocketTimeout);
+ ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
SbSocketAddress address = {0};
EXPECT_TRUE(SbSocketGetLocalAddress(trio.listen_socket, &address));
@@ -108,6 +124,29 @@
EXPECT_TRUE(SbSocketDestroy(trio.listen_socket));
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketGetLocalAddressTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketGetLocalAddressTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketGetLocalAddressTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketGetLocalAddressTest,
+ ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv4)));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_get_local_interface_address_test.cc b/src/starboard/nplb/socket_get_local_interface_address_test.cc
index 1051c24..566aad3 100644
--- a/src/starboard/nplb/socket_get_local_interface_address_test.cc
+++ b/src/starboard/nplb/socket_get_local_interface_address_test.cc
@@ -20,21 +20,45 @@
namespace nplb {
namespace {
-TEST(SbSocketGetLocalInterfaceTest, SunnyDay) {
+class SbSocketGetLocalInterfaceAddressTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+TEST_F(SbSocketGetLocalInterfaceAddressTest, SunnyDay) {
SbSocketAddress address;
// Initialize to something invalid.
SbMemorySet(&address, 0xFE, sizeof(address));
+#if SB_API_VERSION < 4
EXPECT_TRUE(SbSocketGetLocalInterfaceAddress(&address));
+#else
+ EXPECT_TRUE(SbSocketGetInterfaceAddress(NULL, &address, NULL));
+#endif // SB_API_VERSION < 4
EXPECT_EQ(0, address.port);
- EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type);
EXPECT_FALSE(IsUnspecified(&address));
EXPECT_FALSE(IsLocalhost(&address));
}
-TEST(SbSocketGetLocalInterfaceTest, RainyDayNull) {
+TEST_F(SbSocketGetLocalInterfaceAddressTest, RainyDayNull) {
+#if SB_API_VERSION < 4
EXPECT_FALSE(SbSocketGetLocalInterfaceAddress(NULL));
+#else
+ EXPECT_FALSE(SbSocketGetInterfaceAddress(NULL, NULL, NULL));
+#endif // SB_API_VERSION < 4
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketGetLocalInterfaceAddressTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketGetLocalInterfaceAddressTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_helpers.cc b/src/starboard/nplb/socket_helpers.cc
index 6f9b35a..c78a21a 100644
--- a/src/starboard/nplb/socket_helpers.cc
+++ b/src/starboard/nplb/socket_helpers.cc
@@ -30,7 +30,7 @@
void InitializePortNumberForTests() {
// Create a listening socket. Let the system choose a port for us.
- SbSocket socket = CreateListeningTcpIpv4Socket(0);
+ SbSocket socket = CreateListeningTcpSocket(kSbSocketAddressTypeIpv4, 0);
SB_DCHECK(socket != kSbSocketInvalid);
// Query which port this socket was bound to and save it to valid_port_number.
@@ -68,8 +68,7 @@
bool IsLocalhost(const SbSocketAddress* address) {
if (address->type == kSbSocketAddressTypeIpv4) {
- return (address->address[0] == 127 && address->address[1] == 0 &&
- address->address[2] == 0 && address->address[3] == 1);
+ return address->address[0] == 127;
}
if (address->type == kSbSocketAddressTypeIpv6) {
@@ -84,35 +83,34 @@
return false;
}
-SbSocketAddress GetIpv4Localhost(int port) {
- SbSocketAddress address = GetIpv4Unspecified(port);
- address.address[0] = 127;
- address.address[3] = 1;
+SbSocketAddress GetLocalhostAddress(SbSocketAddressType address_type,
+ int port) {
+ SbSocketAddress address = GetUnspecifiedAddress(address_type, port);
+ switch (address_type) {
+ case kSbSocketAddressTypeIpv4: {
+ address.address[0] = 127;
+ address.address[3] = 1;
+ return address;
+ }
+ case kSbSocketAddressTypeIpv6: {
+ address.address[15] = 1;
+ return address;
+ }
+ }
+ ADD_FAILURE() << "GetLocalhostAddress for unknown address type";
return address;
}
-SbSocketAddress GetIpv4Unspecified(int port) {
+SbSocketAddress GetUnspecifiedAddress(SbSocketAddressType address_type,
+ int port) {
SbSocketAddress address = {0};
- address.type = kSbSocketAddressTypeIpv4;
+ address.type = address_type;
address.port = port;
return address;
}
-SbSocketAddress GetIpv6Localhost(int port) {
- SbSocketAddress address = GetIpv6Unspecified(port);
- address.address[15] = 1;
- return address;
-}
-
-SbSocketAddress GetIpv6Unspecified(int port) {
- SbSocketAddress address = {0};
- address.type = kSbSocketAddressTypeIpv6;
- address.port = port;
- return address;
-}
-
-SbSocket CreateServerTcpIpv4Socket() {
- SbSocket server_socket = CreateTcpIpv4Socket();
+SbSocket CreateServerTcpSocket(SbSocketAddressType address_type) {
+ SbSocket server_socket = SbSocketCreate(address_type, kSbSocketProtocolTcp);
if (!SbSocketIsValid(server_socket)) {
ADD_FAILURE() << "SbSocketCreate failed";
return kSbSocketInvalid;
@@ -127,13 +125,13 @@
return server_socket;
}
-SbSocket CreateBoundTcpIpv4Socket(int port) {
- SbSocket server_socket = CreateServerTcpIpv4Socket();
+SbSocket CreateBoundTcpSocket(SbSocketAddressType address_type, int port) {
+ SbSocket server_socket = CreateServerTcpSocket(address_type);
if (!SbSocketIsValid(server_socket)) {
return kSbSocketInvalid;
}
- SbSocketAddress address = GetIpv4Unspecified(port);
+ SbSocketAddress address = GetUnspecifiedAddress(address_type, port);
SbSocketError result = SbSocketBind(server_socket, &address);
if (result != kSbSocketOk) {
ADD_FAILURE() << "SbSocketBind to " << port << " failed: " << result;
@@ -144,8 +142,8 @@
return server_socket;
}
-SbSocket CreateListeningTcpIpv4Socket(int port) {
- SbSocket server_socket = CreateBoundTcpIpv4Socket(port);
+SbSocket CreateListeningTcpSocket(SbSocketAddressType address_type, int port) {
+ SbSocket server_socket = CreateBoundTcpSocket(address_type, port);
if (!SbSocketIsValid(server_socket)) {
return kSbSocketInvalid;
}
@@ -160,15 +158,16 @@
return server_socket;
}
-SbSocket CreateConnectingTcpIpv4Socket(int port) {
- SbSocket client_socket = CreateTcpIpv4Socket();
+namespace {
+SbSocket CreateConnectingTcpSocket(SbSocketAddressType address_type, int port) {
+ SbSocket client_socket = SbSocketCreate(address_type, kSbSocketProtocolTcp);
if (!SbSocketIsValid(client_socket)) {
ADD_FAILURE() << "SbSocketCreate failed";
return kSbSocketInvalid;
}
// Connect to localhost:<port>.
- SbSocketAddress address = GetIpv4Localhost(port);
+ SbSocketAddress address = GetLocalhostAddress(address_type, port);
// This connect will probably return pending, but we'll assume it will connect
// eventually.
@@ -181,6 +180,7 @@
return client_socket;
}
+} // namespace
SbSocket AcceptBySpinning(SbSocket server_socket, SbTime timeout) {
SbTimeMonotonic start = SbTimeGetMonotonicNow();
@@ -260,16 +260,19 @@
return true;
}
-ConnectedTrio CreateAndConnect(int port, SbTime timeout) {
- // Set up a listening socket.
- SbSocket listen_socket = CreateListeningTcpIpv4Socket(port);
+ConnectedTrio CreateAndConnect(SbSocketAddressType server_address_type,
+ SbSocketAddressType client_address_type,
+ int port,
+ SbTime timeout) {
+ // Verify the listening socket.
+ SbSocket listen_socket = CreateListeningTcpSocket(server_address_type, port);
if (!SbSocketIsValid(listen_socket)) {
ADD_FAILURE() << "Could not create listen socket.";
return ConnectedTrio();
}
- // Create a new socket to connect to the listening socket.
- SbSocket client_socket = CreateConnectingTcpIpv4Socket(port);
+ // Verify the socket to connect to the listening socket.
+ SbSocket client_socket = CreateConnectingTcpSocket(client_address_type, port);
if (!SbSocketIsValid(client_socket)) {
ADD_FAILURE() << "Could not create client socket.";
EXPECT_TRUE(SbSocketDestroy(listen_socket));
diff --git a/src/starboard/nplb/socket_helpers.h b/src/starboard/nplb/socket_helpers.h
index a01c02d..8cb8922 100644
--- a/src/starboard/nplb/socket_helpers.h
+++ b/src/starboard/nplb/socket_helpers.h
@@ -25,16 +25,6 @@
const SbTime kSocketTimeout = kSbTimeSecond / 5;
-// Creates a plain TCP/IPv4 socket.
-static inline SbSocket CreateTcpIpv4Socket() {
- return SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
-}
-
-// Creates a plain UDP/IPv4 socket.
-static inline SbSocket CreateUdpIpv4Socket() {
- return SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp);
-}
-
// Returns true if the given address is the unspecified address (all zeros),
// supporting both address types.
bool IsUnspecified(const SbSocketAddress* address);
@@ -47,29 +37,21 @@
// This will always return the same port number.
int GetPortNumberForTests();
-// Returns an IPv4 localhost address with the given port.
-SbSocketAddress GetIpv4Localhost(int port);
+// Returns an IP localhost address with the given port.
+SbSocketAddress GetLocalhostAddress(SbSocketAddressType address_type, int port);
-// Returns an IPv4 unspecified address with the given port.
-SbSocketAddress GetIpv4Unspecified(int port);
+// Returns an IP unspecified address with the given port.
+SbSocketAddress GetUnspecifiedAddress(SbSocketAddressType address_type,
+ int port);
-// Returns an IPv6 localhost address with the given port.
-SbSocketAddress GetIpv6Localhost(int port);
+// Creates a TCP/IP server socket (sets Reuse Address option).
+SbSocket CreateServerTcpSocket(SbSocketAddressType address_type);
-// Returns an IPv6 unspecified address with the given port.
-SbSocketAddress GetIpv6Unspecified(int port);
+// Creates a TCP/IP socket bound to all interfaces on the given port.
+SbSocket CreateBoundTcpSocket(SbSocketAddressType address_type, int port);
-// Creates a TCP/IPv4 server socket (sets Reuse Address option).
-SbSocket CreateServerTcpIpv4Socket();
-
-// Creates a TCP/IPv4 socket bound to all interfaces on the given port.
-SbSocket CreateBoundTcpIpv4Socket(int port);
-
-// Creates a TCP/IPv4 socket listening on all interfaces on the given port.
-SbSocket CreateListeningTcpIpv4Socket(int port);
-
-// Creates a TCP/IPv4 socket connecting to the given port on localhost.
-SbSocket CreateConnectingTcpIpv4Socket(int port);
+// Creates a TCP/IP socket listening on all interfaces on the given port.
+SbSocket CreateListeningTcpSocket(SbSocketAddressType address_type, int port);
// Tries to accept a new connection from the given listening socket by checking,
// yielding, and retrying for up to timeout. Returns kSbSocketInvalid if no
@@ -104,10 +86,13 @@
SbSocket server_socket;
} ConnectedTrio;
-// Creates and returns 3 sockets, a connected client and server, and a listener
-// on the given port. If anything fails, adds a failure and returns three
-// invalid sockets.
-ConnectedTrio CreateAndConnect(int port, SbTime timeout);
+// Creates and returns 3 TCP/IP sockets, a connected client and server, and a
+// listener on the given port. If anything fails, adds a failure and returns
+// three invalid sockets.
+ConnectedTrio CreateAndConnect(SbSocketAddressType server_address_type,
+ SbSocketAddressType client_address_type,
+ int port,
+ SbTime timeout);
// Waits on the given waiter, and returns the elapsed time.
SbTimeMonotonic TimedWait(SbSocketWaiter waiter);
diff --git a/src/starboard/nplb/socket_is_connected_and_idle_test.cc b/src/starboard/nplb/socket_is_connected_and_idle_test.cc
index 74a082c..29b047c 100644
--- a/src/starboard/nplb/socket_is_connected_and_idle_test.cc
+++ b/src/starboard/nplb/socket_is_connected_and_idle_test.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <utility>
+
#include "starboard/log.h"
#include "starboard/nplb/socket_helpers.h"
#include "starboard/socket.h"
@@ -22,6 +24,20 @@
namespace nplb {
namespace {
+class SbSocketIsConnectedAndIdleTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketIsConnectedAndIdleTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+ SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+ SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
bool IsNonIdleWithin(SbSocket socket, SbTimeMonotonic timeout) {
SbTimeMonotonic deadline = SbTimeGetMonotonicNow() + timeout;
while (SbTimeGetMonotonicNow() < deadline) {
@@ -32,16 +48,15 @@
return false;
}
-TEST(SbSocketIsConnectedAndIdleTest, RainyDayInvalidSocket) {
+TEST_F(SbSocketIsConnectedAndIdleTest, RainyDayInvalidSocket) {
EXPECT_FALSE(SbSocketIsConnectedAndIdle(kSbSocketInvalid));
}
-TEST(SbSocketIsConnectedAndIdleTest, SunnyDay) {
+TEST_P(PairSbSocketIsConnectedAndIdleTest, SunnyDay) {
ConnectedTrio trio =
- CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
- if (!SbSocketIsValid(trio.server_socket)) {
- return;
- }
+ CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+ GetPortNumberForTests(), kSocketTimeout);
+ ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
EXPECT_FALSE(SbSocketIsConnectedAndIdle(trio.listen_socket));
EXPECT_TRUE(SbSocketIsConnectedAndIdle(trio.server_socket));
@@ -63,28 +78,44 @@
EXPECT_TRUE(SbSocketDestroy(trio.listen_socket));
}
-TEST(SbSocketIsConnectedAndIdleTest, SunnyDayNotConnected) {
- SbSocket socket = CreateTcpIpv4Socket();
- if (!SbSocketIsValid(socket)) {
- return;
- }
-
+TEST_P(SbSocketIsConnectedAndIdleTest, SunnyDayNotConnected) {
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_TRUE(IsNonIdleWithin(socket, kSocketTimeout));
EXPECT_TRUE(SbSocketDestroy(socket));
}
-TEST(SbSocketIsConnectedAndIdleTest, SunnyDayListeningNotConnected) {
+TEST_P(SbSocketIsConnectedAndIdleTest, SunnyDayListeningNotConnected) {
SbSocket server_socket =
- CreateListeningTcpIpv4Socket(GetPortNumberForTests());
- if (!SbSocketIsValid(server_socket)) {
- return;
- }
-
+ CreateListeningTcpSocket(GetAddressType(), GetPortNumberForTests());
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
EXPECT_FALSE(SbSocketIsConnectedAndIdle(server_socket));
-
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketIsConnectedAndIdleTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketIsConnectedAndIdleTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketIsConnectedAndIdleTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketIsConnectedAndIdleTest,
+ ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv4)));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_is_connected_test.cc b/src/starboard/nplb/socket_is_connected_test.cc
index e69fc4d..6bdf082 100644
--- a/src/starboard/nplb/socket_is_connected_test.cc
+++ b/src/starboard/nplb/socket_is_connected_test.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <utility>
+
#include "starboard/log.h"
#include "starboard/nplb/socket_helpers.h"
#include "starboard/socket.h"
@@ -21,17 +23,29 @@
namespace nplb {
namespace {
-TEST(SbSocketIsConnectedTest, RainyDayInvalidSocket) {
+class SbSocketIsConnectedTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketIsConnectedTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+ SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+ SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
+TEST_F(SbSocketIsConnectedTest, RainyDayInvalidSocket) {
EXPECT_FALSE(SbSocketIsConnected(kSbSocketInvalid));
}
-TEST(SbSocketIsConnectedTest, SunnyDay) {
+TEST_P(PairSbSocketIsConnectedTest, SunnyDay) {
ConnectedTrio trio =
- CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
- if (!SbSocketIsValid(trio.server_socket)) {
- return;
- }
-
+ CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+ GetPortNumberForTests(), kSocketTimeout);
+ ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
EXPECT_FALSE(SbSocketIsConnected(trio.listen_socket));
EXPECT_TRUE(SbSocketIsConnected(trio.server_socket));
EXPECT_TRUE(SbSocketIsConnected(trio.client_socket));
@@ -48,29 +62,44 @@
EXPECT_TRUE(SbSocketDestroy(trio.listen_socket));
}
-TEST(SbSocketIsConnectedTest, SunnyDayNotConnected) {
- SbSocket socket = CreateTcpIpv4Socket();
- if (!SbSocketIsValid(socket)) {
- return;
- }
-
+TEST_P(SbSocketIsConnectedTest, SunnyDayNotConnected) {
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_FALSE(SbSocketIsConnected(socket));
-
EXPECT_TRUE(SbSocketDestroy(socket));
}
-TEST(SbSocketIsConnectedTest, SunnyDayListeningNotConnected) {
+TEST_P(SbSocketIsConnectedTest, SunnyDayListeningNotConnected) {
SbSocket server_socket =
- CreateListeningTcpIpv4Socket(GetPortNumberForTests());
- if (!SbSocketIsValid(server_socket)) {
- return;
- }
-
+ CreateListeningTcpSocket(GetAddressType(), GetPortNumberForTests());
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
EXPECT_FALSE(SbSocketIsConnected(server_socket));
-
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketIsConnectedTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketIsConnectedTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketIsConnectedTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketIsConnectedTest,
+ ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv4)));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_join_multicast_group_test.cc b/src/starboard/nplb/socket_join_multicast_group_test.cc
index 115abbf..0ad788c 100644
--- a/src/starboard/nplb/socket_join_multicast_group_test.cc
+++ b/src/starboard/nplb/socket_join_multicast_group_test.cc
@@ -22,12 +22,14 @@
namespace {
SbSocket CreateMulticastSocket(const SbSocketAddress& address) {
- SbSocket socket = CreateUdpIpv4Socket();
+ SbSocket socket =
+ SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp);
EXPECT_TRUE(SbSocketIsValid(socket));
EXPECT_TRUE(SbSocketSetReuseAddress(socket, true));
// It will choose a port for me.
- SbSocketAddress bind_address = GetIpv4Unspecified(0);
+ SbSocketAddress bind_address =
+ GetUnspecifiedAddress(kSbSocketAddressTypeIpv4, 0);
EXPECT_EQ(kSbSocketOk, SbSocketBind(socket, &bind_address));
EXPECT_TRUE(SbSocketJoinMulticastGroup(socket, &address));
@@ -35,7 +37,8 @@
}
SbSocket CreateSendSocket() {
- SbSocket socket = CreateUdpIpv4Socket();
+ SbSocket socket =
+ SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp);
EXPECT_TRUE(SbSocketIsValid(socket));
EXPECT_TRUE(SbSocketSetReuseAddress(socket, true));
return socket;
@@ -45,7 +48,7 @@
// "224.0.2.0" is an unassigned ad-hoc multicast address. Hopefully no one is
// spamming it on the local network.
// http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml
- SbSocketAddress address = GetIpv4Unspecified(0);
+ SbSocketAddress address = GetUnspecifiedAddress(kSbSocketAddressTypeIpv4, 0);
address.address[0] = 224;
address.address[2] = 2;
@@ -96,13 +99,14 @@
}
TEST(SbSocketJoinMulticastGroupTest, RainyDayInvalidSocket) {
- SbSocketAddress address = GetIpv4Unspecified(0);
+ SbSocketAddress address = GetUnspecifiedAddress(kSbSocketAddressTypeIpv4, 0);
EXPECT_FALSE(SbSocketJoinMulticastGroup(kSbSocketInvalid, &address));
}
TEST(SbSocketJoinMulticastGroupTest, RainyDayInvalidAddress) {
- SbSocket socket = CreateUdpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+ SbSocket socket =
+ SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolUdp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_FALSE(SbSocketJoinMulticastGroup(socket, NULL));
EXPECT_TRUE(SbSocketDestroy(socket));
}
diff --git a/src/starboard/nplb/socket_listen_test.cc b/src/starboard/nplb/socket_listen_test.cc
index 9a403a7..aaeabab 100644
--- a/src/starboard/nplb/socket_listen_test.cc
+++ b/src/starboard/nplb/socket_listen_test.cc
@@ -23,13 +23,19 @@
namespace nplb {
namespace {
-TEST(SbSocketListenTest, RainyDayInvalid) {
+class SbSocketListenTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+TEST_F(SbSocketListenTest, RainyDayInvalid) {
EXPECT_EQ(kSbSocketErrorFailed, SbSocketListen(kSbSocketInvalid));
}
-TEST(SbSocketListenTest, SunnyDayUnbound) {
- SbSocket server_socket = CreateServerTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(server_socket));
+TEST_P(SbSocketListenTest, SunnyDayUnbound) {
+ SbSocket server_socket = CreateServerTcpSocket(GetAddressType());
+ ASSERT_TRUE(SbSocketIsValid(server_socket));
EXPECT_EQ(kSbSocketOk, SbSocketListen(server_socket));
@@ -37,13 +43,24 @@
// all local interfaces.
SbSocketAddress address = {0};
EXPECT_TRUE(SbSocketGetLocalAddress(server_socket, &address));
- EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type);
+ EXPECT_EQ(GetAddressType(), address.type);
EXPECT_NE(0, address.port);
EXPECT_TRUE(IsUnspecified(&address));
EXPECT_TRUE(SbSocketDestroy(server_socket));
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketListenTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketListenTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_receive_from_test.cc b/src/starboard/nplb/socket_receive_from_test.cc
index 5cd8c43..f4dba24 100644
--- a/src/starboard/nplb/socket_receive_from_test.cc
+++ b/src/starboard/nplb/socket_receive_from_test.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <utility>
+
#include "starboard/nplb/socket_helpers.h"
#include "starboard/socket.h"
#include "starboard/time.h"
@@ -21,6 +23,14 @@
namespace nplb {
namespace {
+class PairSbSocketReceiveFromTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+ SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+ SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
// Transfers data between the two connected local sockets, spinning until |size|
// has been transfered, or an error occurs.
int Transfer(SbSocket receive_socket,
@@ -59,15 +69,14 @@
return size;
}
-TEST(SbSocketReceiveFromTest, SunnyDay) {
+TEST_P(PairSbSocketReceiveFromTest, SunnyDay) {
const int kBufSize = 256 * 1024;
const int kSockBufSize = kBufSize / 8;
ConnectedTrio trio =
- CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
- if (!SbSocketIsValid(trio.server_socket)) {
- return;
- }
+ CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+ GetPortNumberForTests(), kSocketTimeout);
+ ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
// Let's set the buffers small to create partial reads and writes.
SbSocketSetReceiveBufferSize(trio.client_socket, kSockBufSize);
@@ -116,6 +125,22 @@
EXPECT_EQ(-1, result);
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketReceiveFromTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketReceiveFromTest,
+ ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv4)));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_resolve_test.cc b/src/starboard/nplb/socket_resolve_test.cc
index b088a0d..ebca117 100644
--- a/src/starboard/nplb/socket_resolve_test.cc
+++ b/src/starboard/nplb/socket_resolve_test.cc
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/nplb/socket_helpers.h"
+#include <utility>
+
#include "starboard/log.h"
+#include "starboard/nplb/socket_helpers.h"
#include "starboard/socket.h"
#include "starboard/string.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -22,6 +24,14 @@
namespace nplb {
namespace {
+class SbSocketResolveTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketResolveFilter, SbSocketAddressType> > {
+ public:
+ SbSocketResolveFilter GetResolveFilter() { return GetParam().first; }
+ SbSocketAddressType GetAddressType() { return GetParam().second; }
+};
+
// This is to use NULL in asserts, which otherwise complain about long
// vs. pointer type.
const void* kNull = NULL;
@@ -31,7 +41,7 @@
#define LOG_ADDRESS(i) "In returned address #" << (i)
-TEST(SbSocketResolveTest, SunnyDay) {
+TEST_F(SbSocketResolveTest, SunnyDay) {
SbSocketResolution* resolution = SbSocketResolve(kTestHostName, 0);
ASSERT_NE(kNull, resolution);
EXPECT_LT(0, resolution->address_count);
@@ -47,35 +57,20 @@
SbSocketFreeResolution(resolution);
}
-TEST(SbSocketResolveTest, SunnyDayIpv4) {
+TEST_P(SbSocketResolveTest, SunnyDayFiltered) {
SbSocketResolution* resolution =
- SbSocketResolve(kTestHostName, kSbSocketResolveFilterIpv4);
+ SbSocketResolve(kTestHostName, GetResolveFilter());
ASSERT_NE(kNull, resolution);
EXPECT_LT(0, resolution->address_count);
EXPECT_NE(kNull, resolution->addresses);
for (int i = 0; i < resolution->address_count; ++i) {
const SbSocketAddress& address = resolution->addresses[i];
- EXPECT_EQ(kSbSocketAddressTypeIpv4, address.type) << LOG_ADDRESS(i);
+ EXPECT_EQ(GetAddressType(), address.type) << LOG_ADDRESS(i);
}
SbSocketFreeResolution(resolution);
}
-#if SB_HAS(IPV6)
-TEST(SbSocketResolveTest, SunnyDayIpv6) {
- SbSocketResolution* resolution =
- SbSocketResolve(kTestHostName, kSbSocketResolveFilterIpv6);
- ASSERT_NE(kNull, resolution);
- EXPECT_LT(0, resolution->address_count);
- EXPECT_NE(kNull, resolution->addresses);
- for (int i = 0; i < resolution->address_count; ++i) {
- const SbSocketAddress& address = resolution->addresses[i];
- EXPECT_EQ(kSbSocketAddressTypeIpv6, address.type) << LOG_ADDRESS(i);
- }
- SbSocketFreeResolution(resolution);
-}
-#endif
-
-TEST(SbSocketResolveTest, IgnoreExtraBits) {
+TEST_F(SbSocketResolveTest, IgnoreExtraBits) {
// Even with this extra bit set, the resolution should come out the same.
SbSocketResolution* resolution1 = SbSocketResolve(kTestHostName, 1 << 14);
SbSocketResolution* resolution2 = SbSocketResolve(kTestHostName, 0);
@@ -89,11 +84,26 @@
SbSocketFreeResolution(resolution2);
}
-TEST(SbSocketResolveTest, RainyDayNullHostname) {
+TEST_F(SbSocketResolveTest, RainyDayNullHostname) {
SbSocketResolution* resolution = SbSocketResolve(NULL, 0);
ASSERT_EQ(kNull, resolution);
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ SbSocketResolveTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketResolveFilterIpv4, kSbSocketAddressTypeIpv4),
+ std::make_pair(kSbSocketResolveFilterIpv6, kSbSocketAddressTypeIpv6)));
+#else
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ SbSocketResolveTest,
+ ::testing::Values(std::make_pair(kSbSocketResolveFilterIpv4,
+ kSbSocketAddressTypeIpv4)));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_send_to_test.cc b/src/starboard/nplb/socket_send_to_test.cc
index 9db1d36..fae82a2 100644
--- a/src/starboard/nplb/socket_send_to_test.cc
+++ b/src/starboard/nplb/socket_send_to_test.cc
@@ -15,6 +15,8 @@
// SendTo is largely tested with ReceiveFrom, so look there for more invovled
// tests.
+#include <utility>
+
#include "starboard/memory.h"
#include "starboard/nplb/socket_helpers.h"
#include "starboard/socket.h"
@@ -26,6 +28,14 @@
namespace nplb {
namespace {
+class PairSbSocketSendToTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+ SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+ SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
// Thread entry point to continuously write to a socket that is expected to
// be closed on another thread.
void* SendToServerSocketEntryPoint(void* trio_as_void_ptr) {
@@ -58,9 +68,10 @@
EXPECT_EQ(-1, result);
}
-TEST(SbSocketSendToTest, RainyDaySendToClosedSocket) {
+TEST_P(PairSbSocketSendToTest, RainyDaySendToClosedSocket) {
ConnectedTrio trio =
- CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
+ CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+ GetPortNumberForTests(), kSocketTimeout);
EXPECT_NE(trio.client_socket, kSbSocketInvalid);
EXPECT_NE(trio.server_socket, kSbSocketInvalid);
EXPECT_NE(trio.listen_socket, kSbSocketInvalid);
@@ -87,6 +98,21 @@
EXPECT_TRUE(SbSocketDestroy(trio.server_socket));
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketSendToTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketSendToTest,
+ ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv4)));
+#endif
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_set_options_test.cc b/src/starboard/nplb/socket_set_options_test.cc
index 4e075d9..a6557a8 100644
--- a/src/starboard/nplb/socket_set_options_test.cc
+++ b/src/starboard/nplb/socket_set_options_test.cc
@@ -21,8 +21,14 @@
namespace nplb {
namespace {
-TEST(SbSocketSetOptionsTest, TryThemAll) {
- SbSocket socket = CreateTcpIpv4Socket();
+class SbSocketSetOptionsTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+TEST_P(SbSocketSetOptionsTest, TryThemAll) {
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
EXPECT_TRUE(SbSocketSetBroadcast(socket, true));
EXPECT_TRUE(SbSocketSetReuseAddress(socket, true));
@@ -40,6 +46,17 @@
// TODO: Come up with some way to test the effects of these options.
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketSetOptionsTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketSetOptionsTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_waiter_add_test.cc b/src/starboard/nplb/socket_waiter_add_test.cc
index 25b3c3d..c17a4a1 100644
--- a/src/starboard/nplb/socket_waiter_add_test.cc
+++ b/src/starboard/nplb/socket_waiter_add_test.cc
@@ -21,17 +21,23 @@
namespace nplb {
namespace {
+class SbSocketWaiterAddTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
void NoOpSocketWaiterCallback(SbSocketWaiter waiter,
SbSocket socket,
void* context,
int ready_interests) {}
-TEST(SbSocketWaiterAddTest, SunnyDay) {
+TEST_P(SbSocketWaiterAddTest, SunnyDay) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_TRUE(SbSocketWaiterAdd(
waiter, socket, NULL, &NoOpSocketWaiterCallback,
@@ -41,12 +47,12 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
-TEST(SbSocketWaiterAddTest, SunnyDayPersistent) {
+TEST_P(SbSocketWaiterAddTest, SunnyDayPersistent) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_TRUE(SbSocketWaiterAdd(
waiter, socket, NULL, &NoOpSocketWaiterCallback,
@@ -56,15 +62,15 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
-TEST(SbSocketWaiterAddTest, SunnyDayMany) {
+TEST_P(SbSocketWaiterAddTest, SunnyDayMany) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
const int kMany = SB_FILE_MAX_OPEN;
SbSocket sockets[kMany] = {0};
for (int i = 0; i < kMany; ++i) {
- sockets[i] = SbSocketCreate(kSbSocketAddressTypeIpv4, kSbSocketProtocolTcp);
- EXPECT_TRUE(SbSocketIsValid(sockets[i]));
+ sockets[i] = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(sockets[i]));
EXPECT_TRUE(SbSocketWaiterAdd(
waiter, sockets[i], NULL, &NoOpSocketWaiterCallback,
kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite, false));
@@ -77,12 +83,12 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
-TEST(SbSocketWaiterAddTest, RainyDayAddToSameWaiter) {
+TEST_P(SbSocketWaiterAddTest, RainyDayAddToSameWaiter) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
// First add should succeed.
EXPECT_TRUE(SbSocketWaiterAdd(
@@ -111,15 +117,15 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
-TEST(SbSocketWaiterAddTest, RainyDayAddToOtherWaiter) {
+TEST_P(SbSocketWaiterAddTest, RainyDayAddToOtherWaiter) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
SbSocketWaiter waiter2 = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter2));
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
// The first add should succeed.
EXPECT_TRUE(SbSocketWaiterAdd(
@@ -149,7 +155,7 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter2));
}
-TEST(SbSocketWaiterRemoveTest, RainyDayInvalidSocket) {
+TEST_F(SbSocketWaiterAddTest, RainyDayInvalidSocket) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
@@ -160,9 +166,9 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
-TEST(SbSocketWaiterRemoveTest, RainyDayInvalidWaiter) {
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+TEST_P(SbSocketWaiterAddTest, RainyDayInvalidWaiter) {
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_FALSE(SbSocketWaiterAdd(
kSbSocketWaiterInvalid, socket, NULL, &NoOpSocketWaiterCallback,
@@ -171,11 +177,11 @@
EXPECT_TRUE(SbSocketDestroy(socket));
}
-TEST(SbSocketWaiterRemoveTest, RainyDayNoCallback) {
+TEST_P(SbSocketWaiterAddTest, RainyDayNoCallback) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_FALSE(SbSocketWaiterAdd(
waiter, socket, NULL, NULL,
@@ -185,11 +191,11 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
-TEST(SbSocketWaiterRemoveTest, RainyDayNoInterest) {
+TEST_P(SbSocketWaiterAddTest, RainyDayNoInterest) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_FALSE(SbSocketWaiterAdd(waiter, socket, NULL,
&NoOpSocketWaiterCallback,
@@ -199,6 +205,17 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketWaiterAddTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketWaiterAddTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_waiter_remove_test.cc b/src/starboard/nplb/socket_waiter_remove_test.cc
index 5407cc4..6bb7a67 100644
--- a/src/starboard/nplb/socket_waiter_remove_test.cc
+++ b/src/starboard/nplb/socket_waiter_remove_test.cc
@@ -21,12 +21,18 @@
namespace nplb {
namespace {
+class SbSocketWaiterRemoveTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
void NoOpSocketWaiterCallback(SbSocketWaiter waiter,
SbSocket socket,
void* context,
int ready_interests) {}
-TEST(SbSocketWaiterRemoveTest, RainyDayInvalidSocket) {
+TEST_F(SbSocketWaiterRemoveTest, RainyDayInvalidSocket) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
@@ -35,20 +41,20 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
-TEST(SbSocketWaiterRemoveTest, RainyDayInvalidWaiter) {
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+TEST_P(SbSocketWaiterRemoveTest, RainyDayInvalidWaiter) {
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_FALSE(SbSocketWaiterRemove(kSbSocketWaiterInvalid, socket));
EXPECT_TRUE(SbSocketDestroy(socket));
}
-TEST(SbSocketWaiterRemoveTest, RainyDayNotAdded) {
+TEST_P(SbSocketWaiterRemoveTest, RainyDayNotAdded) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_FALSE(SbSocketWaiterRemove(waiter, socket));
@@ -56,11 +62,11 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
-TEST(SbSocketWaiterRemoveTest, RainyDayAlreadyRemoved) {
+TEST_P(SbSocketWaiterRemoveTest, RainyDayAlreadyRemoved) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
- SbSocket socket = CreateTcpIpv4Socket();
- EXPECT_TRUE(SbSocketIsValid(socket));
+ SbSocket socket = SbSocketCreate(GetAddressType(), kSbSocketProtocolTcp);
+ ASSERT_TRUE(SbSocketIsValid(socket));
EXPECT_TRUE(SbSocketWaiterAdd(
waiter, socket, NULL, &NoOpSocketWaiterCallback,
@@ -72,6 +78,17 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketWaiterRemoveTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketWaiterRemoveTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_waiter_wait_test.cc b/src/starboard/nplb/socket_waiter_wait_test.cc
index 8b67583..d70734a 100644
--- a/src/starboard/nplb/socket_waiter_wait_test.cc
+++ b/src/starboard/nplb/socket_waiter_wait_test.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <utility>
+
#include "starboard/log.h"
#include "starboard/nplb/socket_helpers.h"
#include "starboard/nplb/thread_helpers.h"
@@ -25,6 +27,20 @@
namespace nplb {
namespace {
+class SbSocketWaiterWaitTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketWaiterWaitTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+ SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+ SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
struct CallbackValues {
int count;
SbSocketWaiter waiter;
@@ -55,18 +71,16 @@
ADD_FAILURE() << __FUNCTION__ << " was called.";
}
-TEST(SbSocketWaiterWaitTest, SunnyDay) {
+TEST_P(PairSbSocketWaiterWaitTest, SunnyDay) {
const int kBufSize = 1024;
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
ConnectedTrio trio =
- CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
- if (!SbSocketIsValid(trio.server_socket)) {
- ADD_FAILURE();
- return;
- }
+ CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+ GetPortNumberForTests(), kSocketTimeout);
+ ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
// The client socket should be ready to write right away, but not read until
// it gets some data.
@@ -198,12 +212,14 @@
// This test ensures that if the socket gets written to while it is not being
// waited on, and inside a callback, it will become ready immediately the next
// time it is waited on.
-TEST(SbSocketWaiterWaitTest, SunnyDayAlreadyReady) {
+TEST_P(PairSbSocketWaiterWaitTest, SunnyDayAlreadyReady) {
AlreadyReadyContext context;
context.waiter = SbSocketWaiterCreate();
ASSERT_TRUE(SbSocketWaiterIsValid(context.waiter));
- context.trio = CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
+ context.trio =
+ CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+ GetPortNumberForTests(), kSocketTimeout);
ASSERT_TRUE(SbSocketIsValid(context.trio.server_socket));
EXPECT_TRUE(SbSocketWaiterAdd(context.waiter, context.trio.client_socket,
@@ -221,10 +237,33 @@
EXPECT_TRUE(SbThreadJoin(thread, NULL));
}
-TEST(SbSocketWaiterWaitTest, RainyDayInvalidWaiter) {
+TEST_F(SbSocketWaiterWaitTest, RainyDayInvalidWaiter) {
WaitShouldNotBlock(kSbSocketWaiterInvalid);
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketWaiterWaitTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketWaiterWaitTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketWaiterWaitTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketWaiterWaitTest,
+ ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv4)));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/socket_waiter_wait_timed_test.cc b/src/starboard/nplb/socket_waiter_wait_timed_test.cc
index 0166743..ec18c3e 100644
--- a/src/starboard/nplb/socket_waiter_wait_timed_test.cc
+++ b/src/starboard/nplb/socket_waiter_wait_timed_test.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <utility>
+
#include "starboard/log.h"
#include "starboard/nplb/socket_helpers.h"
#include "starboard/socket.h"
@@ -23,6 +25,20 @@
namespace nplb {
namespace {
+class SbSocketWaiterWaitTimedTest
+ : public ::testing::TestWithParam<SbSocketAddressType> {
+ public:
+ SbSocketAddressType GetAddressType() { return GetParam(); }
+};
+
+class PairSbSocketWaiterWaitTimedTest
+ : public ::testing::TestWithParam<
+ std::pair<SbSocketAddressType, SbSocketAddressType> > {
+ public:
+ SbSocketAddressType GetServerAddressType() { return GetParam().first; }
+ SbSocketAddressType GetClientAddressType() { return GetParam().second; }
+};
+
struct CallbackValues {
int count;
SbSocketWaiter waiter;
@@ -46,18 +62,16 @@
SbSocketWaiterWakeUp(waiter);
}
-TEST(SbSocketWaiterWaitTimedTest, SunnyDay) {
+TEST_P(PairSbSocketWaiterWaitTimedTest, SunnyDay) {
const int kBufSize = 1024;
SbSocketWaiter waiter = SbSocketWaiterCreate();
EXPECT_TRUE(SbSocketWaiterIsValid(waiter));
ConnectedTrio trio =
- CreateAndConnect(GetPortNumberForTests(), kSocketTimeout);
- if (!SbSocketIsValid(trio.server_socket)) {
- ADD_FAILURE();
- return;
- }
+ CreateAndConnect(GetServerAddressType(), GetClientAddressType(),
+ GetPortNumberForTests(), kSocketTimeout);
+ ASSERT_TRUE(SbSocketIsValid(trio.server_socket));
// The client socket should be ready to write right away, but not read until
// it gets some data.
@@ -107,10 +121,33 @@
EXPECT_TRUE(SbSocketWaiterDestroy(waiter));
}
-TEST(SbSocketWaiterWaitTimedTest, RainyDayInvalidWaiter) {
+TEST_F(SbSocketWaiterWaitTimedTest, RainyDayInvalidWaiter) {
TimedWaitShouldNotBlock(kSbSocketWaiterInvalid, kSocketTimeout);
}
+#if SB_HAS(IPV6)
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketWaiterWaitTimedTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv6));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketWaiterWaitTimedTest,
+ ::testing::Values(
+ std::make_pair(kSbSocketAddressTypeIpv4, kSbSocketAddressTypeIpv4),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv6),
+ std::make_pair(kSbSocketAddressTypeIpv6, kSbSocketAddressTypeIpv4)));
+#else
+INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
+ SbSocketWaiterWaitTimedTest,
+ ::testing::Values(kSbSocketAddressTypeIpv4));
+INSTANTIATE_TEST_CASE_P(
+ SbSocketAddressTypes,
+ PairSbSocketWaiterWaitTimedTest,
+ ::testing::Values(std::make_pair(kSbSocketAddressTypeIpv4,
+ kSbSocketAddressTypeIpv4)));
+#endif
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/system_get_path_test.cc b/src/starboard/nplb/system_get_path_test.cc
index fea6b9e..2e9ddf0 100644
--- a/src/starboard/nplb/system_get_path_test.cc
+++ b/src/starboard/nplb/system_get_path_test.cc
@@ -75,6 +75,10 @@
BasicTest(kSbSystemPathSourceDirectory, false, false, __LINE__);
BasicTest(kSbSystemPathTempDirectory, false, false, __LINE__);
BasicTest(kSbSystemPathTestOutputDirectory, false, false, __LINE__);
+#if SB_API_VERSION >= 4
+ BasicTest(kSbSystemPathFontDirectory, false, false, __LINE__);
+ BasicTest(kSbSystemPathFontConfigurationDirectory, false, false, __LINE__);
+#endif // SB_API_VERSION >= 4
}
} // namespace
diff --git a/src/starboard/player.h b/src/starboard/player.h
index 69074a1..de12f42 100644
--- a/src/starboard/player.h
+++ b/src/starboard/player.h
@@ -79,13 +79,13 @@
kSbPlayerStateError,
} SbPlayerState;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
typedef enum SbPlayerOutputMode {
// Requests for SbPlayer to produce an OpenGL texture that the client must
// draw every frame with its graphics rendering. It may be that we get a
// texture handle, but cannot perform operations like glReadPixels on it if it
// is DRM-protected, or it may not support DRM-protected content at all. When
- // this output mode is provided to SbCreatePlayer(), the application will be
+ // this output mode is provided to SbPlayerCreate(), the application will be
// able to pull frames via calls to SbPlayerGetCurrentFrame().
kSbPlayerOutputModeDecodeToTexture,
@@ -100,7 +100,7 @@
// An invalid output mode.
kSbPlayerOutputModeInvalid,
} SbPlayerOutputMode;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
// Information about the current media playback state.
typedef struct SbPlayerInfo {
@@ -138,7 +138,7 @@
// the player.
int corrupted_video_frames;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
// The rate of playback. The video is played back in a speed that is
// proportional to this. By default it is 1.0 which indicates that the
// playback is at normal speed. When it is greater than one, the video is
@@ -146,7 +146,7 @@
// is played in a slower than normal speed. Negative speeds are not
// supported.
double playback_rate;
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
} SbPlayerInfo;
// An opaque handle to an implementation-private structure representing a
@@ -278,9 +278,10 @@
//
// |provider|: Only present in Starboard version 3 and up. If not |NULL|,
// then when output_mode == kSbPlayerOutputModeDecodeToTexture, the player MAY
-// use the provider to create SbDecodeTargets. A provider could also
-// potentially be required by the player, in which case, if the provider is
-// not given, the player will fail by returning kSbPlayerInvalid.
+// use the provider to create SbDecodeTargets on the renderer thread. A
+// provider may not always be needed by the player, but if it is needed, and
+// the provider is not given, the player will fail by returning
+// kSbPlayerInvalid.
SB_EXPORT SbPlayer SbPlayerCreate(
SbWindow window,
SbMediaVideoCodec video_codec,
@@ -292,24 +293,24 @@
SbPlayerDecoderStatusFunc decoder_status_func,
SbPlayerStatusFunc player_status_func,
void* context
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
,
- SbPlayerOutputMode output_mode
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider* context_provider
+#elif SB_API_VERSION >= 3
,
SbDecodeTargetProvider* provider
-#endif // SB_API_VERSION >= 3
+#endif
); // NOLINT
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// Returns true if the given player output mode is supported by the platform.
// If this function returns true, it is okay to call SbPlayerCreate() with
// the given |output_mode|.
SB_EXPORT bool SbPlayerOutputModeSupported(SbPlayerOutputMode output_mode,
SbMediaVideoCodec codec,
SbDrmSystem drm_system);
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
// Destroys |player|, freeing all associated resources. Each callback must
// receive one more callback to say that the player was destroyed. Callbacks
@@ -352,6 +353,54 @@
SbMediaTime seek_to_pts,
int ticket);
+#if SB_API_VERSION >= 4
+
+// Writes a single sample of the given media type to |player|'s input stream.
+// Its data may be passed in via more than one buffers. The lifetime of
+// |sample_buffers|, |sample_buffer_sizes|, |video_sample_info|, and
+// |sample_drm_info| (as well as member |subsample_mapping| contained inside it)
+// are not guaranteed past the call to SbPlayerWriteSample. That means that
+// before returning, the implementation must synchronously copy any information
+// it wants to retain from those structures.
+//
+// |player|: The player to which the sample is written.
+// |sample_type|: The type of sample being written. See the |SbMediaType|
+// enum in media.h.
+// |sample_buffers|: A pointer to an array of buffers with
+// |number_of_sample_buffers| elements that hold the data for this sample. The
+// buffers are expected to be a portion of a bytestream of the codec type that
+// the player was created with. The buffers should contain a sequence of whole
+// NAL Units for video, or a complete audio frame. |sample_buffers| cannot be
+// assumed to live past the call into SbPlayerWriteSample(), so it must be
+// copied if its content will be used after SbPlayerWriteSample() returns.
+// |sample_buffer_sizes|: A pointer to an array of sizes with
+// |number_of_sample_buffers| elements. Each of them specify the number of
+// bytes in the corresponding buffer contained in |sample_buffers|. None of
+// them can be 0. |sample_buffer_sizes| cannot be assumed to live past the
+// call into SbPlayerWriteSample(), so it must be copied if its content will
+// be used after SbPlayerWriteSample() returns.
+// |number_of_sample_buffers|: Specify the number of elements contained inside
+// |sample_buffers| and |sample_buffer_sizes|. It has to be at least one, or
+// the call will be ignored.
+// |sample_pts|: The timestamp of the sample in 90KHz ticks (PTS). Note that
+// samples MAY be written "slightly" out of order.
+// |video_sample_info|: Information about a video sample. This value is
+// required if |sample_type| is |kSbMediaTypeVideo|. Otherwise, it must be
+// |NULL|.
+// |sample_drm_info|: The DRM system for the media sample. This value is
+// required for encrypted samples. Otherwise, it must be |NULL|.
+SB_EXPORT void SbPlayerWriteSample(
+ SbPlayer player,
+ SbMediaType sample_type,
+ const void** sample_buffers,
+ int* sample_buffer_sizes,
+ int number_of_sample_buffers,
+ SbMediaTime sample_pts,
+ const SbMediaVideoSampleInfo* video_sample_info,
+ const SbDrmSampleInfo* sample_drm_info);
+
+#else // SB_API_VERSION >= 4
+
// Writes a sample of the given media type to |player|'s input stream. The
// lifetime of |video_sample_info| and |sample_drm_info| (as well as member
// |subsample_mapping| contained inside it) are not guaranteed past the call
@@ -383,6 +432,8 @@
const SbMediaVideoSampleInfo* video_sample_info,
const SbDrmSampleInfo* sample_drm_info);
+#endif // SB_API_VERSION >= 4
+
// Writes a marker to |player|'s input stream of |stream_type| indicating that
// there are no more samples for that media type for the remainder of this
// media stream. This marker is invalidated, along with the rest of the stream's
@@ -393,8 +444,7 @@
SB_EXPORT void SbPlayerWriteEndOfStream(SbPlayer player,
SbMediaType stream_type);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
// Sets the player bounds to the given graphics plane coordinates. The changes
// do not take effect until the next graphics frame buffer swap. The default
// bounds for a player is the full screen. This function is only relevant when
@@ -408,26 +458,31 @@
// with such frequent calls.
//
// |player|: The player that is being resized.
+// |z_index|: The z-index of the player. When the bounds of multiple players
+// are overlapped, the one with larger z-index will be rendered on
+// top of the ones with smaller z-index.
// |x|: The x-coordinate of the upper-left corner of the player.
// |y|: The y-coordinate of the upper-left corner of the player.
// |width|: The width of the player, in pixels.
// |height|: The height of the player, in pixels.
SB_EXPORT void SbPlayerSetBounds(SbPlayer player,
+#if SB_API_VERSION >= 4
+ int z_index,
+#endif // SB_API_VERSION >= 4
int x,
int y,
int width,
int height);
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#endif // SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
// Pauses or unpauses the |player|. If the |player|'s state is
// |kPlayerStatePrerolling|, this function sets the initial pause state for
// the current seek target.
SB_EXPORT void SbPlayerSetPause(SbPlayer player, bool pause);
-#else // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#else // SB_API_VERSION < 4
// Set the playback rate of the |player|. |rate| is default to 1.0 which
// indicates the playback is at its original speed. A |rate| greater than one
@@ -441,7 +496,7 @@
// |playback_rate| is negative or if it is too high to support.
SB_EXPORT bool SbPlayerSetPlaybackRate(SbPlayer player, double playback_rate);
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
// Sets the player's volume.
//
@@ -459,7 +514,7 @@
// |out_player_info|: The information retrieved for the player.
SB_EXPORT void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info);
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
#if SB_IS(PLAYER_COMPOSITED)
// Gets a handle that represents the player's video output, for the purpose of
// composing with |SbCompositor|, which is currently undefined.
@@ -468,9 +523,9 @@
SB_EXPORT SbPlayerCompositionHandle
SbPlayerGetCompositionHandle(SbPlayer player);
#endif // SB_IS(PLAYER_COMPOSITED)
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
#if SB_IS(PLAYER_PRODUCING_TEXTURE)
// Gets an OpenGL texture ID that points to the player's video output frame at
// the time it was called. This function can be called once, and the texture ID
@@ -479,9 +534,9 @@
// |player|: The player for which the texture ID is retrieved.
SB_EXPORT uint32_t SbPlayerGetTextureId(SbPlayer player);
#endif // SB_IS(PLAYER_PRODUCING_TEXTURE)
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// Given a player created with the kSbPlayerOutputModeDecodeToTexture
// output mode, it will return a SbDecodeTarget representing the current frame
// to be rasterized. On GLES systems, this function must be called on a
@@ -490,7 +545,7 @@
// |player| object that was created with an output mode other than
// kSbPlayerOutputModeDecodeToTexture, kSbDecodeTargetInvalid is returned.
SB_EXPORT SbDecodeTarget SbPlayerGetCurrentFrame(SbPlayer player);
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
#ifdef __cplusplus
} // extern "C"
diff --git a/src/starboard/raspi/1/gyp_configuration.gypi b/src/starboard/raspi/1/gyp_configuration.gypi
index 2aa6431..f76edaf 100644
--- a/src/starboard/raspi/1/gyp_configuration.gypi
+++ b/src/starboard/raspi/1/gyp_configuration.gypi
@@ -14,24 +14,6 @@
{
'variables': {
- 'target_arch': 'arm',
- 'target_os': 'linux',
-
- 'in_app_dial%': 0,
- 'sysroot%': '/',
- 'gl_type': 'system_gles2',
-
- # VideoCore's tiled renderer will do a render for every tile of a render
- # target even if only part of that target was rendered to. Since the
- # scratch surface cache is designed to choose large offscreen surfaces so
- # that they can be maximally reused, it is not a very good fit for a tiled
- # renderer.
- 'scratch_surface_cache_size_in_bytes' : 0,
-
- # This should have a default value in cobalt/base.gypi. See the comment
- # there for acceptable values for this variable.
- 'javascript_engine': 'mozjs',
- 'cobalt_enable_jit': 0,
'cobalt_minimum_frame_time_in_milliseconds': '33',
# RasPi 1 is ARMv6
@@ -39,134 +21,16 @@
'armv7': 0,
'arm_neon': 0,
- # Define platform specific compiler and linker flags.
- # Refer to base.gypi for a list of all available variables.
- 'compiler_flags_host': [
- '-O2',
- ],
'compiler_flags': [
- # We'll pretend not to be Linux, but Starboard instead.
- '-U__linux__',
-
- # Force char to be signed.
- '-fsigned-char',
-
- # 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', # Talk to my lawyer.
- '-Wno-narrowing',
- '-Wno-unknown-pragmas',
- '-Wno-type-limits', # TODO: We should actually look into these.
-
- # Specify the sysroot with all your include dependencies.
- '--sysroot=<(sysroot)',
-
# Optimize for Raspberry Pi 1 chips.
'-march=armv6zk',
'-mcpu=arm1176jzf-s',
'-mfloat-abi=hard',
'-mfpu=vfp',
-
- # This is a quirk of Raspbian, these are required to include any
- # GL-related headers.
- '-I<(sysroot)/opt/vc/include',
- '-I<(sysroot)/opt/vc/include/interface/vcos/pthreads',
- '-I<(sysroot)/opt/vc/include/interface/vmcs_host/linux',
- ],
- 'linker_flags': [
- '--sysroot=<(sysroot)',
- # This is a quirk of Raspbian, these are required to link any GL-related
- # libraries.
- '-L<(sysroot)/opt/vc/lib',
- '-Wl,-rpath=<(sysroot)/opt/vc/lib',
-
- # We don't wrap these symbols, but this ensures that they aren't
- # linked in.
- '-Wl,--wrap=malloc',
- '-Wl,--wrap=calloc',
- '-Wl,--wrap=realloc',
- '-Wl,--wrap=memalign',
- '-Wl,--wrap=reallocalign',
- '-Wl,--wrap=free',
- '-Wl,--wrap=strdup',
- '-Wl,--wrap=malloc_usable_size',
- '-Wl,--wrap=malloc_stats_fast',
- '-Wl,--wrap=__cxa_demangle',
- ],
- 'compiler_flags_debug': [
- '-O0',
- ],
- 'compiler_flags_cc_debug': [
- '-frtti',
- ],
- 'compiler_flags_devel': [
- '-O2',
- ],
- 'compiler_flags_cc_devel': [
- '-frtti',
- ],
- 'compiler_flags_qa': [
- '-O2',
- ],
- 'compiler_flags_cc_qa': [
- '-fno-rtti',
- ],
- 'compiler_flags_gold': [
- '-O2',
- ],
- 'compiler_flags_cc_gold': [
- '-fno-rtti',
- ],
- 'platform_libraries': [
- '-lasound',
- '-lavcodec',
- '-lavformat',
- '-lavresample',
- '-lavutil',
- '-lEGL',
- '-lGLESv2',
- '-lpthread',
- '-lpulse',
- '-lrt',
- '-lopenmaxil',
- '-lbcm_host',
- '-lvcos',
- '-lvchiq_arm',
- ],
- 'conditions': [
- ['cobalt_fastbuild==0', {
- 'compiler_flags_debug': [
- '-g',
- ],
- 'compiler_flags_devel': [
- '-g',
- ],
- 'compiler_flags_qa': [
- ],
- 'compiler_flags_gold': [
- ],
- }],
],
},
'target_defaults': {
- 'defines': [
- # Cobalt on Linux flag
- 'COBALT_LINUX',
- '__STDC_FORMAT_MACROS', # so that we get PRI*
- # Enable GNU extensions to get prototypes like ffsl.
- '_GNU_SOURCE=1',
- ],
- 'cflags_c': [
- '-std=c11',
- ],
- 'cflags_cc': [
- '-std=gnu++11',
- '-Wno-literal-suffix',
- ],
'default_configuration': 'raspi-1_debug',
'configurations': {
'raspi-1_debug': {
@@ -182,21 +46,9 @@
'inherit_from': ['gold_base'],
},
}, # end of configurations
- 'target_conditions': [
- ['cobalt_code==1', {
- 'cflags': [
- '-Wall',
- '-Wextra',
- '-Wunreachable-code',
- ],
- },{
- 'cflags': [
- # Do not warn about unused function params.
- '-Wno-unused-parameter',
- # Do not warn for implicit type conversions that may change a value.
- '-Wno-conversion',
- ],
- }],
- ],
- }, # end of target_defaults
+ },
+
+ 'includes': [
+ '../shared/gyp_configuration.gypi',
+ ],
}
diff --git a/src/starboard/raspi/2/architecture.gypi b/src/starboard/raspi/2/architecture.gypi
new file mode 100644
index 0000000..a216f12
--- /dev/null
+++ b/src/starboard/raspi/2/architecture.gypi
@@ -0,0 +1,33 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'variables': {
+ # RasPi 2 is ARMv7
+ 'arm_version': 7,
+ 'armv7': 1,
+ 'arm_neon': 0, # Disable neon until it shows measurable
+ # benefit under cobalt benchmarks.
+ 'arm_float_abi': 'hard',
+
+ 'compiler_flags': [
+ # Optimize for Raspberry Pi 2 chips.
+ '-march=armv7-a',
+ '-mtune=cortex-a8',
+ '-mfloat-abi=hard',
+ #'-mfpu=neon-vfpv4', # Disable neon until it shows measurable
+ # benefit under cobalt benchmarks.
+ ],
+ },
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/raspi/2/directgles/atomic_public.h
similarity index 67%
copy from src/cobalt/base/math.cc
copy to src/starboard/raspi/2/directgles/atomic_public.h
index d335495..d8c1445 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/raspi/2/directgles/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#ifndef STARBOARD_RASPI_2_DIRECTGLES_ATOMIC_PUBLIC_H_
+#define STARBOARD_RASPI_2_DIRECTGLES_ATOMIC_PUBLIC_H_
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#include "starboard/raspi/2/atomic_public.h"
+
+#endif // STARBOARD_RASPI_2_DIRECTGLES_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/raspi/2/directgles/configuration_public.h b/src/starboard/raspi/2/directgles/configuration_public.h
new file mode 100644
index 0000000..92b76a8
--- /dev/null
+++ b/src/starboard/raspi/2/directgles/configuration_public.h
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The Starboard configuration for Raspberry PI 2 Raspbian.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_RASPI_2_DIRECTGLES_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_RASPI_2_DIRECTGLES_CONFIGURATION_PUBLIC_H_
+
+#include "starboard/raspi/2/configuration_public.h"
+
+#endif // STARBOARD_RASPI_2_DIRECTGLES_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/raspi/2/directgles/gyp_configuration.gypi b/src/starboard/raspi/2/directgles/gyp_configuration.gypi
new file mode 100644
index 0000000..fbac27e
--- /dev/null
+++ b/src/starboard/raspi/2/directgles/gyp_configuration.gypi
@@ -0,0 +1,64 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'variables': {
+ # Use the direct-to-GLES rasterizer.
+ # This rasterizer falls back to the hardware skia rasterizer in certain
+ # situations.
+ # NOTE: This rasterizer requires a 16-bit depth buffer and a full frame
+ # scratch surface (without depth buffer).
+ 'rasterizer_type': 'direct-gles',
+
+ # Accommodate the direct-to-GLES rasterizer's additional memory overhead
+ # by reducing the image cache size. This is only an issue when additional
+ # memory is required for video frames, so shrink the image cache size
+ # only during video playback.
+ 'image_cache_capacity_multiplier_when_playing_video': '0.5f',
+
+ # This dictates how much GPU memory will be used for the offscreen render
+ # target atlases. One will be used as a cache and the other used as a
+ # working scratch. It is recommended to allot enough memory for two
+ # atlases that are roughly a quarter of the framebuffer. (The render
+ # target atlases use power-of-2 dimenions.)
+ 'surface_cache_size_in_bytes': 2 * (1024 * 512 * 4),
+
+ # The rasterizer does not benefit much from rendering only the dirty
+ # region. Disable this option since it costs GPU time.
+ 'render_dirty_region_only': 0,
+ },
+
+ 'target_defaults': {
+ 'default_configuration': 'raspi-2-directgles_debug',
+ 'configurations': {
+ 'raspi-2-directgles_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'raspi-2-directgles_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'raspi-2-directgles_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'raspi-2-directgles_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ '../architecture.gypi',
+ '../../shared/gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/raspi/2/directgles/gyp_configuration.py b/src/starboard/raspi/2/directgles/gyp_configuration.py
new file mode 100644
index 0000000..172b1d2
--- /dev/null
+++ b/src/starboard/raspi/2/directgles/gyp_configuration.py
@@ -0,0 +1,32 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Raspberry Pi 2 platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+_SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
+sys.path.insert(0, os.path.join(_SCRIPT_DIR, '../..'))
+
+# pylint: disable=g-import-not-at-top
+from shared.gyp_configuration import RaspiPlatformConfig
+
+
+def CreatePlatformConfig():
+ try:
+ return RaspiPlatformConfig('raspi-2-directgles')
+ except RuntimeError as e:
+ logging.critical(e)
+ return None
diff --git a/src/starboard/raspi/2/directgles/starboard_platform.gyp b/src/starboard/raspi/2/directgles/starboard_platform.gyp
new file mode 100644
index 0000000..a304ac6
--- /dev/null
+++ b/src/starboard/raspi/2/directgles/starboard_platform.gyp
@@ -0,0 +1,18 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+{
+ 'includes': [
+ '../../shared/starboard_platform.gypi',
+ ],
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/raspi/2/directgles/thread_types_public.h
similarity index 65%
copy from src/cobalt/base/math.cc
copy to src/starboard/raspi/2/directgles/thread_types_public.h
index d335495..8e76ba5 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/raspi/2/directgles/thread_types_public.h
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#ifndef STARBOARD_RASPI_2_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_RASPI_2_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#include "starboard/raspi/2/thread_types_public.h"
+
+#endif // STARBOARD_RASPI_2_DIRECTGLES_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/raspi/2/gyp_configuration.gypi b/src/starboard/raspi/2/gyp_configuration.gypi
index ac73a07..dca287b 100644
--- a/src/starboard/raspi/2/gyp_configuration.gypi
+++ b/src/starboard/raspi/2/gyp_configuration.gypi
@@ -13,163 +13,7 @@
# limitations under the License.
{
- 'variables': {
- 'target_arch': 'arm',
- 'target_os': 'linux',
-
- 'in_app_dial%': 0,
- 'sysroot%': '/',
- 'gl_type': 'system_gles2',
-
- # VideoCore's tiled renderer will do a render for every tile of a render
- # target even if only part of that target was rendered to. Since the
- # scratch surface cache is designed to choose large offscreen surfaces so
- # that they can be maximally reused, it is not a very good fit for a tiled
- # renderer.
- 'scratch_surface_cache_size_in_bytes' : 0,
-
- # This should have a default value in cobalt/base.gypi. See the comment
- # there for acceptable values for this variable.
- 'javascript_engine': 'mozjs',
- 'cobalt_enable_jit': 0,
-
- # RasPi 2 is ARMv7
- 'arm_version': 7,
- 'armv7': 1,
- 'arm_neon': 0, # Disable neon until it shows measurable
- # benefit under cobalt benchmarks.
- 'arm_float_abi': 'hard',
-
- # Define platform specific compiler and linker flags.
- # Refer to base.gypi for a list of all available variables.
- 'compiler_flags_host': [
- '-O2',
- ],
- 'compiler_flags': [
- # We'll pretend not to be Linux, but Starboard instead.
- '-U__linux__',
-
- # Force char to be signed.
- '-fsigned-char',
-
- # 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', # Talk to my lawyer.
- '-Wno-narrowing',
- '-Wno-unknown-pragmas',
- '-Wno-type-limits', # TODO: We should actually look into these.
-
- # Specify the sysroot with all your include dependencies.
- '--sysroot=<(sysroot)',
-
- # Optimize for Raspberry Pi 2 chips.
- '-march=armv7-a',
- '-mtune=cortex-a8',
- '-mfloat-abi=hard',
- #'-mfpu=neon-vfpv4', # Disable neon until it shows measurable
- # benefit under cobalt benchmarks.
-
- # This is a quirk of Raspbian, these are required to include any
- # GL-related headers.
- '-I<(sysroot)/usr/local/include',
- '-I<(sysroot)/opt/vc/include',
- '-I<(sysroot)/opt/vc/include/interface/vcos/pthreads',
- '-I<(sysroot)/opt/vc/include/interface/vmcs_host/linux',
- ],
- 'linker_flags': [
- '--sysroot=<(sysroot)',
- # This is a quirk of Raspbian, these are required to link any GL-related
- # libraries.
- '-L<(sysroot)/opt/vc/lib',
- '-Wl,-rpath=<(sysroot)/opt/vc/lib',
-
- # We don't wrap these symbols, but this ensures that they aren't
- # linked in.
- '-Wl,--wrap=malloc',
- '-Wl,--wrap=calloc',
- '-Wl,--wrap=realloc',
- '-Wl,--wrap=memalign',
- '-Wl,--wrap=reallocalign',
- '-Wl,--wrap=free',
- '-Wl,--wrap=strdup',
- '-Wl,--wrap=malloc_usable_size',
- '-Wl,--wrap=malloc_stats_fast',
- '-Wl,--wrap=__cxa_demangle',
- ],
- 'compiler_flags_debug': [
- '-O0',
- ],
- 'compiler_flags_cc_debug': [
- '-frtti',
- ],
- 'compiler_flags_devel': [
- '-O2',
- ],
- 'compiler_flags_cc_devel': [
- '-frtti',
- ],
- 'compiler_flags_qa': [
- '-O2',
- ],
- 'compiler_flags_cc_qa': [
- '-fno-rtti',
- ],
- 'compiler_flags_gold': [
- '-O2',
- ],
- 'compiler_flags_cc_gold': [
- '-fno-rtti',
- ],
- 'platform_libraries': [
- '-lasound',
- '-lavcodec',
- '-lavformat',
- '-lavresample',
- '-lavutil',
- '-lEGL',
- '-lGLESv2',
- '-lpthread',
- '-lpulse',
- '-lrt',
- '-lopenmaxil',
- '-lbcm_host',
- '-lvcos',
- '-lvchiq_arm',
- ],
- 'conditions': [
- ['cobalt_fastbuild==0', {
- 'compiler_flags_debug': [
- '-g',
- ],
- 'compiler_flags_devel': [
- '-g',
- ],
- 'compiler_flags_qa': [
- ],
- 'compiler_flags_gold': [
- ],
- }],
- ],
- },
-
'target_defaults': {
- 'defines': [
- # Cobalt on Linux flag
- 'COBALT_LINUX',
- '__STDC_FORMAT_MACROS', # so that we get PRI*
- # Enable GNU extensions to get prototypes like ffsl.
- '_GNU_SOURCE=1',
- ],
- 'cflags_c': [
- '-std=c11',
- ],
- 'cflags_cc': [
- '-std=gnu++11',
- '-Wno-literal-suffix',
- ],
'default_configuration': 'raspi-2_debug',
'configurations': {
'raspi-2_debug': {
@@ -185,21 +29,10 @@
'inherit_from': ['gold_base'],
},
}, # end of configurations
- 'target_conditions': [
- ['cobalt_code==1', {
- 'cflags': [
- '-Wall',
- '-Wextra',
- '-Wunreachable-code',
- ],
- },{
- 'cflags': [
- # Do not warn about unused function params.
- '-Wno-unused-parameter',
- # Do not warn for implicit type conversions that may change a value.
- '-Wno-conversion',
- ],
- }],
- ],
- }, # end of target_defaults
+ },
+
+ 'includes': [
+ 'architecture.gypi',
+ '../shared/gyp_configuration.gypi',
+ ],
}
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index 39bb1c7..f89c9a5 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -18,7 +18,7 @@
#define STARBOARD_RASPI_SHARED_CONFIGURATION_PUBLIC_H_
// The API version implemented by this platform.
-#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
+#define SB_API_VERSION 4
// --- System Header Configuration -------------------------------------------
@@ -52,6 +52,9 @@
// Whether the current platform has microphone supported.
#define SB_HAS_MICROPHONE 0
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
// Whether the current platform has speech synthesis.
#define SB_HAS_SPEECH_SYNTHESIS 0
@@ -260,7 +263,7 @@
// supported composition methods below.
#define SB_HAS_PLAYER 1
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
// Specifies whether this platform's player will produce an OpenGL texture that
// the client must draw every frame with its graphics rendering. It may be that
// we get a texture handle, but cannot perform operations like GlReadPixels on
@@ -279,7 +282,9 @@
// this case, changing the video bounds must be tightly synchronized between the
// player and the graphics plane.
#define SB_IS_PLAYER_PUNCHED_OUT 1
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
+
+#if SB_API_VERSION < 4
// Specifies the maximum amount of memory used by audio buffers of media source
// before triggering a garbage collection. A large value will cause more memory
@@ -308,7 +313,7 @@
// macro should be set to a value that is greater than the sum of the above
// source buffer stream memory limits with extra room to take account of
// fragmentations and memory used by demuxers.
-#define SB_MEDIA_MAIN_BUFFER_BUDGET (32U * 1024U * 1024U)
+#define SB_MEDIA_MAIN_BUFFER_BUDGET (24U * 1024U * 1024U)
// Specifies how much GPU memory to reserve up-front for media source buffers.
// This should only be set to non-zero on system with limited CPU memory and
@@ -317,6 +322,18 @@
// media buffers being decoded when being stored in GPU.
#define SB_MEDIA_GPU_BUFFER_BUDGET 0U
+#endif // SB_API_VERSION < 4
+
+// The maximum audio bitrate the platform can decode. The following value
+// equals to 5M bytes per seconds which is more than enough for compressed
+// audio.
+#define SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND (40 * 1024 * 1024)
+
+// The maximum video bitrate the platform can decode. The following value
+// equals to 25M bytes per seconds which is more than enough for compressed
+// video.
+#define SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND (200 * 1024 * 1024)
+
// Specifies whether this platform has webm/vp9 support. This should be set to
// non-zero on platforms with webm/vp9 support.
#define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
diff --git a/src/starboard/raspi/shared/gyp_configuration.gypi b/src/starboard/raspi/shared/gyp_configuration.gypi
new file mode 100644
index 0000000..96dca33
--- /dev/null
+++ b/src/starboard/raspi/shared/gyp_configuration.gypi
@@ -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.
+
+{
+ 'variables': {
+ 'target_arch': 'arm',
+ 'target_os': 'linux',
+
+ 'in_app_dial%': 0,
+ 'sysroot%': '/',
+ 'gl_type': 'system_gles2',
+
+ # VideoCore's tiled renderer will do a render for every tile of a render
+ # target even if only part of that target was rendered to. Since the
+ # scratch surface cache is designed to choose large offscreen surfaces so
+ # that they can be maximally reused, it is not a very good fit for a tiled
+ # renderer.
+ 'scratch_surface_cache_size_in_bytes' : 0,
+
+ # This should have a default value in cobalt/base.gypi. See the comment
+ # there for acceptable values for this variable.
+ 'javascript_engine': 'mozjs',
+ 'cobalt_enable_jit': 0,
+ 'cobalt_media_source_2016': 1,
+
+ # This atlas size works better than the auto-mem setting.
+ 'skia_glyph_atlas_width%': '2048',
+ 'skia_glyph_atlas_height%': '2048',
+
+ # Define platform specific compiler and linker flags.
+ # Refer to base.gypi for a list of all available variables.
+ 'compiler_flags_host': [
+ '-O2',
+ ],
+ 'compiler_flags': [
+ # We'll pretend not to be Linux, but Starboard instead.
+ '-U__linux__',
+
+ # Force char to be signed.
+ '-fsigned-char',
+
+ # 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', # Talk to my lawyer.
+ '-Wno-narrowing',
+ '-Wno-unknown-pragmas',
+ '-Wno-type-limits', # TODO: We should actually look into these.
+
+ # Specify the sysroot with all your include dependencies.
+ '--sysroot=<(sysroot)',
+
+ # This is a quirk of Raspbian, these are required to include any
+ # GL-related headers.
+ '-I<(sysroot)/opt/vc/include',
+ '-I<(sysroot)/opt/vc/include/interface/vcos/pthreads',
+ '-I<(sysroot)/opt/vc/include/interface/vmcs_host/linux',
+ ],
+ 'linker_flags': [
+ '--sysroot=<(sysroot)',
+ # This is a quirk of Raspbian, these are required to link any GL-related
+ # libraries.
+ '-L<(sysroot)/opt/vc/lib',
+ '-Wl,-rpath=<(sysroot)/opt/vc/lib',
+
+ # We don't wrap these symbols, but this ensures that they aren't
+ # linked in.
+ '-Wl,--wrap=malloc',
+ '-Wl,--wrap=calloc',
+ '-Wl,--wrap=realloc',
+ '-Wl,--wrap=memalign',
+ '-Wl,--wrap=reallocalign',
+ '-Wl,--wrap=free',
+ '-Wl,--wrap=strdup',
+ '-Wl,--wrap=malloc_usable_size',
+ '-Wl,--wrap=malloc_stats_fast',
+ '-Wl,--wrap=__cxa_demangle',
+ ],
+ 'compiler_flags_debug': [
+ '-O0',
+ ],
+ 'compiler_flags_cc_debug': [
+ '-frtti',
+ ],
+ 'compiler_flags_devel': [
+ '-O2',
+ ],
+ 'compiler_flags_cc_devel': [
+ '-frtti',
+ ],
+ 'compiler_flags_qa': [
+ '-O2',
+ ],
+ 'compiler_flags_cc_qa': [
+ '-fno-rtti',
+ ],
+ 'compiler_flags_gold': [
+ '-O2',
+ ],
+ 'compiler_flags_cc_gold': [
+ '-fno-rtti',
+ ],
+ 'platform_libraries': [
+ '-lasound',
+ '-lavcodec',
+ '-lavformat',
+ '-lavresample',
+ '-lavutil',
+ '-lEGL',
+ '-lGLESv2',
+ '-lpthread',
+ '-lpulse',
+ '-lrt',
+ '-lopenmaxil',
+ '-lbcm_host',
+ '-lvcos',
+ '-lvchiq_arm',
+ ],
+ 'conditions': [
+ ['cobalt_fastbuild==0', {
+ 'compiler_flags_debug': [
+ '-g',
+ ],
+ 'compiler_flags_devel': [
+ '-g',
+ ],
+ 'compiler_flags_qa': [
+ ],
+ 'compiler_flags_gold': [
+ ],
+ }],
+ ],
+ },
+
+ 'target_defaults': {
+ 'defines': [
+ # Cobalt on Linux flag
+ 'COBALT_LINUX',
+ '__STDC_FORMAT_MACROS', # so that we get PRI*
+ # Enable GNU extensions to get prototypes like ffsl.
+ '_GNU_SOURCE=1',
+ ],
+ 'cflags_c': [
+ '-std=c11',
+ ],
+ 'cflags_cc': [
+ '-std=gnu++11',
+ '-Wno-literal-suffix',
+ ],
+ 'target_conditions': [
+ ['cobalt_code==1', {
+ 'cflags': [
+ '-Wall',
+ '-Wextra',
+ '-Wunreachable-code',
+ ],
+ },{
+ 'cflags': [
+ # Do not warn about unused function params.
+ '-Wno-unused-parameter',
+ # Do not warn for implicit type conversions that may change a value.
+ '-Wno-conversion',
+ ],
+ }],
+ ],
+ }, # end of target_defaults
+}
diff --git a/src/starboard/raspi/shared/open_max/decode_target_create.cc b/src/starboard/raspi/shared/open_max/decode_target_create.cc
index 76c34f3..41667c8 100644
--- a/src/starboard/raspi/shared/open_max/decode_target_create.cc
+++ b/src/starboard/raspi/shared/open_max/decode_target_create.cc
@@ -12,23 +12,34 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/decode_target.h"
+#include "starboard/raspi/shared/open_max/decode_target_create.h"
#include <GLES2/gl2.h>
+#include "starboard/decode_target.h"
#include "starboard/log.h"
#include "starboard/raspi/shared/open_max/decode_target_internal.h"
#include "starboard/shared/gles/gl_call.h"
-SbDecodeTarget SbDecodeTargetCreate(EGLDisplay display,
- EGLContext context,
- SbDecodeTargetFormat format,
- int width,
- int height) {
- SB_DCHECK(format == kSbDecodeTargetFormat1PlaneRGBA);
- if (format != kSbDecodeTargetFormat1PlaneRGBA) {
- return kSbDecodeTargetInvalid;
- }
+namespace starboard {
+namespace raspi {
+namespace shared {
+namespace open_max {
+
+namespace {
+
+struct CreateParams {
+ int width;
+ int height;
+ SbDecodeTargetFormat format;
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+
+ SbDecodeTarget decode_target_out;
+};
+
+void CreateWithContextRunner(void* context) {
+ CreateParams* params = static_cast<CreateParams*>(context);
GLuint texture;
GL_CALL(glGenTextures(1, &texture));
@@ -38,32 +49,62 @@
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
- GL_UNSIGNED_BYTE, NULL));
+ GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, params->width, params->height,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL));
SbDecodeTargetPrivate* target = new SbDecodeTargetPrivate;
- target->display = display;
- target->info.format = format;
+ target->display = params->egl_display;
+ target->info.format = params->format;
// Data to be set by the decoder.
target->info.is_opaque = false;
- target->info.width = width;
- target->info.height = height;
+ target->info.width = params->width;
+ target->info.height = params->height;
target->info.planes[0].texture = texture;
target->info.planes[0].gl_texture_target = GL_TEXTURE_2D;
- target->info.planes[0].width = width;
- target->info.planes[0].height = height;
+ target->info.planes[0].width = params->width;
+ target->info.planes[0].height = params->height;
target->info.planes[0].content_region.left = 0;
target->info.planes[0].content_region.top = 0;
- target->info.planes[0].content_region.right = width;
- target->info.planes[0].content_region.bottom = height;
+ target->info.planes[0].content_region.right = params->width;
+ target->info.planes[0].content_region.bottom = params->height;
- target->images[0] =
- eglCreateImageKHR(display, context, EGL_GL_TEXTURE_2D_KHR,
- (EGLClientBuffer)target->info.planes[0].texture, NULL);
+ target->images[0] = eglCreateImageKHR(
+ params->egl_display, params->egl_context, EGL_GL_TEXTURE_2D_KHR,
+ (EGLClientBuffer)target->info.planes[0].texture, NULL);
SB_DCHECK(eglGetError() == EGL_SUCCESS);
SB_DCHECK(target->images[0] != EGL_NO_IMAGE_KHR);
GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
- return target;
+ params->decode_target_out = target;
}
+} // namespace
+
+SbDecodeTarget DecodeTargetCreate(
+ SbDecodeTargetGraphicsContextProvider* provider,
+ SbDecodeTargetFormat format,
+ int width,
+ int height) {
+ SB_DCHECK(format == kSbDecodeTargetFormat1PlaneRGBA);
+ if (format != kSbDecodeTargetFormat1PlaneRGBA) {
+ return kSbDecodeTargetInvalid;
+ }
+
+ CreateParams params;
+ params.width = width;
+ params.height = height;
+ params.format = format;
+ params.egl_display = provider->egl_display;
+ params.egl_context = provider->egl_context;
+ params.decode_target_out = kSbDecodeTargetInvalid;
+
+ SbDecodeTargetRunInGlesContext(
+ provider, &CreateWithContextRunner, static_cast<void*>(¶ms));
+
+ return params.decode_target_out;
+}
+
+} // namespace open_max
+} // namespace shared
+} // namespace raspi
+} // namespace starboard
diff --git a/src/starboard/raspi/shared/open_max/decode_target_create.h b/src/starboard/raspi/shared/open_max/decode_target_create.h
new file mode 100644
index 0000000..ef582b3
--- /dev/null
+++ b/src/starboard/raspi/shared/open_max/decode_target_create.h
@@ -0,0 +1,36 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_RASPI_SHARED_OPEN_MAX_DECODE_TARGET_CREATE_H_
+#define STARBOARD_RASPI_SHARED_OPEN_MAX_DECODE_TARGET_CREATE_H_
+
+#include "starboard/decode_target.h"
+
+namespace starboard {
+namespace raspi {
+namespace shared {
+namespace open_max {
+
+SbDecodeTarget DecodeTargetCreate(
+ SbDecodeTargetGraphicsContextProvider* provider,
+ SbDecodeTargetFormat format,
+ int width,
+ int height);
+
+} // namespace open_max
+} // namespace shared
+} // namespace raspi
+} // namespace starboard
+
+#endif // STARBOARD_RASPI_SHARED_OPEN_MAX_DECODE_TARGET_CREATE_H_
diff --git a/src/starboard/raspi/shared/open_max/image_decode.cc b/src/starboard/raspi/shared/open_max/image_decode.cc
index 30b551a..43948dd 100644
--- a/src/starboard/raspi/shared/open_max/image_decode.cc
+++ b/src/starboard/raspi/shared/open_max/image_decode.cc
@@ -23,7 +23,7 @@
starboard::Mutex decode_lock_;
}
-SbDecodeTarget SbImageDecode(SbDecodeTargetProvider* provider,
+SbDecodeTarget SbImageDecode(SbDecodeTargetGraphicsContextProvider* provider,
void* data,
int data_size,
const char* mime_type,
diff --git a/src/starboard/raspi/shared/open_max/image_is_decode_supported.cc b/src/starboard/raspi/shared/open_max/image_is_decode_supported.cc
index d9a98ff..6d559ce 100644
--- a/src/starboard/raspi/shared/open_max/image_is_decode_supported.cc
+++ b/src/starboard/raspi/shared/open_max/image_is_decode_supported.cc
@@ -20,8 +20,17 @@
bool SbImageIsDecodeSupported(const char* mime_type,
SbDecodeTargetFormat format) {
+ // Unfortunately, while openmax image decoding is implemented and supported,
+ // there is a very sporadic (i.e. around every 1 in 200 image decodes) crash
+ // bug that will go off. This may be due to a threading issue somewhere,
+ // but it is not clear right now. Temporarily, we report that we do not
+ // support Starboard image decoding.
+#if 1
+ return false;
+#else
bool type_supported =
OMX_IMAGE_CodingMax !=
open_max::OpenMaxImageDecodeComponent::GetCompressionFormat(mime_type);
return type_supported && format == kSbDecodeTargetFormat1PlaneRGBA;
+#endif
}
diff --git a/src/starboard/raspi/shared/open_max/open_max_image_decode_component.cc b/src/starboard/raspi/shared/open_max/open_max_image_decode_component.cc
index f2dfe44..a915af2 100644
--- a/src/starboard/raspi/shared/open_max/open_max_image_decode_component.cc
+++ b/src/starboard/raspi/shared/open_max/open_max_image_decode_component.cc
@@ -18,6 +18,7 @@
#include "starboard/configuration.h"
#include "starboard/memory.h"
+#include "starboard/raspi/shared/open_max/decode_target_create.h"
#include "starboard/raspi/shared/open_max/decode_target_internal.h"
#include "starboard/thread.h"
@@ -46,7 +47,7 @@
OpenMaxImageDecodeComponent::OpenMaxImageDecodeComponent()
: OpenMaxComponent("OMX.broadcom.image_decode"),
state_(kStateIdle),
- target_provider_(NULL),
+ graphics_context_provider_(NULL),
target_(NULL),
input_format_(OMX_IMAGE_CodingMax) {}
@@ -62,14 +63,14 @@
}
SbDecodeTarget OpenMaxImageDecodeComponent::Decode(
- SbDecodeTargetProvider* provider,
+ SbDecodeTargetGraphicsContextProvider* provider,
const char* mime_type,
SbDecodeTargetFormat output_format,
const void* data,
int data_size) {
SB_DCHECK(target_ == NULL);
- target_provider_ = provider;
+ graphics_context_provider_ = provider;
SetInputFormat(mime_type, output_format);
// Start the component and enable the input port.
@@ -117,8 +118,8 @@
void OpenMaxImageDecodeComponent::SetOutputFormat(OMX_COLOR_FORMATTYPE format,
int width,
int height) {
- target_ = SbDecodeTargetAcquireFromProvider(
- target_provider_, kSbDecodeTargetFormat1PlaneRGBA, width, height);
+ target_ = DecodeTargetCreate(graphics_context_provider_,
+ kSbDecodeTargetFormat1PlaneRGBA, width, height);
target_->info.is_opaque =
(input_format_ == OMX_IMAGE_CodingPNG) ? false : true;
render_component_.SetOutputImage(target_->images[0]);
diff --git a/src/starboard/raspi/shared/open_max/open_max_image_decode_component.h b/src/starboard/raspi/shared/open_max/open_max_image_decode_component.h
index a12458b..c99f02b 100644
--- a/src/starboard/raspi/shared/open_max/open_max_image_decode_component.h
+++ b/src/starboard/raspi/shared/open_max/open_max_image_decode_component.h
@@ -35,7 +35,7 @@
static OMX_IMAGE_CODINGTYPE GetCompressionFormat(const char* mime_type);
// Decode the given data to a decode target.
- SbDecodeTarget Decode(SbDecodeTargetProvider* provider,
+ SbDecodeTarget Decode(SbDecodeTargetGraphicsContextProvider* provider,
const char* mime_type,
SbDecodeTargetFormat output_format,
const void* data,
@@ -59,7 +59,7 @@
OpenMaxEglRenderComponent render_component_;
State state_;
- SbDecodeTargetProvider* target_provider_;
+ SbDecodeTargetGraphicsContextProvider* graphics_context_provider_;
SbDecodeTargetPrivate* target_;
OMX_IMAGE_CODINGTYPE input_format_;
};
diff --git a/src/starboard/raspi/shared/open_max/video_decoder.h b/src/starboard/raspi/shared/open_max/video_decoder.h
index 454f33c..fa950fe 100644
--- a/src/starboard/raspi/shared/open_max/video_decoder.h
+++ b/src/starboard/raspi/shared/open_max/video_decoder.h
@@ -35,7 +35,7 @@
namespace open_max {
class VideoDecoder
- : public starboard::shared::starboard::player::filter::VideoDecoder {
+ : public starboard::shared::starboard::player::filter::HostedVideoDecoder {
public:
typedef starboard::shared::starboard::player::InputBuffer InputBuffer;
typedef starboard::shared::starboard::player::VideoFrame VideoFrame;
diff --git a/src/starboard/raspi/shared/player_components_impl.cc b/src/starboard/raspi/shared/player_components_impl.cc
index bd3a737..5644922 100644
--- a/src/starboard/raspi/shared/player_components_impl.cc
+++ b/src/starboard/raspi/shared/player_components_impl.cc
@@ -47,8 +47,8 @@
scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
audio_parameters.audio_header);
- VideoRendererImpl* video_renderer =
- new VideoRendererImpl(scoped_ptr<VideoDecoder>(video_decoder).Pass());
+ VideoRendererImpl* video_renderer = new VideoRendererImpl(
+ scoped_ptr<HostedVideoDecoder>(video_decoder).Pass());
return scoped_ptr<PlayerComponents>(
new PlayerComponents(audio_renderer, video_renderer));
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index ffb22bf..65951fd 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -37,6 +37,7 @@
'<(DEPTH)/starboard/raspi/shared/dispmanx_util.h',
'<(DEPTH)/starboard/raspi/shared/main.cc',
'<(DEPTH)/starboard/raspi/shared/open_max/decode_target_create.cc',
+ '<(DEPTH)/starboard/raspi/shared/open_max/decode_target_create.h',
'<(DEPTH)/starboard/raspi/shared/open_max/decode_target_get_info.cc',
'<(DEPTH)/starboard/raspi/shared/open_max/decode_target_internal.h',
'<(DEPTH)/starboard/raspi/shared/open_max/decode_target_release.cc',
@@ -130,6 +131,7 @@
'<(DEPTH)/starboard/shared/linux/get_home_directory.cc',
'<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc',
'<(DEPTH)/starboard/shared/linux/page_internal.cc',
+ '<(DEPTH)/starboard/shared/linux/socket_get_interface_address.cc',
'<(DEPTH)/starboard/shared/linux/socket_get_local_interface_address.cc',
'<(DEPTH)/starboard/shared/linux/system_get_random_data.cc',
'<(DEPTH)/starboard/shared/linux/system_get_stack.cc',
@@ -256,9 +258,17 @@
'<(DEPTH)/starboard/shared/starboard/log_message.cc',
'<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc',
'<(DEPTH)/starboard/shared/starboard/log_raw_format.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/codec_util.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/codec_util.h',
'<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_sfr_only.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_util.cc',
+ '<(DEPTH)/starboard/shared/starboard/media/media_util.h',
'<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
'<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
'<(DEPTH)/starboard/shared/starboard/new.cc',
@@ -304,18 +314,25 @@
'<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
'<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
'<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
'<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
'<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
'<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
'<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
'<(DEPTH)/starboard/shared/stub/drm_close_session.cc',
'<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
'<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
'<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
- '<(DEPTH)/starboard/shared/stub/media_get_audio_configuration.cc',
- '<(DEPTH)/starboard/shared/stub/media_get_audio_output_count.cc',
'<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
'<(DEPTH)/starboard/shared/stub/system_clear_platform_error.cc',
'<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
diff --git a/src/starboard/raspi/shared/thread_create_priority.cc b/src/starboard/raspi/shared/thread_create_priority.cc
index b36cd1f..451c85d 100644
--- a/src/starboard/raspi/shared/thread_create_priority.cc
+++ b/src/starboard/raspi/shared/thread_create_priority.cc
@@ -15,6 +15,7 @@
#include "starboard/shared/pthread/thread_create_priority.h"
#include <sched.h>
+#include <sys/resource.h>
#include "starboard/log.h"
@@ -23,47 +24,89 @@
namespace pthread {
#if SB_HAS(THREAD_PRIORITY_SUPPORT)
-void ThreadSetPriority(SbThreadPriority priority) {
- // Note that use of sched_setscheduler() has been found to be more reliably
- // supported than pthread_setschedparam(), so we are using that.
+// Note that use of sched_setscheduler() has been found to be more reliably
+// supported than pthread_setschedparam(), so we are using that.
+void SetIdleScheduler() {
+ struct sched_param thread_sched_param;
+ thread_sched_param.sched_priority = 0;
+ int result = sched_setscheduler(0, SCHED_IDLE, &thread_sched_param);
+ if (result != 0) {
+ SB_NOTREACHED();
+ }
+}
+
+void SetOtherScheduler() {
+ struct sched_param thread_sched_param;
+ thread_sched_param.sched_priority = 0;
+ int result = sched_setscheduler(0, SCHED_OTHER, &thread_sched_param);
+ if (result != 0) {
+ SB_NOTREACHED();
+ }
+}
+
+// Here |priority| is a number between >= 0, where the higher the number, the
+// higher the priority. The actual real time priority setting will be:
+//
+// std::min(sched_get_priority_min(SCHED_RR) + priority,
+// sched_get_priority_max(SCHED_RR))
+//
+// If the desired priority is not supported on this platform, we fall back to
+// SCHED_OTHER.
+void SetRoundRobinScheduler(int priority) {
+ // First determine what the system has setup for the min/max priorities.
+ int min_priority = sched_get_priority_min(SCHED_RR);
+ int max_priority = sched_get_priority_max(SCHED_RR);
+
+ struct rlimit rlimit_rtprio;
+ getrlimit(RLIMIT_RTPRIO, &rlimit_rtprio);
+
+ if (rlimit_rtprio.rlim_cur < min_priority) {
+ SB_LOG(WARNING) << "Unable to set real time round-robin thread priority "
+ << "because `ulimit -r` is too low ("
+ << rlimit_rtprio.rlim_cur << " < " << min_priority << ").";
+
+ // Fallback to SCHED_OTHER.
+ SetOtherScheduler();
+ } else {
+ struct sched_param thread_sched_param;
+ thread_sched_param.sched_priority =
+ std::min(min_priority + priority, max_priority);
+ int result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+ if (result != 0) {
+ SB_NOTREACHED();
+ }
+ }
+}
+
+void ThreadSetPriority(SbThreadPriority priority) {
// Use different schedulers according to priority. This is preferred over
// using SCHED_RR for all threads because the scheduler time slice is too
// high (defaults to 100ms) for the desired threading behavior.
- struct sched_param thread_sched_param;
- int result = 0;
-
- thread_sched_param.sched_priority = 0;
switch (priority) {
case kSbThreadPriorityLowest:
case kSbThreadPriorityLow:
- result = sched_setscheduler(0, SCHED_IDLE, &thread_sched_param);
+ SetIdleScheduler();
break;
case kSbThreadNoPriority:
case kSbThreadPriorityNormal:
- result = sched_setscheduler(0, SCHED_OTHER, &thread_sched_param);
+ SetOtherScheduler();
break;
case kSbThreadPriorityHigh:
- thread_sched_param.sched_priority = 1;
- result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+ SetRoundRobinScheduler(0);
break;
case kSbThreadPriorityHighest:
- thread_sched_param.sched_priority = 2;
- result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+ SetRoundRobinScheduler(1);
break;
case kSbThreadPriorityRealTime:
- thread_sched_param.sched_priority = 3;
- result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+ SetRoundRobinScheduler(2);
break;
default:
SB_NOTREACHED();
break;
}
-
- if (result != 0) {
- SB_NOTREACHED();
- }
}
+
#endif // SB_HAS(THREAD_PRIORITY_SUPPORT)
} // namespace pthread
diff --git a/src/starboard/shared/alsa/alsa_audio_sink_type.cc b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
index 62306c0..25688a5 100644
--- a/src/starboard/shared/alsa/alsa_audio_sink_type.cc
+++ b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
@@ -92,12 +92,12 @@
bool IsType(Type* type) SB_OVERRIDE { return type_ == type; }
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
void SetPlaybackRate(double playback_rate) SB_OVERRIDE {
ScopedLock lock(mutex_);
playback_rate_ = playback_rate;
}
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
bool is_valid() { return playback_handle_ != NULL; }
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
index f0c4d34..8951eaf 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -64,16 +64,29 @@
av_free(context);
}
+AVCodecID GetFfmpegCodecIdByMediaCodec(SbMediaAudioCodec audio_codec) {
+ switch (audio_codec) {
+ case kSbMediaAudioCodecAac:
+ return AV_CODEC_ID_AAC;
+ case kSbMediaAudioCodecOpus:
+ return AV_CODEC_ID_OPUS;
+ default:
+ return AV_CODEC_ID_NONE;
+ }
+}
+
} // namespace
AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
const SbMediaAudioHeader& audio_header)
- : sample_type_(GetSupportedSampleType()),
+ : audio_codec_(audio_codec),
+ sample_type_(GetSupportedSampleType()),
codec_context_(NULL),
av_frame_(NULL),
stream_ended_(false),
audio_header_(audio_header) {
- SB_DCHECK(audio_codec == kSbMediaAudioCodecAac);
+ SB_DCHECK(GetFfmpegCodecIdByMediaCodec(audio_codec) != AV_CODEC_ID_NONE)
+ << "Unsupported audio codec " << audio_codec;
InitializeCodec();
}
@@ -118,7 +131,7 @@
codec_context_->channels * av_frame_->nb_samples *
(sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4));
if (codec_context_->sample_fmt == codec_context_->request_sample_fmt) {
- SbMemoryCopy(decoded_audio->buffer(), av_frame_->extended_data,
+ SbMemoryCopy(decoded_audio->buffer(), *av_frame_->extended_data,
decoded_audio->size());
} else {
ConvertSamples(codec_context_->sample_fmt,
@@ -177,7 +190,7 @@
}
codec_context_->codec_type = AVMEDIA_TYPE_AUDIO;
- codec_context_->codec_id = AV_CODEC_ID_AAC;
+ codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(audio_codec_);
// Request_sample_fmt is set by us, but sample_fmt is set by the decoder.
if (sample_type_ == kSbMediaAudioSampleTypeInt16) {
codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
@@ -199,7 +212,7 @@
return;
}
- int rv = avcodec_open2(codec_context_, codec, NULL);
+ int rv = OpenCodec(codec_context_, codec);
if (rv < 0) {
SB_LOG(ERROR) << "Unable to open codec";
TeardownCodec();
@@ -215,7 +228,7 @@
void AudioDecoder::TeardownCodec() {
if (codec_context_) {
- avcodec_close(codec_context_);
+ CloseCodec(codec_context_);
av_free(codec_context_);
codec_context_ = NULL;
}
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
index 4d6ba4c..a74b0b3 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -49,6 +49,7 @@
void InitializeCodec();
void TeardownCodec();
+ SbMediaAudioCodec audio_codec_;
SbMediaAudioSampleType sample_type_;
AVCodecContext* codec_context_;
AVFrame* av_frame_;
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_common.cc b/src/starboard/shared/ffmpeg/ffmpeg_common.cc
index b19a10c..b3fd3b6 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_common.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_common.cc
@@ -15,6 +15,7 @@
#include "starboard/shared/ffmpeg/ffmpeg_common.h"
#include "starboard/log.h"
+#include "starboard/mutex.h"
#include "starboard/once.h"
namespace starboard {
@@ -24,6 +25,7 @@
namespace {
SbOnceControl ffmpeg_initialization_once = SB_ONCE_INITIALIZER;
+SbMutex codec_mutex = SB_MUTEX_INITIALIZER;
} // namespace
@@ -32,6 +34,19 @@
SB_DCHECK(initialized);
}
+int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec) {
+ SbMutexAcquire(&codec_mutex);
+ int result = avcodec_open2(codec_context, codec, NULL);
+ SbMutexRelease(&codec_mutex);
+ return result;
+}
+
+void CloseCodec(AVCodecContext* codec_context) {
+ SbMutexAcquire(&codec_mutex);
+ avcodec_close(codec_context);
+ SbMutexRelease(&codec_mutex);
+}
+
} // namespace ffmpeg
} // namespace shared
} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_common.h b/src/starboard/shared/ffmpeg/ffmpeg_common.h
index 4ba3573..4203e91 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_common.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_common.h
@@ -33,6 +33,14 @@
void InitializeFfmpeg();
+// In Ffmpeg, the calls to avcodec_open2() and avcodec_close() are not
+// synchronized internally so it is the responsibility of its user to ensure
+// that these calls don't overlap. The following functions acquires a lock
+// internally before calling avcodec_open2() and avcodec_close() to enforce
+// this.
+int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec);
+void CloseCodec(AVCodecContext* codec_context);
+
} // namespace ffmpeg
} // namespace shared
} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
index e26dc9c..2d4e659 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -265,7 +265,7 @@
return;
}
- int rv = avcodec_open2(codec_context_, codec, NULL);
+ int rv = OpenCodec(codec_context_, codec);
if (rv < 0) {
SB_LOG(ERROR) << "Unable to open codec";
TeardownCodec();
@@ -281,7 +281,7 @@
void VideoDecoder::TeardownCodec() {
if (codec_context_) {
- avcodec_close(codec_context_);
+ CloseCodec(codec_context_);
av_free(codec_context_);
codec_context_ = NULL;
}
@@ -297,7 +297,7 @@
namespace player {
namespace filter {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// static
bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
SbMediaVideoCodec codec,
@@ -307,7 +307,7 @@
return output_mode == kSbPlayerOutputModePunchOut;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
} // namespace filter
} // namespace player
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
index 6fcc2f1..03639ed 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
@@ -29,7 +29,7 @@
namespace shared {
namespace ffmpeg {
-class VideoDecoder : public starboard::player::filter::VideoDecoder {
+class VideoDecoder : public starboard::player::filter::HostedVideoDecoder {
public:
typedef starboard::player::InputBuffer InputBuffer;
typedef starboard::player::VideoFrame VideoFrame;
diff --git a/src/starboard/shared/linux/socket_get_interface_address.cc b/src/starboard/shared/linux/socket_get_interface_address.cc
new file mode 100644
index 0000000..752d898
--- /dev/null
+++ b/src/starboard/shared/linux/socket_get_interface_address.cc
@@ -0,0 +1,382 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/socket.h"
+
+#if SB_API_VERSION >= 4
+
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <linux/if.h>
+#include <linux/if_addr.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "net/base/net_util.h"
+#include "starboard/byte_swap.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/posix/socket_internal.h"
+
+namespace sbposix = starboard::shared::posix;
+
+namespace {
+
+bool IsAnyAddress(const SbSocketAddress& address) {
+ switch (address.type) {
+ case kSbSocketAddressTypeIpv4:
+ return (address.address[0] == 0 && address.address[1] == 0 &&
+ address.address[2] == 0 && address.address[3] == 0);
+#if SB_HAS(IPV6)
+ case kSbSocketAddressTypeIpv6: {
+ bool found_nonzero = false;
+ for (std::size_t i = 0; i != net::kIPv6AddressSize; ++i) {
+ found_nonzero |= (address.address[i] != 0);
+ }
+ return !found_nonzero;
+ }
+#endif
+ default:
+ SB_NOTREACHED() << "Invalid address type " << address.type;
+ break;
+ }
+
+ return false;
+}
+
+template <typename T, int source_size>
+void CopyIntoObjectFromArray(T* out_destination,
+ const unsigned char(&source)[source_size]) {
+ SB_COMPILE_ASSERT(sizeof(T) <= source_size, destination_is_too_small);
+ SbMemoryCopy(out_destination, source, sizeof(T));
+}
+
+bool GetPotentialMatch(const sockaddr* input_addr,
+ const in_addr** out_interface_addr) {
+ if (!input_addr || input_addr->sa_family != AF_INET) {
+ *out_interface_addr = NULL;
+ return false;
+ }
+
+ const sockaddr_in* v4input_addr =
+ reinterpret_cast<const sockaddr_in*>(input_addr);
+ *out_interface_addr = &(v4input_addr->sin_addr);
+ return true;
+}
+
+bool GetPotentialMatch(const sockaddr* input_addr,
+ const in6_addr** out_interface_addr) {
+ if (!input_addr || input_addr->sa_family != AF_INET6) {
+ *out_interface_addr = NULL;
+ return false;
+ }
+
+ const sockaddr_in6* v6input_addr =
+ reinterpret_cast<const sockaddr_in6*>(input_addr);
+ *out_interface_addr = &(v6input_addr->sin6_addr);
+ return true;
+}
+
+template <typename in_addr_type>
+bool GetNetmaskForInterfaceAddress(const SbSocketAddress& interface_address,
+ SbSocketAddress* out_netmask) {
+ SB_DCHECK(interface_address.type == kSbSocketAddressTypeIpv4);
+ struct ifaddrs* interface_addrs = NULL;
+
+ int retval = getifaddrs(&interface_addrs);
+ if (retval != 0) {
+ return false;
+ }
+
+ in_addr_type to_match;
+ CopyIntoObjectFromArray(&to_match, interface_address.address);
+
+ bool found_netmask = false;
+ for (struct ifaddrs* interface = interface_addrs; interface != NULL;
+ interface = interface->ifa_next) {
+ if (!(IFF_UP & interface->ifa_flags) ||
+ (IFF_LOOPBACK & interface->ifa_flags)) {
+ continue;
+ }
+
+ const in_addr_type* potential_match;
+ if (!GetPotentialMatch(interface->ifa_addr, &potential_match))
+ continue;
+
+ if (SbMemoryCompare(&to_match, potential_match, sizeof(in_addr_type)) !=
+ 0) {
+ continue;
+ }
+
+ sbposix::SockAddr sock_addr;
+ sock_addr.FromSockaddr(interface->ifa_addr);
+ if (sock_addr.ToSbSocketAddress(out_netmask)) {
+ found_netmask = true;
+ break;
+ }
+ }
+
+ freeifaddrs(interface_addrs);
+
+ return found_netmask;
+}
+
+bool GetNetMaskForIPv4InterfaceAddress(const SbSocketAddress& interface_address,
+ SbSocketAddress* out_netmask) {
+ return GetNetmaskForInterfaceAddress<in_addr>(interface_address, out_netmask);
+}
+
+#if SB_HAS(IPV6)
+bool GetNetMaskForIPv6InterfaceAddress(const SbSocketAddress& interface_address,
+ SbSocketAddress* out_netmask) {
+ return GetNetmaskForInterfaceAddress<in6_addr>(interface_address,
+ out_netmask);
+}
+#endif
+
+bool GetNetMaskForInterfaceAddress(const SbSocketAddress& interface_address,
+ SbSocketAddress* out_netmask) {
+ SB_DCHECK(out_netmask);
+
+ switch (interface_address.type) {
+ case kSbSocketAddressTypeIpv4:
+ return GetNetMaskForIPv4InterfaceAddress(interface_address, out_netmask);
+#if SB_HAS(IPV6)
+ case kSbSocketAddressTypeIpv6:
+ return GetNetMaskForIPv6InterfaceAddress(interface_address, out_netmask);
+#endif
+ default:
+ SB_NOTREACHED() << "Invalid address type " << interface_address.type;
+ break;
+ }
+
+ return false;
+}
+
+bool FindIPv4InterfaceIP(SbSocketAddress* out_interface_ip,
+ SbSocketAddress* out_netmask) {
+ if (out_interface_ip == NULL) {
+ SB_NOTREACHED() << "out_interface_ip must be specified";
+ return false;
+ }
+ struct ifaddrs* interface_addrs = NULL;
+
+ int retval = getifaddrs(&interface_addrs);
+ if (retval != 0) {
+ return false;
+ }
+
+ bool success = false;
+ for (struct ifaddrs* interface = interface_addrs; interface != NULL;
+ interface = interface->ifa_next) {
+ if (!(IFF_UP & interface->ifa_flags) ||
+ (IFF_LOOPBACK & interface->ifa_flags)) {
+ continue;
+ }
+
+ const struct sockaddr* addr = interface->ifa_addr;
+ const struct sockaddr* netmask = interface->ifa_netmask;
+ if (!addr || !netmask || (addr->sa_family != AF_INET)) {
+ // IPv4 addresses only.
+ continue;
+ }
+
+ sbposix::SockAddr sock_addr;
+ sock_addr.FromSockaddr(addr);
+ if (sock_addr.ToSbSocketAddress(out_interface_ip)) {
+ if (out_netmask) {
+ sbposix::SockAddr netmask_addr;
+ netmask_addr.FromSockaddr(netmask);
+ if (!netmask_addr.ToSbSocketAddress(out_netmask)) {
+ continue;
+ }
+ }
+
+ success = true;
+ break;
+ }
+ }
+
+ freeifaddrs(interface_addrs);
+
+ return success;
+}
+
+#if SB_HAS(IPV6)
+bool IsUniqueLocalAddress(const unsigned char ip[16]) {
+ // Unique Local Addresses are in fd08::/8.
+ return ip[0] == 0xfd && ip[1] == 0x08;
+}
+
+bool FindIPv6InterfaceIP(SbSocketAddress* out_interface_ip,
+ SbSocketAddress* out_netmask) {
+ if (!out_interface_ip) {
+ SB_NOTREACHED() << "out_interface_ip must be specified";
+ return false;
+ }
+ struct ifaddrs* interface_addrs = NULL;
+
+ int retval = getifaddrs(&interface_addrs);
+ if (retval != 0) {
+ return false;
+ }
+
+ int max_scope_interface_value = -1;
+
+ bool ip_found = false;
+ SbSocketAddress temp_interface_ip;
+ SbSocketAddress temp_netmask;
+
+ for (struct ifaddrs* interface = interface_addrs; interface != NULL;
+ interface = interface->ifa_next) {
+ if (!(IFF_UP & interface->ifa_flags) ||
+ (IFF_LOOPBACK & interface->ifa_flags)) {
+ continue;
+ }
+
+ const struct sockaddr* addr = interface->ifa_addr;
+ const struct sockaddr* netmask = interface->ifa_netmask;
+ if (!addr || !netmask || addr->sa_family != AF_INET6) {
+ // IPv6 addresses only.
+ continue;
+ }
+
+ const in6_addr* potential_match;
+ if (!GetPotentialMatch(interface->ifa_addr, &potential_match))
+ continue;
+
+ // Check the IP for loopback again, just in case flags were incorrect.
+ if (IN6_IS_ADDR_LOOPBACK(potential_match) ||
+ IN6_IS_ADDR_LINKLOCAL(potential_match)) {
+ continue;
+ }
+
+ const sockaddr_in6* v6addr =
+ reinterpret_cast<const sockaddr_in6*>(interface->ifa_addr);
+ if (!v6addr) {
+ continue;
+ }
+
+ int current_interface_scope = v6addr->sin6_scope_id;
+
+ if (IsUniqueLocalAddress(v6addr->sin6_addr.s6_addr)) {
+ // ULAs have global scope, but not globally routable. So prefer
+ // non ULA addresses with global scope by adjusting their "scope"
+ current_interface_scope -= 1;
+ }
+
+ if (current_interface_scope <= max_scope_interface_value) {
+ continue;
+ }
+ max_scope_interface_value = current_interface_scope;
+
+ sbposix::SockAddr sock_addr;
+ sock_addr.FromSockaddr(addr);
+ if (sock_addr.ToSbSocketAddress(&temp_interface_ip)) {
+ if (netmask) {
+ sbposix::SockAddr netmask_addr;
+ netmask_addr.FromSockaddr(netmask);
+ if (!netmask_addr.ToSbSocketAddress(&temp_netmask)) {
+ continue;
+ }
+ }
+
+ ip_found = true;
+ }
+ }
+
+ freeifaddrs(interface_addrs);
+
+ if (!ip_found) {
+ return false;
+ }
+
+ SbMemoryCopy(out_interface_ip, &temp_interface_ip, sizeof(SbSocketAddress));
+ if (out_netmask != NULL) {
+ SbMemoryCopy(out_netmask, &temp_netmask, sizeof(SbSocketAddress));
+ }
+
+ return true;
+}
+#endif
+
+bool FindInterfaceIP(const SbSocketAddressType type,
+ SbSocketAddress* out_interface_ip,
+ SbSocketAddress* out_netmask) {
+ switch (type) {
+ case kSbSocketAddressTypeIpv4:
+ return FindIPv4InterfaceIP(out_interface_ip, out_netmask);
+#if SB_HAS(IPV6)
+ case kSbSocketAddressTypeIpv6:
+ return FindIPv6InterfaceIP(out_interface_ip, out_netmask);
+#endif
+ default:
+ SB_NOTREACHED() << "Invalid socket address type " << type;
+ }
+
+ return false;
+}
+
+bool FindSourceAddressForDestination(const SbSocketAddress& destination,
+ SbSocketAddress* out_source_address) {
+ SbSocket socket = SbSocketCreate(destination.type, kSbSocketProtocolUdp);
+ if (!SbSocketIsValid(socket)) {
+ return false;
+ }
+
+ SbSocketError connect_retval = SbSocketConnect(socket, &destination);
+ if (connect_retval != kSbSocketOk) {
+ bool socket_destroyed = SbSocketDestroy(socket);
+ SB_DCHECK(socket_destroyed);
+ return false;
+ }
+
+ bool success = SbSocketGetLocalAddress(socket, out_source_address);
+ bool socket_destroyed = SbSocketDestroy(socket);
+ SB_DCHECK(socket_destroyed);
+ return success;
+}
+
+} // namespace
+
+bool SbSocketGetInterfaceAddress(const SbSocketAddress* const destination,
+ SbSocketAddress* out_source_address,
+ SbSocketAddress* out_netmask) {
+ if (!out_source_address) {
+ return false;
+ }
+
+ if (destination == NULL) {
+#if SB_HAS(IPV6)
+ // Return either a v4 or a v6 address. Per spec.
+ return (FindIPv4InterfaceIP(out_source_address, out_netmask) ||
+ FindIPv6InterfaceIP(out_source_address, out_netmask));
+#else
+ return FindIPv4InterfaceIP(out_source_address, out_netmask);
+#endif
+
+ } else if (IsAnyAddress(*destination)) {
+ return FindInterfaceIP(destination->type, out_source_address, out_netmask);
+ } else {
+ return (FindSourceAddressForDestination(*destination, out_source_address) &&
+ GetNetMaskForInterfaceAddress(*out_source_address, out_netmask));
+ }
+
+ return false;
+}
+
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/linux/socket_get_local_interface_address.cc b/src/starboard/shared/linux/socket_get_local_interface_address.cc
index 1c95514..61ef792 100644
--- a/src/starboard/shared/linux/socket_get_local_interface_address.cc
+++ b/src/starboard/shared/linux/socket_get_local_interface_address.cc
@@ -14,6 +14,8 @@
#include "starboard/socket.h"
+#if SB_API_VERSION < 4
+
#include <arpa/inet.h>
#include <ifaddrs.h>
@@ -67,3 +69,5 @@
freeifaddrs(ifaddr);
return false;
}
+
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/posix/socket_bind.cc b/src/starboard/shared/posix/socket_bind.cc
index 15db625..9cea55f 100644
--- a/src/starboard/shared/posix/socket_bind.cc
+++ b/src/starboard/shared/posix/socket_bind.cc
@@ -18,6 +18,7 @@
#include <sys/socket.h>
#include "starboard/log.h"
+#include "starboard/memory.h"
#include "starboard/shared/posix/handle_eintr.h"
#include "starboard/shared/posix/socket_internal.h"
@@ -45,6 +46,20 @@
return (socket->error = sbposix::TranslateSocketErrno(EAFNOSUPPORT));
}
+#if SB_HAS(IPV6)
+ // When binding to the IPV6 any address, ensure that the IPV6_V6ONLY flag is
+ // off to allow incoming IPV4 connections on the same socket.
+ // See https://www.ietf.org/rfc/rfc3493.txt for details.
+ if (local_address && (local_address->type == kSbSocketAddressTypeIpv6) &&
+ SbMemoryIsZero(local_address->address, 16)) {
+ if (!sbposix::SetBooleanSocketOption(socket, IPPROTO_IPV6, IPV6_V6ONLY,
+ "IPV6_V6ONLY", false)) {
+ // Silently ignore errors, assume the default behavior is as expected.
+ socket->error = kSbSocketOk;
+ }
+ }
+#endif
+
int result = HANDLE_EINTR(
bind(socket->socket_fd, sock_addr.sockaddr(), sock_addr.length));
if (result != 0) {
diff --git a/src/starboard/shared/posix/socket_resolve.cc b/src/starboard/shared/posix/socket_resolve.cc
index f36ed0c..dd305d2 100644
--- a/src/starboard/shared/posix/socket_resolve.cc
+++ b/src/starboard/shared/posix/socket_resolve.cc
@@ -57,11 +57,13 @@
// Translate all the sockaddrs.
sbposix::SockAddr* sock_addrs = new sbposix::SockAddr[address_count];
+ bool* parsed = new bool[address_count];
int index = 0;
int skip = 0;
for (const struct addrinfo *i = ai; i != NULL; i = i->ai_next, ++index) {
// Skip over any addresses we can't parse.
- if (!sock_addrs[index].FromSockaddr(i->ai_addr)) {
+ parsed[index] = sock_addrs[index].FromSockaddr(i->ai_addr);
+ if (!parsed[index]) {
++skip;
}
}
@@ -71,11 +73,13 @@
int result_index = 0;
for (int i = 0; i < address_count; ++i) {
- if (sock_addrs[i].ToSbSocketAddress(&result->addresses[result_index])) {
+ if (parsed[i] &&
+ sock_addrs[i].ToSbSocketAddress(&result->addresses[result_index])) {
++result_index;
}
}
+ delete[] parsed;
delete[] sock_addrs;
freeaddrinfo(ai);
return result;
diff --git a/src/starboard/shared/speechd/speech_synthesis_set_language.cc b/src/starboard/shared/speechd/speech_synthesis_set_language.cc
index 6943fb7..fe083f7 100644
--- a/src/starboard/shared/speechd/speech_synthesis_set_language.cc
+++ b/src/starboard/shared/speechd/speech_synthesis_set_language.cc
@@ -14,7 +14,7 @@
#include "starboard/speech_synthesis.h"
-#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION < 4
// DEPRECATED IN API VERSION 4
#include "starboard/shared/speechd/speechd_internal.h"
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc
index c3a12dd..1402d12 100644
--- a/src/starboard/shared/starboard/application.cc
+++ b/src/starboard/shared/starboard/application.cc
@@ -128,9 +128,7 @@
CancelTimedEvent(id);
}
-#if SB_HAS(PLAYER) && \
- (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT))
+#if SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
void Application::HandleFrame(SbPlayer player,
const scoped_refptr<VideoFrame>& frame,
@@ -140,9 +138,7 @@
int height) {
AcceptFrame(player, frame, x, y, width, height);
}
-#endif // SB_HAS(PLAYER) && \
- (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT))
+#endif // SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
void Application::SetStartLink(const char* start_link) {
SbMemoryDeallocate(start_link_);
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h
index 51f5a84..2469a6e 100644
--- a/src/starboard/shared/starboard/application.h
+++ b/src/starboard/shared/starboard/application.h
@@ -207,9 +207,7 @@
// external thread.
void Cancel(SbEventId id);
-#if SB_HAS(PLAYER) && \
- (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT))
+#if SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
// Handles receiving a new video frame of |player| from the media system. Only
// used when the application needs to composite video frames with punch-out
// video manually (should be rare). Will be called from an external thread.
@@ -219,9 +217,7 @@
int y,
int width,
int height);
-#endif // SB_HAS(PLAYER) && \
- (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT))
+#endif // SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
// Registers a |callback| function that will be called when |Teardown| is
// called.
@@ -249,9 +245,7 @@
// processed the Resume event.
virtual void OnResume() {}
-#if SB_HAS(PLAYER) && \
- (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT))
+#if SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
// Subclasses may override this method to accept video frames from the media
// system. Will be called from an external thread.
virtual void AcceptFrame(SbPlayer player,
@@ -260,9 +254,7 @@
int y,
int width,
int height) {}
-#endif // SB_HAS(PLAYER) && \
- (SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT))
+#endif // SB_HAS(PLAYER) && (SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT))
// Blocks until the next event is available. Subclasses must implement this
// method to provide events for the platform. Gives ownership to the caller.
diff --git a/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h b/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
index 10b6b68..cf40ad5 100644
--- a/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
+++ b/src/starboard/shared/starboard/audio_sink/audio_sink_internal.h
@@ -39,9 +39,9 @@
};
virtual ~SbAudioSinkPrivate() {}
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
virtual void SetPlaybackRate(double playback_rate) = 0;
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
virtual bool IsType(Type* type) = 0;
// The following two functions will be called during application startup and
diff --git a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
index 2fef267..a34477e 100644
--- a/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
+++ b/src/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc
@@ -37,11 +37,11 @@
~StubAudioSink() SB_OVERRIDE;
bool IsType(Type* type) SB_OVERRIDE { return type_ == type; }
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
void SetPlaybackRate(double playback_rate) SB_OVERRIDE {
SB_NOTIMPLEMENTED();
}
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
private:
static void* ThreadEntryPoint(void* context);
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc b/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc
new file mode 100644
index 0000000..63b5a66
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_create_transformer.cc
@@ -0,0 +1,112 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+#include "starboard/string.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+using starboard::shared::starboard::cryptography::AES_KEY;
+using starboard::shared::starboard::cryptography::AES_gcm128_init;
+using starboard::shared::starboard::cryptography::Algorithm;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Cbc;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Ctr;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
+
+SbCryptographyTransformer SbCryptographyCreateTransformer(
+ const char* algorithm,
+ int block_size_bits,
+ SbCryptographyDirection direction,
+ SbCryptographyBlockCipherMode mode,
+ const void* initialization_vector,
+ int initialization_vector_size,
+ const void* key,
+ int key_size) {
+ if (SbStringCompareAll(algorithm, kSbCryptographyAlgorithmAes) != 0) {
+ SB_DLOG(WARNING) << "Unsupported algorithm: " << algorithm;
+ return kSbCryptographyInvalidTransformer;
+ }
+
+ if (block_size_bits != 128) {
+ SB_DLOG(WARNING) << "Unsupported block size: " << block_size_bits;
+ return kSbCryptographyInvalidTransformer;
+ }
+
+ // TODO: Support 64-bit IV with CTR mode.
+ if ((mode == kSbCryptographyBlockCipherModeGcm &&
+ initialization_vector_size != 0) ||
+ (mode != kSbCryptographyBlockCipherModeGcm &&
+ initialization_vector_size != block_size_bits / 8)) {
+ SB_DLOG(WARNING) << "Unsupported initialization_vector_size: "
+ << initialization_vector_size;
+ return kSbCryptographyInvalidTransformer;
+ }
+
+ if (key_size != 16 && key_size != 24 && key_size != 32) {
+ SB_DLOG(WARNING) << "Unsupported key_size: " << key_size;
+ return kSbCryptographyInvalidTransformer;
+ }
+
+ Algorithm combined_algorithm;
+ if (mode == kSbCryptographyBlockCipherModeCbc) {
+ combined_algorithm = kAlgorithmAes128Cbc;
+ } else if (mode == kSbCryptographyBlockCipherModeCtr) {
+ combined_algorithm = kAlgorithmAes128Ctr;
+ } else if (mode == kSbCryptographyBlockCipherModeGcm) {
+ combined_algorithm = kAlgorithmAes128Gcm;
+ } else {
+ SB_DLOG(WARNING) << "Unsupported block cipher mode: " << mode;
+ return kSbCryptographyInvalidTransformer;
+ }
+
+ AES_KEY aeskey = {0};
+ int result = -1;
+ if (direction == kSbCryptographyDirectionDecode &&
+ mode != kSbCryptographyBlockCipherModeCtr &&
+ mode != kSbCryptographyBlockCipherModeGcm) {
+ result = AES_set_decrypt_key(key, key_size * 8, &aeskey);
+ } else {
+ result = AES_set_encrypt_key(key, key_size * 8, &aeskey);
+ }
+
+ if (result != 0) {
+ SB_DLOG(WARNING) << "Error setting key: " << result;
+ return kSbCryptographyInvalidTransformer;
+ }
+
+ SbCryptographyTransformer transformer =
+ new SbCryptographyTransformerPrivate();
+ SbMemorySet(transformer, 0, sizeof(transformer));
+ transformer->key = aeskey;
+ transformer->algorithm = combined_algorithm;
+ transformer->direction = direction;
+ if (initialization_vector_size) {
+ SbMemoryCopy(transformer->ivec, initialization_vector,
+ initialization_vector_size);
+ }
+
+ if (transformer->algorithm == kAlgorithmAes128Gcm) {
+ AES_gcm128_init(&transformer->gcm_context, &transformer->key,
+ direction == kSbCryptographyDirectionEncode ? 1 : 0);
+ }
+
+ return transformer;
+}
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_destroy_transformer.cc b/src/starboard/shared/starboard/cryptography/cryptography_destroy_transformer.cc
new file mode 100644
index 0000000..fede3c3
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_destroy_transformer.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+void SbCryptographyDestroyTransformer(SbCryptographyTransformer transformer) {
+ if (!transformer) {
+ return;
+ }
+
+ delete transformer;
+}
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_get_tag.cc b/src/starboard/shared/starboard/cryptography/cryptography_get_tag.cc
new file mode 100644
index 0000000..ee09892
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_get_tag.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+using starboard::shared::starboard::cryptography::AES_gcm128_tag;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
+
+bool SbCryptographyGetTag(
+ SbCryptographyTransformer transformer,
+ void* out_tag,
+ int out_tag_size) {
+ if (transformer->algorithm != kAlgorithmAes128Gcm) {
+ SB_DLOG(ERROR) << "Trying to get tag on non-GCM transformer.";
+ return false;
+ }
+
+ AES_gcm128_tag(&transformer->gcm_context, out_tag, out_tag_size);
+ return true;
+}
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_internal.h b/src/starboard/shared/starboard/cryptography/cryptography_internal.h
new file mode 100644
index 0000000..526e50a
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_internal.h
@@ -0,0 +1,51 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_CRYPTOGRAPHY_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_CRYPTOGRAPHY_INTERNAL_H_
+
+#include "starboard/cryptography.h"
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace cryptography {
+
+// The modes supported by this software-only implementation.
+enum Algorithm {
+ kAlgorithmAes128Cbc,
+ kAlgorithmAes128Ctr,
+ kAlgorithmAes128Ecb,
+ kAlgorithmAes128Gcm,
+};
+
+} // namespace cryptography
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+struct SbCryptographyTransformerPrivate {
+ starboard::shared::starboard::cryptography::Algorithm algorithm;
+ SbCryptographyDirection direction;
+ starboard::shared::starboard::cryptography::GCM128_CONTEXT gcm_context;
+ starboard::shared::starboard::cryptography::AES_KEY key;
+ uint8_t ivec[SB_AES_BLOCK_SIZE];
+ uint8_t ecount_buf[SB_AES_BLOCK_SIZE];
+ uint32_t counter;
+};
+
+#endif // STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_CRYPTOGRAPHY_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_set_authenticated_data.cc b/src/starboard/shared/starboard/cryptography/cryptography_set_authenticated_data.cc
new file mode 100644
index 0000000..6178bd6
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_set_authenticated_data.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+using starboard::shared::starboard::cryptography::AES_gcm128_aad;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
+
+bool SbCryptographySetAuthenticatedData(
+ SbCryptographyTransformer transformer,
+ const void* data,
+ int data_size) {
+ if (transformer->algorithm != kAlgorithmAes128Gcm) {
+ SB_DLOG(ERROR) << "Trying to set AAD on non-GCM transformer.";
+ return false;
+ }
+
+ int result = AES_gcm128_aad(&transformer->gcm_context, data, data_size);
+ return result == 1;
+}
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc b/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc
new file mode 100644
index 0000000..0eb09a9
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_set_initialization_vector.cc
@@ -0,0 +1,39 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+using starboard::shared::starboard::cryptography::AES_gcm128_setiv;
+using starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm;
+
+void SbCryptographySetInitializationVector(
+ SbCryptographyTransformer transformer,
+ const void* initialization_vector,
+ int initialization_vector_size) {
+ if (transformer->algorithm != kAlgorithmAes128Gcm) {
+ SB_DLOG(ERROR) << "Trying to set initialization vector on non-GCM "
+ << "transformer.";
+ return;
+ }
+
+ AES_gcm128_setiv(&transformer->gcm_context, &transformer->key,
+ initialization_vector, initialization_vector_size);
+}
diff --git a/src/starboard/shared/starboard/cryptography/cryptography_transform.cc b/src/starboard/shared/starboard/cryptography/cryptography_transform.cc
new file mode 100644
index 0000000..0981a63
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/cryptography_transform.cc
@@ -0,0 +1,65 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+#include "starboard/shared/starboard/cryptography/cryptography_internal.h"
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+int SbCryptographyTransform(SbCryptographyTransformer transformer,
+ const void* in_data,
+ int in_data_size,
+ void* out_data) {
+ if (!SbCryptographyIsTransformerValid(transformer) || !in_data || !out_data) {
+ return -1;
+ }
+
+ if (in_data_size == 0) {
+ return 0;
+ }
+
+ if (transformer->algorithm ==
+ starboard::shared::starboard::cryptography::kAlgorithmAes128Cbc) {
+ int enc = transformer->direction == kSbCryptographyDirectionEncode
+ ? SB_AES_ENCRYPT
+ : SB_AES_DECRYPT;
+ starboard::shared::starboard::cryptography::AES_cbc_encrypt(
+ in_data, out_data, in_data_size, &(transformer->key), transformer->ivec,
+ enc);
+ } else if (transformer->algorithm ==
+ starboard::shared::starboard::cryptography::kAlgorithmAes128Ctr) {
+ starboard::shared::starboard::cryptography::AES_ctr128_encrypt(
+ in_data, out_data, in_data_size, &(transformer->key), transformer->ivec,
+ transformer->ecount_buf, &transformer->counter);
+ } else if (transformer->algorithm ==
+ starboard::shared::starboard::cryptography::kAlgorithmAes128Gcm) {
+ if (transformer->direction == kSbCryptographyDirectionEncode) {
+ starboard::shared::starboard::cryptography::AES_gcm128_encrypt(
+ &transformer->gcm_context, &transformer->key, in_data, out_data,
+ in_data_size);
+ } else if (transformer->direction == kSbCryptographyDirectionDecode) {
+ starboard::shared::starboard::cryptography::AES_gcm128_decrypt(
+ &transformer->gcm_context, &transformer->key, in_data, out_data,
+ in_data_size);
+ } else {
+ SB_NOTREACHED();
+ }
+ }
+
+ return in_data_size;
+}
diff --git a/src/starboard/shared/starboard/cryptography/software_aes.cc b/src/starboard/shared/starboard/cryptography/software_aes.cc
new file mode 100644
index 0000000..c9a3453
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/software_aes.cc
@@ -0,0 +1,1570 @@
+/* ====================================================================
+ * Copyright (c) 2002-2006 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+// Modifications Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/cryptography/software_aes.h"
+
+#include "starboard/byte_swap.h"
+#include "starboard/memory.h"
+#include "starboard/types.h"
+
+#pragma message "software_aes.cc is not meant for production use! Instead, use "
+#pragma message "starboard/shared/stub/cryptography_* if you don't have "
+#pragma message "hardware-accelerated AES support."
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace cryptography {
+
+namespace {
+inline uint32_t GETU32(const void *in) {
+ uint32_t value;
+ SbMemoryCopy(&value, in, sizeof(value));
+ return SbByteSwapU32(value);
+}
+
+inline void PUTU32(void *out, uint32_t value) {
+ value = SbByteSwapU32(value);
+ SbMemoryCopy(out, &value, sizeof(value));
+}
+
+inline uint32_t GETU32_aligned(const void *in) {
+ const char *alias = static_cast<const char*>(in);
+ return SbByteSwapU32(*reinterpret_cast<const uint32_t*>(alias));
+}
+
+inline void PUTU32_aligned(void *in, uint32_t value) {
+ char *alias = static_cast<char*>(in);
+ *reinterpret_cast<uint32_t*>(alias) = SbByteSwapU32(value);
+}
+
+/* Te0[x] = S [x].[02, 01, 01, 03];
+ * Te1[x] = S [x].[03, 02, 01, 01];
+ * Te2[x] = S [x].[01, 03, 02, 01];
+ * Te3[x] = S [x].[01, 01, 03, 02];
+ *
+ * Td0[x] = Si[x].[0e, 09, 0d, 0b];
+ * Td1[x] = Si[x].[0b, 0e, 09, 0d];
+ * Td2[x] = Si[x].[0d, 0b, 0e, 09];
+ * Td3[x] = Si[x].[09, 0d, 0b, 0e];
+ * Td4[x] = Si[x].[01]; */
+static const uint32_t Te0[256] = {
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU,
+ 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U,
+ 0xce6767a9U, 0x562b2b7dU, 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U,
+ 0xec76769aU, 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, 0x41adadecU,
+ 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, 0x239c9cbfU, 0x53a4a4f7U,
+ 0xe4727296U, 0x9bc0c05bU, 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU,
+ 0x4c26266aU, 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, 0xe2717193U,
+ 0xabd8d873U, 0x62313153U, 0x2a15153fU, 0x0804040cU, 0x95c7c752U,
+ 0x46232365U, 0x9dc3c35eU, 0x30181828U, 0x379696a1U, 0x0a05050fU,
+ 0x2f9a9ab5U, 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, 0x1209091bU,
+ 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, 0x361b1b2dU, 0xdc6e6eb2U,
+ 0xb45a5aeeU, 0x5ba0a0fbU, 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U,
+ 0x7db3b3ceU, 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, 0x40202060U,
+ 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, 0xd46a6abeU, 0x8dcbcb46U,
+ 0x67bebed9U, 0x7239394bU, 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U,
+ 0x85cfcf4aU, 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, 0x8a4545cfU,
+ 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, 0xa05050f0U, 0x783c3c44U,
+ 0x259f9fbaU, 0x4ba8a8e3U, 0xa25151f3U, 0x5da3a3feU, 0x804040c0U,
+ 0x058f8f8aU, 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, 0x20101030U,
+ 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, 0x81cdcd4cU, 0x180c0c14U,
+ 0x26131335U, 0xc3ecec2fU, 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU,
+ 0x2e171739U, 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, 0xc06060a0U,
+ 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, 0x44222266U, 0x542a2a7eU,
+ 0x3b9090abU, 0x0b888883U, 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U,
+ 0x2814143cU, 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, 0x924949dbU,
+ 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, 0x9fc2c25dU, 0xbdd3d36eU,
+ 0x43acacefU, 0xc46262a6U, 0x399191a8U, 0x319595a4U, 0xd3e4e437U,
+ 0xf279798bU, 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, 0xd86c6cb4U,
+ 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, 0xca6565afU, 0xf47a7a8eU,
+ 0x47aeaee9U, 0x10080818U, 0x6fbabad5U, 0xf0787888U, 0x4a25256fU,
+ 0x5c2e2e72U, 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, 0x964b4bddU,
+ 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, 0xe0707090U, 0x7c3e3e42U,
+ 0x71b5b5c4U, 0xcc6666aaU, 0x904848d8U, 0x06030305U, 0xf7f6f601U,
+ 0x1c0e0e12U, 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, 0xd9e1e138U,
+ 0xebf8f813U, 0x2b9898b3U, 0x22111133U, 0xd26969bbU, 0xa9d9d970U,
+ 0x078e8e89U, 0x339494a7U, 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U,
+ 0xc9e9e920U, 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, 0x65bfbfdaU,
+ 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, 0x824141c3U, 0x299999b0U,
+ 0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U,
+ 0x2c16163aU,
+};
+static const uint32_t Te1[256] = {
+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U,
+ 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U,
+ 0xa9ce6767U, 0x7d562b2bU, 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU,
+ 0x9aec7676U, 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, 0xec41adadU,
+ 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, 0xbf239c9cU, 0xf753a4a4U,
+ 0x96e47272U, 0x5b9bc0c0U, 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U,
+ 0x6a4c2626U, 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, 0x93e27171U,
+ 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, 0x0c080404U, 0x5295c7c7U,
+ 0x65462323U, 0x5e9dc3c3U, 0x28301818U, 0xa1379696U, 0x0f0a0505U,
+ 0xb52f9a9aU, 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, 0x1b120909U,
+ 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, 0x2d361b1bU, 0xb2dc6e6eU,
+ 0xeeb45a5aU, 0xfb5ba0a0U, 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U,
+ 0xce7db3b3U, 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, 0x60402020U,
+ 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, 0xbed46a6aU, 0x468dcbcbU,
+ 0xd967bebeU, 0x4b723939U, 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U,
+ 0x4a85cfcfU, 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, 0xcf8a4545U,
+ 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, 0xf0a05050U, 0x44783c3cU,
+ 0xba259f9fU, 0xe34ba8a8U, 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U,
+ 0x8a058f8fU, 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, 0x30201010U,
+ 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, 0x4c81cdcdU, 0x14180c0cU,
+ 0x35261313U, 0x2fc3ececU, 0xe1be5f5fU, 0xa2359797U, 0xcc884444U,
+ 0x392e1717U, 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, 0xa0c06060U,
+ 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, 0x66442222U, 0x7e542a2aU,
+ 0xab3b9090U, 0x830b8888U, 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U,
+ 0x3c281414U, 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, 0xdb924949U,
+ 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, 0x5d9fc2c2U, 0x6ebdd3d3U,
+ 0xef43acacU, 0xa6c46262U, 0xa8399191U, 0xa4319595U, 0x37d3e4e4U,
+ 0x8bf27979U, 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, 0xb4d86c6cU,
+ 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, 0xafca6565U, 0x8ef47a7aU,
+ 0xe947aeaeU, 0x18100808U, 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U,
+ 0x725c2e2eU, 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, 0xdd964b4bU,
+ 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, 0x90e07070U, 0x427c3e3eU,
+ 0xc471b5b5U, 0xaacc6666U, 0xd8904848U, 0x05060303U, 0x01f7f6f6U,
+ 0x121c0e0eU, 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, 0x38d9e1e1U,
+ 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, 0xbbd26969U, 0x70a9d9d9U,
+ 0x89078e8eU, 0xa7339494U, 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U,
+ 0x20c9e9e9U, 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, 0xda65bfbfU,
+ 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, 0xc3824141U, 0xb0299999U,
+ 0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU,
+ 0x3a2c1616U,
+};
+static const uint32_t Te2[256] = {
+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U,
+ 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U,
+ 0x67a9ce67U, 0x2b7d562bU, 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU,
+ 0x769aec76U, 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, 0xadec41adU,
+ 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, 0x9cbf239cU, 0xa4f753a4U,
+ 0x7296e472U, 0xc05b9bc0U, 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U,
+ 0x266a4c26U, 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, 0x7193e271U,
+ 0xd873abd8U, 0x31536231U, 0x153f2a15U, 0x040c0804U, 0xc75295c7U,
+ 0x23654623U, 0xc35e9dc3U, 0x18283018U, 0x96a13796U, 0x050f0a05U,
+ 0x9ab52f9aU, 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, 0x091b1209U,
+ 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, 0x1b2d361bU, 0x6eb2dc6eU,
+ 0x5aeeb45aU, 0xa0fb5ba0U, 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U,
+ 0xb3ce7db3U, 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, 0x20604020U,
+ 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, 0x6abed46aU, 0xcb468dcbU,
+ 0xbed967beU, 0x394b7239U, 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U,
+ 0xcf4a85cfU, 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, 0x45cf8a45U,
+ 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, 0x50f0a050U, 0x3c44783cU,
+ 0x9fba259fU, 0xa8e34ba8U, 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U,
+ 0x8f8a058fU, 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, 0x10302010U,
+ 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, 0xcd4c81cdU, 0x0c14180cU,
+ 0x13352613U, 0xec2fc3ecU, 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U,
+ 0x17392e17U, 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, 0x60a0c060U,
+ 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, 0x22664422U, 0x2a7e542aU,
+ 0x90ab3b90U, 0x88830b88U, 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U,
+ 0x143c2814U, 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, 0x49db9249U,
+ 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, 0xc25d9fc2U, 0xd36ebdd3U,
+ 0xacef43acU, 0x62a6c462U, 0x91a83991U, 0x95a43195U, 0xe437d3e4U,
+ 0x798bf279U, 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, 0x6cb4d86cU,
+ 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, 0x65afca65U, 0x7a8ef47aU,
+ 0xaee947aeU, 0x08181008U, 0xbad56fbaU, 0x7888f078U, 0x256f4a25U,
+ 0x2e725c2eU, 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, 0x4bdd964bU,
+ 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, 0x7090e070U, 0x3e427c3eU,
+ 0xb5c471b5U, 0x66aacc66U, 0x48d89048U, 0x03050603U, 0xf601f7f6U,
+ 0x0e121c0eU, 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, 0xe138d9e1U,
+ 0xf813ebf8U, 0x98b32b98U, 0x11332211U, 0x69bbd269U, 0xd970a9d9U,
+ 0x8e89078eU, 0x94a73394U, 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U,
+ 0xe920c9e9U, 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, 0xbfda65bfU,
+ 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, 0x41c38241U, 0x99b02999U,
+ 0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU,
+ 0x163a2c16U,
+};
+static const uint32_t Te3[256] = {
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU,
+ 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, 0x30305060U, 0x01010302U,
+ 0x6767a9ceU, 0x2b2b7d56U, 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU,
+ 0x76769aecU, 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, 0xadadec41U,
+ 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, 0x9c9cbf23U, 0xa4a4f753U,
+ 0x727296e4U, 0xc0c05b9bU, 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU,
+ 0x26266a4cU, 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, 0x717193e2U,
+ 0xd8d873abU, 0x31315362U, 0x15153f2aU, 0x04040c08U, 0xc7c75295U,
+ 0x23236546U, 0xc3c35e9dU, 0x18182830U, 0x9696a137U, 0x05050f0aU,
+ 0x9a9ab52fU, 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, 0x09091b12U,
+ 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, 0x1b1b2d36U, 0x6e6eb2dcU,
+ 0x5a5aeeb4U, 0xa0a0fb5bU, 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U,
+ 0xb3b3ce7dU, 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, 0x20206040U,
+ 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, 0x6a6abed4U, 0xcbcb468dU,
+ 0xbebed967U, 0x39394b72U, 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U,
+ 0xcfcf4a85U, 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, 0x4545cf8aU,
+ 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, 0x5050f0a0U, 0x3c3c4478U,
+ 0x9f9fba25U, 0xa8a8e34bU, 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U,
+ 0x8f8f8a05U, 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, 0x10103020U,
+ 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, 0xcdcd4c81U, 0x0c0c1418U,
+ 0x13133526U, 0xecec2fc3U, 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U,
+ 0x1717392eU, 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, 0x6060a0c0U,
+ 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, 0x22226644U, 0x2a2a7e54U,
+ 0x9090ab3bU, 0x8888830bU, 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU,
+ 0x14143c28U, 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, 0x4949db92U,
+ 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, 0xc2c25d9fU, 0xd3d36ebdU,
+ 0xacacef43U, 0x6262a6c4U, 0x9191a839U, 0x9595a431U, 0xe4e437d3U,
+ 0x79798bf2U, 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, 0x6c6cb4d8U,
+ 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, 0x6565afcaU, 0x7a7a8ef4U,
+ 0xaeaee947U, 0x08081810U, 0xbabad56fU, 0x787888f0U, 0x25256f4aU,
+ 0x2e2e725cU, 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, 0x4b4bdd96U,
+ 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, 0x707090e0U, 0x3e3e427cU,
+ 0xb5b5c471U, 0x6666aaccU, 0x4848d890U, 0x03030506U, 0xf6f601f7U,
+ 0x0e0e121cU, 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, 0xe1e138d9U,
+ 0xf8f813ebU, 0x9898b32bU, 0x11113322U, 0x6969bbd2U, 0xd9d970a9U,
+ 0x8e8e8907U, 0x9494a733U, 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U,
+ 0xe9e920c9U, 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, 0xbfbfda65U,
+ 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, 0x4141c382U, 0x9999b029U,
+ 0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU,
+ 0x16163a2cU,
+};
+static const uint32_t Td0[256] = {
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU,
+ 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, 0x2030fa55U, 0xad766df6U,
+ 0x88cc7691U, 0xf5024c25U, 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U,
+ 0xb562a38fU, 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, 0x038f5fe7U,
+ 0x15929c95U, 0xbf6d7aebU, 0x955259daU, 0xd4be832dU, 0x587421d3U,
+ 0x49e06929U, 0x8ec9c844U, 0x75c2896aU, 0xf48e7978U, 0x99583e6bU,
+ 0x27b971ddU, 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, 0xb16477e0U,
+ 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, 0x70486858U, 0x8f45fd19U,
+ 0x94de6c87U, 0x527bf8b7U, 0xab73d323U, 0x724b02e2U, 0xe31f8f57U,
+ 0x6655ab2aU, 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, 0x8acf1c2bU,
+ 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, 0x65daf4cdU, 0x0605bed5U,
+ 0xd134621fU, 0xc4a6fe8aU, 0x342e539dU, 0xa2f355a0U, 0x058ae132U,
+ 0xa4f6eb75U, 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, 0x91548db5U,
+ 0x71c45d05U, 0x0406d46fU, 0x605015ffU, 0x1998fb24U, 0xd6bde997U,
+ 0x894043ccU, 0x67d99e77U, 0xb0e842bdU, 0x07898b88U, 0xe7195b38U,
+ 0x79c8eedbU, 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, 0xfd0efffbU,
+ 0x0f853856U, 0x3daed51eU, 0x362d3927U, 0x0a0fd964U, 0x685ca621U,
+ 0x9b5b54d1U, 0x24362e3aU, 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U,
+ 0x1b9b919eU, 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, 0x0e090d0bU,
+ 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, 0x57f11985U, 0xaf75074cU,
+ 0xee99ddbbU, 0xa37f60fdU, 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U,
+ 0x5bfb7e34U, 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, 0x854a247dU,
+ 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, 0x1d9e2f4bU, 0xdcb230f3U,
+ 0x0d8652ecU, 0x77c1e3d0U, 0x2bb3166cU, 0xa970b999U, 0x119448faU,
+ 0x47e96422U, 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, 0xa6f581cfU,
+ 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, 0x2c3a9de4U, 0x5078920dU,
+ 0x6a5fcc9bU, 0x547e4662U, 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU,
+ 0x82c3aff5U, 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, 0xcd267809U,
+ 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, 0xe6956e65U, 0xaaffe67eU,
+ 0x21bccf08U, 0xef15e8e6U, 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U,
+ 0x29b07cd6U, 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, 0xf104984aU,
+ 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, 0x764dd68dU, 0x43efb04dU,
+ 0xccaa4d54U, 0xe49604dfU, 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U,
+ 0x4665517fU, 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, 0x9ad7618cU,
+ 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, 0xcea927eeU, 0xb761c935U,
+ 0xe11ce5edU, 0x7a47b13cU, 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U,
+ 0x73c737bfU, 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, 0x161dc372U,
+ 0xbce2250cU, 0x283c498bU, 0xff0d9541U, 0x39a80171U, 0x080cb3deU,
+ 0xd8b4e49cU, 0x6456c190U, 0x7bcb8461U, 0xd532b670U, 0x486c5c74U,
+ 0xd0b85742U,
+};
+static const uint32_t Td1[256] = {
+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU,
+ 0xf11f9d45U, 0xabacfa58U, 0x934be303U, 0x552030faU, 0xf6ad766dU,
+ 0x9188cc76U, 0x25f5024cU, 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U,
+ 0x8fb562a3U, 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, 0xe7038f5fU,
+ 0x9515929cU, 0xebbf6d7aU, 0xda955259U, 0x2dd4be83U, 0xd3587421U,
+ 0x2949e069U, 0x448ec9c8U, 0x6a75c289U, 0x78f48e79U, 0x6b99583eU,
+ 0xdd27b971U, 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, 0xe0b16477U,
+ 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, 0x58704868U, 0x198f45fdU,
+ 0x8794de6cU, 0xb7527bf8U, 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU,
+ 0x2a6655abU, 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, 0x2b8acf1cU,
+ 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, 0xcd65daf4U, 0xd50605beU,
+ 0x1fd13462U, 0x8ac4a6feU, 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U,
+ 0x75a4f6ebU, 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, 0xb591548dU,
+ 0x0571c45dU, 0x6f0406d4U, 0xff605015U, 0x241998fbU, 0x97d6bde9U,
+ 0xcc894043U, 0x7767d99eU, 0xbdb0e842U, 0x8807898bU, 0x38e7195bU,
+ 0xdb79c8eeU, 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, 0xfbfd0effU,
+ 0x560f8538U, 0x1e3daed5U, 0x27362d39U, 0x640a0fd9U, 0x21685ca6U,
+ 0xd19b5b54U, 0x3a24362eU, 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U,
+ 0x9e1b9b91U, 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, 0x0b0e090dU,
+ 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, 0x8557f119U, 0x4caf7507U,
+ 0xbbee99ddU, 0xfda37f60U, 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU,
+ 0x345bfb7eU, 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, 0x7d854a24U,
+ 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, 0x4b1d9e2fU, 0xf3dcb230U,
+ 0xec0d8652U, 0xd077c1e3U, 0x6c2bb316U, 0x99a970b9U, 0xfa119448U,
+ 0x2247e964U, 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, 0xcfa6f581U,
+ 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, 0xe42c3a9dU, 0x0d507892U,
+ 0x9b6a5fccU, 0x62547e46U, 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U,
+ 0xf582c3afU, 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, 0x09cd2678U,
+ 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, 0x65e6956eU, 0x7eaaffe6U,
+ 0x0821bccfU, 0xe6ef15e8U, 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U,
+ 0xd629b07cU, 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, 0x4af10498U,
+ 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, 0x8d764dd6U, 0x4d43efb0U,
+ 0x54ccaa4dU, 0xdfe49604U, 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU,
+ 0x7f466551U, 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, 0x8c9ad761U,
+ 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, 0xeecea927U, 0x35b761c9U,
+ 0xede11ce5U, 0x3c7a47b1U, 0x599cd2dfU, 0x3f55f273U, 0x791814ceU,
+ 0xbf73c737U, 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, 0x72161dc3U,
+ 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, 0x7139a801U, 0xde080cb3U,
+ 0x9cd8b4e4U, 0x906456c1U, 0x617bcb84U, 0x70d532b6U, 0x74486c5cU,
+ 0x42d0b857U,
+};
+static const uint32_t Td2[256] = {
+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, 0x6bcb3babU,
+ 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, 0xfa552030U, 0x6df6ad76U,
+ 0x769188ccU, 0x4c25f502U, 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U,
+ 0xa38fb562U, 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, 0x5fe7038fU,
+ 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, 0x832dd4beU, 0x21d35874U,
+ 0x692949e0U, 0xc8448ec9U, 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U,
+ 0x71dd27b9U, 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, 0x77e0b164U,
+ 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, 0x68587048U, 0xfd198f45U,
+ 0x6c8794deU, 0xf8b7527bU, 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU,
+ 0xab2a6655U, 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, 0x1c2b8acfU,
+ 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, 0xf4cd65daU, 0xbed50605U,
+ 0x621fd134U, 0xfe8ac4a6U, 0x539d342eU, 0x55a0a2f3U, 0xe132058aU,
+ 0xeb75a4f6U, 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, 0x8db59154U,
+ 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, 0xfb241998U, 0xe997d6bdU,
+ 0x43cc8940U, 0x9e7767d9U, 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U,
+ 0xeedb79c8U, 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, 0xfffbfd0eU,
+ 0x38560f85U, 0xd51e3daeU, 0x3927362dU, 0xd9640a0fU, 0xa621685cU,
+ 0x54d19b5bU, 0x2e3a2436U, 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU,
+ 0x919e1b9bU, 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, 0x0d0b0e09U,
+ 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, 0x198557f1U, 0x074caf75U,
+ 0xddbbee99U, 0x60fda37fU, 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U,
+ 0x7e345bfbU, 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, 0x247d854aU,
+ 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, 0x2f4b1d9eU, 0x30f3dcb2U,
+ 0x52ec0d86U, 0xe3d077c1U, 0x166c2bb3U, 0xb999a970U, 0x48fa1194U,
+ 0x642247e9U, 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, 0x81cfa6f5U,
+ 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, 0x9de42c3aU, 0x920d5078U,
+ 0xcc9b6a5fU, 0x4662547eU, 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U,
+ 0xaff582c3U, 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, 0x7809cd26U,
+ 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, 0x6e65e695U, 0xe67eaaffU,
+ 0xcf0821bcU, 0xe8e6ef15U, 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU,
+ 0x7cd629b0U, 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, 0x984af104U,
+ 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, 0xd68d764dU, 0xb04d43efU,
+ 0x4d54ccaaU, 0x04dfe496U, 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU,
+ 0x517f4665U, 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, 0x618c9ad7U,
+ 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, 0x27eecea9U, 0xc935b761U,
+ 0xe5ede11cU, 0xb13c7a47U, 0xdf599cd2U, 0x733f55f2U, 0xce791814U,
+ 0x37bf73c7U, 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, 0xc372161dU,
+ 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, 0x017139a8U, 0xb3de080cU,
+ 0xe49cd8b4U, 0xc1906456U, 0x84617bcbU, 0xb670d532U, 0x5c74486cU,
+ 0x5742d0b8U,
+};
+static const uint32_t Td3[256] = {
+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, 0xab6bcb3bU,
+ 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, 0x30fa5520U, 0x766df6adU,
+ 0xcc769188U, 0x024c25f5U, 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U,
+ 0x62a38fb5U, 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, 0x8f5fe703U,
+ 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, 0xbe832dd4U, 0x7421d358U,
+ 0xe0692949U, 0xc9c8448eU, 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U,
+ 0xb971dd27U, 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, 0x6477e0b1U,
+ 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, 0x48685870U, 0x45fd198fU,
+ 0xde6c8794U, 0x7bf8b752U, 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U,
+ 0x55ab2a66U, 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, 0xcf1c2b8aU,
+ 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, 0xdaf4cd65U, 0x05bed506U,
+ 0x34621fd1U, 0xa6fe8ac4U, 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U,
+ 0xf6eb75a4U, 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, 0x548db591U,
+ 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, 0x98fb2419U, 0xbde997d6U,
+ 0x4043cc89U, 0xd99e7767U, 0xe842bdb0U, 0x898b8807U, 0x195b38e7U,
+ 0xc8eedb79U, 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, 0x0efffbfdU,
+ 0x8538560fU, 0xaed51e3dU, 0x2d392736U, 0x0fd9640aU, 0x5ca62168U,
+ 0x5b54d19bU, 0x362e3a24U, 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U,
+ 0x9b919e1bU, 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, 0x090d0b0eU,
+ 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, 0xf1198557U, 0x75074cafU,
+ 0x99ddbbeeU, 0x7f60fda3U, 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U,
+ 0xfb7e345bU, 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, 0x4a247d85U,
+ 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, 0x9e2f4b1dU, 0xb230f3dcU,
+ 0x8652ec0dU, 0xc1e3d077U, 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U,
+ 0xe9642247U, 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, 0xf581cfa6U,
+ 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, 0x3a9de42cU, 0x78920d50U,
+ 0x5fcc9b6aU, 0x7e466254U, 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU,
+ 0xc3aff582U, 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, 0x267809cdU,
+ 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, 0x956e65e6U, 0xffe67eaaU,
+ 0xbccf0821U, 0x15e8e6efU, 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU,
+ 0xb07cd629U, 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, 0x04984af1U,
+ 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, 0x4dd68d76U, 0xefb04d43U,
+ 0xaa4d54ccU, 0x9604dfe4U, 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U,
+ 0x65517f46U, 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, 0xd7618c9aU,
+ 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, 0xa927eeceU, 0x61c935b7U,
+ 0x1ce5ede1U, 0x47b13c7aU, 0xd2df599cU, 0xf2733f55U, 0x14ce7918U,
+ 0xc737bf73U, 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, 0x1dc37216U,
+ 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, 0xa8017139U, 0x0cb3de08U,
+ 0xb4e49cd8U, 0x56c19064U, 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U,
+ 0xb85742d0U,
+};
+static const uint8_t Td4[256] = {
+ 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, 0xbfU, 0x40U, 0xa3U,
+ 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU,
+ 0xffU, 0x87U, 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, 0x54U,
+ 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, 0xeeU, 0x4cU, 0x95U, 0x0bU,
+ 0x42U, 0xfaU, 0xc3U, 0x4eU, 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U,
+ 0xb2U, 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, 0x72U, 0xf8U,
+ 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU,
+ 0x65U, 0xb6U, 0x92U, 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+ 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, 0x90U, 0xd8U, 0xabU,
+ 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U,
+ 0x45U, 0x06U, 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, 0xc1U,
+ 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, 0x3aU, 0x91U, 0x11U, 0x41U,
+ 0x4fU, 0x67U, 0xdcU, 0xeaU, 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U,
+ 0x73U, 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, 0xe2U, 0xf9U,
+ 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU,
+ 0x29U, 0xc5U, 0x89U, 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+ 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, 0x9aU, 0xdbU, 0xc0U,
+ 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U,
+ 0xc7U, 0x31U, 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, 0x60U,
+ 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, 0x2dU, 0xe5U, 0x7aU, 0x9fU,
+ 0x93U, 0xc9U, 0x9cU, 0xefU, 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U,
+ 0xb0U, 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, 0x17U, 0x2bU,
+ 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U,
+ 0x21U, 0x0cU, 0x7dU,
+};
+static const uint32_t rcon[] = {
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
+ 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
+ /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+inline void ctr128_inc(void* counter_pointer) {
+ uint8_t* counter = reinterpret_cast<uint8_t*>(counter_pointer);
+ uint32_t n = 16;
+ uint8_t c;
+
+ do {
+ --n;
+ c = counter[n];
+ ++c;
+ counter[n] = c;
+ if (c)
+ return;
+ } while (n);
+}
+
+inline void ctr128_inc_aligned(void* counter_pointer) {
+ uint8_t* counter = reinterpret_cast<uint8_t*>(counter_pointer);
+ size_t *data, c, n;
+ const union {
+ uint32_t one;
+ char little;
+ } is_endian = {1};
+
+ if (is_endian.little) {
+ ctr128_inc(counter);
+ return;
+ }
+
+ data = reinterpret_cast<size_t*>(counter_pointer);
+ n = 16 / sizeof(size_t);
+ do {
+ --n;
+ c = data[n];
+ ++c;
+ data[n] = c;
+ if (c)
+ return;
+ } while (n);
+}
+
+/*
+ * The input encrypted as though 128bit counter mode is being used. The
+ * extra state information to record how much of the 128bit block we have
+ * used is contained in *num, and the encrypted counter is kept in
+ * ecount_buf. Both *num and ecount_buf must be initialised with zeros
+ * before the first call to CRYPTO_ctr128_encrypt(). This algorithm assumes
+ * that the counter is in the x lower bits of the IV (ivec), and that the
+ * application has full control over overflow and the rest of the IV. This
+ * implementation takes NO responsability for checking that the counter
+ * doesn't overflow into the rest of the IV when incremented.
+ */
+void CRYPTO_ctr128_encrypt(const void* in_pointer,
+ void* out_pointer,
+ size_t len,
+ const AES_KEY* key,
+ unsigned char ivec[16],
+ unsigned char ecount_buf[16],
+ unsigned int* num,
+ block128_f block) {
+ const uint8_t* in = reinterpret_cast<const uint8_t*>(in_pointer);
+ uint8_t* out = reinterpret_cast<uint8_t*>(out_pointer);
+
+ uint32_t n;
+ size_t l = 0;
+
+ SB_DCHECK(in && out && key && ecount_buf && num);
+ SB_DCHECK(*num < 16);
+
+ n = *num;
+
+ if (16 % sizeof(size_t) == 0) { /* always true actually */
+ do {
+ while (n && len) {
+ *(out++) = *(in++) ^ ecount_buf[n];
+ --len;
+ n = (n + 1) % 16;
+ }
+
+ while (len >= 16) {
+ (*block)(ivec, ecount_buf, key);
+ ctr128_inc_aligned(ivec);
+ for (; n < 16; n += sizeof(size_t))
+ *reinterpret_cast<size_t*>(out + n) =
+ *reinterpret_cast<const size_t*>(in + n) ^
+ *reinterpret_cast<size_t*>(ecount_buf + n);
+ len -= 16;
+ out += 16;
+ in += 16;
+ n = 0;
+ }
+ if (len) {
+ (*block)(ivec, ecount_buf, key);
+ ctr128_inc_aligned(ivec);
+ while (len--) {
+ out[n] = in[n] ^ ecount_buf[n];
+ ++n;
+ }
+ }
+ *num = n;
+ return;
+ } while (0);
+ }
+
+ /* the rest would be commonly eliminated by x86* compiler */
+ while (l < len) {
+ if (n == 0) {
+ (*block)(ivec, ecount_buf, key);
+ ctr128_inc(ivec);
+ }
+ out[l] = in[l] ^ ecount_buf[n];
+ ++l;
+ n = (n + 1) % 16;
+ }
+
+ *num = n;
+}
+
+void CRYPTO_cbc128_encrypt(const void* in_pointer,
+ void* out_pointer,
+ size_t len,
+ const AES_KEY* key,
+ unsigned char ivec[16],
+ block128_f block) {
+ const uint8_t* in = reinterpret_cast<const uint8_t*>(in_pointer);
+ uint8_t* out = reinterpret_cast<uint8_t*>(out_pointer);
+ size_t n;
+ const unsigned char* iv = ivec;
+
+ SB_DCHECK(in && out && key && ivec);
+
+ while (len >= 16) {
+ for (n = 0; n < 16; n += sizeof(size_t))
+ *reinterpret_cast<size_t*>(out + n) =
+ *reinterpret_cast<const size_t*>(in + n) ^
+ *reinterpret_cast<const size_t*>(iv + n);
+ (*block)(out, out, key);
+ iv = out;
+ len -= 16;
+ in += 16;
+ out += 16;
+ }
+
+ while (len) {
+ for (n = 0; n < 16 && n < len; ++n)
+ out[n] = in[n] ^ iv[n];
+ for (; n < 16; ++n)
+ out[n] = iv[n];
+ (*block)(out, out, key);
+ iv = out;
+ if (len <= 16)
+ break;
+ len -= 16;
+ in += 16;
+ out += 16;
+ }
+ SbMemoryCopy(ivec, iv, 16);
+}
+
+void CRYPTO_cbc128_decrypt(const void* in_pointer,
+ void* out_pointer,
+ size_t len,
+ const AES_KEY* key,
+ unsigned char ivec[16],
+ block128_f block) {
+ const uint8_t* in = reinterpret_cast<const uint8_t*>(in_pointer);
+ uint8_t* out = reinterpret_cast<uint8_t*>(out_pointer);
+ size_t n;
+ union {
+ size_t t[16 / sizeof(size_t)];
+ unsigned char c[16];
+ } tmp;
+
+ SB_DCHECK(in && out && key && ivec);
+
+ if (in != out) {
+ const unsigned char* iv = ivec;
+
+ if (16 % sizeof(size_t) == 0) { /* always true */
+ while (len >= 16) {
+ size_t* out_t = reinterpret_cast<size_t*>(out);
+ const size_t* iv_t = reinterpret_cast<const size_t*>(iv);
+
+ (*block)(in, out, key);
+ for (n = 0; n < 16 / sizeof(size_t); n++)
+ out_t[n] ^= iv_t[n];
+ iv = in;
+ len -= 16;
+ in += 16;
+ out += 16;
+ }
+ }
+ SbMemoryCopy(ivec, iv, 16);
+ } else {
+ if (16 % sizeof(size_t) == 0) { /* always true */
+ while (len >= 16) {
+ size_t c, *out_t = reinterpret_cast<size_t *>(out);
+ size_t* ivec_t = reinterpret_cast<size_t*>(ivec);
+ const size_t* in_t = reinterpret_cast<const size_t*>(in);
+
+ (*block)(in, tmp.c, key);
+ for (n = 0; n < 16 / sizeof(size_t); n++) {
+ c = in_t[n];
+ out_t[n] = tmp.t[n] ^ ivec_t[n];
+ ivec_t[n] = c;
+ }
+ len -= 16;
+ in += 16;
+ out += 16;
+ }
+ }
+ }
+
+ while (len) {
+ unsigned char c;
+ (*block)(in, tmp.c, key);
+ for (n = 0; n < 16 && n < len; ++n) {
+ c = in[n];
+ out[n] = tmp.c[n] ^ ivec[n];
+ ivec[n] = c;
+ }
+ if (len <= 16) {
+ for (; n < 16; ++n)
+ ivec[n] = in[n];
+ break;
+ }
+ len -= 16;
+ in += 16;
+ out += 16;
+ }
+}
+} // namespace
+
+int AES_set_encrypt_key(const void* key_pointer,
+ unsigned bits,
+ AES_KEY* aeskey) {
+ const uint8_t* key = reinterpret_cast<const uint8_t*>(key_pointer);
+ uint32_t* rk;
+ int i = 0;
+ uint32_t temp;
+ if (!key || !aeskey) {
+ return -1;
+ }
+ switch (bits) {
+ case 128:
+ aeskey->rounds = 10;
+ break;
+ case 192:
+ aeskey->rounds = 12;
+ break;
+ case 256:
+ aeskey->rounds = 14;
+ break;
+ default:
+ return -2;
+ }
+ rk = aeskey->rd_key;
+ rk[0] = GETU32(key);
+ rk[1] = GETU32(key + 4);
+ rk[2] = GETU32(key + 8);
+ rk[3] = GETU32(key + 12);
+ if (bits == 128) {
+ while (1) {
+ temp = rk[3];
+ rk[4] = rk[0] ^ (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te0[(temp)&0xff] & 0x0000ff00) ^
+ (Te1[(temp >> 24)] & 0x000000ff) ^ rcon[i];
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ if (++i == 10) {
+ return 0;
+ }
+ rk += 4;
+ }
+ }
+ rk[4] = GETU32(key + 16);
+ rk[5] = GETU32(key + 20);
+ if (bits == 192) {
+ while (1) {
+ temp = rk[5];
+ rk[6] = rk[0] ^ (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te0[(temp)&0xff] & 0x0000ff00) ^
+ (Te1[(temp >> 24)] & 0x000000ff) ^ rcon[i];
+ rk[7] = rk[1] ^ rk[6];
+ rk[8] = rk[2] ^ rk[7];
+ rk[9] = rk[3] ^ rk[8];
+ if (++i == 8) {
+ return 0;
+ }
+ rk[10] = rk[4] ^ rk[9];
+ rk[11] = rk[5] ^ rk[10];
+ rk += 6;
+ }
+ }
+ rk[6] = GETU32(key + 24);
+ rk[7] = GETU32(key + 28);
+ if (bits == 256) {
+ while (1) {
+ temp = rk[7];
+ rk[8] = rk[0] ^ (Te2[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te0[(temp)&0xff] & 0x0000ff00) ^
+ (Te1[(temp >> 24)] & 0x000000ff) ^ rcon[i];
+ rk[9] = rk[1] ^ rk[8];
+ rk[10] = rk[2] ^ rk[9];
+ rk[11] = rk[3] ^ rk[10];
+ if (++i == 7) {
+ return 0;
+ }
+ temp = rk[11];
+ rk[12] = rk[4] ^ (Te2[(temp >> 24)] & 0xff000000) ^
+ (Te3[(temp >> 16) & 0xff] & 0x00ff0000) ^
+ (Te0[(temp >> 8) & 0xff] & 0x0000ff00) ^
+ (Te1[(temp)&0xff] & 0x000000ff);
+ rk[13] = rk[5] ^ rk[12];
+ rk[14] = rk[6] ^ rk[13];
+ rk[15] = rk[7] ^ rk[14];
+ rk += 8;
+ }
+ }
+ return 0;
+}
+
+int AES_set_decrypt_key(const void* key_pointer,
+ unsigned bits,
+ AES_KEY* aeskey) {
+ const uint8_t* key = reinterpret_cast<const uint8_t*>(key_pointer);
+ uint32_t* rk;
+ int i, j, status;
+ uint32_t temp;
+ /* first, start with an encryption schedule */
+ status = AES_set_encrypt_key(key, bits, aeskey);
+ if (status < 0) {
+ return status;
+ }
+ rk = aeskey->rd_key;
+ /* invert the order of the round keys: */
+ for (i = 0, j = 4 * aeskey->rounds; i < j; i += 4, j -= 4) {
+ temp = rk[i];
+ rk[i] = rk[j];
+ rk[j] = temp;
+ temp = rk[i + 1];
+ rk[i + 1] = rk[j + 1];
+ rk[j + 1] = temp;
+ temp = rk[i + 2];
+ rk[i + 2] = rk[j + 2];
+ rk[j + 2] = temp;
+ temp = rk[i + 3];
+ rk[i + 3] = rk[j + 3];
+ rk[j + 3] = temp;
+ }
+ /* apply the inverse MixColumn transform to all round keys but the first and
+ * the last: */
+ for (i = 1; i < static_cast<int>(aeskey->rounds); i++) {
+ rk += 4;
+ rk[0] =
+ Td0[Te1[(rk[0] >> 24)] & 0xff] ^ Td1[Te1[(rk[0] >> 16) & 0xff] & 0xff] ^
+ Td2[Te1[(rk[0] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[0]) & 0xff] & 0xff];
+ rk[1] =
+ Td0[Te1[(rk[1] >> 24)] & 0xff] ^ Td1[Te1[(rk[1] >> 16) & 0xff] & 0xff] ^
+ Td2[Te1[(rk[1] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[1]) & 0xff] & 0xff];
+ rk[2] =
+ Td0[Te1[(rk[2] >> 24)] & 0xff] ^ Td1[Te1[(rk[2] >> 16) & 0xff] & 0xff] ^
+ Td2[Te1[(rk[2] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[2]) & 0xff] & 0xff];
+ rk[3] =
+ Td0[Te1[(rk[3] >> 24)] & 0xff] ^ Td1[Te1[(rk[3] >> 16) & 0xff] & 0xff] ^
+ Td2[Te1[(rk[3] >> 8) & 0xff] & 0xff] ^ Td3[Te1[(rk[3]) & 0xff] & 0xff];
+ }
+ return 0;
+}
+
+void AES_encrypt(const void* in_pointer,
+ void* out_pointer,
+ const AES_KEY* key) {
+ const uint8_t* in = reinterpret_cast<const uint8_t*>(in_pointer);
+ uint8_t* out = reinterpret_cast<uint8_t*>(out_pointer);
+ const uint32_t* rk;
+ uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
+ int r;
+ SB_DCHECK(in && out && key);
+ rk = key->rd_key;
+ /* map byte array block to cipher state
+ * and add initial round key: */
+ s0 = GETU32(in) ^ rk[0];
+ s1 = GETU32(in + 4) ^ rk[1];
+ s2 = GETU32(in + 8) ^ rk[2];
+ s3 = GETU32(in + 12) ^ rk[3];
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = key->rounds >> 1;
+ for (;;) {
+ t0 = Te0[(s0 >> 24)] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^
+ Te3[(s3)&0xff] ^ rk[4];
+ t1 = Te0[(s1 >> 24)] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^
+ Te3[(s0)&0xff] ^ rk[5];
+ t2 = Te0[(s2 >> 24)] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^
+ Te3[(s1)&0xff] ^ rk[6];
+ t3 = Te0[(s3 >> 24)] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^
+ Te3[(s2)&0xff] ^ rk[7];
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+ s0 = Te0[(t0 >> 24)] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^
+ Te3[(t3)&0xff] ^ rk[0];
+ s1 = Te0[(t1 >> 24)] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^
+ Te3[(t0)&0xff] ^ rk[1];
+ s2 = Te0[(t2 >> 24)] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^
+ Te3[(t1)&0xff] ^ rk[2];
+ s3 = Te0[(t3 >> 24)] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^
+ Te3[(t2)&0xff] ^ rk[3];
+ }
+
+ /* apply last round and map cipher state to byte array block: */
+ s0 = (Te2[(t0 >> 24)] & 0xff000000) ^ (Te3[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te0[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t3)&0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(out, s0);
+ s1 = (Te2[(t1 >> 24)] & 0xff000000) ^ (Te3[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te0[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t0)&0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(out + 4, s1);
+ s2 = (Te2[(t2 >> 24)] & 0xff000000) ^ (Te3[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te0[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t1)&0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(out + 8, s2);
+ s3 = (Te2[(t3 >> 24)] & 0xff000000) ^ (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te0[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t2)&0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(out + 12, s3);
+}
+
+void AES_decrypt(const void* in_pointer,
+ void* out_pointer,
+ const AES_KEY* key) {
+ const uint8_t* in = reinterpret_cast<const uint8_t*>(in_pointer);
+ uint8_t* out = reinterpret_cast<uint8_t*>(out_pointer);
+ const uint32_t* rk;
+ uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
+ int r;
+ SB_DCHECK(in && out && key);
+ rk = key->rd_key;
+
+ /* map byte array block to cipher state
+ * and add initial round key: */
+ s0 = GETU32(in) ^ rk[0];
+ s1 = GETU32(in + 4) ^ rk[1];
+ s2 = GETU32(in + 8) ^ rk[2];
+ s3 = GETU32(in + 12) ^ rk[3];
+
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = key->rounds >> 1;
+ for (;;) {
+ t0 = Td0[(s0 >> 24)] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^
+ Td3[(s1)&0xff] ^ rk[4];
+ t1 = Td0[(s1 >> 24)] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^
+ Td3[(s2)&0xff] ^ rk[5];
+ t2 = Td0[(s2 >> 24)] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^
+ Td3[(s3)&0xff] ^ rk[6];
+ t3 = Td0[(s3 >> 24)] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^
+ Td3[(s0)&0xff] ^ rk[7];
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+ s0 = Td0[(t0 >> 24)] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^
+ Td3[(t1)&0xff] ^ rk[0];
+ s1 = Td0[(t1 >> 24)] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^
+ Td3[(t2)&0xff] ^ rk[1];
+ s2 = Td0[(t2 >> 24)] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^
+ Td3[(t3)&0xff] ^ rk[2];
+ s3 = Td0[(t3 >> 24)] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^
+ Td3[(t0)&0xff] ^ rk[3];
+ }
+
+ /* apply last round and
+ * map cipher state to byte array block: */
+ s0 = ((uint32_t)Td4[(t0 >> 24)] << 24) ^
+ ((uint32_t)Td4[(t3 >> 16) & 0xff] << 16) ^
+ ((uint32_t)Td4[(t2 >> 8) & 0xff] << 8) ^ ((uint32_t)Td4[(t1)&0xff]) ^
+ rk[0];
+ PUTU32(out, s0);
+ s1 = ((uint32_t)Td4[(t1 >> 24)] << 24) ^
+ ((uint32_t)Td4[(t0 >> 16) & 0xff] << 16) ^
+ ((uint32_t)Td4[(t3 >> 8) & 0xff] << 8) ^ ((uint32_t)Td4[(t2)&0xff]) ^
+ rk[1];
+ PUTU32(out + 4, s1);
+ s2 = ((uint32_t)Td4[(t2 >> 24)] << 24) ^
+ ((uint32_t)Td4[(t1 >> 16) & 0xff] << 16) ^
+ ((uint32_t)Td4[(t0 >> 8) & 0xff] << 8) ^ ((uint32_t)Td4[(t3)&0xff]) ^
+ rk[2];
+ PUTU32(out + 8, s2);
+ s3 = ((uint32_t)Td4[(t3 >> 24)] << 24) ^
+ ((uint32_t)Td4[(t2 >> 16) & 0xff] << 16) ^
+ ((uint32_t)Td4[(t1 >> 8) & 0xff] << 8) ^ ((uint32_t)Td4[(t0)&0xff]) ^
+ rk[3];
+ PUTU32(out + 12, s3);
+}
+
+void AES_ctr128_encrypt(const void* in,
+ void* out,
+ size_t len,
+ const AES_KEY* key,
+ uint8_t ivec[SB_AES_BLOCK_SIZE],
+ uint8_t ecount_buf[SB_AES_BLOCK_SIZE],
+ unsigned int* num) {
+ CRYPTO_ctr128_encrypt(in, out, len, key, ivec, ecount_buf, num, AES_encrypt);
+}
+
+void AES_cbc_encrypt(const void* in,
+ void* out,
+ size_t len,
+ const AES_KEY* key,
+ uint8_t* ivec,
+ const int enc) {
+ if (enc) {
+ CRYPTO_cbc128_encrypt(in, out, len, key, ivec, AES_encrypt);
+ } else {
+ CRYPTO_cbc128_decrypt(in, out, len, key, ivec, AES_decrypt);
+ }
+}
+
+// --- GCM -------------------------------------------------------------------
+
+#define GCM_MUL(ctx, Xi) gcm_gmult_4bit((ctx)->Xi.u, (ctx)->Htable)
+#define PACK(s) ((size_t)(s) << (sizeof(size_t) * 8 - 16))
+#define REDUCE1BIT(V) \
+ do { \
+ if (sizeof(size_t) == 8) { \
+ uint64_t T = UINT64_C(0xe100000000000000) & (0 - ((V).lo & 1)); \
+ (V).lo = ((V).hi << 63) | ((V).lo >> 1); \
+ (V).hi = ((V).hi >> 1) ^ T; \
+ } else { \
+ uint32_t T = 0xe1000000U & (0 - (uint32_t)((V).lo & 1)); \
+ (V).lo = ((V).hi << 63) | ((V).lo >> 1); \
+ (V).hi = ((V).hi >> 1) ^ ((uint64_t)T << 32); \
+ } \
+ } while (0)
+
+namespace {
+const size_t rem_4bit[16] = {
+ PACK(0x0000), PACK(0x1C20), PACK(0x3840), PACK(0x2460),
+ PACK(0x7080), PACK(0x6CA0), PACK(0x48C0), PACK(0x54E0),
+ PACK(0xE100), PACK(0xFD20), PACK(0xD940), PACK(0xC560),
+ PACK(0x9180), PACK(0x8DA0), PACK(0xA9C0), PACK(0xB5E0)};
+
+static void gcm_init_4bit(u128 Htable[16], uint64_t H[2]) {
+ u128 V;
+
+ Htable[0].hi = 0;
+ Htable[0].lo = 0;
+ V.hi = H[0];
+ V.lo = H[1];
+
+ Htable[8] = V;
+ REDUCE1BIT(V);
+ Htable[4] = V;
+ REDUCE1BIT(V);
+ Htable[2] = V;
+ REDUCE1BIT(V);
+ Htable[1] = V;
+ Htable[3].hi = V.hi ^ Htable[2].hi, Htable[3].lo = V.lo ^ Htable[2].lo;
+ V = Htable[4];
+ Htable[5].hi = V.hi ^ Htable[1].hi, Htable[5].lo = V.lo ^ Htable[1].lo;
+ Htable[6].hi = V.hi ^ Htable[2].hi, Htable[6].lo = V.lo ^ Htable[2].lo;
+ Htable[7].hi = V.hi ^ Htable[3].hi, Htable[7].lo = V.lo ^ Htable[3].lo;
+ V = Htable[8];
+ Htable[9].hi = V.hi ^ Htable[1].hi, Htable[9].lo = V.lo ^ Htable[1].lo;
+ Htable[10].hi = V.hi ^ Htable[2].hi, Htable[10].lo = V.lo ^ Htable[2].lo;
+ Htable[11].hi = V.hi ^ Htable[3].hi, Htable[11].lo = V.lo ^ Htable[3].lo;
+ Htable[12].hi = V.hi ^ Htable[4].hi, Htable[12].lo = V.lo ^ Htable[4].lo;
+ Htable[13].hi = V.hi ^ Htable[5].hi, Htable[13].lo = V.lo ^ Htable[5].lo;
+ Htable[14].hi = V.hi ^ Htable[6].hi, Htable[14].lo = V.lo ^ Htable[6].lo;
+ Htable[15].hi = V.hi ^ Htable[7].hi, Htable[15].lo = V.lo ^ Htable[7].lo;
+}
+
+void gcm_gmult_4bit(uint64_t Xi[2], const u128 Htable[16]) {
+ u128 Z;
+ int cnt = 15;
+ size_t rem, nlo, nhi;
+
+ nlo = ((const uint8_t *)Xi)[15];
+ nhi = nlo >> 4;
+ nlo &= 0xf;
+
+ Z.hi = Htable[nlo].hi;
+ Z.lo = Htable[nlo].lo;
+
+ while (1) {
+ rem = (size_t)Z.lo & 0xf;
+ Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+ Z.hi = (Z.hi >> 4);
+ if (sizeof(size_t) == 8) {
+ Z.hi ^= rem_4bit[rem];
+ } else {
+ Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+ }
+
+ Z.hi ^= Htable[nhi].hi;
+ Z.lo ^= Htable[nhi].lo;
+
+ if (--cnt < 0) {
+ break;
+ }
+
+ nlo = ((const uint8_t *)Xi)[cnt];
+ nhi = nlo >> 4;
+ nlo &= 0xf;
+
+ rem = (size_t)Z.lo & 0xf;
+ Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+ Z.hi = (Z.hi >> 4);
+ if (sizeof(size_t) == 8) {
+ Z.hi ^= rem_4bit[rem];
+ } else {
+ Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+ }
+
+ Z.hi ^= Htable[nlo].hi;
+ Z.lo ^= Htable[nlo].lo;
+ }
+
+ Xi[0] = SbByteSwapU64(Z.hi);
+ Xi[1] = SbByteSwapU64(Z.lo);
+}
+
+/* Streamed gcm_mult_4bit, see CRYPTO_gcm128_[en|de]crypt for
+ * details... Compiler-generated code doesn't seem to give any
+ * performance improvement, at least not on x86[_64]. It's here
+ * mostly as reference and a placeholder for possible future
+ * non-trivial optimization[s]... */
+void gcm_ghash_4bit(uint64_t Xi[2], const u128 Htable[16],
+ const uint8_t *inp, size_t len) {
+ u128 Z;
+ int cnt;
+ size_t rem, nlo, nhi;
+
+ do {
+ cnt = 15;
+ nlo = ((const uint8_t *)Xi)[15];
+ nlo ^= inp[15];
+ nhi = nlo >> 4;
+ nlo &= 0xf;
+
+ Z.hi = Htable[nlo].hi;
+ Z.lo = Htable[nlo].lo;
+
+ while (1) {
+ rem = (size_t)Z.lo & 0xf;
+ Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+ Z.hi = (Z.hi >> 4);
+ if (sizeof(size_t) == 8) {
+ Z.hi ^= rem_4bit[rem];
+ } else {
+ Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+ }
+
+ Z.hi ^= Htable[nhi].hi;
+ Z.lo ^= Htable[nhi].lo;
+
+ if (--cnt < 0) {
+ break;
+ }
+
+ nlo = ((const uint8_t *)Xi)[cnt];
+ nlo ^= inp[cnt];
+ nhi = nlo >> 4;
+ nlo &= 0xf;
+
+ rem = (size_t)Z.lo & 0xf;
+ Z.lo = (Z.hi << 60) | (Z.lo >> 4);
+ Z.hi = (Z.hi >> 4);
+ if (sizeof(size_t) == 8) {
+ Z.hi ^= rem_4bit[rem];
+ } else {
+ Z.hi ^= (uint64_t)rem_4bit[rem] << 32;
+ }
+
+ Z.hi ^= Htable[nlo].hi;
+ Z.lo ^= Htable[nlo].lo;
+ }
+
+ Xi[0] = SbByteSwapU64(Z.hi);
+ Xi[1] = SbByteSwapU64(Z.lo);
+ } while (inp += 16, len -= 16);
+}
+} // namespace
+
+void CRYPTO_ghash_init(gmult_func *out_mult, ghash_func *out_hash,
+ u128 *out_key, u128 out_table[16],
+ int *out_is_avx,
+ const uint8_t *gcm_key) {
+ *out_is_avx = 0;
+
+ union {
+ uint64_t u[2];
+ uint8_t c[16];
+ } H;
+
+ SbMemoryCopy(H.c, gcm_key, 16);
+
+ /* H is stored in host byte order */
+ H.u[0] = SbByteSwapU64(H.u[0]);
+ H.u[1] = SbByteSwapU64(H.u[1]);
+
+ SbMemoryCopy(out_key, H.c, 16);
+
+ gcm_init_4bit(out_table, H.u);
+ *out_mult = gcm_gmult_4bit;
+ *out_hash = gcm_ghash_4bit;
+}
+
+void AES_gcm128_init(GCM128_CONTEXT *ctx, const AES_KEY *aes_key, int enc) {
+ SbMemorySet(ctx, 0, sizeof(*ctx));
+ ctx->block = AES_encrypt;
+
+ uint8_t gcm_key[16];
+ SbMemorySet(gcm_key, 0, sizeof(gcm_key));
+ (*ctx->block)(gcm_key, gcm_key, aes_key);
+
+ int is_avx;
+ CRYPTO_ghash_init(&ctx->gmult, &ctx->ghash, &ctx->H, ctx->Htable, &is_avx,
+ gcm_key);
+}
+
+void AES_gcm128_setiv(GCM128_CONTEXT *ctx, const AES_KEY *key,
+ const void *iv_void, size_t len) {
+ unsigned int ctr;
+ const uint8_t *iv = static_cast<const uint8_t*>(iv_void);
+
+ ctx->Yi.u[0] = 0;
+ ctx->Yi.u[1] = 0;
+ ctx->Xi.u[0] = 0;
+ ctx->Xi.u[1] = 0;
+ ctx->len.u[0] = 0; /* AAD length */
+ ctx->len.u[1] = 0; /* message length */
+ ctx->ares = 0;
+ ctx->mres = 0;
+
+ if (len == 12) {
+ SbMemoryCopy(ctx->Yi.c, iv, 12);
+ ctx->Yi.c[15] = 1;
+ ctr = 1;
+ } else {
+ uint64_t len0 = len;
+
+ while (len >= 16) {
+ for (size_t i = 0; i < 16; ++i) {
+ ctx->Yi.c[i] ^= iv[i];
+ }
+ GCM_MUL(ctx, Yi);
+ iv += 16;
+ len -= 16;
+ }
+ if (len) {
+ for (size_t i = 0; i < len; ++i) {
+ ctx->Yi.c[i] ^= iv[i];
+ }
+ GCM_MUL(ctx, Yi);
+ }
+ len0 <<= 3;
+ ctx->Yi.u[1] ^= SbByteSwapU64(len0);
+
+ GCM_MUL(ctx, Yi);
+ ctr = GETU32_aligned(ctx->Yi.c + 12);
+ }
+
+ (*ctx->block)(ctx->Yi.c, ctx->EK0.c, key);
+ ++ctr;
+ PUTU32_aligned(ctx->Yi.c + 12, ctr);
+}
+
+int AES_gcm128_aad(GCM128_CONTEXT *ctx, const void *aad_void, size_t len) {
+ unsigned int n;
+ uint64_t alen = ctx->len.u[0];
+ const uint8_t *aad = static_cast<const uint8_t*>(aad_void);
+
+ if (ctx->len.u[1]) {
+ return 0;
+ }
+
+ alen += len;
+ if (alen > (UINT64_C(1) << 61) || (sizeof(len) == 8 && alen < len)) {
+ return 0;
+ }
+ ctx->len.u[0] = alen;
+
+ n = ctx->ares;
+ if (n) {
+ while (n && len) {
+ ctx->Xi.c[n] ^= *(aad++);
+ --len;
+ n = (n + 1) % 16;
+ }
+ if (n == 0) {
+ GCM_MUL(ctx, Xi);
+ } else {
+ ctx->ares = n;
+ return 1;
+ }
+ }
+
+ /* Process a whole number of blocks. */
+ while (len >= 16) {
+ for (size_t i = 0; i < 16; ++i) {
+ ctx->Xi.c[i] ^= aad[i];
+ }
+ GCM_MUL(ctx, Xi);
+ aad += 16;
+ len -= 16;
+ }
+
+ /* Process the remainder. */
+ if (len != 0) {
+ n = (unsigned int)len;
+ for (size_t i = 0; i < len; ++i) {
+ ctx->Xi.c[i] ^= aad[i];
+ }
+ }
+
+ ctx->ares = n;
+ return 1;
+}
+
+int AES_gcm128_encrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
+ const void *in_void, void *out_void, size_t len) {
+ unsigned int n, ctr;
+ uint64_t mlen = ctx->len.u[1];
+ block128_f block = ctx->block;
+ const uint8_t *in = static_cast<const uint8_t*>(in_void);
+ uint8_t *out = static_cast<uint8_t*>(out_void);
+
+ mlen += len;
+ if (mlen > ((UINT64_C(1) << 36) - 32) ||
+ (sizeof(len) == 8 && mlen < len)) {
+ return 0;
+ }
+ ctx->len.u[1] = mlen;
+
+ if (ctx->ares) {
+ /* First call to encrypt finalizes GHASH(AAD) */
+ GCM_MUL(ctx, Xi);
+ ctx->ares = 0;
+ }
+
+ ctr = GETU32_aligned(ctx->Yi.c + 12);
+
+ n = ctx->mres;
+ if (n) {
+ while (n && len) {
+ ctx->Xi.c[n] ^= *(out++) = *(in++) ^ ctx->EKi.c[n];
+ --len;
+ n = (n + 1) % 16;
+ }
+ if (n == 0) {
+ GCM_MUL(ctx, Xi);
+ } else {
+ ctx->mres = n;
+ return 1;
+ }
+ }
+
+ while (len >= 16) {
+ size_t *out_t = reinterpret_cast<size_t*>(out);
+ const size_t *in_t = reinterpret_cast<const size_t*>(in);
+
+ (*block)(ctx->Yi.c, ctx->EKi.c, key);
+ ++ctr;
+ PUTU32_aligned(ctx->Yi.c + 12, ctr);
+ for (size_t i = 0; i < 16 / sizeof(size_t); ++i) {
+ ctx->Xi.t[i] ^= out_t[i] = in_t[i] ^ ctx->EKi.t[i];
+ }
+ GCM_MUL(ctx, Xi);
+ out += 16;
+ in += 16;
+ len -= 16;
+ }
+
+ if (len) {
+ (*block)(ctx->Yi.c, ctx->EKi.c, key);
+ ++ctr;
+ PUTU32_aligned(ctx->Yi.c + 12, ctr);
+ while (len--) {
+ ctx->Xi.c[n] ^= out[n] = in[n] ^ ctx->EKi.c[n];
+ ++n;
+ }
+ }
+
+ ctx->mres = n;
+ return 1;
+}
+
+int AES_gcm128_decrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
+ const void *in_void, void *out_void, size_t len) {
+ unsigned int n, ctr;
+ uint64_t mlen = ctx->len.u[1];
+ block128_f block = ctx->block;
+ const uint8_t *in = static_cast<const uint8_t*>(in_void);
+ uint8_t *out = static_cast<uint8_t*>(out_void);
+
+ mlen += len;
+ if (mlen > ((UINT64_C(1) << 36) - 32) ||
+ (sizeof(len) == 8 && mlen < len)) {
+ return 0;
+ }
+ ctx->len.u[1] = mlen;
+
+ if (ctx->ares) {
+ /* First call to decrypt finalizes GHASH(AAD) */
+ GCM_MUL(ctx, Xi);
+ ctx->ares = 0;
+ }
+
+ ctr = GETU32_aligned(ctx->Yi.c + 12);
+
+ n = ctx->mres;
+ if (n) {
+ while (n && len) {
+ uint8_t c = *(in++);
+ *(out++) = c ^ ctx->EKi.c[n];
+ ctx->Xi.c[n] ^= c;
+ --len;
+ n = (n + 1) % 16;
+ }
+ if (n == 0) {
+ GCM_MUL(ctx, Xi);
+ } else {
+ ctx->mres = n;
+ return 1;
+ }
+ }
+
+ while (len >= 16) {
+ size_t *out_t = reinterpret_cast<size_t*>(out);
+ const size_t *in_t = reinterpret_cast<const size_t*>(in);
+
+ (*block)(ctx->Yi.c, ctx->EKi.c, key);
+ ++ctr;
+ PUTU32_aligned(ctx->Yi.c + 12, ctr);
+ for (size_t i = 0; i < 16 / sizeof(size_t); ++i) {
+ size_t c = in_t[i];
+ out_t[i] = c ^ ctx->EKi.t[i];
+ ctx->Xi.t[i] ^= c;
+ }
+ GCM_MUL(ctx, Xi);
+ out += 16;
+ in += 16;
+ len -= 16;
+ }
+
+ if (len) {
+ (*block)(ctx->Yi.c, ctx->EKi.c, key);
+ ++ctr;
+ PUTU32_aligned(ctx->Yi.c + 12, ctr);
+ while (len--) {
+ uint8_t c = in[n];
+ ctx->Xi.c[n] ^= c;
+ out[n] = c ^ ctx->EKi.c[n];
+ ++n;
+ }
+ }
+
+ ctx->mres = n;
+ return 1;
+}
+
+void AES_gcm128_tag(GCM128_CONTEXT *ctx, void *tag, size_t len) {
+ uint64_t alen = ctx->len.u[0] << 3;
+ uint64_t clen = ctx->len.u[1] << 3;
+
+ if (ctx->mres || ctx->ares) {
+ GCM_MUL(ctx, Xi);
+ }
+
+ alen = SbByteSwapU64(alen);
+ clen = SbByteSwapU64(clen);
+
+ ctx->Xi.u[0] ^= alen;
+ ctx->Xi.u[1] ^= clen;
+ GCM_MUL(ctx, Xi);
+
+ ctx->Xi.u[0] ^= ctx->EK0.u[0];
+ ctx->Xi.u[1] ^= ctx->EK0.u[1];
+
+ SbMemoryCopy(tag, ctx->Xi.c,
+ len <= sizeof(ctx->Xi.c) ? len : sizeof(ctx->Xi.c));
+}
+
+} // namespace cryptography
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/cryptography/software_aes.h b/src/starboard/shared/starboard/cryptography/software_aes.h
new file mode 100644
index 0000000..0087995
--- /dev/null
+++ b/src/starboard/shared/starboard/cryptography/software_aes.h
@@ -0,0 +1,215 @@
+/* ====================================================================
+ * Copyright (c) 2002-2006,2008 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
+
+// Modifications Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_SOFTWARE_AES_H_
+#define STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_SOFTWARE_AES_H_
+
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace cryptography {
+
+/* Raw AES functions. */
+#define SB_AES_ENCRYPT 1
+#define SB_AES_DECRYPT 0
+
+/* AES_MAXNR is the maximum number of AES rounds. */
+#define SB_AES_MAXNR 14
+#define SB_AES_BLOCK_SIZE 16
+
+/* aes_key_st should be an opaque type, but EVP requires that the size be
+ * known. */
+struct aes_key_st {
+ uint32_t rd_key[4 * (SB_AES_MAXNR + 1)];
+ unsigned rounds;
+};
+
+typedef struct aes_key_st AES_KEY;
+
+typedef void (*block128_f)(const void* in, void* out, const AES_KEY* key);
+
+/* AES_set_encrypt_key configures |aeskey| to encrypt with the |bits|-bit key,
+ * |key|.
+ *
+ * WARNING: unlike other OpenSSL functions, this returns zero on success and a
+ * negative number on error. */
+int AES_set_encrypt_key(const void* key, unsigned bits, AES_KEY* aeskey);
+
+/* AES_set_decrypt_key configures |aeskey| to decrypt with the |bits|-bit key,
+ * |key|.
+ *
+ * WARNING: unlike other OpenSSL functions, this returns zero on success and a
+ * negative number on error. */
+int AES_set_decrypt_key(const void* key, unsigned bits, AES_KEY* aeskey);
+
+/* AES_encrypt encrypts a single block from |in| to |out| with |key|. The |in|
+ * and |out| pointers may overlap. */
+void AES_encrypt(const void* in, void* out, const AES_KEY* key);
+
+/* AES_decrypt decrypts a single block from |in| to |out| with |key|. The |in|
+ * and |out| pointers may overlap. */
+void AES_decrypt(const void* in, void* out, const AES_KEY* key);
+
+/* Block cipher modes. */
+/* AES_ctr128_encrypt encrypts (or decrypts, it's the same in CTR mode) |len|
+ * bytes from |in| to |out|. The |num| parameter must be set to zero on the
+ * first call and |ivec| will be incremented. */
+void AES_ctr128_encrypt(const void* in,
+ void* out,
+ size_t len,
+ const AES_KEY* key,
+ uint8_t ivec[SB_AES_BLOCK_SIZE],
+ uint8_t ecount_buf[SB_AES_BLOCK_SIZE],
+ uint32_t* num);
+
+/* AES_cbc_encrypt encrypts (or decrypts, if |enc| == |AES_DECRYPT|) |len|
+ * bytes from |in| to |out|. The length must be a multiple of the block size. */
+void AES_cbc_encrypt(const void* in,
+ void* out,
+ size_t len,
+ const AES_KEY* key,
+ uint8_t* ivec,
+ const int enc);
+
+/* GCM */
+
+typedef struct {
+ uint64_t hi;
+ uint64_t lo;
+} u128;
+
+/* gmult_func multiplies |Xi| by the GCM key and writes the result back to
+ * |Xi|. */
+typedef void (*gmult_func)(uint64_t Xi[2], const u128 Htable[16]);
+
+/* ghash_func repeatedly multiplies |Xi| by the GCM key and adds in blocks from
+ * |inp|. The result is written back to |Xi| and the |len| argument must be a
+ * multiple of 16. */
+typedef void (*ghash_func)(uint64_t Xi[2], const u128 Htable[16],
+ const uint8_t *inp, size_t len);
+
+/* This differs from upstream's |gcm128_context| in that it does not have the
+ * |key| pointer, in order to make it |memcpy|-friendly. Rather the key is
+ * passed into each call that needs it. */
+struct gcm128_context {
+ /* Following 6 names follow names in GCM specification */
+ union {
+ uint64_t u[2];
+ uint32_t d[4];
+ uint8_t c[16];
+ size_t t[16 / sizeof(size_t)];
+ } Yi, EKi, EK0, len, Xi;
+ /* Note that the order of |Xi|, |H| and |Htable| is fixed by the MOVBE-based,
+ * x86-64, GHASH assembly. */
+ u128 H;
+ u128 Htable[16];
+ gmult_func gmult;
+ ghash_func ghash;
+ unsigned int mres, ares;
+ block128_f block;
+};
+
+/* This API differs from the upstream API slightly. The |GCM128_CONTEXT| does
+ * not have a |key| pointer that points to the key as upstream's version does.
+ * Instead, every function takes a |key| parameter. This way |GCM128_CONTEXT|
+ * can be safely copied. */
+typedef struct gcm128_context GCM128_CONTEXT;
+
+/* AES_gcm128_init initialises |ctx| to use the given key. */
+void AES_gcm128_init(GCM128_CONTEXT *ctx, const AES_KEY *key, int enc);
+
+/* AES_gcm128_setiv sets the IV (nonce) for |ctx|. The |key| must be the same
+ * key that was passed to |AES_gcm128_init|. */
+void AES_gcm128_setiv(GCM128_CONTEXT *ctx, const AES_KEY *key,
+ const void *iv, size_t iv_len);
+
+/* AES_gcm128_aad sets the authenticated data for an instance of GCM. This must
+ * be called before and data is encrypted. It returns one on success and zero
+ * otherwise. */
+int AES_gcm128_aad(GCM128_CONTEXT *ctx, const void *aad, size_t len);
+
+/* AES_gcm128_encrypt encrypts |len| bytes from |in| to |out|. The |key| must be
+ * the same key that was passed to |AES_gcm128_init|. It returns one on success
+ * and zero otherwise. */
+int AES_gcm128_encrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
+ const void *in, void *out, size_t len);
+
+/* AES_gcm128_decrypt decrypts |len| bytes from |in| to |out|. The |key| must be
+ * the same key that was passed to |AES_gcm128_init|. It returns one on success
+ * and zero otherwise. */
+int AES_gcm128_decrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
+ const void *in, void *out, size_t len);
+
+/* AES_gcm128_tag calculates the authenticator and copies it into |tag|. The
+ * minimum of |len| and 16 bytes are copied into |tag|. */
+void AES_gcm128_tag(GCM128_CONTEXT *ctx, void *tag, size_t len);
+
+} // namespace cryptography
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_CRYPTOGRAPHY_SOFTWARE_AES_H_
diff --git a/src/starboard/shared/starboard/drm/drm_generate_session_update_request.cc b/src/starboard/shared/starboard/drm/drm_generate_session_update_request.cc
index 3333f83..efedc7e 100644
--- a/src/starboard/shared/starboard/drm/drm_generate_session_update_request.cc
+++ b/src/starboard/shared/starboard/drm/drm_generate_session_update_request.cc
@@ -18,14 +18,27 @@
#include "starboard/shared/starboard/drm/drm_system_internal.h"
void SbDrmGenerateSessionUpdateRequest(SbDrmSystem drm_system,
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
const char* type,
const void* initialization_data,
int initialization_data_size) {
if (!SbDrmSystemIsValid(drm_system)) {
- SB_DLOG(WARNING) << "Invalid drm system";
+ SB_DLOG(ERROR) << "Invalid DRM system.";
return;
}
- drm_system->GenerateSessionUpdateRequest(type, initialization_data,
- initialization_data_size);
+#if SB_API_VERSION >= 4
+ if (ticket == kSbDrmTicketInvalid) {
+ SB_DLOG(ERROR) << "Ticket must be specified.";
+ return;
+ }
+#endif // SB_API_VERSION >= 4
+
+ drm_system->GenerateSessionUpdateRequest(
+#if SB_API_VERSION >= 4
+ ticket,
+#endif // SB_API_VERSION >= 4
+ type, initialization_data, initialization_data_size);
}
diff --git a/src/starboard/shared/starboard/drm/drm_system_internal.h b/src/starboard/shared/starboard/drm/drm_system_internal.h
index 73e9da8..a392fe1 100644
--- a/src/starboard/shared/starboard/drm/drm_system_internal.h
+++ b/src/starboard/shared/starboard/drm/drm_system_internal.h
@@ -26,13 +26,21 @@
virtual ~SbDrmSystemPrivate() {}
- virtual void GenerateSessionUpdateRequest(const char* type,
- const void* initialization_data,
- int initialization_data_size) = 0;
- virtual void UpdateSession(const void* key,
- int key_size,
- const void* session_id,
- int session_id_size) = 0;
+ virtual void GenerateSessionUpdateRequest(
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
+ const char* type,
+ const void* initialization_data,
+ int initialization_data_size) = 0;
+ virtual void UpdateSession(
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
+ const void* key,
+ int key_size,
+ const void* session_id,
+ int session_id_size) = 0;
virtual void CloseSession(const void* session_id, int session_id_size) = 0;
virtual DecryptStatus Decrypt(InputBuffer* buffer) = 0;
diff --git a/src/starboard/shared/starboard/drm/drm_update_session.cc b/src/starboard/shared/starboard/drm/drm_update_session.cc
index 287096e..57862d5 100644
--- a/src/starboard/shared/starboard/drm/drm_update_session.cc
+++ b/src/starboard/shared/starboard/drm/drm_update_session.cc
@@ -18,6 +18,9 @@
#include "starboard/shared/starboard/drm/drm_system_internal.h"
void SbDrmUpdateSession(SbDrmSystem drm_system,
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
const void* key,
int key_size,
const void* session_id,
@@ -27,5 +30,9 @@
return;
}
- drm_system->UpdateSession(key, key_size, session_id, session_id_size);
+ drm_system->UpdateSession(
+#if SB_API_VERSION >= 4
+ ticket,
+#endif // SB_API_VERSION >= 4
+ key, key_size, session_id, session_id_size);
}
diff --git a/src/starboard/shared/starboard/media/codec_util.cc b/src/starboard/shared/starboard/media/codec_util.cc
new file mode 100644
index 0000000..9ec76dc
--- /dev/null
+++ b/src/starboard/shared/starboard/media/codec_util.cc
@@ -0,0 +1,149 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/codec_util.h"
+
+#include "starboard/character.h"
+#include "starboard/log.h"
+#include "starboard/string.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+namespace {
+
+int hex_to_int(char hex) {
+ if (hex >= '0' && hex <= '9') {
+ return hex - '0';
+ }
+ if (hex >= 'A' && hex <= 'F') {
+ return hex - 'A' + 10;
+ }
+ if (hex >= 'a' && hex <= 'f') {
+ return hex - 'a' + 10;
+ }
+ SB_NOTREACHED();
+ return 0;
+}
+
+} // namespace
+
+SbMediaAudioCodec GetAudioCodecFromString(const char* codec) {
+ if (SbStringCompare(codec, "mp4a.40.", 8) == 0) {
+ return kSbMediaAudioCodecAac;
+ }
+ if (SbStringCompare(codec, "opus", 4) == 0) {
+ return kSbMediaAudioCodecOpus;
+ }
+ if (SbStringCompare(codec, "vorbis", 6) == 0) {
+ return kSbMediaAudioCodecVorbis;
+ }
+ return kSbMediaAudioCodecNone;
+}
+
+SbMediaVideoCodec GetVideoCodecFromString(const char* codec) {
+ if (SbStringCompare(codec, "avc1.", 5) == 0 ||
+ SbStringCompare(codec, "avc3.", 5) == 0) {
+ return kSbMediaVideoCodecH264;
+ }
+ if (SbStringCompare(codec, "hev1.", 5) == 0 ||
+ SbStringCompare(codec, "hvc1.", 5) == 0) {
+ return kSbMediaVideoCodecH265;
+ }
+ if (SbStringCompare(codec, "vp8", 3) == 0) {
+ return kSbMediaVideoCodecVp8;
+ }
+ if (SbStringCompare(codec, "vp9", 3) == 0) {
+ return kSbMediaVideoCodecVp9;
+ }
+ return kSbMediaVideoCodecNone;
+}
+
+bool ParseH264Info(const char* codec, int* width, int* height, int* fps) {
+ SB_DCHECK(width);
+ SB_DCHECK(height);
+ SB_DCHECK(fps);
+
+ if (GetVideoCodecFromString(codec) != kSbMediaVideoCodecH264) {
+ return false;
+ }
+
+ if (SbStringGetLength(codec) != 11 || !SbCharacterIsHexDigit(codec[9]) ||
+ !SbCharacterIsHexDigit(codec[10])) {
+ return false;
+ }
+
+ // According to https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Levels, the
+ // only thing that determinates resolution and fps is level. Profile and
+ // constraint set only affect bitrate and features.
+ //
+ // Note that a level can map to more than one resolution/fps combinations.
+ // So the returned resolution/fps is hand-picked to best fit for common cases.
+ // For example, level 4.2 indicates 1280 x 720 at 145.1 fps or 1920 x 1080 at
+ // 64.0 fps or 2048 x 1080 @ 60.9 fps. In this case, the function returns
+ // 1920 x 1080 at 60 fps.
+ int level = hex_to_int(codec[9]) * 16 + hex_to_int(codec[10]);
+
+ // Level 3.1 is the minimum to support 720p at 30 fps. We assume all devices
+ // can support it.
+ if (level <= 31) {
+ *width = 1280;
+ *height = 720;
+ *fps = 30;
+ return true;
+ } else if (level <= 32) {
+ *width = 1280;
+ *height = 720;
+ *fps = 60;
+ return true;
+ } else if (level <= 40) {
+ *width = 1920;
+ *height = 1080;
+ *fps = 30;
+ return true;
+ } else if (level <= 41) {
+ *width = 1920;
+ *height = 1080;
+ *fps = 30;
+ return true;
+ } else if (level <= 42) {
+ *width = 1920;
+ *height = 1080;
+ *fps = 60;
+ return true;
+ } else if (level <= 50) {
+ *width = 2560;
+ *height = 1440;
+ *fps = 30;
+ return true;
+ } else if (level <= 51) {
+ *width = 3840;
+ *height = 2160;
+ *fps = 30;
+ return true;
+ }
+
+ // Level >= 52
+ *width = 3840;
+ *height = 2160;
+ *fps = 60;
+ return true;
+}
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/media/codec_util.h b/src/starboard/shared/starboard/media/codec_util.h
new file mode 100644
index 0000000..144ff03
--- /dev/null
+++ b/src/starboard/shared/starboard/media/codec_util.h
@@ -0,0 +1,42 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_CODEC_UTIL_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_CODEC_UTIL_H_
+
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+SbMediaAudioCodec GetAudioCodecFromString(const char* codec);
+SbMediaVideoCodec GetVideoCodecFromString(const char* codec);
+
+// This function parses an h264 codec in the form of {avc1|avc3}.PPCCLL as
+// specificed by https://tools.ietf.org/html/rfc6381#section-3.3.
+//
+// Note that the leading codec is not necessarily to be "avc1" or "avc3" per
+// spec but this function only parses "avc1" and "avc3". This function returns
+// false when |codec| doesn't contain a valid codec string.
+bool ParseH264Info(const char* codec, int* width, int* height, int* fps);
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_MEDIA_CODEC_UTIL_H_
diff --git a/src/starboard/shared/starboard/media/interleaved_sinc_resampler.cc b/src/starboard/shared/starboard/media/interleaved_sinc_resampler.cc
new file mode 100644
index 0000000..d4df9ff
--- /dev/null
+++ b/src/starboard/shared/starboard/media/interleaved_sinc_resampler.cc
@@ -0,0 +1,335 @@
+// Copyright (c) 2015 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.
+//
+// Input buffer layout, dividing the total buffer into regions (r0_ - r5_):
+//
+// |----------------|-----------------------------------------|----------------|
+//
+// kBlockSize + kKernelSize / 2
+// <--------------------------------------------------------->
+// r0_
+//
+// kKernelSize / 2 kKernelSize / 2 kKernelSize / 2 kKernelSize / 2
+// <---------------> <---------------> <---------------> <--------------->
+// r1_ r2_ r3_ r4_
+//
+// kBlockSize
+// <--------------------------------------->
+// r5_
+//
+// The algorithm:
+//
+// 1) Consume input frames into r0_ (r1_ is zero-initialized).
+// 2) Position kernel centered at start of r0_ (r2_) and generate output frames
+// until kernel is centered at start of r4_ or we've finished generating all
+// the output frames.
+// 3) Copy r3_ to r1_ and r4_ to r2_.
+// 4) Consume input frames into r5_ (zero-pad if we run out of input).
+// 5) Goto (2) until all of input is consumed.
+//
+// Note: we're glossing over how the sub-sample handling works with
+// |virtual_source_idx_|, etc.
+
+#include "media/base/interleaved_sinc_resampler.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+
+namespace media {
+
+namespace {
+
+// The kernel size can be adjusted for quality (higher is better) at the
+// expense of performance. Must be a multiple of 32.
+const int kKernelSize = 32;
+
+// The number of destination frames generated per processing pass. Affects
+// how often and for how much InterleavedSincResampler calls back for input.
+// Must be greater than kKernelSize.
+const int kBlockSize = 512;
+
+// The kernel offset count is used for interpolation and is the number of
+// sub-sample kernel shifts. Can be adjusted for quality (higher is better)
+// at the expense of allocating more memory.
+const int kKernelOffsetCount = 32;
+const int kKernelStorageSize = kKernelSize * (kKernelOffsetCount + 1);
+
+// The size (in samples) of the internal buffer used by the resampler.
+const int kBufferSize = kBlockSize + kKernelSize;
+
+// The maximum numbers of buffer can be queued.
+const int kMaximumPendingBuffers = 8;
+
+} // namespace
+
+InterleavedSincResampler::InterleavedSincResampler(double io_sample_rate_ratio,
+ int channel_count)
+ : io_sample_rate_ratio_(io_sample_rate_ratio),
+ virtual_source_idx_(0),
+ buffer_primed_(false),
+ channel_count_(channel_count),
+ frame_size_in_bytes_(sizeof(float) * channel_count_),
+ // Create buffers with a 16-byte alignment for possible optimizations.
+ kernel_storage_(static_cast<float*>(
+ base::AlignedAlloc(sizeof(float) * kKernelStorageSize, 16))),
+ input_buffer_(static_cast<float*>(
+ base::AlignedAlloc(frame_size_in_bytes_ * kBufferSize, 16))),
+ offset_in_frames_(0),
+ frames_resampled_(0),
+ frames_queued_(0),
+ // Setup various region pointers in the buffer (see diagram above).
+ r0_(input_buffer_.get() + kKernelSize / 2 * channel_count_),
+ r1_(input_buffer_.get()),
+ r2_(r0_),
+ r3_(r0_ + (kBlockSize - kKernelSize / 2) * channel_count_),
+ r4_(r0_ + kBlockSize * channel_count_),
+ r5_(r0_ + kKernelSize / 2 * channel_count_) {
+ // Ensure kKernelSize is a multiple of 32 for easy SSE optimizations; causes
+ // r0_ and r5_ (used for input) to always be 16-byte aligned by virtue of
+ // input_buffer_ being 16-byte aligned.
+ DCHECK_EQ(kKernelSize % 32, 0) << "kKernelSize must be a multiple of 32!";
+ DCHECK_GT(kBlockSize, kKernelSize)
+ << "kBlockSize must be greater than kKernelSize!";
+ // Basic sanity checks to ensure buffer regions are laid out correctly:
+ // r0_ and r2_ should always be the same position.
+ DCHECK_EQ(r0_, r2_);
+ // r1_ at the beginning of the buffer.
+ DCHECK_EQ(r1_, input_buffer_.get());
+ // r1_ left of r2_, r2_ left of r5_ and r1_, r2_ size correct.
+ DCHECK_EQ(r2_ - r1_, r5_ - r2_);
+ // r3_ left of r4_, r5_ left of r0_ and r3_ size correct.
+ DCHECK_EQ(r4_ - r3_, r5_ - r0_);
+ // r3_, r4_ size correct and r4_ at the end of the buffer.
+ DCHECK_EQ(r4_ + (r4_ - r3_), r1_ + kBufferSize * channel_count_);
+ // r5_ size correct and at the end of the buffer.
+ DCHECK_EQ(r5_ + kBlockSize * channel_count_,
+ r1_ + kBufferSize * channel_count_);
+
+ memset(kernel_storage_.get(), 0,
+ sizeof(*kernel_storage_.get()) * kKernelStorageSize);
+ memset(input_buffer_.get(), 0, frame_size_in_bytes_ * kBufferSize);
+
+ InitializeKernel();
+}
+
+void InterleavedSincResampler::InitializeKernel() {
+ // Blackman window parameters.
+ static const double kAlpha = 0.16;
+ static const double kA0 = 0.5 * (1.0 - kAlpha);
+ static const double kA1 = 0.5;
+ static const double kA2 = 0.5 * kAlpha;
+
+ // |sinc_scale_factor| is basically the normalized cutoff frequency of the
+ // low-pass filter.
+ double sinc_scale_factor =
+ io_sample_rate_ratio_ > 1.0 ? 1.0 / io_sample_rate_ratio_ : 1.0;
+
+ // The sinc function is an idealized brick-wall filter, but since we're
+ // windowing it the transition from pass to stop does not happen right away.
+ // So we should adjust the low pass filter cutoff slightly downward to avoid
+ // some aliasing at the very high-end.
+ // TODO(crogers): this value is empirical and to be more exact should vary
+ // depending on kKernelSize.
+ sinc_scale_factor *= 0.9;
+
+ // Generates a set of windowed sinc() kernels.
+ // We generate a range of sub-sample offsets from 0.0 to 1.0.
+ for (int offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
+ double subsample_offset =
+ static_cast<double>(offset_idx) / kKernelOffsetCount;
+
+ for (int i = 0; i < kKernelSize; ++i) {
+ // Compute the sinc with offset.
+ double s =
+ sinc_scale_factor * M_PI * (i - kKernelSize / 2 - subsample_offset);
+ double sinc = (!s ? 1.0 : sin(s) / s) * sinc_scale_factor;
+
+ // Compute Blackman window, matching the offset of the sinc().
+ double x = (i - subsample_offset) / kKernelSize;
+ double window =
+ kA0 - kA1 * cos(2.0 * M_PI * x) + kA2 * cos(4.0 * M_PI * x);
+
+ // Window the sinc() function and store at the correct offset.
+ kernel_storage_.get()[i + offset_idx * kKernelSize] = sinc * window;
+ }
+ }
+}
+
+void InterleavedSincResampler::QueueBuffer(
+ const scoped_refptr<Buffer>& buffer) {
+ DCHECK(buffer);
+ DCHECK(CanQueueBuffer());
+
+ if (!pending_buffers_.empty() && pending_buffers_.back()->IsEndOfStream()) {
+ DCHECK(buffer->IsEndOfStream());
+ return;
+ }
+
+ if (!buffer->IsEndOfStream()) {
+ frames_queued_ += buffer->GetDataSize() / frame_size_in_bytes_;
+ }
+
+ pending_buffers_.push(buffer);
+}
+
+bool InterleavedSincResampler::Resample(float* destination, int frames) {
+ if (!HasEnoughData(frames)) {
+ return false;
+ }
+
+ int remaining_frames = frames;
+
+ // Step (1) -- Prime the input buffer at the start of the input stream.
+ if (!buffer_primed_) {
+ Read(r0_, kBlockSize + kKernelSize / 2);
+ buffer_primed_ = true;
+ }
+
+ // Step (2) -- Resample!
+ while (remaining_frames) {
+ while (virtual_source_idx_ < kBlockSize) {
+ // |virtual_source_idx_| lies in between two kernel offsets so figure out
+ // what they are.
+ int source_idx = static_cast<int>(virtual_source_idx_);
+ double subsample_remainder = virtual_source_idx_ - source_idx;
+
+ double virtual_offset_idx = subsample_remainder * kKernelOffsetCount;
+ int offset_idx = static_cast<int>(virtual_offset_idx);
+
+ // We'll compute "convolutions" for the two kernels which straddle
+ // |virtual_source_idx_|.
+ float* k1 = kernel_storage_.get() + offset_idx * kKernelSize;
+ float* k2 = k1 + kKernelSize;
+
+ // Initialize input pointer based on quantized |virtual_source_idx_|.
+ float* input_ptr = r1_ + source_idx * channel_count_;
+
+ // Figure out how much to weight each kernel's "convolution".
+ double kernel_interpolation_factor = virtual_offset_idx - offset_idx;
+ for (int i = 0; i < channel_count_; ++i) {
+ *destination++ =
+ Convolve(input_ptr + i, k1, k2, kernel_interpolation_factor);
+ }
+
+ // Advance the virtual index.
+ virtual_source_idx_ += io_sample_rate_ratio_;
+
+ if (!--remaining_frames) {
+ frames_resampled_ += frames;
+ return true;
+ }
+ }
+
+ // Wrap back around to the start.
+ virtual_source_idx_ -= kBlockSize;
+
+ // Step (3) Copy r3_ to r1_ and r4_ to r2_.
+ // This wraps the last input frames back to the start of the buffer.
+ memcpy(r1_, r3_, frame_size_in_bytes_ * (kKernelSize / 2));
+ memcpy(r2_, r4_, frame_size_in_bytes_ * (kKernelSize / 2));
+
+ // Step (4)
+ // Refresh the buffer with more input.
+ Read(r5_, kBlockSize);
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+void InterleavedSincResampler::Flush() {
+ virtual_source_idx_ = 0;
+ buffer_primed_ = false;
+ memset(input_buffer_.get(), 0, frame_size_in_bytes_ * kBufferSize);
+ while (!pending_buffers_.empty()) {
+ pending_buffers_.pop();
+ }
+ offset_in_frames_ = 0;
+ frames_resampled_ = 0;
+ frames_queued_ = 0;
+}
+
+bool InterleavedSincResampler::CanQueueBuffer() const {
+ if (pending_buffers_.empty()) {
+ return true;
+ }
+ if (pending_buffers_.back()->IsEndOfStream()) {
+ return false;
+ }
+ return pending_buffers_.size() < kMaximumPendingBuffers;
+}
+
+bool InterleavedSincResampler::ReachedEOS() const {
+ if (pending_buffers_.empty() || !pending_buffers_.back()->IsEndOfStream()) {
+ return false;
+ }
+ return frames_resampled_ * io_sample_rate_ratio_ >= frames_queued_;
+}
+
+bool InterleavedSincResampler::HasEnoughData(int frames_to_resample) const {
+ // Always return true if EOS is seen, as in this case we will just fill 0.
+ if (!pending_buffers_.empty() && pending_buffers_.back()->IsEndOfStream()) {
+ return true;
+ }
+
+ // We have to decrease frames_queued_ down as the Read()s are always done in
+ // blocks of kBlockSize or kBufferSize. We have to ensure that there is buffer
+ // for an extra Read().
+ return (frames_resampled_ + frames_to_resample) * io_sample_rate_ratio_ <
+ frames_queued_ - kBufferSize;
+}
+
+void InterleavedSincResampler::Read(float* destination, int frames) {
+ while (frames > 0 && !pending_buffers_.empty()) {
+ scoped_refptr<Buffer> buffer = pending_buffers_.front();
+ if (buffer->IsEndOfStream()) {
+ // Zero fill the buffer after EOS has reached.
+ memset(destination, 0, frame_size_in_bytes_ * frames);
+ return;
+ }
+ // Copy the data over.
+ int frames_in_buffer = buffer->GetDataSize() / frame_size_in_bytes_;
+ int frames_to_copy = std::min(frames_in_buffer - offset_in_frames_, frames);
+ const uint8* source = buffer->GetData();
+ source += frame_size_in_bytes_ * offset_in_frames_;
+ memcpy(destination, source, frame_size_in_bytes_ * frames_to_copy);
+ offset_in_frames_ += frames_to_copy;
+ // Pop the first buffer if all its content has been read.
+ if (offset_in_frames_ == frames_in_buffer) {
+ offset_in_frames_ = 0;
+ pending_buffers_.pop();
+ }
+ frames -= frames_to_copy;
+ destination += frames_to_copy * channel_count_;
+ }
+
+ // Read should always be satisfied as otherwise Resample should return false
+ // to the caller directly.
+ DCHECK_EQ(frames, 0);
+}
+
+float InterleavedSincResampler::Convolve(const float* input_ptr,
+ const float* k1,
+ const float* k2,
+ double kernel_interpolation_factor) {
+ float sum1 = 0;
+ float sum2 = 0;
+
+ // Generate a single output sample. Unrolling this loop hurt performance in
+ // local testing.
+ int n = kKernelSize;
+ while (n--) {
+ sum1 += *input_ptr * *k1++;
+ sum2 += *input_ptr * *k2++;
+ input_ptr += channel_count_;
+ }
+
+ // Linearly interpolate the two "convolutions".
+ return (1.0 - kernel_interpolation_factor) * sum1 +
+ kernel_interpolation_factor * sum2;
+}
+
+} // namespace media
diff --git a/src/starboard/shared/starboard/media/interleaved_sinc_resampler.h b/src/starboard/shared/starboard/media/interleaved_sinc_resampler.h
new file mode 100644
index 0000000..edfdada
--- /dev/null
+++ b/src/starboard/shared/starboard/media/interleaved_sinc_resampler.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2015 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 MEDIA_BASE_INTERLEAVED_SINC_RESAMPLER_H_
+#define MEDIA_BASE_INTERLEAVED_SINC_RESAMPLER_H_
+
+#include <queue>
+
+#include "base/memory/aligned_memory.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/buffers.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// InterleavedSincResampler is a high-quality interleaved multi-channel sample
+//-rate converter operating on samples in float. It uses the same algorithm as
+// SincResampler. Unlike SincResampler, it works in push mode instead of pull
+// mode.
+class MEDIA_EXPORT InterleavedSincResampler {
+ public:
+ // |io_sample_rate_ratio| is the ratio of input / output sample rates.
+ // |channel_count| is the number of channels in the interleaved audio stream.
+ InterleavedSincResampler(double io_sample_rate_ratio, int channel_count);
+
+ // Append a buffer to the queue. The samples in the buffer has to be floats.
+ void QueueBuffer(const scoped_refptr<Buffer>& buffer);
+
+ // Resample |frames| of data from enqueued buffers. Return false if no sample
+ // is read. Return true if all requested samples have been written into
+ // |destination|. It will never do a partial read. After the stream reaches
+ // the end, the function will fill the rest of buffer with 0.
+ bool Resample(float* destination, int frames);
+
+ // Flush all buffered data and reset internal indices.
+ void Flush();
+
+ // Return false if we shouldn't queue more buffers to the resampler.
+ bool CanQueueBuffer() const;
+
+ // Returning true when we start to return zero filled data because of EOS.
+ bool ReachedEOS() const;
+
+ private:
+ void InitializeKernel();
+ bool HasEnoughData(int frames_to_resample) const;
+ void Read(float* destination, int frames);
+
+ float Convolve(const float* input_ptr,
+ const float* k1,
+ const float* k2,
+ double kernel_interpolation_factor);
+
+ // The ratio of input / output sample rates.
+ double io_sample_rate_ratio_;
+
+ // An index on the source input buffer with sub-sample precision. It must be
+ // double precision to avoid drift.
+ double virtual_source_idx_;
+
+ // The buffer is primed once at the very beginning of processing.
+ bool buffer_primed_;
+
+ // Number of audio channels.
+ int channel_count_;
+
+ // The size of bytes for an audio frame.
+ const int frame_size_in_bytes_;
+
+ // Contains kKernelOffsetCount kernels back-to-back, each of size kKernelSize.
+ // The kernel offsets are sub-sample shifts of a windowed sinc shifted from
+ // 0.0 to 1.0 sample.
+ scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> kernel_storage_;
+
+ // Data from the source is copied into this buffer for each processing pass.
+ scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> input_buffer_;
+
+ // A queue of buffers to be resampled.
+ std::queue<scoped_refptr<Buffer> > pending_buffers_;
+
+ // The current offset to read when reading from the first pending buffer.
+ int offset_in_frames_;
+
+ // The following two variables are used to calculate EOS and in HasEnoughData.
+ int frames_resampled_;
+ int frames_queued_;
+
+ // Pointers to the various regions inside |input_buffer_|. See the diagram at
+ // the top of the .cc file for more information.
+ float* const r0_;
+ float* const r1_;
+ float* const r2_;
+ float* const r3_;
+ float* const r4_;
+ float* const r5_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterleavedSincResampler);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_INTERLEAVED_SINC_RESAMPLER_H_
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 50e64a0..390abe4 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
@@ -19,6 +19,8 @@
#include "starboard/shared/starboard/media/mime_type.h"
#include "starboard/string.h"
+#if SB_API_VERSION < 4
+
using starboard::shared::starboard::media::MimeType;
namespace {
@@ -86,3 +88,213 @@
#endif // SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
return kSbMediaSupportTypeNotSupported;
}
+
+#else // SB_API_VERSION < 4
+
+#include "starboard/shared/starboard/media/codec_util.h"
+#include "starboard/shared/starboard/media/media_support_internal.h"
+#include "starboard/shared/starboard/media/media_util.h"
+
+using starboard::shared::starboard::media::GetAudioCodecFromString;
+using starboard::shared::starboard::media::GetVideoCodecFromString;
+using starboard::shared::starboard::media::IsAudioOutputSupported;
+using starboard::shared::starboard::media::MimeType;
+using starboard::shared::starboard::media::ParseH264Info;
+
+namespace {
+
+const int64_t kDefaultBitRate = 0;
+const int64_t kDefaultAudioChannels = 2;
+
+// A progressive video contains both audio and video data. The JS app uses
+// canPlayType() like "canPlayType(video/mp4; codecs="avc1.42001E, mp4a.40.2",)"
+// to query for support on both the audio and video codecs, which is being
+// forwarded to here.
+//
+// Note that canPlayType() doesn't support extra parameters like width, height
+// and channels.
+SbMediaSupportType CanPlayProgressiveVideo(const MimeType& mime_type) {
+ const std::vector<std::string>& codecs = mime_type.GetCodecs();
+
+ SB_DCHECK(codecs.size() == 2) << codecs.size();
+
+ bool has_audio_codec = false;
+ bool has_video_codec = false;
+ for (size_t i = 0; i < codecs.size(); ++i) {
+ SbMediaAudioCodec audio_codec = GetAudioCodecFromString(codecs[i].c_str());
+ if (audio_codec != kSbMediaAudioCodecNone) {
+ if (!SbMediaIsAudioSupported(audio_codec, kDefaultBitRate)) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ has_audio_codec = true;
+ continue;
+ }
+ SbMediaVideoCodec video_codec = GetVideoCodecFromString(codecs[i].c_str());
+ if (video_codec == kSbMediaVideoCodecNone) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ has_video_codec = true;
+
+ int width = 0;
+ int height = 0;
+ int fps = 0;
+
+ if (video_codec == kSbMediaVideoCodecH264) {
+ if (!ParseH264Info(codecs[i].c_str(), &width, &height, &fps)) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+ if (!SbMediaIsVideoSupported(video_codec, width, height, kDefaultBitRate,
+ fps)) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+
+ if (has_audio_codec && has_video_codec) {
+ return kSbMediaSupportTypeProbably;
+ }
+
+ return kSbMediaSupportTypeNotSupported;
+}
+
+// Calls to isTypeSupported() are redirected to this function. Following are
+// some example inputs:
+// isTypeSupported(video/webm; codecs="vp9")
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; width=640)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; width=99999)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; height=360)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; height=99999)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; framerate=30)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; framerate=9999)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; bitrate=300000)
+// isTypeSupported(video/mp4; codecs="avc1.4d401e"; bitrate=2000000000)
+// isTypeSupported(audio/mp4; codecs="mp4a.40.2")
+// isTypeSupported(audio/webm; codecs="vorbis")
+// isTypeSupported(video/webm; codecs="vp9")
+// isTypeSupported(video/webm; codecs="vp9")
+// isTypeSupported(audio/webm; codecs="opus")
+// isTypeSupported(audio/mp4; codecs="mp4a.40.2"; channels=2)
+// isTypeSupported(audio/mp4; codecs="mp4a.40.2"; channels=99)
+SbMediaSupportType CanPlayMimeAndKeySystem(const MimeType& mime_type,
+ const char* key_system) {
+ SB_DCHECK(mime_type.is_valid());
+
+ const std::vector<std::string>& codecs = mime_type.GetCodecs();
+
+ // Pre-filter for |key_system|.
+ if (SbStringGetLength(key_system) != 0) {
+ if (!SbMediaIsSupported(kSbMediaVideoCodecNone, kSbMediaAudioCodecNone,
+ key_system)) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+
+ if (codecs.size() == 0) {
+ // When there is no codecs listed, returns |kSbMediaSupportTypeMaybe| and
+ // reject unsupported formats when query again with valid "codecs".
+ return kSbMediaSupportTypeMaybe;
+ }
+
+ if (codecs.size() > 2) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ if (codecs.size() == 2) {
+ SB_DCHECK(SbStringGetLength(key_system) == 0);
+ return CanPlayProgressiveVideo(mime_type);
+ }
+
+ SB_DCHECK(codecs.size() == 1);
+
+ if (mime_type.type() == "audio") {
+ SbMediaAudioCodec audio_codec = GetAudioCodecFromString(codecs[0].c_str());
+ if (audio_codec == kSbMediaAudioCodecNone) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ if (SbStringGetLength(key_system) != 0) {
+ if (!SbMediaIsSupported(kSbMediaVideoCodecNone, audio_codec,
+ key_system)) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+
+ int channels =
+ mime_type.GetParamIntValue("channels", kDefaultAudioChannels);
+ if (!IsAudioOutputSupported(kSbMediaAudioCodingTypePcm, channels)) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ int bitrate = mime_type.GetParamIntValue("bitrate", kDefaultBitRate);
+
+ if (SbMediaIsAudioSupported(audio_codec, bitrate)) {
+ return kSbMediaSupportTypeProbably;
+ }
+
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ if (mime_type.type() == "video") {
+ SbMediaVideoCodec video_codec = GetVideoCodecFromString(codecs[0].c_str());
+ if (video_codec == kSbMediaVideoCodecNone) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ if (SbStringGetLength(key_system) != 0) {
+ if (!SbMediaIsSupported(video_codec, kSbMediaAudioCodecNone,
+ key_system)) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+
+ int width = 0;
+ int height = 0;
+ int fps = 0;
+
+ if (video_codec == kSbMediaVideoCodecH264) {
+ if (!ParseH264Info(codecs[0].c_str(), &width, &height, &fps)) {
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+
+ width = mime_type.GetParamIntValue("width", width);
+ height = mime_type.GetParamIntValue("height", height);
+ fps = mime_type.GetParamIntValue("framerate", fps);
+
+ int bitrate = mime_type.GetParamIntValue("bitrate", kDefaultBitRate);
+
+ if (SbMediaIsVideoSupported(video_codec, width, height, bitrate, fps)) {
+ return kSbMediaSupportTypeProbably;
+ }
+
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ return kSbMediaSupportTypeNotSupported;
+}
+
+} // namespace
+
+SbMediaSupportType SbMediaCanPlayMimeAndKeySystem(const char* mime,
+ const char* key_system) {
+ if (mime == NULL) {
+ SB_DLOG(WARNING) << "mime cannot be NULL";
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ if (key_system == NULL) {
+ SB_DLOG(WARNING) << "key_system cannot be NULL";
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ MimeType mime_type(mime);
+ if (!mime_type.is_valid()) {
+ SB_DLOG(WARNING) << mime << " is not a valid mime type";
+ return kSbMediaSupportTypeNotSupported;
+ }
+
+ return CanPlayMimeAndKeySystem(mime_type, key_system);
+}
+
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc
new file mode 100644
index 0000000..e09901d
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc
@@ -0,0 +1,27 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+ int64_t bitrate) {
+ if (audio_codec != kSbMediaAudioCodecAac &&
+ audio_codec != kSbMediaAudioCodecOpus) {
+ return false;
+ }
+ return bitrate <= SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND;
+}
diff --git a/src/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc
new file mode 100644
index 0000000..996b4d4
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_is_audio_supported_aac_only.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+ int64_t bitrate) {
+ return audio_codec == kSbMediaAudioCodecAac &&
+ bitrate <= SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND;
+}
diff --git a/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_hfr_only.cc b/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_hfr_only.cc
new file mode 100644
index 0000000..a820091
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_hfr_only.cc
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+ int frame_width,
+ int frame_height,
+ int64_t bitrate,
+ int fps) {
+ return video_codec == kSbMediaVideoCodecH264 && frame_width <= 1920 &&
+ frame_height <= 1080 &&
+ bitrate <= SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND && fps <= 60;
+}
diff --git a/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_sfr_only.cc b/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_sfr_only.cc
new file mode 100644
index 0000000..637e835
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_is_video_supported_h264_1080p_sfr_only.cc
@@ -0,0 +1,28 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/configuration.h"
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+ int frame_width,
+ int frame_height,
+ int64_t bitrate,
+ int fps) {
+ return video_codec == kSbMediaVideoCodecH264 && frame_width <= 1920 &&
+ frame_height <= 1080 &&
+ bitrate <= SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND && fps <= 30;
+}
diff --git a/src/starboard/shared/starboard/media/media_support_internal.h b/src/starboard/shared/starboard/media/media_support_internal.h
new file mode 100644
index 0000000..8ad5b21
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_support_internal.h
@@ -0,0 +1,61 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_SUPPORT_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_SUPPORT_INTERNAL_H_
+
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+
+#if SB_API_VERSION < 4
+#error "Incorrect SB_API_VERSION to include media_support_internal.h"
+#endif // SB_API_VERSION < 4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Indicates whether a given combination of (|frame_width| x |frame_height|)
+// frames at |bitrate| and |fps| is supported on this platform with
+// |video_codec|. If |video_codec| is not supported under any condition, this
+// function returns |false|.
+//
+// Setting any of the parameters to |0| indicates that they shouldn't be
+// considered.
+//
+// |video_codec|: The video codec used in the media content.
+// |frame_width|: The frame width of the media content.
+// |frame_height|: The frame height of the media content.
+// |bitrate|: The bitrate of the media content.
+// |fps|: The number of frames per second in the media content.
+SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
+ int frame_width,
+ int frame_height,
+ int64_t bitrate,
+ int fps);
+
+// Indicates whether this platform supports |audio_codec| at |bitrate|.
+// If |audio_codec| is not supported under any condition, this function
+// returns |false|.
+//
+// |audio_codec|: The media's audio codec (|SbMediaAudioCodec|).
+// |bitrate|: The media's bitrate.
+SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
+ int64_t bitrate);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_SUPPORT_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/media/media_util.cc b/src/starboard/shared/starboard/media/media_util.cc
new file mode 100644
index 0000000..b6fd9d7
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_util.cc
@@ -0,0 +1,43 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_util.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+bool IsAudioOutputSupported(SbMediaAudioCodingType coding_type, int channels) {
+ int count = SbMediaGetAudioOutputCount();
+
+ for (int output_index = 0; output_index < count; ++output_index) {
+ SbMediaAudioConfiguration configuration;
+ if (!SbMediaGetAudioConfiguration(output_index, &configuration)) {
+ continue;
+ }
+
+ if (configuration.coding_type == coding_type &&
+ configuration.number_of_channels >= channels) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/media/media_util.h b/src/starboard/shared/starboard/media/media_util.h
new file mode 100644
index 0000000..293a34e
--- /dev/null
+++ b/src/starboard/shared/starboard/media/media_util.h
@@ -0,0 +1,33 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_UTIL_H_
+#define STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_UTIL_H_
+
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+
+bool IsAudioOutputSupported(SbMediaAudioCodingType coding_type, int channels);
+
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_UTIL_H_
diff --git a/src/starboard/shared/starboard/media/mime_type.cc b/src/starboard/shared/starboard/media/mime_type.cc
index 50da87a..4986d87 100644
--- a/src/starboard/shared/starboard/media/mime_type.cc
+++ b/src/starboard/shared/starboard/media/mime_type.cc
@@ -136,12 +136,16 @@
is_valid_ = true;
}
-std::vector<std::string> MimeType::GetCodecs() const {
+const std::vector<std::string>& MimeType::GetCodecs() const {
+ if (!codecs_.empty()) {
+ return codecs_;
+ }
int codecs_index = GetParamIndexByName("codecs");
if (codecs_index != 0) {
- return std::vector<std::string>();
+ return codecs_;
}
- return SplitAndTrim(params_[0].value, ',');
+ codecs_ = SplitAndTrim(params_[0].value, ',');
+ return codecs_;
}
int MimeType::GetParamCount() const {
diff --git a/src/starboard/shared/starboard/media/mime_type.h b/src/starboard/shared/starboard/media/mime_type.h
index 21ac4ef..fc3a2cd 100644
--- a/src/starboard/shared/starboard/media/mime_type.h
+++ b/src/starboard/shared/starboard/media/mime_type.h
@@ -59,7 +59,7 @@
const std::string& type() const { return type_; }
const std::string& subtype() const { return subtype_; }
- std::vector<std::string> GetCodecs() const;
+ const std::vector<std::string>& GetCodecs() const;
int GetParamCount() const;
ParamType GetParamType(int index) const;
@@ -92,6 +92,7 @@
std::string type_;
std::string subtype_;
Params params_;
+ mutable std::vector<std::string> codecs_;
};
} // namespace media
diff --git a/src/starboard/shared/starboard/new.cc b/src/starboard/shared/starboard/new.cc
index 7e08ed0..478a74c 100644
--- a/src/starboard/shared/starboard/new.cc
+++ b/src/starboard/shared/starboard/new.cc
@@ -12,23 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// TODO: I believe this code will have to be linked into all DLLs on
-// Windows. I think Starboard can do this through GYP when the time comes.
-
-#include "starboard/memory.h"
-
-void* operator new(size_t size) {
- return SbMemoryAllocate(size);
-}
-
-void operator delete(void* pointer) {
- SbMemoryDeallocate(pointer);
-}
-
-void* operator new[](size_t size) {
- return SbMemoryAllocate(size);
-}
-
-void operator delete[](void* pointer) {
- SbMemoryDeallocate(pointer);
-}
+// TODO: Remove this file. The code has been moved to starboard/common/new.cc
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
index ac98df9..13b9db4 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.cc
@@ -136,7 +136,7 @@
paused_ = true;
}
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
void AudioRendererImpl::SetPlaybackRate(double playback_rate) {
SB_DCHECK(job_queue_->BelongsToCurrentThread());
@@ -146,7 +146,7 @@
audio_sink_->SetPlaybackRate(playback_rate);
}
}
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
void AudioRendererImpl::Seek(SbMediaTime seek_to_pts) {
SB_DCHECK(job_queue_->BelongsToCurrentThread());
@@ -308,9 +308,9 @@
reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_),
kMaxCachedFrames, &AudioRendererImpl::UpdateSourceStatusFunc,
&AudioRendererImpl::ConsumeFramesFunc, this);
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
audio_sink_->SetPlaybackRate(playback_rate_);
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
}
}
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
index 78dff81..70915f6 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h
@@ -51,9 +51,9 @@
void Play() SB_OVERRIDE;
void Pause() SB_OVERRIDE;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
void SetPlaybackRate(double playback_rate) SB_OVERRIDE;
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
void Seek(SbMediaTime seek_to_pts) SB_OVERRIDE;
bool IsEndOfStreamWritten() const SB_OVERRIDE {
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
index abae295..816c959 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -36,9 +36,9 @@
virtual void WriteEndOfStream() = 0;
virtual void Play() = 0;
virtual void Pause() = 0;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
virtual void SetPlaybackRate(double playback_rate) = 0;
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
virtual void Seek(SbMediaTime seek_to_pts) = 0;
virtual bool IsEndOfStreamWritten() const = 0;
virtual bool IsEndOfStreamPlayed() const = 0;
diff --git a/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc b/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
index 2830d2b..e5c1529 100644
--- a/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/ffmpeg_player_components_impl.cc
@@ -50,8 +50,8 @@
new AudioRendererImpl(audio_parameters.job_queue,
scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
audio_parameters.audio_header);
- VideoRendererImpl* video_renderer =
- new VideoRendererImpl(scoped_ptr<VideoDecoder>(video_decoder).Pass());
+ VideoRendererImpl* video_renderer = new VideoRendererImpl(
+ scoped_ptr<HostedVideoDecoder>(video_decoder).Pass());
return scoped_ptr<PlayerComponents>(
new PlayerComponents(audio_renderer, video_renderer));
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index b27bb8c..9e0bbe4 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -42,14 +42,14 @@
SbMediaAudioCodec audio_codec,
SbDrmSystem drm_system,
const SbMediaAudioHeader& audio_header
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
,
- SbPlayerOutputMode output_mode
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider* provider
+#elif SB_API_VERSION >= 3
,
SbDecodeTargetProvider* provider
-#endif // SB_API_VERSION >= 3
+#endif // SB_API_VERSION >= 4
)
: player_worker_(NULL),
job_queue_(NULL),
@@ -62,14 +62,14 @@
drm_system_(drm_system),
audio_header_(audio_header),
paused_(false)
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
,
- output_mode_(output_mode)
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+ output_mode_(output_mode),
+ decode_target_graphics_context_provider_(provider)
+#elif SB_API_VERSION >= 3
,
decode_target_provider_(provider)
-#endif // SB_API_VERSION >= 3
+#endif // SB_API_VERSION >= 4
{
bounds_ = PlayerWorker::Bounds();
}
@@ -106,14 +106,13 @@
video_codec_,
drm_system_,
job_queue_
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
,
output_mode_,
- decode_target_provider_
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
- }; // NOLINT(whitespace/parens)
+ decode_target_graphics_context_provider_
+#endif // SB_API_VERSION >= 4
+ }; // NOLINT(whitespace/parens)
- ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
scoped_ptr<PlayerComponents> media_components =
PlayerComponents::Create(audio_parameters, video_parameters);
if (!media_components) {
@@ -121,6 +120,7 @@
}
SB_DCHECK(media_components->is_valid());
+ ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
media_components->GetRenderers(&audio_renderer_, &video_renderer_);
update_closure_ = Bind(&FilterBasedPlayerWorkerHandler::Update, this);
@@ -233,14 +233,14 @@
return true;
}
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
bool FilterBasedPlayerWorkerHandler::SetPlaybackRate(double playback_rate) {
SB_DCHECK(job_queue_->BelongsToCurrentThread());
audio_renderer_->SetPlaybackRate(playback_rate);
return true;
}
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
bool FilterBasedPlayerWorkerHandler::SetBounds(
const PlayerWorker::Bounds& bounds) {
@@ -281,9 +281,9 @@
player_worker_->UpdateDroppedVideoFrames(
video_renderer_->GetDroppedFrames());
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
if (output_mode_ == kSbPlayerOutputModePunchOut)
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
{
shared::starboard::Application::Get()->HandleFrame(
player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);
@@ -299,14 +299,21 @@
job_queue_->Remove(update_closure_);
audio_renderer_.reset();
- {
- ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
- video_renderer_.reset();
- }
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+ scoped_ptr<VideoRenderer> video_renderer;
+ {
+ // Set |video_renderer_| to null with the lock, but we actually destroy
+ // it outside of the lock. This is because the VideoRenderer destructor
+ // may post a task to destroy the SbDecodeTarget to the same thread that
+ // might call GetCurrentDecodeTarget(), which would try to take this lock.
+ ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
+ video_renderer = video_renderer_.Pass();
+ }
+ video_renderer.reset();
+
+#if SB_API_VERSION >= 4
if (output_mode_ == kSbPlayerOutputModePunchOut)
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
{
// Clear the video frame as we terminate.
shared::starboard::Application::Get()->HandleFrame(
@@ -314,7 +321,7 @@
}
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget FilterBasedPlayerWorkerHandler::GetCurrentDecodeTarget() {
::starboard::ScopedLock lock(video_renderer_existence_mutex_);
if (video_renderer_) {
@@ -323,7 +330,7 @@
return kSbDecodeTargetInvalid;
}
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
} // namespace filter
} // namespace player
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
index b0bf86a..3024054 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -42,14 +42,14 @@
SbMediaAudioCodec audio_codec,
SbDrmSystem drm_system,
const SbMediaAudioHeader& audio_header
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
,
- SbPlayerOutputMode output_mode
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider* provider
+#elif SB_API_VERSION >= 3
,
SbDecodeTargetProvider* provider
-#endif // SB_API_VERSION >= 3
+#endif // SB_API_VERSION >= 4
); // NOLINT(whitespace/parens)
private:
@@ -63,17 +63,17 @@
bool WriteSample(InputBuffer input_buffer, bool* written) SB_OVERRIDE;
bool WriteEndOfStream(SbMediaType sample_type) SB_OVERRIDE;
bool SetPause(bool pause) SB_OVERRIDE;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
bool SetPlaybackRate(double playback_rate) SB_OVERRIDE;
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
bool SetBounds(const PlayerWorker::Bounds& bounds) SB_OVERRIDE;
void Stop() SB_OVERRIDE;
void Update();
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget GetCurrentDecodeTarget() SB_OVERRIDE;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
PlayerWorker* player_worker_;
JobQueue* job_queue_;
@@ -104,12 +104,13 @@
// accesses are happening from the same thread.
::starboard::Mutex video_renderer_existence_mutex_;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbPlayerOutputMode output_mode_;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider_;
+#elif SB_API_VERSION >= 3
SbDecodeTargetProvider* decode_target_provider_;
-#endif // SB_API_VERSION >= 3
+#endif // SB_API_VERSION >= 4
};
} // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/player_components.h b/src/starboard/shared/starboard/player/filter/player_components.h
index d883751..cf2a60d 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.h
+++ b/src/starboard/shared/starboard/player/filter/player_components.h
@@ -38,10 +38,11 @@
SbMediaVideoCodec video_codec;
SbDrmSystem drm_system;
JobQueue* job_queue;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbPlayerOutputMode output_mode;
- SbDecodeTargetProvider* decode_target_provider;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider;
+#endif // SB_API_VERSION >= 4
};
// All the platform specific components that a
diff --git a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
index 91e09cb..a5cddd7 100644
--- a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
@@ -14,18 +14,155 @@
#include "starboard/shared/starboard/player/filter/player_components.h"
+#include <queue>
+
+#include "starboard/shared/starboard/player/filter/audio_renderer_impl_internal.h"
+#include "starboard/shared/starboard/player/filter/video_renderer_impl_internal.h"
+
namespace starboard {
namespace shared {
namespace starboard {
namespace player {
namespace filter {
+namespace {
+
+SbMediaAudioSampleType GetSupportedSampleType() {
+ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
+ return kSbMediaAudioSampleTypeFloat32;
+ }
+ SB_DCHECK(
+ SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16));
+ return kSbMediaAudioSampleTypeInt16;
+}
+
+} // namespace
+
+class StubAudioDecoder : public AudioDecoder {
+ public:
+ explicit StubAudioDecoder(const SbMediaAudioHeader& audio_header)
+ : sample_type_(GetSupportedSampleType()), audio_header_(audio_header) {}
+ void Decode(const InputBuffer& input_buffer) SB_OVERRIDE {
+ // Values to represent what kind of dummy audio to fill the decoded audio
+ // we produce with.
+ enum FillType {
+ kSilence,
+ kWave,
+ };
+ // Can be set locally to fill with different types.
+ const FillType fill_type = kSilence;
+
+ if (last_input_buffer_.is_valid()) {
+ SbMediaTime diff = input_buffer.pts() - last_input_buffer_.pts();
+ size_t sample_size =
+ GetSampleType() == kSbMediaAudioSampleTypeInt16 ? 2 : 4;
+ size_t size = diff * GetSamplesPerSecond() * sample_size *
+ audio_header_.number_of_channels / kSbMediaTimeSecond;
+ decoded_audios_.push(new DecodedAudio(input_buffer.pts(), size));
+
+ if (fill_type == kSilence) {
+ SbMemorySet(decoded_audios_.back()->buffer(), 0, size);
+ } else {
+ SB_DCHECK(fill_type == kWave);
+ for (int i = 0; i < size / sample_size; ++i) {
+ if (sample_size == 2) {
+ *(reinterpret_cast<int16_t*>(decoded_audios_.back()->buffer()) +
+ i) = i;
+ } else {
+ SB_DCHECK(sample_size == 4);
+ *(reinterpret_cast<float*>(decoded_audios_.back()->buffer()) + i) =
+ ((i % 1024) - 512) / 512.0f;
+ }
+ }
+ }
+ }
+ last_input_buffer_ = input_buffer;
+ }
+ void WriteEndOfStream() SB_OVERRIDE {
+ if (last_input_buffer_.is_valid()) {
+ // There won't be a next pts, so just guess that the decoded size is
+ // 4 times the encoded size.
+ decoded_audios_.push(new DecodedAudio(last_input_buffer_.pts(),
+ 4 * last_input_buffer_.size()));
+ }
+ decoded_audios_.push(new DecodedAudio());
+ }
+ scoped_refptr<DecodedAudio> Read() SB_OVERRIDE {
+ scoped_refptr<DecodedAudio> result;
+ if (!decoded_audios_.empty()) {
+ result = decoded_audios_.front();
+ decoded_audios_.pop();
+ }
+ return result;
+ }
+ void Reset() SB_OVERRIDE {
+ while (!decoded_audios_.empty()) {
+ decoded_audios_.pop();
+ }
+ last_input_buffer_ = InputBuffer();
+ }
+ SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE {
+ return sample_type_;
+ }
+ int GetSamplesPerSecond() const SB_OVERRIDE {
+ return audio_header_.samples_per_second;
+ }
+
+ private:
+ SbMediaAudioSampleType sample_type_;
+ SbMediaAudioHeader audio_header_;
+ std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
+ InputBuffer last_input_buffer_;
+};
+
+class StubVideoDecoder : public HostedVideoDecoder {
+ public:
+ StubVideoDecoder() : host_(NULL) {}
+ void WriteInputBuffer(const InputBuffer& input_buffer) SB_OVERRIDE {
+ SB_DCHECK(host_ != NULL);
+ host_->OnDecoderStatusUpdate(
+ kNeedMoreInput, VideoFrame::CreateEmptyFrame(input_buffer.pts()));
+ }
+ void WriteEndOfStream() SB_OVERRIDE {
+ SB_DCHECK(host_ != NULL);
+ host_->OnDecoderStatusUpdate(kBufferFull, VideoFrame::CreateEOSFrame());
+ }
+ void Reset() SB_OVERRIDE {}
+ void SetHost(Host* host) {
+ SB_DCHECK(host != NULL);
+ SB_DCHECK(host_ == NULL);
+ host_ = host;
+ }
+
+ private:
+ Host* host_;
+};
+
+#if SB_API_VERSION >= 4
+// static
+bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
+ SbMediaVideoCodec codec,
+ SbDrmSystem drm_system) {
+ return output_mode == kSbPlayerOutputModePunchOut;
+}
+#endif // SB_API_VERSION >= 4
+
// static
scoped_ptr<PlayerComponents> PlayerComponents::Create(
const AudioParameters& audio_parameters,
const VideoParameters& video_parameters) {
- // TODO: Implement stubs that do something.
- return scoped_ptr<PlayerComponents>(NULL);
+ StubAudioDecoder* audio_decoder =
+ new StubAudioDecoder(audio_parameters.audio_header);
+ StubVideoDecoder* video_decoder = new StubVideoDecoder();
+ AudioRendererImpl* audio_renderer =
+ new AudioRendererImpl(audio_parameters.job_queue,
+ scoped_ptr<AudioDecoder>(audio_decoder).Pass(),
+ audio_parameters.audio_header);
+ VideoRendererImpl* video_renderer = new VideoRendererImpl(
+ scoped_ptr<HostedVideoDecoder>(video_decoder).Pass());
+
+ return scoped_ptr<PlayerComponents>(
+ new PlayerComponents(audio_renderer, video_renderer));
}
} // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/video_decoder_internal.h b/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
index bd5b5f0..246ee6c 100644
--- a/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
@@ -32,6 +32,45 @@
public:
enum Status { kNeedMoreInput, kBufferFull, kFatalError };
+ virtual ~VideoDecoder() {}
+
+ // Send encoded video frame stored in |input_buffer| to decode.
+ virtual void WriteInputBuffer(const InputBuffer& input_buffer) = 0;
+ // Note that there won't be more input data unless Reset() is called.
+ // OnDecoderStatusUpdate will still be called on Host during flushing until
+ // the |frame| is an EOS frame.
+ virtual void WriteEndOfStream() = 0;
+ // Clear any cached buffer of the codec and reset the state of the codec.
+ // This function will be called during seek to ensure that there is no left
+ // over data from previous buffers. No DecoderStatusFunc call will be made
+ // after this function returns unless WriteInputFrame() or WriteEndOfStream()
+ // is called again.
+ virtual void Reset() = 0;
+
+#if SB_API_VERSION >= 3
+ // May be called from an arbitrary thread (e.g. a renderer thread).
+ virtual SbDecodeTarget GetCurrentDecodeTarget() {
+ return kSbDecodeTargetInvalid;
+ }
+#endif // SB_API_VERSION >= 3
+
+#if SB_API_VERSION >= 4
+ // Individual implementations must implement this function to indicate which
+ // output modes they support.
+ static bool OutputModeSupported(SbPlayerOutputMode output_mode,
+ SbMediaVideoCodec codec,
+ SbDrmSystem drm_system);
+#endif // SB_API_VERSION >= 3
+};
+
+// An extended |VideoDecoder| that is capable of providing |VideoFrame|s to
+// |Host| that owns it. If the platform's video decoder implementation can
+// satisfy this interface, then the default video renderer implementation may
+// be used. If not, then the platform will also likely require a video
+// renderer implementation that is somehow capable of receiving video frames
+// (through |VideoFrame| or possibly some other means).
+class HostedVideoDecoder : public VideoDecoder {
+ public:
class Host {
public:
// |frame| can contain a decoded frame or be NULL when |status| is not
@@ -47,43 +86,7 @@
~Host() {}
};
- virtual ~VideoDecoder() {}
-
virtual void SetHost(Host* host) = 0;
-
- // Send encoded video frame stored in |input_buffer| to decode.
- virtual void WriteInputBuffer(const InputBuffer& input_buffer) = 0;
- // Note that there won't be more input data unless Reset() is called.
- // OnDecoderStatusUpdate will still be called on Host during flushing until
- // the |frame| is an EOS frame.
- virtual void WriteEndOfStream() = 0;
- // Clear any cached buffer of the codec and reset the state of the codec.
- // This function will be called during seek to ensure that there is no left
- // over data from previous buffers. No DecoderStatusFunc call will be made
- // after this function returns unless WriteInputFrame() or WriteEndOfStream()
- // is called again.
- virtual void Reset() = 0;
-
- // In certain cases, a decoder may benefit from knowing the current time of
- // the audio decoder that it is following. This optional function provides
- // the renderer that owns the decoder an opportunity to provide this
- // information to the decoder.
- virtual void SetCurrentTime(SbMediaTime current_time) {}
-
-#if SB_API_VERSION >= 3
- // May be called from an arbitrary thread (e.g. a renderer thread).
- virtual SbDecodeTarget GetCurrentDecodeTarget() {
- return kSbDecodeTargetInvalid;
- }
-#endif // SB_API_VERSION >= 3
-
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
- // Individual implementations must implement this function to indicate which
- // output modes they support.
- static bool OutputModeSupported(SbPlayerOutputMode output_mode,
- SbMediaVideoCodec codec,
- SbDrmSystem drm_system);
-#endif // SB_API_VERSION >= 3
};
} // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc
index 6c09f90..810f5d2 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.cc
@@ -22,7 +22,7 @@
namespace player {
namespace filter {
-VideoRendererImpl::VideoRendererImpl(scoped_ptr<VideoDecoder> decoder)
+VideoRendererImpl::VideoRendererImpl(scoped_ptr<HostedVideoDecoder> decoder)
: seeking_(false),
seeking_to_pts_(0),
end_of_stream_written_(false),
@@ -81,8 +81,6 @@
SbMediaTime media_time) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
- decoder_->SetCurrentTime(media_time);
-
if (frames_.empty()) {
return last_displayed_frame_;
}
@@ -142,7 +140,7 @@
need_more_input_ = (status == VideoDecoder::kNeedMoreInput);
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget VideoRendererImpl::GetCurrentDecodeTarget() {
if (decoder_) {
return decoder_->GetCurrentDecodeTarget();
@@ -150,7 +148,7 @@
return kSbDecodeTargetInvalid;
}
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
} // namespace filter
} // namespace player
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
index c954ef0..e56051d 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_impl_internal.h
@@ -35,10 +35,12 @@
namespace filter {
// A default implementation of |VideoRenderer| that only depends on the
-// |VideoDecoder| interface, rather than a platform specific implementation.
-class VideoRendererImpl : public VideoRenderer {
+// |HostedVideoDecoder| interface, rather than a platform specific
+// implementation.
+class VideoRendererImpl : public VideoRenderer,
+ private HostedVideoDecoder::Host {
public:
- explicit VideoRendererImpl(scoped_ptr<VideoDecoder> decoder);
+ explicit VideoRendererImpl(scoped_ptr<HostedVideoDecoder> decoder);
int GetDroppedFrames() const SB_OVERRIDE { return dropped_frames_; }
@@ -56,9 +58,9 @@
bool CanAcceptMoreData() const SB_OVERRIDE;
bool IsSeekingInProgress() const SB_OVERRIDE;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget GetCurrentDecodeTarget() SB_OVERRIDE;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
private:
typedef std::list<scoped_refptr<VideoFrame> > Frames;
@@ -94,7 +96,7 @@
bool need_more_input_;
int dropped_frames_;
- scoped_ptr<VideoDecoder> decoder_;
+ scoped_ptr<HostedVideoDecoder> decoder_;
};
} // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
index a409eb9..df3beef 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
@@ -31,7 +31,7 @@
namespace player {
namespace filter {
-class VideoRenderer : protected VideoDecoder::Host {
+class VideoRenderer {
public:
virtual ~VideoRenderer() {}
@@ -44,9 +44,9 @@
virtual bool IsEndOfStreamPlayed() const = 0;
virtual bool CanAcceptMoreData() const = 0;
virtual bool IsSeekingInProgress() const = 0;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
virtual SbDecodeTarget GetCurrentDecodeTarget() = 0;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
};
} // namespace filter
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc
index 5b0e64c..5c83020 100644
--- a/src/starboard/shared/starboard/player/player_create.cc
+++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -17,6 +17,9 @@
#include "starboard/configuration.h"
#include "starboard/decode_target.h"
#include "starboard/log.h"
+#if SB_API_VERSION >= 4
+#include "starboard/shared/starboard/media/media_support_internal.h"
+#endif // SB_API_VERSION >= 4
#include "starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h"
#include "starboard/shared/starboard/player/player_internal.h"
#include "starboard/shared/starboard/player/player_worker.h"
@@ -35,24 +38,36 @@
SbPlayerDecoderStatusFunc decoder_status_func,
SbPlayerStatusFunc player_status_func,
void* context
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
,
- SbPlayerOutputMode output_mode
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_API_VERSION >= 3
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider* provider
+#elif SB_API_VERSION >= 3
,
SbDecodeTargetProvider* provider
-#endif // SB_API_VERSION >= 3
+#endif // SB_API_VERSION >= 4
) {
SB_UNREFERENCED_PARAMETER(window);
- if (audio_codec != kSbMediaAudioCodecAac) {
+#if SB_API_VERSION >= 4
+ const int64_t kDefaultBitRate = 0;
+ if (!SbMediaIsAudioSupported(audio_codec, kDefaultBitRate)) {
SB_LOG(ERROR) << "Unsupported audio codec " << audio_codec;
return kSbPlayerInvalid;
}
- if (!audio_header) {
- SB_LOG(ERROR) << "SbPlayerCreate() requires a non-NULL SbMediaAudioHeader";
+ const int kDefaultFrameWidth = 0;
+ const int kDefaultFrameHeight = 0;
+ const int kDefaultFrameRate = 0;
+ if (!SbMediaIsVideoSupported(video_codec, kDefaultFrameWidth,
+ kDefaultFrameHeight, kDefaultBitRate,
+ kDefaultFrameRate)) {
+ SB_LOG(ERROR) << "Unsupported video codec " << video_codec;
+ return kSbPlayerInvalid;
+ }
+#else // SB_API_VERSION >= 4
+ if (audio_codec != kSbMediaAudioCodecAac) {
+ SB_LOG(ERROR) << "Unsupported audio codec " << audio_codec;
return kSbPlayerInvalid;
}
@@ -60,15 +75,21 @@
SB_LOG(ERROR) << "Unsupported video codec " << video_codec;
return kSbPlayerInvalid;
}
+#endif // SB_API_VERSION >= 4
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+ if (!audio_header) {
+ SB_LOG(ERROR) << "SbPlayerCreate() requires a non-NULL SbMediaAudioHeader";
+ return kSbPlayerInvalid;
+ }
+
+#if SB_API_VERSION >= 4
if (!SbPlayerOutputModeSupported(output_mode, video_codec, drm_system)) {
SB_LOG(ERROR) << "Unsupported player output mode " << output_mode;
return kSbPlayerInvalid;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
starboard::scoped_ptr<PlayerWorker::Handler> handler(
new FilterBasedPlayerWorkerHandler(video_codec, audio_codec, drm_system,
*audio_header, output_mode, provider));
@@ -76,11 +97,11 @@
starboard::scoped_ptr<PlayerWorker::Handler> handler(
new FilterBasedPlayerWorkerHandler(video_codec, audio_codec, drm_system,
*audio_header, provider));
-#else // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // SB_API_VERSION >= 4
starboard::scoped_ptr<PlayerWorker::Handler> handler(
new FilterBasedPlayerWorkerHandler(video_codec, audio_codec, drm_system,
*audio_header));
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
return new SbPlayerPrivate(duration_pts, sample_deallocate_func,
decoder_status_func, player_status_func, context,
diff --git a/src/starboard/shared/starboard/player/player_get_current_frame.cc b/src/starboard/shared/starboard/player/player_get_current_frame.cc
index 6fca433..47eb30f 100644
--- a/src/starboard/shared/starboard/player/player_get_current_frame.cc
+++ b/src/starboard/shared/starboard/player/player_get_current_frame.cc
@@ -18,7 +18,7 @@
#include "starboard/log.h"
#include "starboard/shared/starboard/player/player_internal.h"
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget SbPlayerGetCurrentFrame(SbPlayer player) {
if (!SbPlayerIsValid(player)) {
@@ -29,4 +29,4 @@
return player->GetCurrentDecodeTarget();
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index b3c104f..a64fed3 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -44,9 +44,9 @@
frame_width_(0),
frame_height_(0),
is_paused_(true),
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
playback_rate_(1.0),
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
volume_(1.0),
total_video_frames_(0),
dropped_video_frames_(0),
@@ -114,21 +114,21 @@
out_player_info->total_video_frames = total_video_frames_;
out_player_info->dropped_video_frames = dropped_video_frames_;
out_player_info->corrupted_video_frames = 0;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
out_player_info->playback_rate = playback_rate_;
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
}
void SbPlayerPrivate::SetPause(bool pause) {
worker_->SetPause(pause);
}
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
void SbPlayerPrivate::SetPlaybackRate(double playback_rate) {
playback_rate_ = playback_rate;
worker_->SetPlaybackRate(playback_rate);
}
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
void SbPlayerPrivate::SetVolume(double volume) {
SB_NOTIMPLEMENTED();
@@ -148,8 +148,8 @@
dropped_video_frames_ = dropped_video_frames;
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget SbPlayerPrivate::GetCurrentDecodeTarget() {
return worker_->GetCurrentDecodeTarget();
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/starboard/player/player_internal.h b/src/starboard/shared/starboard/player/player_internal.h
index b0c8d27..d04d0f9 100644
--- a/src/starboard/shared/starboard/player/player_internal.h
+++ b/src/starboard/shared/starboard/player/player_internal.h
@@ -49,14 +49,14 @@
void GetInfo(SbPlayerInfo* out_player_info);
void SetPause(bool pause);
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
void SetPlaybackRate(double playback_rate);
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
void SetVolume(double volume);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget GetCurrentDecodeTarget();
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
private:
// PlayerWorker::Host methods.
@@ -74,9 +74,9 @@
int frame_width_;
int frame_height_;
bool is_paused_;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
double playback_rate_;
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
double volume_;
int total_video_frames_;
int dropped_video_frames_;
diff --git a/src/starboard/shared/starboard/player/player_output_mode_supported.cc b/src/starboard/shared/starboard/player/player_output_mode_supported.cc
index 4889ddf..4d34cce 100644
--- a/src/starboard/shared/starboard/player/player_output_mode_supported.cc
+++ b/src/starboard/shared/starboard/player/player_output_mode_supported.cc
@@ -18,7 +18,7 @@
#include "starboard/log.h"
#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
bool SbPlayerOutputModeSupported(SbPlayerOutputMode output_mode,
SbMediaVideoCodec codec,
@@ -27,4 +27,4 @@
OutputModeSupported(output_mode, codec, drm_system);
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/starboard/player/player_set_bounds.cc b/src/starboard/shared/starboard/player/player_set_bounds.cc
index a2275ea..6974370 100644
--- a/src/starboard/shared/starboard/player/player_set_bounds.cc
+++ b/src/starboard/shared/starboard/player/player_set_bounds.cc
@@ -17,10 +17,16 @@
#include "starboard/log.h"
#include "starboard/shared/starboard/player/player_internal.h"
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
-void SbPlayerSetBounds(SbPlayer player, int x, int y, int width, int height) {
+void SbPlayerSetBounds(SbPlayer player,
+#if SB_API_VERSION >= 4
+ int /*z_index*/,
+#endif // SB_API_VERSION >= 4
+ int x,
+ int y,
+ int width,
+ int height) {
if (!SbPlayerIsValid(player)) {
SB_DLOG(WARNING) << "player is invalid.";
return;
@@ -28,5 +34,5 @@
player->SetBounds(x, y, width, height);
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif // SB_API_VERSION >= 4 || \
SB_IS(PLAYER_PUNCHED_OUT)
diff --git a/src/starboard/shared/starboard/player/player_set_pause.cc b/src/starboard/shared/starboard/player/player_set_pause.cc
index 7ce922b..ae5cd95 100644
--- a/src/starboard/shared/starboard/player/player_set_pause.cc
+++ b/src/starboard/shared/starboard/player/player_set_pause.cc
@@ -17,7 +17,7 @@
#include "starboard/log.h"
#include "starboard/shared/starboard/player/player_internal.h"
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
void SbPlayerSetPause(SbPlayer player, bool pause) {
if (!SbPlayerIsValid(player)) {
@@ -27,4 +27,4 @@
player->SetPause(pause);
}
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/starboard/player/player_set_playback_rate.cc b/src/starboard/shared/starboard/player/player_set_playback_rate.cc
index 60abbc6..7928293 100644
--- a/src/starboard/shared/starboard/player/player_set_playback_rate.cc
+++ b/src/starboard/shared/starboard/player/player_set_playback_rate.cc
@@ -17,7 +17,7 @@
#include "starboard/log.h"
#include "starboard/shared/starboard/player/player_internal.h"
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
bool SbPlayerSetPlaybackRate(SbPlayer player, double playback_rate) {
if (!SbPlayerIsValid(player)) {
@@ -33,4 +33,4 @@
return true;
}
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc
index a5fe4d5..5e9f302 100644
--- a/src/starboard/shared/starboard/player/player_worker.cc
+++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -233,15 +233,14 @@
}
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
void PlayerWorker::DoSetBounds(Bounds bounds) {
SB_DCHECK(job_queue_->BelongsToCurrentThread());
if (!handler_->SetBounds(bounds)) {
UpdatePlayerState(kSbPlayerStateError);
}
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif // SB_API_VERSION >= 4 || \
SB_IS(PLAYER_PUNCHED_OUT)
void PlayerWorker::DoSetPause(bool pause) {
@@ -252,7 +251,7 @@
}
}
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
void PlayerWorker::DoSetPlaybackRate(double playback_rate) {
SB_DCHECK(job_queue_->BelongsToCurrentThread());
@@ -260,7 +259,7 @@
UpdatePlayerState(kSbPlayerStateError);
}
}
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
void PlayerWorker::DoStop() {
SB_DCHECK(job_queue_->BelongsToCurrentThread());
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h
index 18149c9..4599be9 100644
--- a/src/starboard/shared/starboard/player/player_worker.h
+++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -78,9 +78,9 @@
virtual bool WriteSample(InputBuffer input_buffer, bool* written) = 0;
virtual bool WriteEndOfStream(SbMediaType sample_type) = 0;
virtual bool SetPause(bool pause) = 0;
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
virtual bool SetPlaybackRate(double playback_rate) = 0;
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
virtual bool SetBounds(const Bounds& bounds) = 0;
// Once this function returns, all processing on the Handler and related
@@ -88,9 +88,9 @@
// after and is no longer safe to access.
virtual void Stop() = 0;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
virtual SbDecodeTarget GetCurrentDecodeTarget() = 0;
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
};
PlayerWorker(Host* host,
@@ -116,19 +116,18 @@
Bind(&PlayerWorker::DoWriteEndOfStream, this, sample_type));
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
void SetBounds(Bounds bounds) {
job_queue_->Schedule(Bind(&PlayerWorker::DoSetBounds, this, bounds));
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif // SB_API_VERSION >= 4 || \
SB_IS(PLAYER_PUNCHED_OUT)
void SetPause(bool pause) {
job_queue_->Schedule(Bind(&PlayerWorker::DoSetPause, this, pause));
}
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
void SetPlaybackRate(double playback_rate) {
// Arbitrary values to give the playback rate a valid range. A particular
// implementation may have stricter or looser requirement, or even only
@@ -145,17 +144,17 @@
job_queue_->Schedule(
Bind(&PlayerWorker::DoSetPlaybackRate, this, playback_rate));
}
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
void UpdateDroppedVideoFrames(int dropped_video_frames) {
host_->UpdateDroppedVideoFrames(dropped_video_frames);
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget GetCurrentDecodeTarget() {
return handler_->GetCurrentDecodeTarget();
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
private:
void UpdateMediaTime(SbMediaTime time);
@@ -170,15 +169,14 @@
void DoWriteSample(InputBuffer input_buffer);
void DoWritePendingSamples();
void DoWriteEndOfStream(SbMediaType sample_type);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
void DoSetBounds(Bounds bounds);
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif // SB_API_VERSION >= 4 || \
SB_IS(PLAYER_PUNCHED_OUT)
void DoSetPause(bool pause);
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
void DoSetPlaybackRate(double rate);
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
void DoStop();
void UpdateDecoderState(SbMediaType type, SbPlayerDecoderState state);
diff --git a/src/starboard/shared/starboard/player/player_write_sample.cc b/src/starboard/shared/starboard/player/player_write_sample.cc
index d796f37..477e434 100644
--- a/src/starboard/shared/starboard/player/player_write_sample.cc
+++ b/src/starboard/shared/starboard/player/player_write_sample.cc
@@ -17,6 +17,43 @@
#include "starboard/log.h"
#include "starboard/shared/starboard/player/player_internal.h"
+#if SB_API_VERSION >= 4
+
+void SbPlayerWriteSample(SbPlayer player,
+ SbMediaType sample_type,
+ const void** sample_buffers,
+ int* sample_buffer_sizes,
+ int number_of_sample_buffers,
+ SbMediaTime sample_pts,
+ const SbMediaVideoSampleInfo* video_sample_info,
+ const SbDrmSampleInfo* sample_drm_info) {
+ if (!SbPlayerIsValid(player)) {
+ SB_DLOG(WARNING) << "player is invalid.";
+ return;
+ }
+
+ if (number_of_sample_buffers != 1) {
+ SB_DLOG(WARNING) << "SbPlayerWriteSample() doesn't support"
+ << " |number_of_sample_buffers| other than one.";
+ return;
+ }
+
+ if (sample_buffers == NULL) {
+ SB_DLOG(WARNING) << "|sample_buffers| cannot be NULL";
+ return;
+ }
+
+ if (sample_buffer_sizes == NULL) {
+ SB_DLOG(WARNING) << "|sample_buffer_sizes| cannot be NULL";
+ return;
+ }
+
+ player->WriteSample(sample_type, sample_buffers[0], sample_buffer_sizes[0],
+ sample_pts, video_sample_info, sample_drm_info);
+}
+
+#else // SB_API_VERSION >= 4
+
void SbPlayerWriteSample(SbPlayer player,
SbMediaType sample_type,
const void* sample_buffer,
@@ -32,3 +69,5 @@
player->WriteSample(sample_type, sample_buffer, sample_buffer_size,
sample_pts, video_sample_info, sample_drm_info);
}
+
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/starboard/player/video_frame_internal.cc b/src/starboard/shared/starboard/player/video_frame_internal.cc
index 69818c1..c000c26 100644
--- a/src/starboard/shared/starboard/player/video_frame_internal.cc
+++ b/src/starboard/shared/starboard/player/video_frame_internal.cc
@@ -231,8 +231,7 @@
// static
scoped_refptr<VideoFrame> VideoFrame::CreateEmptyFrame(SbMediaTime pts) {
- VideoFrame* frame = new VideoFrame;
- frame->format_ = kYV12;
+ VideoFrame* frame = new VideoFrame();
frame->pts_ = pts;
return frame;
}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/starboard/system_request_pause.cc
similarity index 62%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/starboard/system_request_pause.cc
index d335495..98feeb4 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/starboard/system_request_pause.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#include "starboard/shared/starboard/application.h"
+
+#if SB_API_VERSION < 4
+#error "SbSystemRequestPause requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestPause() {
+ starboard::shared::starboard::Application::Get()->Pause(NULL, NULL);
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/starboard/system_request_suspend.cc
similarity index 62%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/starboard/system_request_suspend.cc
index d335495..779ea2d 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/starboard/system_request_suspend.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#include "starboard/shared/starboard/application.h"
+
+#if SB_API_VERSION < 4
+#error "SbSystemRequestSuspend requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestSuspend() {
+ starboard::shared::starboard::Application::Get()->Suspend(NULL, NULL);
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/starboard/system_request_unpause.cc
similarity index 62%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/starboard/system_request_unpause.cc
index d335495..c9baab5 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/starboard/system_request_unpause.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#include "starboard/shared/starboard/application.h"
+
+#if SB_API_VERSION < 4
+#error "SbSystemRequestUnpause requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestUnpause() {
+ starboard::shared::starboard::Application::Get()->Unpause(NULL, NULL);
+}
diff --git a/src/starboard/shared/starboard/thread_local_storage_internal.cc b/src/starboard/shared/starboard/thread_local_storage_internal.cc
index 895d218..df7dd39 100644
--- a/src/starboard/shared/starboard/thread_local_storage_internal.cc
+++ b/src/starboard/shared/starboard/thread_local_storage_internal.cc
@@ -21,6 +21,7 @@
#include <utility>
#include <vector>
+#include "starboard/common/flat_map.h"
#include "starboard/log.h"
#include "starboard/memory.h"
#include "starboard/once.h"
@@ -77,6 +78,107 @@
};
};
+// A map of ThreadId -> int. This map is highly concurrent and allows
+// access of elements without much contention.
+class ConcurrentThreadIdMap {
+ public:
+ ConcurrentThreadIdMap() {
+ // Prime number reduces collisions.
+ static const size_t kNumBuckets = 101;
+ map_vector_.resize(kNumBuckets);
+
+ for (size_t i = 0; i < map_vector_.size(); ++i) {
+ void* memory_block = SbMemoryAllocateNoReport(sizeof(LockedMap));
+ map_vector_[i] = new (memory_block) LockedMap;
+ }
+ }
+
+ ~ConcurrentThreadIdMap() {
+ for (size_t i = 0; i < map_vector_.size(); ++i) {
+ LockedMap* obj = map_vector_[i];
+ obj->~LockedMap();
+ SbMemoryDeallocateNoReport(obj);
+ }
+ }
+
+ bool GetIfExists(SbThreadId key, int* value) const {
+ const LockedMap& map = GetBucket(key);
+ ScopedLock lock(map.mutex_);
+ ThreadIdMap::const_iterator it = map.map_.find(key);
+ if (it != map.map_.end()) {
+ *value = it->second;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void Insert(SbThreadId key, int value) {
+ LockedMap& map = GetBucket(key);
+ ScopedLock lock(map.mutex_);
+ map.map_[key] = value;
+ }
+
+ void Erase(SbThreadId key) {
+ LockedMap& map = GetBucket(key);
+ ScopedLock lock(map.mutex_);
+ map.map_.erase(key);
+ }
+
+ private:
+ typedef std::map<SbThreadId,
+ int,
+ std::less<SbThreadId>,
+ RawAllocator<std::pair<const SbThreadId, int> > >
+ ThreadIdMap;
+
+ struct LockedMap {
+ Mutex mutex_;
+ ThreadIdMap map_;
+ };
+
+ // Simple hashing function for 32 bit numbers.
+ // Based off of Jenkins hash found at this url:
+ // https://gist.github.com/badboy/6267743#file-inthash-md
+ static size_t Hash(SbThreadId id) {
+ static const uint32_t kMagicNum1 = 0x7ed55d16;
+ static const uint32_t kMagicNum2 = 0xc761c23c;
+ static const uint32_t kMagicNum3 = 0x165667b1;
+ static const uint32_t kMagicNum4 = 0xd3a2646c;
+ static const uint32_t kMagicNum5 = 0xfd7046c5;
+ static const uint32_t kMagicNum6 = 0xfd7046c5;
+
+ static const uint32_t kMagicShift1 = 12;
+ static const uint32_t kMagicShift2 = 19;
+ static const uint32_t kMagicShift3 = 5;
+ static const uint32_t kMagicShift4 = 9;
+ static const uint32_t kMagicShift5 = 3;
+ static const uint32_t kMagicShift6 = 16;
+
+ uint32_t key = static_cast<uint32_t>(id);
+ key = (key + kMagicNum1) + (key << kMagicShift1);
+ key = (key ^ kMagicNum2) ^ (key >> kMagicShift2);
+ key = (key + kMagicNum3) + (key << kMagicShift3);
+ key = (key + kMagicNum4) ^ (key << kMagicShift4);
+ key = (key + kMagicNum5) + (key << kMagicShift5);
+ key = (key ^ kMagicNum6) ^ (key >> kMagicShift6);
+ return static_cast<size_t>(key);
+ }
+
+ const LockedMap& GetBucket(SbThreadId key) const {
+ size_t bucket_index = Hash(key) % map_vector_.size();
+ return *map_vector_[bucket_index];
+ }
+
+ LockedMap& GetBucket(SbThreadId key) {
+ size_t bucket_index = Hash(key) % map_vector_.size();
+ return *map_vector_[bucket_index];
+ }
+
+ typedef std::vector<LockedMap*, RawAllocator<LockedMap*> > MapVector;
+ MapVector map_vector_;
+};
+
} // namespace
struct TLSKeyManager::InternalData {
@@ -84,11 +186,7 @@
// any platform using this TLSKeyManager will crash during memory reporting.
typedef std::vector<KeyRecord, RawAllocator<KeyRecord> > VectorKeyRecord;
typedef std::vector<int, RawAllocator<int> > VectorInt;
- typedef std::map<SbThreadId,
- int,
- std::less<SbThreadId>,
- RawAllocator<std::pair<const SbThreadId, int> > >
- ThreadIdMap;
+ typedef ConcurrentThreadIdMap ThreadIdMap;
// Overrides new/delete for InternalData to bypass memory reporting.
static void* operator new(size_t n) { return SbMemoryAllocateNoReport(n); }
@@ -119,7 +217,11 @@
}
SbThreadLocalKey TLSKeyManager::CreateKey(SbThreadLocalDestructor destructor) {
- SbThreadLocalKey key = new SbThreadLocalKeyPrivate();
+ // Allocate key and bypass the the normal allocator. Otherwise there
+ // could be a re-entrant loop that kills the process.
+ void* memory_block =
+ SbMemoryAllocateNoReport(sizeof(SbThreadLocalKeyPrivate));
+ SbThreadLocalKey key = new (memory_block) SbThreadLocalKeyPrivate();
ScopedLock lock(mutex_);
key->index = GetUnusedKeyIndex();
@@ -143,7 +245,8 @@
SB_DCHECK(IsKeyActive(key));
data_->key_table_[key->index].valid = false;
- delete key;
+ key.~SbThreadLocalKey();
+ SbMemoryDeallocateNoReport(key);
}
bool TLSKeyManager::SetLocalValue(SbThreadLocalKey key, void* value) {
@@ -151,13 +254,15 @@
return false;
}
+ int current_thread_id = GetCurrentThreadId();
+
ScopedLock lock(mutex_);
if (!IsKeyActive(key)) {
return false;
}
- data_->key_table_[key->index].values[GetCurrentThreadId()] = value;
+ data_->key_table_[key->index].values[current_thread_id] = value;
return true;
}
@@ -167,20 +272,22 @@
return NULL;
}
+ int current_thread_id = GetCurrentThreadId();
+
ScopedLock lock(mutex_);
if (!IsKeyActive(key)) {
return NULL;
}
- return data_->key_table_[key->index].values[GetCurrentThreadId()];
+ return data_->key_table_[key->index].values[current_thread_id];
}
void TLSKeyManager::InitializeTLSForThread() {
- ScopedLock lock(mutex_);
-
int current_thread_id = GetCurrentThreadId();
+ ScopedLock lock(mutex_);
+
const size_t table_size = data_->key_table_.size();
for (int i = 0; i < table_size; ++i) {
KeyRecord* key_record = &data_->key_table_[i];
@@ -191,10 +298,10 @@
}
void TLSKeyManager::ShutdownTLSForThread() {
- ScopedLock lock(mutex_);
-
int current_thread_id = GetCurrentThreadId();
+ ScopedLock lock(mutex_);
+
// Apply the destructors multiple times (4 is the minimum value
// according to the specifications). This is necessary if one of
// the destructors adds new values to the key map.
@@ -220,7 +327,7 @@
}
}
- data_->thread_id_map_.erase(SbThreadGetId());
+ data_->thread_id_map_.Erase(SbThreadGetId());
data_->available_thread_ids_.push_back(current_thread_id);
}
@@ -243,17 +350,20 @@
}
int TLSKeyManager::GetCurrentThreadId() {
- InternalData::ThreadIdMap::const_iterator found =
- data_->thread_id_map_.find(SbThreadGetId());
- if (found != data_->thread_id_map_.end()) {
- return found->second;
+ const SbThreadId thread_id = SbThreadGetId();
+
+ int value = -1;
+ if (data_->thread_id_map_.GetIfExists(thread_id, &value)) {
+ return value;
}
+ ScopedLock lock(mutex_);
+
SB_DCHECK(!data_->available_thread_ids_.empty());
int thread_tls_id = data_->available_thread_ids_.back();
data_->available_thread_ids_.pop_back();
- data_->thread_id_map_.insert(std::make_pair(SbThreadGetId(), thread_tls_id));
+ data_->thread_id_map_.Insert(thread_id, thread_tls_id);
return thread_tls_id;
}
diff --git a/src/starboard/shared/stub/cryptography_create_transformer.cc b/src/starboard/shared/stub/cryptography_create_transformer.cc
new file mode 100644
index 0000000..502a006
--- /dev/null
+++ b/src/starboard/shared/stub/cryptography_create_transformer.cc
@@ -0,0 +1,40 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+SbCryptographyTransformer SbCryptographyCreateTransformer(
+ const char* algorithm,
+ int block_size_bits,
+ SbCryptographyDirection direction,
+ SbCryptographyBlockCipherMode mode,
+ const void* initialization_vector,
+ int initialization_vector_size,
+ const void* key,
+ int key_size) {
+ SB_UNREFERENCED_PARAMETER(algorithm);
+ SB_UNREFERENCED_PARAMETER(block_size_bits);
+ SB_UNREFERENCED_PARAMETER(direction);
+ SB_UNREFERENCED_PARAMETER(mode);
+ SB_UNREFERENCED_PARAMETER(initialization_vector);
+ SB_UNREFERENCED_PARAMETER(initialization_vector_size);
+ SB_UNREFERENCED_PARAMETER(key);
+ SB_UNREFERENCED_PARAMETER(key_size);
+ return kSbCryptographyInvalidTransformer;
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/cryptography_destroy_transformer.cc
similarity index 62%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/cryptography_destroy_transformer.cc
index d335495..0547106 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/cryptography_destroy_transformer.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+void SbCryptographyDestroyTransformer(SbCryptographyTransformer transformer) {
+ SB_UNREFERENCED_PARAMETER(transformer);
+}
diff --git a/src/starboard/shared/stub/cryptography_get_tag.cc b/src/starboard/shared/stub/cryptography_get_tag.cc
new file mode 100644
index 0000000..5ca1cbf
--- /dev/null
+++ b/src/starboard/shared/stub/cryptography_get_tag.cc
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+bool SbCryptographyGetTag(
+ SbCryptographyTransformer transformer,
+ void* out_tag,
+ int out_tag_size) {
+ SB_UNREFERENCED_PARAMETER(transformer);
+ SB_UNREFERENCED_PARAMETER(out_tag);
+ SB_UNREFERENCED_PARAMETER(out_tag_size);
+ return false;
+}
diff --git a/src/starboard/shared/stub/cryptography_set_authenticated_data.cc b/src/starboard/shared/stub/cryptography_set_authenticated_data.cc
new file mode 100644
index 0000000..93968dd
--- /dev/null
+++ b/src/starboard/shared/stub/cryptography_set_authenticated_data.cc
@@ -0,0 +1,30 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+bool SbCryptographySetAuthenticatedData(
+ SbCryptographyTransformer transformer,
+ const void* data,
+ int data_size) {
+ SB_UNREFERENCED_PARAMETER(transformer);
+ SB_UNREFERENCED_PARAMETER(data);
+ SB_UNREFERENCED_PARAMETER(data_size);
+ return false;
+}
diff --git a/src/starboard/shared/stub/cryptography_set_initialization_vector.cc b/src/starboard/shared/stub/cryptography_set_initialization_vector.cc
new file mode 100644
index 0000000..a070c26
--- /dev/null
+++ b/src/starboard/shared/stub/cryptography_set_initialization_vector.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+void SbCryptographySetInitializationVector(
+ SbCryptographyTransformer transformer,
+ const void* initialization_vector,
+ int initialization_vector_size) {
+ SB_UNREFERENCED_PARAMETER(transformer);
+ SB_UNREFERENCED_PARAMETER(initialization_vector);
+ SB_UNREFERENCED_PARAMETER(initialization_vector_size);
+}
diff --git a/src/starboard/shared/stub/cryptography_transform.cc b/src/starboard/shared/stub/cryptography_transform.cc
new file mode 100644
index 0000000..4488a1f
--- /dev/null
+++ b/src/starboard/shared/stub/cryptography_transform.cc
@@ -0,0 +1,31 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/configuration.h"
+#include "starboard/cryptography.h"
+
+#if SB_API_VERSION < 4
+#error "SbCryptography requires SB_API_VERSION >= 4."
+#endif
+
+int SbCryptographyTransform(SbCryptographyTransformer transformer,
+ const void* in_data,
+ int in_data_size,
+ void* out_data) {
+ SB_UNREFERENCED_PARAMETER(transformer);
+ SB_UNREFERENCED_PARAMETER(in_data);
+ SB_UNREFERENCED_PARAMETER(in_data_size);
+ SB_UNREFERENCED_PARAMETER(out_data);
+ return 0;
+}
diff --git a/src/starboard/shared/stub/decode_target_create_blitter.cc b/src/starboard/shared/stub/decode_target_create_blitter.cc
index 0982fe7..5d807a7 100644
--- a/src/starboard/shared/stub/decode_target_create_blitter.cc
+++ b/src/starboard/shared/stub/decode_target_create_blitter.cc
@@ -14,14 +14,14 @@
#include "starboard/decode_target.h"
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
SbDecodeTarget SbDecodeTargetCreate(SbDecodeTargetFormat /*format*/,
SbBlitterSurface* /*planes*/) {
return kSbDecodeTargetInvalid;
}
-#else // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // SB_API_VERSION < 4
SbDecodeTarget SbDecodeTargetCreate(SbBlitterDevice device,
SbDecodeTargetFormat format,
@@ -30,4 +30,4 @@
return kSbDecodeTargetInvalid;
}
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_create_egl.cc b/src/starboard/shared/stub/decode_target_create_egl.cc
index 17c33c5..82f151f 100644
--- a/src/starboard/shared/stub/decode_target_create_egl.cc
+++ b/src/starboard/shared/stub/decode_target_create_egl.cc
@@ -14,14 +14,14 @@
#include "starboard/decode_target.h"
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
SbDecodeTarget SbDecodeTargetCreate(EGLDisplay /*display*/,
EGLContext /*context*/,
SbDecodeTargetFormat /*format*/,
GLuint* /*planes*/) {
return kSbDecodeTargetInvalid;
}
-#else // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#else // SB_API_VERSION < 4
SbDecodeTarget SbDecodeTargetCreate(void* /*display*/,
void* /*context*/,
SbDecodeTargetFormat /*format*/,
@@ -29,4 +29,4 @@
int /*height*/) {
return kSbDecodeTargetInvalid;
}
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_get_format.cc b/src/starboard/shared/stub/decode_target_get_format.cc
index a64143a..28532f3 100644
--- a/src/starboard/shared/stub/decode_target_get_format.cc
+++ b/src/starboard/shared/stub/decode_target_get_format.cc
@@ -14,10 +14,10 @@
#include "starboard/decode_target.h"
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
SbDecodeTargetFormat SbDecodeTargetGetFormat(SbDecodeTarget /*decode_target*/) {
return kSbDecodeTargetFormatInvalid;
}
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_get_info.cc b/src/starboard/shared/stub/decode_target_get_info.cc
index b37d7e8..c8fe44c 100644
--- a/src/starboard/shared/stub/decode_target_get_info.cc
+++ b/src/starboard/shared/stub/decode_target_get_info.cc
@@ -14,11 +14,11 @@
#include "starboard/decode_target.h"
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
bool SbDecodeTargetGetInfo(SbDecodeTarget /*decode_target*/,
SbDecodeTargetInfo* /*out_info*/) {
return false;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/decode_target_get_plane_blitter.cc b/src/starboard/shared/stub/decode_target_get_plane_blitter.cc
index 70ecf95..eac0538 100644
--- a/src/starboard/shared/stub/decode_target_get_plane_blitter.cc
+++ b/src/starboard/shared/stub/decode_target_get_plane_blitter.cc
@@ -14,11 +14,13 @@
#include "starboard/decode_target.h"
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
+#if SB_HAS(BLITTER)
SbBlitterSurface SbDecodeTargetGetPlane(SbDecodeTarget /*decode_target*/,
SbDecodeTargetPlane /*plane*/) {
return kSbBlitterInvalidSurface;
}
+#endif // SB_HAS(BLITTER)
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_get_plane_egl.cc b/src/starboard/shared/stub/decode_target_get_plane_egl.cc
index 0d01b57..dec9300 100644
--- a/src/starboard/shared/stub/decode_target_get_plane_egl.cc
+++ b/src/starboard/shared/stub/decode_target_get_plane_egl.cc
@@ -14,11 +14,13 @@
#include "starboard/decode_target.h"
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
+#if SB_HAS(GLES2)
GLuint SbDecodeTargetGetPlane(SbDecodeTarget decode_target,
SbDecodeTargetPlane plane) {
return 0;
}
+#endif // SB_HAS(GLES2)
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_get_size.cc b/src/starboard/shared/stub/decode_target_get_size.cc
index 9e82f71..09ee7a5 100644
--- a/src/starboard/shared/stub/decode_target_get_size.cc
+++ b/src/starboard/shared/stub/decode_target_get_size.cc
@@ -14,7 +14,7 @@
#include "starboard/decode_target.h"
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
void SbDecodeTargetGetSize(SbDecodeTarget decode_target,
int* out_width,
@@ -24,4 +24,4 @@
SB_UNREFERENCED_PARAMETER(out_height);
}
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/decode_target_is_opaque.cc b/src/starboard/shared/stub/decode_target_is_opaque.cc
index 5cb3596..3759b36 100644
--- a/src/starboard/shared/stub/decode_target_is_opaque.cc
+++ b/src/starboard/shared/stub/decode_target_is_opaque.cc
@@ -15,7 +15,7 @@
#include "starboard/configuration.h"
#include "starboard/decode_target.h"
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
#if !(SB_VERSION(3) && SB_HAS(GRAPHICS))
#error "SbDecodeTargetIsOpaque requires SB_VERSION(3) and SB_HAS(GRAPHICS)."
@@ -25,4 +25,4 @@
return false;
}
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/drm_generate_session_update_request.cc b/src/starboard/shared/stub/drm_generate_session_update_request.cc
index 2837255..5d14858 100644
--- a/src/starboard/shared/stub/drm_generate_session_update_request.cc
+++ b/src/starboard/shared/stub/drm_generate_session_update_request.cc
@@ -15,6 +15,9 @@
#include "starboard/drm.h"
void SbDrmGenerateSessionUpdateRequest(SbDrmSystem drm_system,
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
const char* type,
const void* initialization_data,
int initialization_data_size) {
diff --git a/src/starboard/shared/stub/drm_update_session.cc b/src/starboard/shared/stub/drm_update_session.cc
index 67a9d44..7f0d11f 100644
--- a/src/starboard/shared/stub/drm_update_session.cc
+++ b/src/starboard/shared/stub/drm_update_session.cc
@@ -15,6 +15,9 @@
#include "starboard/drm.h"
void SbDrmUpdateSession(SbDrmSystem drm_system,
+#if SB_API_VERSION >= 4
+ int ticket,
+#endif // SB_API_VERSION >= 4
const void* key,
int key_size,
const void* session_id,
diff --git a/src/starboard/shared/stub/image_decode.cc b/src/starboard/shared/stub/image_decode.cc
index ca1ff97..3072b96 100644
--- a/src/starboard/shared/stub/image_decode.cc
+++ b/src/starboard/shared/stub/image_decode.cc
@@ -19,6 +19,15 @@
#error "SbImageDecode requires SB_VERSION(3) and SB_HAS(GRAPHICS)."
#endif
+#if SB_API_VERSION >= 4
+SbDecodeTarget SbImageDecode(SbDecodeTargetGraphicsContextProvider* provider,
+ void* data,
+ int data_size,
+ const char* mime_type,
+ SbDecodeTargetFormat format) {
+ return kSbDecodeTargetInvalid;
+}
+#else // SB_API_VERSION >= 4
SbDecodeTarget SbImageDecode(SbDecodeTargetProvider* provider,
void* data,
int data_size,
@@ -26,3 +35,4 @@
SbDecodeTargetFormat format) {
return kSbDecodeTargetInvalid;
}
+#endif // SB_API_VERSION >= 4
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/media_is_audio_supported.cc
similarity index 64%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/media_is_audio_supported.cc
index d335495..32d7b61 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/media_is_audio_supported.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/shared/starboard/media/media_support_internal.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec /*audio_codec*/,
+ int64_t /*bitrate*/) {
+ return false;
+}
diff --git a/src/starboard/shared/stub/media_is_video_supported.cc b/src/starboard/shared/stub/media_is_video_supported.cc
new file mode 100644
index 0000000..ebe95da
--- /dev/null
+++ b/src/starboard/shared/stub/media_is_video_supported.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/media/media_support_internal.h"
+
+#include "starboard/media.h"
+
+SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec /*video_codec*/,
+ int /*frame_width*/,
+ int /*frame_height*/,
+ int64_t /*bitrate*/,
+ int /*fps*/) {
+ return false;
+}
diff --git a/src/starboard/shared/stub/player_create.cc b/src/starboard/shared/stub/player_create.cc
index de2ad10..7d5eb89 100644
--- a/src/starboard/shared/stub/player_create.cc
+++ b/src/starboard/shared/stub/player_create.cc
@@ -28,11 +28,11 @@
SbPlayerDecoderStatusFunc /*decoder_status_func*/,
SbPlayerStatusFunc /*player_status_func*/,
void* /*context*/
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
,
- SbPlayerOutputMode output_mode
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
-#if SB_VERSION(3)
+ SbPlayerOutputMode output_mode,
+ SbDecodeTargetGraphicsContextProvider* /*provider*/
+#elif SB_VERSION(3)
,
SbDecodeTargetProvider* /*provider*/
#endif
diff --git a/src/starboard/shared/stub/player_get_current_frame.cc b/src/starboard/shared/stub/player_get_current_frame.cc
index ffec4f0..917e0f3 100644
--- a/src/starboard/shared/stub/player_get_current_frame.cc
+++ b/src/starboard/shared/stub/player_get_current_frame.cc
@@ -14,10 +14,10 @@
#include "starboard/player.h"
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
SbDecodeTarget SbPlayerGetCurrentFrame(SbPlayer /*player*/) {
return kSbDecodeTargetInvalid;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/player_output_mode_supported.cc b/src/starboard/shared/stub/player_output_mode_supported.cc
index 1ac4b8c..437d5e6 100644
--- a/src/starboard/shared/stub/player_output_mode_supported.cc
+++ b/src/starboard/shared/stub/player_output_mode_supported.cc
@@ -14,7 +14,7 @@
#include "starboard/player.h"
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
bool SbPlayerOutputModeSupported(SbPlayerOutputMode /*output_mode*/,
SbMediaVideoCodec /*codec*/,
@@ -22,4 +22,4 @@
return false;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/player_set_bounds.cc b/src/starboard/shared/stub/player_set_bounds.cc
index a1deeca..5fbee67 100644
--- a/src/starboard/shared/stub/player_set_bounds.cc
+++ b/src/starboard/shared/stub/player_set_bounds.cc
@@ -14,14 +14,16 @@
#include "starboard/player.h"
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
void SbPlayerSetBounds(SbPlayer /*player*/,
+#if SB_API_VERSION >= 4
+ int /*z_index*/,
+#endif // SB_API_VERSION >= 4
int /*x*/,
int /*y*/,
int /*width*/,
- int /*height*/) {}
+ int /*height*/) {
+}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#endif // SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
diff --git a/src/starboard/shared/stub/player_set_pause.cc b/src/starboard/shared/stub/player_set_pause.cc
index 496624c..c2f4d60 100644
--- a/src/starboard/shared/stub/player_set_pause.cc
+++ b/src/starboard/shared/stub/player_set_pause.cc
@@ -14,8 +14,8 @@
#include "starboard/player.h"
-#if SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION < 4
void SbPlayerSetPause(SbPlayer /*player*/, bool /*pause*/) {}
-#endif // SB_API_VERSION < SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION < 4
diff --git a/src/starboard/shared/stub/player_set_playback_rate.cc b/src/starboard/shared/stub/player_set_playback_rate.cc
index d9fc358..c9103aa 100644
--- a/src/starboard/shared/stub/player_set_playback_rate.cc
+++ b/src/starboard/shared/stub/player_set_playback_rate.cc
@@ -14,10 +14,10 @@
#include "starboard/player.h"
-#if SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#if SB_API_VERSION >= 4
bool SbPlayerSetPlaybackRate(SbPlayer /*player*/, double /*playback_rate*/) {
return false;
}
-#endif // SB_API_VERSION >= SB_PLAYER_SET_PLAYBACK_RATE_VERSION
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/player_write_sample.cc b/src/starboard/shared/stub/player_write_sample.cc
index b1fd839..1fd2d52 100644
--- a/src/starboard/shared/stub/player_write_sample.cc
+++ b/src/starboard/shared/stub/player_write_sample.cc
@@ -14,6 +14,19 @@
#include "starboard/player.h"
+#if SB_API_VERSION >= 4
+
+void SbPlayerWriteSample(SbPlayer /*player*/,
+ SbMediaType /*sample_type*/,
+ const void** /*sample_buffers*/,
+ int* /*sample_buffer_sizes*/,
+ int /*number_of_sample_buffers*/,
+ SbMediaTime /*sample_pts*/,
+ const SbMediaVideoSampleInfo* /*video_sample_info*/,
+ const SbDrmSampleInfo* /*sample_drm_info*/) {}
+
+#else // SB_API_VERSION >= 4
+
void SbPlayerWriteSample(SbPlayer /*player*/,
SbMediaType /*sample_type*/,
const void* /*sample_buffer*/,
@@ -21,3 +34,5 @@
SbMediaTime /*sample_pts*/,
const SbMediaVideoSampleInfo* /*video_sample_info*/,
const SbDrmSampleInfo* /*sample_drm_info*/) {}
+
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/socket_get_interface_address.cc b/src/starboard/shared/stub/socket_get_interface_address.cc
new file mode 100644
index 0000000..c31cd84
--- /dev/null
+++ b/src/starboard/shared/stub/socket_get_interface_address.cc
@@ -0,0 +1,25 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/socket.h"
+
+#if SB_API_VERSION >= 4
+
+bool SbSocketGetInterfaceAddress(const SbSocketAddress* const /*destination*/,
+ SbSocketAddress* /*out_source_address*/,
+ SbSocketAddress* /*out_netmask*/) {
+ return false;
+}
+
+#endif // SB_API_VERSION >= 4
diff --git a/src/starboard/shared/stub/socket_get_local_interface_address.cc b/src/starboard/shared/stub/socket_get_local_interface_address.cc
index 91cc212..17e4ab5 100644
--- a/src/starboard/shared/stub/socket_get_local_interface_address.cc
+++ b/src/starboard/shared/stub/socket_get_local_interface_address.cc
@@ -14,6 +14,10 @@
#include "starboard/socket.h"
+#if SB_API_VERSION < 4
+
bool SbSocketGetLocalInterfaceAddress(SbSocketAddress* /*out_address*/) {
return false;
}
+
+#endif // SB_API_VERSION < 4
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/speech_recognizer_cancel.cc
similarity index 60%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/speech_recognizer_cancel.cc
index d335495..a7c263a 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/speech_recognizer_cancel.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/speech_recognizer.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+ SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+void SbSpeechRecognizerCancel(SbSpeechRecognizer /*recognizer*/) {}
+
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+ // SB_SPEECH_RECOGNIZER_API_VERSION
diff --git a/src/starboard/shared/stub/speech_recognizer_create.cc b/src/starboard/shared/stub/speech_recognizer_create.cc
new file mode 100644
index 0000000..5acdf3a
--- /dev/null
+++ b/src/starboard/shared/stub/speech_recognizer_create.cc
@@ -0,0 +1,26 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/speech_recognizer.h"
+
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+ SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+SbSpeechRecognizer SbSpeechRecognizerCreate(
+ const SbSpeechRecognizerHandler* /*handler*/) {
+ return kSbSpeechRecognizerInvalid;
+}
+
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+ // SB_SPEECH_RECOGNIZER_API_VERSION
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/speech_recognizer_destroy.cc
similarity index 60%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/speech_recognizer_destroy.cc
index d335495..090417c 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/speech_recognizer_destroy.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/speech_recognizer.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+ SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+void SbSpeechRecognizerDestroy(SbSpeechRecognizer /*recognizer*/) {}
+
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+ // SB_SPEECH_RECOGNIZER_API_VERSION
diff --git a/src/starboard/shared/stub/speech_recognizer_start.cc b/src/starboard/shared/stub/speech_recognizer_start.cc
new file mode 100644
index 0000000..fc2d43d
--- /dev/null
+++ b/src/starboard/shared/stub/speech_recognizer_start.cc
@@ -0,0 +1,26 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/speech_recognizer.h"
+
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+ SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+bool SbSpeechRecognizerStart(SbSpeechRecognizer /*recognizer*/,
+ const SbSpeechConfiguration* /*configuration*/) {
+ return false;
+}
+
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+ // SB_SPEECH_RECOGNIZER_API_VERSION
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/speech_recognizer_stop.cc
similarity index 60%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/speech_recognizer_stop.cc
index d335495..b799661 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/speech_recognizer_stop.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/speech_recognizer.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+ SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+void SbSpeechRecognizerStop(SbSpeechRecognizer /*recognizer*/) {}
+
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+ // SB_SPEECH_RECOGNIZER_API_VERSION
diff --git a/src/starboard/shared/stub/speech_synthesis_set_language.cc b/src/starboard/shared/stub/speech_synthesis_set_language.cc
index 8e7d758..6cc1b33 100644
--- a/src/starboard/shared/stub/speech_synthesis_set_language.cc
+++ b/src/starboard/shared/stub/speech_synthesis_set_language.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION < 4
#include "starboard/speech_synthesis.h"
diff --git a/src/starboard/shared/stub/system_break_into_debugger.cc b/src/starboard/shared/stub/system_break_into_debugger.cc
index 773e271..59d081e 100644
--- a/src/starboard/shared/stub/system_break_into_debugger.cc
+++ b/src/starboard/shared/stub/system_break_into_debugger.cc
@@ -15,4 +15,5 @@
#include "starboard/system.h"
void SbSystemBreakIntoDebugger() {
+ __builtin_unreachable();
}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/system_request_pause.cc
similarity index 71%
rename from src/cobalt/base/math.cc
rename to src/starboard/shared/stub/system_request_pause.cc
index d335495..618cfb8 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/system_request_pause.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#if SB_API_VERSION < 4
+#error "SbSystemRequestPause requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestPause() {
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/system_request_suspend.cc
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/system_request_suspend.cc
index d335495..f9e28b0 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/system_request_suspend.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#if SB_API_VERSION < 4
+#error "SbSystemRequestSuspend requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestSuspend() {
+}
diff --git a/src/cobalt/base/math.cc b/src/starboard/shared/stub/system_request_unpause.cc
similarity index 70%
copy from src/cobalt/base/math.cc
copy to src/starboard/shared/stub/system_request_unpause.cc
index d335495..b3820dc 100644
--- a/src/cobalt/base/math.cc
+++ b/src/starboard/shared/stub/system_request_unpause.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/base/math.h"
+#include "starboard/system.h"
-#if defined(COMPILER_MSVC)
-float roundf(float x) { return x < 0.0f ? ceilf(x - 0.5f) : floorf(x + 0.5f); }
-#endif // defined(COMPILER_MSVC)
+#if SB_API_VERSION < 4
+#error "SbSystemRequestUnpause requires SB_API_VERSION >= 4."
+#endif
+
+void SbSystemRequestUnpause() {
+}
diff --git a/src/starboard/shared/x11/window_get_platform_handle.cc b/src/starboard/shared/x11/window_get_platform_handle.cc
index f7af64f..267c9bd 100644
--- a/src/starboard/shared/x11/window_get_platform_handle.cc
+++ b/src/starboard/shared/x11/window_get_platform_handle.cc
@@ -23,8 +23,7 @@
}
Window handle = None;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
handle = window->gl_window;
#else
handle = window->window;
diff --git a/src/starboard/shared/x11/window_internal.cc b/src/starboard/shared/x11/window_internal.cc
index 3294543..8235d61 100644
--- a/src/starboard/shared/x11/window_internal.cc
+++ b/src/starboard/shared/x11/window_internal.cc
@@ -26,11 +26,10 @@
#include "starboard/log.h"
#include "starboard/player.h"
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xrender.h>
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION ||
+#endif // SB_API_VERSION >= 4 ||
// SB_IS(PLAYER_PUNCHED_OUT)
namespace {
@@ -38,22 +37,20 @@
const int kWindowWidth = 1920;
const int kWindowHeight = 1080;
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
bool HasNeededExtensionsForPunchedOutVideo(Display* display) {
int dummy;
return (XRenderQueryExtension(display, &dummy, &dummy) &&
XCompositeQueryExtension(display, &dummy, &dummy));
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION ||
+#endif // SB_API_VERSION >= 4 ||
// SB_IS(PLAYER_PUNCHED_OUT)
} // namespace
SbWindowPrivate::SbWindowPrivate(Display* display,
const SbWindowOptions* options)
: window(None)
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
,
window_picture(None),
composition_pixmap(None),
@@ -65,7 +62,7 @@
video_picture(None),
gl_window(None),
gl_picture(None)
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION ||
+#endif // SB_API_VERSION >= 4 ||
// SB_IS(PLAYER_PUNCHED_OUT)
,
display(display) {
@@ -107,8 +104,7 @@
Atom wm_delete = XInternAtom(display, "WM_DELETE_WINDOW", True);
XSetWMProtocols(display, window, &wm_delete, 1);
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
SB_CHECK(HasNeededExtensionsForPunchedOutVideo(display));
gl_window = XCreateSimpleWindow(display, window, 0, 0, width, height, 0,
WhitePixel(display, DefaultScreen(display)),
@@ -132,15 +128,14 @@
XRenderFindVisualFormat(display, x_visual_info.visual);
window_picture = XRenderCreatePicture(display, window, pict_format, 0, NULL);
SB_CHECK(window_picture != None);
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION ||
+#endif // SB_API_VERSION >= 4 ||
// SB_IS(PLAYER_PUNCHED_OUT)
XMapWindow(display, window);
}
SbWindowPrivate::~SbWindowPrivate() {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
if (composition_pixmap != None) {
XFreePixmap(display, composition_pixmap);
XRenderFreePicture(display, composition_picture);
@@ -153,13 +148,12 @@
XRenderFreePicture(display, gl_picture);
XDestroyWindow(display, gl_window);
XRenderFreePicture(display, window_picture);
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif // SB_API_VERSION >= 4 || \
SB_IS(PLAYER_PUNCHED_OUT)
XDestroyWindow(display, window);
}
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
- SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
void SbWindowPrivate::Composite(
int bounds_x,
int bounds_y,
@@ -264,21 +258,21 @@
int dest_x = bounds_x + (bounds_width - video_width) / 2;
int dest_y = bounds_y + (bounds_height - video_height) / 2;
- XRenderComposite(display, PictOpSrc, video_picture, NULL,
+ XRenderComposite(display, PictOpSrc, video_picture, None,
composition_picture, 0, 0, 0, 0, dest_x, dest_y,
video_width, video_height);
}
// Composite (with blending) the GL output on top of the composition pixmap
// that already has the current video frame if video is playing.
- XRenderComposite(display, PictOpOver, gl_picture, NULL, composition_picture,
+ XRenderComposite(display, PictOpOver, gl_picture, None, composition_picture,
0, 0, 0, 0, 0, 0, width, height);
// Now that we have a fully-composited frame in composition_pixmap, render it
// to the window, which acts as our front buffer.
- XRenderComposite(display, PictOpSrc, composition_picture, NULL,
+ XRenderComposite(display, PictOpSrc, composition_picture, None,
window_picture, 0, 0, 0, 0, 0, 0, width, height);
XSynchronize(display, False);
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION || \
+#endif // SB_API_VERSION >= 4 || \
SB_IS(PLAYER_PUNCHED_OUT)
diff --git a/src/starboard/shared/x11/window_internal.h b/src/starboard/shared/x11/window_internal.h
index fd170fd..3c96af9 100644
--- a/src/starboard/shared/x11/window_internal.h
+++ b/src/starboard/shared/x11/window_internal.h
@@ -22,9 +22,9 @@
#include "starboard/shared/starboard/player/video_frame_internal.h"
#include "starboard/window.h"
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION || SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
#include <X11/extensions/Xrender.h>
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION ||
+#endif // SB_API_VERSION >= 4 ||
// SB_IS(PLAYER_PUNCHED_OUT)
struct SbWindowPrivate {
@@ -33,7 +33,7 @@
Window window;
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION || SB_IS(PLAYER_PUNCHED_OUT)
+#if SB_API_VERSION >= 4 || SB_IS(PLAYER_PUNCHED_OUT)
typedef ::starboard::shared::starboard::player::VideoFrame VideoFrame;
// Composites graphics and the given video frame video for this window. In
@@ -80,7 +80,7 @@
// A cached XRender Picture wrapper for |gl_window|.
Picture gl_picture;
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION ||
+#endif // SB_API_VERSION >= 4 ||
// SB_IS(PLAYER_PUNCHED_OUT)
// The display that this window was created from.
diff --git a/src/starboard/socket.h b/src/starboard/socket.h
index d2893ba..a9f403c 100644
--- a/src/starboard/socket.h
+++ b/src/starboard/socket.h
@@ -227,6 +227,57 @@
SB_EXPORT bool SbSocketGetLocalAddress(SbSocket socket,
SbSocketAddress* out_address);
+#if SB_API_VERSION >= 4
+// Gets the source address and the netmask that would be used to connect to the
+// destination. The netmask parameter is optional, and only populated if a
+// non-NULL parameter is passed in. To determine which source IP will be used,
+// the kernel takes into account the protocol, routes, destination
+// ip, etc. The subnet mask, aka netmask, is used to find the routing prefix.
+// In IPv6, this should be derived from the prefix value.
+//
+// Returns whether it was possible to determine the source address and the
+// netmask (if non-NULL value is passed) to be used to connect to the
+// destination. This function could fail if the destination is not reachable,
+// if it an invalid address, etc.
+//
+// |destination|: The destination IP to be connected to. If IP addresses is not
+// 0.0.0.0 or ::, then temporary addresses may be returned.
+//
+// If the destination address is 0.0.0.0, and its |type| is
+// |kSbSocketAddressTypeIpv4|, then any IPv4 local interface that is up and not
+// a loopback interface is a valid return value.
+//
+// If the destination address is ::, and its |type| is
+// |kSbSocketAddressTypeIpv6| then any IPv6 local interface that is up and not
+// loopback or a link-local IP is a valid return value. However, in the case of
+// IPv6, the address with the biggest scope must be returned. E.g., a globally
+// scoped and routable IP is prefered over a unique local address (ULA). Also,
+// the IP address that is returned must be permanent.
+//
+// If destination address is NULL, then any IP address that is valid for
+// |destination| set to 0.0.0.0 (IPv4) or :: (IPv6) can be returned.
+//
+// |out_source_address|: This function places the address of the local interface
+// in this output variable.
+// |out_netmask|: This parameter is optional. If a non-NULL value is passed in,
+// this function places the netmask associated with the source address in this
+// output variable.
+SB_EXPORT bool SbSocketGetInterfaceAddress(
+ const SbSocketAddress* const destination,
+ SbSocketAddress* out_source_address,
+ SbSocketAddress* out_netmask);
+
+#else
+
+// Gets the address of the local IPv4 network interface. The return value
+// indicates whether the address was retrieved successfully.
+//
+// |out_address|: The retrieved address. The address does not include loopback
+// (or IPv6) addresses.
+SB_EXPORT bool SbSocketGetLocalInterfaceAddress(SbSocketAddress* out_address);
+
+#endif // SB_API_VERSION >= 4
+
// Reads up to |data_size| bytes from |socket| into |out_data| and places the
// source address of the packet in |out_source| if out_source is not NULL.
// Returns the number of bytes read, or a negative number if there is an error,
@@ -355,13 +406,6 @@
SB_EXPORT bool SbSocketJoinMulticastGroup(SbSocket socket,
const SbSocketAddress* address);
-// Gets the address of the local IPv4 network interface. The return value
-// indicates whether the address was retrieved successfully.
-//
-// |out_address|: The retrieved address. The address does not include loopback
-// (or IPv6) addresses.
-SB_EXPORT bool SbSocketGetLocalInterfaceAddress(SbSocketAddress* out_address);
-
// Synchronously resolves |hostname| into the returned SbSocketResolution,
// which must be freed with SbSocketFreeResolution. The function returns
// |NULL| if it is unable to resolve |hostname|.
@@ -391,11 +435,28 @@
if (address.type == kSbSocketAddressTypeIpv6) {
os << std::hex << "[";
const uint16_t* fields = reinterpret_cast<const uint16_t*>(address.address);
- for (int i = 0; i < 8; ++i) {
+ int i = 0;
+ while (fields[i] == 0) {
+ if (i == 0) {
+ os << ":";
+ }
+ ++i;
+ if (i == 8) {
+ os << ":";
+ }
+ }
+ for (; i < 8; ++i) {
if (i != 0) {
os << ":";
}
+#if SB_IS(LITTLE_ENDIAN)
+ const uint8_t* fields8 = reinterpret_cast<const uint8_t*>(&fields[i]);
+ const uint16_t value = static_cast<uint16_t>(fields8[0]) * 256 |
+ static_cast<uint16_t>(fields8[1]);
+ os << value;
+#else
os << fields[i];
+#endif
}
os << "]" << std::dec;
} else {
diff --git a/src/starboard/speech_recognizer.h b/src/starboard/speech_recognizer.h
new file mode 100644
index 0000000..48af10c
--- /dev/null
+++ b/src/starboard/speech_recognizer.h
@@ -0,0 +1,201 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Module Overview: Starboard speech recognizer module
+//
+// Defines a streaming speech recognizer API. It provides access to the platform
+// speech recognition service.
+//
+// Note that there can be only one speech recognizer. Attempting to create a
+// second speech recognizer without destroying the first one will result in
+// undefined behavior.
+//
+// |SbSpeechRecognizerCreate|, |SbSpeechRecognizerStart|,
+// |SbSpeechRecognizerStop|, |SbSpeechRecognizerCancel| and
+// |SbSpeechRecognizerDestroy| should be called from a single thread. Callbacks
+// defined in |SbSpeechRecognizerHandler| will happen on another thread, so
+// calls back into the SbSpeechRecognizer from the callback thread are
+// disallowed.
+
+#ifndef STARBOARD_SPEECH_RECOGNIZER_H_
+#define STARBOARD_SPEECH_RECOGNIZER_H_
+
+#include "starboard/configuration.h"
+#include "starboard/export.h"
+#include "starboard/types.h"
+
+#if SB_HAS(SPEECH_RECOGNIZER) && \
+ SB_API_VERSION >= SB_SPEECH_RECOGNIZER_API_VERSION
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// An opaque handle to an implementation-private structure that represents a
+// speech recognizer.
+typedef struct SbSpeechRecognizerPrivate* SbSpeechRecognizer;
+
+// Well-defined value for an invalid speech recognizer handle.
+#define kSbSpeechRecognizerInvalid ((SbSpeechRecognizer)NULL)
+
+// Indicates whether the given speech recognizer is valid.
+static SB_C_INLINE bool SbSpeechRecognizerIsValid(
+ SbSpeechRecognizer recognizer) {
+ return recognizer != kSbSpeechRecognizerInvalid;
+}
+
+// Indicates what has gone wrong with the recognition.
+typedef enum SbSpeechRecognizerError {
+ // No speech was detected. Speech timed out.
+ kSbNoSpeechError,
+ // Speech input was aborted somehow.
+ kSbAborted,
+ // Audio capture failed.
+ kSbAudioCaptureError,
+ // Some network communication that was required to complete the recognition
+ // failed.
+ kSbNetworkError,
+ // The implementation is not allowing any speech input to occur for reasons of
+ // security, privacy or user preference.
+ kSbNotAllowed,
+ // The implementation is not allowing the application requested speech
+ // service, but would allow some speech service, to be used either because the
+ // implementation doesn't support the selected one or because of reasons of
+ // security, privacy or user preference.
+ kSbServiceNotAllowed,
+ // There was an error in the speech recognition grammar or semantic tags, or
+ // the grammar format or semantic tag format is supported.
+ kSbBadGrammar,
+ // The language was not supported.
+ kSbLanguageNotSupported,
+} SbSpeechRecognizerError;
+
+// The recognition response that is received from the recognizer.
+typedef struct SbSpeechResult {
+ // The raw words that the user spoke.
+ char* transcript;
+ // A numeric estimate between 0 and 1 of how confident the recognition system
+ // is that the recognition is correct. A higher number means the system is
+ // more confident. NaN represents an unavailable confidence score.
+ float confidence;
+} SbSpeechResult;
+
+typedef struct SbSpeechConfiguration {
+ // When the continuous value is set to false, the implementation MUST
+ // return no more than one final result in response to starting recognition.
+ // When the continuous attribute is set to true, the implementation MUST
+ // return zero or more final results representing multiple consecutive
+ // recognitions in response to starting recognition. This attribute setting
+ // does not affect interim results.
+ bool continuous;
+ // Controls whether interim results are returned. When set to true, interim
+ // results SHOULD be returned. When set to false, interim results MUST NOT be
+ // returned. This value setting does not affect final results.
+ bool interim_results;
+ // This sets the maximum number of SbSpeechResult in
+ // |SbSpeechRecognizerOnResults| callback.
+ int max_alternatives;
+} SbSpeechConfiguration;
+
+// A function to notify that the user has started to speak or stops speaking.
+// |detected|: true if the user has started to speak, and false if the user
+// stops speaking.
+typedef void (*SbSpeechRecognizerSpeechDetectedFunction)(void* context,
+ bool detected);
+
+// A function to notify that a speech recognition error occurred.
+// |error|: The occurred speech recognition error.
+typedef void (*SbSpeechRecognizerErrorFunction)(void* context,
+ SbSpeechRecognizerError error);
+
+// A function to notify that the recognition results are ready.
+// |results|: the list of recognition results.
+// |results_size|: the number of |results|.
+// |is_final|: indicates if the |results| is final.
+typedef void (*SbSpeechRecognizerResultsFunction)(void* context,
+ SbSpeechResult* results,
+ int results_size,
+ bool is_final);
+
+// Allows receiving notifications from the device when recognition related
+// events occur.
+//
+// The void* context is passed to every function.
+struct SbSpeechRecognizerHandler {
+ // Function to notify the beginning/end of the speech.
+ SbSpeechRecognizerSpeechDetectedFunction on_speech_detected;
+
+ // Function to notify the speech error.
+ SbSpeechRecognizerErrorFunction on_error;
+
+ // Function to notify that the recognition results are available.
+ SbSpeechRecognizerResultsFunction on_results;
+
+ // This is passed to handler functions as first argument.
+ void* context;
+};
+
+// Creates a speech recognizer with a speech recognizer handler.
+//
+// If the system has a speech recognition service available, this function
+// returns the newly created handle.
+//
+// If no speech recognition service is available on the device, this function
+// returns |kSbSpeechRecognizerInvalid|.
+//
+// |SbSpeechRecognizerCreate| does not expect the passed
+// SbSpeechRecognizerHandler structure to live after |SbSpeechRecognizerCreate|
+// is called, so the implementation must copy the contents if necessary.
+SB_EXPORT SbSpeechRecognizer
+SbSpeechRecognizerCreate(const SbSpeechRecognizerHandler* handler);
+
+// Starts listening to audio and recognizing speech with the specified speech
+// configuration. If |SbSpeechRecognizerStart| is called on an already
+// started speech recognizer, the implementation MUST ignore the call and return
+// false.
+//
+// Returns whether the speech recognizer is started successfully.
+SB_EXPORT bool SbSpeechRecognizerStart(
+ SbSpeechRecognizer recognizer,
+ const SbSpeechConfiguration* configuration);
+
+// Stops listening to audio and returns a result using just the audio that it
+// has already received. Once |SbSpeechRecognizerStop| is called, the
+// implementation MUST NOT collect additional audio and MUST NOT continue to
+// listen to the user. This is important for privacy reasons. If
+// |SbSpeechRecognizerStop| is called on a speech recognizer which is already
+// stopped or being stopped, the implementation MUST ignore the call.
+SB_EXPORT void SbSpeechRecognizerStop(SbSpeechRecognizer recognizer);
+
+// Cancels speech recognition. The speech recognizer stops listening to
+// audio and does not return any information. When |SbSpeechRecognizerCancel| is
+// called, the implementation MUST NOT collect additional audio, MUST NOT
+// continue to listen to the user, and MUST stop recognizing. This is important
+// for privacy reasons. If |SbSpeechRecognizerCancel| is called on a speech
+// recognizer which is already stopped or cancelled, the implementation MUST
+// ignore the call.
+SB_EXPORT void SbSpeechRecognizerCancel(SbSpeechRecognizer recognizer);
+
+// Destroys the given speech recognizer. If the speech recognizer is in the
+// started state, it is first stopped and then destroyed.
+SB_EXPORT void SbSpeechRecognizerDestroy(SbSpeechRecognizer recognizer);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >=
+ // SB_SPEECH_RECOGNIZER_API_VERSION
+
+#endif // STARBOARD_SPEECH_RECOGNIZER_H_
diff --git a/src/starboard/speech_synthesis.h b/src/starboard/speech_synthesis.h
index 6bc568e..fe0de16 100644
--- a/src/starboard/speech_synthesis.h
+++ b/src/starboard/speech_synthesis.h
@@ -35,7 +35,7 @@
extern "C" {
#endif
-#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION < 4
// DEPRECATED IN API VERSION 4
// Must be called before any other function in this module,
// or subsequent calls are allowed to fail silently.
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h
index 1b60f71..edf093a 100644
--- a/src/starboard/stub/configuration_public.h
+++ b/src/starboard/stub/configuration_public.h
@@ -134,6 +134,9 @@
// Whether the current platform has microphone supported.
#define SB_HAS_MICROPHONE 1
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 1
+
// Whether the current platform has speech synthesis.
#define SB_HAS_SPEECH_SYNTHESIS 1
@@ -350,7 +353,7 @@
// supported composition methods below.
#define SB_HAS_PLAYER 1
-#if SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION < 4
// Specifies whether this platform's player will produce an OpenGL texture that
// the client must draw every frame with its graphics rendering. It may be that
// we get a texture handle, but cannot perform operations like GlReadPixels on
@@ -369,7 +372,7 @@
// this case, changing the video bounds must be tightly synchronized between the
// player and the graphics plane.
#define SB_IS_PLAYER_PUNCHED_OUT 1
-#endif // SB_API_VERSION < SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION < 4
// After a seek is triggerred, the default behavior is to append video frames
// from the last key frame before the seek time and append audio frames from the
@@ -384,41 +387,19 @@
// not available should define the following quirk.
#undef SB_HAS_QUIRK_NO_FFS
-// Specifies the maximum amount of memory used by audio buffers of media source
-// before triggering a garbage collection. A large value will cause more memory
-// being used by audio buffers but will also make JavaScript app less likely to
-// re-download audio data. Note that the JavaScript app may experience
-// significant difficulty if this value is too low.
-#define SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT (3U * 1024U * 1024U)
+#if SB_API_VERSION >= 4
-// Specifies the maximum amount of memory used by video buffers of media source
-// before triggering a garbage collection. A large value will cause more memory
-// being used by video buffers but will also make JavaScript app less likely to
-// re-download video data. Note that the JavaScript app may experience
-// significant difficulty if this value is too low.
-#define SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT (16U * 1024U * 1024U)
+// The maximum audio bitrate the platform can decode. The following value
+// equals to 5M bytes per seconds which is more than enough for compressed
+// audio.
+#define SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND (40 * 1024 * 1024)
-// Specifies how much memory to reserve up-front for the main media buffer
-// (usually resides inside the CPU memory) used by media source and demuxers.
-// The main media buffer can work in one of the following two ways:
-// 1. If GPU buffer is used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is non-zero), the
-// main buffer will be used as a cache so a media buffer will be copied from
-// GPU memory to main memory before sending to the decoder for further
-// processing. In this case this macro should be set to a value that is
-// large enough to hold all media buffers being decoded.
-// 2. If GPU buffer is not used (i.e. SB_MEDIA_GPU_BUFFER_BUDGET is zero) all
-// media buffers will reside in the main memory buffer. In this case the
-// macro should be set to a value that is greater than the sum of the above
-// source buffer stream memory limits with extra room to take account of
-// fragmentations and memory used by demuxers.
-#define SB_MEDIA_MAIN_BUFFER_BUDGET (32U * 1024U * 1024U)
+// The maximum video bitrate the platform can decode. The following value
+// equals to 25M bytes per seconds which is more than enough for compressed
+// video.
+#define SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND (200 * 1024 * 1024)
-// Specifies how much GPU memory to reserve up-front for media source buffers.
-// This should only be set to non-zero on system with limited CPU memory and
-// excess GPU memory so the app can store media buffer in GPU memory.
-// SB_MEDIA_MAIN_BUFFER_BUDGET has to be set to a non-zero value to avoid
-// media buffers being decoded when being stored in GPU.
-#define SB_MEDIA_GPU_BUFFER_BUDGET 0U
+#endif // SB_API_VERSION >= 4
// Specifies whether this platform has webm/vp9 support. This should be set to
// non-zero on platforms with webm/vp9 support.
diff --git a/src/starboard/stub/gyp_configuration.gypi b/src/starboard/stub/gyp_configuration.gypi
index 01b6680..3a62ad7 100644
--- a/src/starboard/stub/gyp_configuration.gypi
+++ b/src/starboard/stub/gyp_configuration.gypi
@@ -26,6 +26,8 @@
# there for acceptable values for this variable.
'javascript_engine': 'mozjs',
+ 'cobalt_media_source_2016': 1,
+
# Define platform specific compiler and linker flags.
# Refer to base.gypi for a list of all available variables.
'compiler_flags_host': [
diff --git a/src/starboard/stub/starboard_platform.gyp b/src/starboard/stub/starboard_platform.gyp
index ccdd05b..e2ab6dc 100644
--- a/src/starboard/stub/starboard_platform.gyp
+++ b/src/starboard/stub/starboard_platform.gyp
@@ -49,6 +49,12 @@
'<(DEPTH)/starboard/shared/stub/condition_variable_signal.cc',
'<(DEPTH)/starboard/shared/stub/condition_variable_wait.cc',
'<(DEPTH)/starboard/shared/stub/condition_variable_wait_timed.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
+ '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
'<(DEPTH)/starboard/shared/stub/directory_can_open.cc',
'<(DEPTH)/starboard/shared/stub/directory_close.cc',
'<(DEPTH)/starboard/shared/stub/directory_create.cc',
@@ -87,8 +93,10 @@
'<(DEPTH)/starboard/shared/stub/media_can_play_mime_and_key_system.cc',
'<(DEPTH)/starboard/shared/stub/media_get_audio_configuration.cc',
'<(DEPTH)/starboard/shared/stub/media_get_audio_output_count.cc',
+ '<(DEPTH)/starboard/shared/stub/media_is_audio_supported.cc',
'<(DEPTH)/starboard/shared/stub/media_is_output_protected.cc',
'<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
+ '<(DEPTH)/starboard/shared/stub/media_is_video_supported.cc',
'<(DEPTH)/starboard/shared/stub/media_set_output_protection.cc',
'<(DEPTH)/starboard/shared/stub/memory_allocate_aligned_unchecked.cc',
'<(DEPTH)/starboard/shared/stub/memory_allocate_unchecked.cc',
@@ -137,6 +145,7 @@
'<(DEPTH)/starboard/shared/stub/socket_destroy.cc',
'<(DEPTH)/starboard/shared/stub/socket_free_resolution.cc',
'<(DEPTH)/starboard/shared/stub/socket_get_last_error.cc',
+ '<(DEPTH)/starboard/shared/stub/socket_get_interface_address.cc',
'<(DEPTH)/starboard/shared/stub/socket_get_local_address.cc',
'<(DEPTH)/starboard/shared/stub/socket_get_local_interface_address.cc',
'<(DEPTH)/starboard/shared/stub/socket_is_connected.cc',
@@ -160,6 +169,11 @@
'<(DEPTH)/starboard/shared/stub/socket_waiter_wait.cc',
'<(DEPTH)/starboard/shared/stub/socket_waiter_wait_timed.cc',
'<(DEPTH)/starboard/shared/stub/socket_waiter_wake_up.cc',
+ '<(DEPTH)/starboard/shared/stub/speech_recognizer_cancel.cc',
+ '<(DEPTH)/starboard/shared/stub/speech_recognizer_create.cc',
+ '<(DEPTH)/starboard/shared/stub/speech_recognizer_destroy.cc',
+ '<(DEPTH)/starboard/shared/stub/speech_recognizer_start.cc',
+ '<(DEPTH)/starboard/shared/stub/speech_recognizer_stop.cc',
'<(DEPTH)/starboard/shared/stub/speech_synthesis_cancel.cc',
'<(DEPTH)/starboard/shared/stub/speech_synthesis_set_language.cc',
'<(DEPTH)/starboard/shared/stub/speech_synthesis_speak.cc',
@@ -214,7 +228,10 @@
'<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
'<(DEPTH)/starboard/shared/stub/system_is_debugger_attached.cc',
'<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc',
+ '<(DEPTH)/starboard/shared/stub/system_request_pause.cc',
'<(DEPTH)/starboard/shared/stub/system_request_stop.cc',
+ '<(DEPTH)/starboard/shared/stub/system_request_suspend.cc',
+ '<(DEPTH)/starboard/shared/stub/system_request_unpause.cc',
'<(DEPTH)/starboard/shared/stub/system_sort.cc',
'<(DEPTH)/starboard/shared/stub/system_symbolize.cc',
'<(DEPTH)/starboard/shared/stub/thread_create.cc',
diff --git a/src/starboard/system.h b/src/starboard/system.h
index 8532ad8..a56d034 100644
--- a/src/starboard/system.h
+++ b/src/starboard/system.h
@@ -45,6 +45,18 @@
// screenshots) can be written into.
kSbSystemPathDebugOutputDirectory,
+#if SB_API_VERSION >= 4
+ // Path to a directory where system font files can be found. Should only be
+ // specified on platforms that provide fonts usable by Starboard applications.
+ kSbSystemPathFontDirectory,
+
+ // Path to a directory where system font configuration metadata can be
+ // found. May be the same directory as |kSbSystemPathFontDirectory|, but not
+ // necessarily. Should only be specified on platforms that provide fonts
+ // usable by Starboard applications.
+ kSbSystemPathFontConfigurationDirectory,
+#endif // SB_API_VERSION >= 4
+
// Path to the directory containing the root of the source tree.
kSbSystemPathSourceDirectory,
@@ -104,6 +116,10 @@
// Speech APIs and generate a Speech API key.
kSbSystemPropertySpeechApiKey,
#endif // SB_VERSION(2)
+#if SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
+ // A field that, if available, is appended to the user agent
+ kSbSystemPropertyUserAgentAuxField,
+#endif // SB_API_VERSION >= SB_USER_AGENT_AUX_SYSTEM_PROPERTY_API_VERSION
} SbSystemPropertyId;
// Enumeration of device types.
@@ -129,10 +145,10 @@
// Desktop PC.
kSbSystemDeviceTypeDesktopPC,
-#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_API_VERSION >= 4
// An Android TV Device.
kSbSystemDeviceTypeAndroidTV,
-#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#endif // SB_API_VERSION >= 4
// Unknown device.
kSbSystemDeviceTypeUnknown,
@@ -261,6 +277,9 @@
// Breaks the current program into the debugger, if a debugger is attached.
// If a debugger is not attached, this function aborts the program.
+#if SB_API_VERSION >= 4
+SB_NORETURN
+#endif
SB_EXPORT void SbSystemBreakIntoDebugger();
// Attempts to determine whether the current program is running inside or
@@ -431,6 +450,43 @@
char* out_buffer,
int buffer_size);
+#if SB_API_VERSION >= 4
+// Requests that the application move into the Paused state at the next
+// convenient point. This should roughly correspond to "unfocused application"
+// in a traditional window manager, where the application may be partially
+// visible.
+//
+// This function eventually causes a |kSbEventTypePause| event to be dispatched
+// to the application. Before the |kSbEventTypePause| event is dispatched, some
+// work may continue to be done, and unrelated system events may be dispatched.
+SB_EXPORT void SbSystemRequestPause();
+
+// Requests that the application move into the Started state at the next
+// convenient point. This should roughly correspond to a "focused application"
+// in a traditional window manager, where the application is fully visible and
+// the primary receiver of input events.
+//
+// This function eventually causes a |kSbEventTypeUnpause| event to be
+// dispatched to the application. Before |kSbEventTypeUnpause| is dispatched,
+// some work may continue to be done, and unrelated system events may be
+// dispatched.
+SB_EXPORT void SbSystemRequestUnpause();
+
+// Requests that the application move into the Suspended state at the next
+// convenient point. This should roughly correspond to "minimization" in a
+// traditional window manager, where the application is no longer visible.
+//
+// This function eventually causes a |kSbEventTypeSuspend| event to be
+// dispatched to the application. Before the |kSbEventTypeSuspend| event is
+// dispatched, some work may continue to be done, and unrelated system events
+// may be dispatched.
+//
+// In the Suspended state, the application will be resident, but probably not
+// running. The expectation is that an external system event will bring the
+// application out of the Suspended state.
+SB_EXPORT void SbSystemRequestSuspend();
+#endif // SB_API_VERSION >= 4
+
// Requests that the application be terminated gracefully at the next
// convenient point. In the meantime, some work may continue to be done, and
// unrelated system events may be dispatched. This function eventually causes
diff --git a/src/starboard/tizen/armv7l/configuration_public.h b/src/starboard/tizen/armv7l/configuration_public.h
index 9b1c14a..f27972c 100644
--- a/src/starboard/tizen/armv7l/configuration_public.h
+++ b/src/starboard/tizen/armv7l/configuration_public.h
@@ -119,6 +119,8 @@
// player and the graphics plane.
#define SB_IS_PLAYER_PUNCHED_OUT 1
+#if SB_API_VERSION < 4
+
// Specifies the maximum amount of memory used by audio buffers of media source
// before triggering a garbage collection. A large value will cause more memory
// being used by audio buffers but will also make JavaScript app less likely to
@@ -155,6 +157,8 @@
// media buffers being decoded when being stored in GPU.
#define SB_MEDIA_GPU_BUFFER_BUDGET 0U
+#endif // SB_API_VERSION < 4
+
// Specifies whether this platform has webm/vp9 support. This should be set to
// non-zero on platforms with webm/vp9 support.
#define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 0
diff --git a/src/starboard/tizen/armv7l/starboard_common.gyp b/src/starboard/tizen/armv7l/starboard_common.gyp
index faa178c..6da8396 100644
--- a/src/starboard/tizen/armv7l/starboard_common.gyp
+++ b/src/starboard/tizen/armv7l/starboard_common.gyp
@@ -204,7 +204,10 @@
'<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
'<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
'<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
'<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
+ '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
'<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc',
'<(DEPTH)/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc',
diff --git a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
index effc33e..02aaa9e 100644
--- a/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -297,7 +297,7 @@
namespace player {
namespace filter {
-#if SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#if SB_API_VERSION >= 4
// static
bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
SbMediaVideoCodec codec,
@@ -307,7 +307,7 @@
return output_mode == kSbPlayerOutputModePunchOut;
}
-#endif // SB_API_VERSION >= SB_PLAYER_DECODE_TO_TEXTURE_API_VERSION
+#endif // SB_API_VERSION >= 4
} // namespace filter
} // namespace player
diff --git a/src/starboard/tizen/shared/system_get_path.cc b/src/starboard/tizen/shared/system_get_path.cc
index f3f7c6d..b20b051 100644
--- a/src/starboard/tizen/shared/system_get_path.cc
+++ b/src/starboard/tizen/shared/system_get_path.cc
@@ -153,6 +153,10 @@
path_size);
case kSbSystemPathExecutableFile:
return GetExecutablePath(out_path, path_size);
+
+ default:
+ SB_NOTIMPLEMENTED();
+ return false;
}
int length = strlen(path);
diff --git a/src/starboard/types.h b/src/starboard/types.h
index 04a1263..64a27c0 100644
--- a/src/starboard/types.h
+++ b/src/starboard/types.h
@@ -128,6 +128,10 @@
#define LONG_MAX INT_MAX
#endif
+#if !defined(ULONG_MAX)
+#define ULONG_MAX UINT_MAX
+#endif
+
#else // SB_HAS(32_BIT_LONG)
SB_COMPILE_ASSERT(sizeof(long) == sizeof(int64_t), // NOLINT[runtime/int]
@@ -140,6 +144,10 @@
#define LONG_MAX SB_INT64_C(0x7FFFFFFFFFFFFFFF)
#endif
+#if !defined(ULONG_MAX)
+#define ULONG_MAX SB_INT64_C(0xFFFFFFFFFFFFFFFF)
+#endif
+
#endif // SB_HAS(32_BIT_LONG)
#if defined(_MSC_VER)
diff --git a/src/starboard/user.h b/src/starboard/user.h
index b12a764..6e66ed4 100644
--- a/src/starboard/user.h
+++ b/src/starboard/user.h
@@ -56,7 +56,7 @@
kSbUserPropertyUserId,
} SbUserPropertyId;
-#if SB_API_VERSION < SB_DELETE_USER_APPLICATION_TOKEN_VERSION
+#if SB_API_VERSION < 4
#if SB_HAS(USER_APPLICATION_LINKING_SUPPORT)
// Information about an application-specific authorization token.
typedef struct SbUserApplicationTokenResults {
diff --git a/src/testing/gtest.gyp b/src/testing/gtest.gyp
index 7b6821f..2b125f1 100644
--- a/src/testing/gtest.gyp
+++ b/src/testing/gtest.gyp
@@ -173,10 +173,7 @@
'GTEST_USE_OWN_TR1_TUPLE=1',
],
}],
- ['target_arch=="xb1" or target_arch=="xb360"', {
- 'gtest_defines' : [
- '_VARIADIC_MAX=10',
- ],
+ ['target_os=="win"', {
'gtest_defines!' : [
'GTEST_USE_OWN_TR1_TUPLE=1',
],
diff --git a/src/testing/gtest/src/gtest.cc b/src/testing/gtest/src/gtest.cc
index 9268208..550b156 100644
--- a/src/testing/gtest/src/gtest.cc
+++ b/src/testing/gtest/src/gtest.cc
@@ -47,6 +47,7 @@
#else
#include <math.h>
#include <stdarg.h>
+#include "starboard/system.h"
#endif
#include <algorithm>
@@ -58,7 +59,11 @@
#include <sstream>
#include <vector>
-#if GTEST_OS_LINUX
+#if GTEST_OS_STARBOARD
+
+// Starboard does not require any additional includes.
+
+#elif GTEST_OS_LINUX
// TODO(kenton@google.com): Use autoconf to detect availability of
// gettimeofday().
@@ -4181,6 +4186,9 @@
// when a failure happens and both the --gtest_break_on_failure and
// the --gtest_catch_exceptions flags are specified.
DebugBreak();
+
+#elif GTEST_OS_STARBOARD
+ SbSystemBreakIntoDebugger();
#else
// Dereference NULL through a volatile pointer to prevent the compiler
// from removing. We use this rather than abort() or __builtin_trap() for
diff --git a/src/third_party/blink/Source/bindings/scripts/compute_interfaces_info_individual.py b/src/third_party/blink/Source/bindings/scripts/compute_interfaces_info_individual.py
index f64a7f9..6799f9a 100755
--- a/src/third_party/blink/Source/bindings/scripts/compute_interfaces_info_individual.py
+++ b/src/third_party/blink/Source/bindings/scripts/compute_interfaces_info_individual.py
@@ -173,7 +173,7 @@
'full_paths': [],
'include_paths': [],
})
- self.enumerations = set()
+ self.enumerations = {}
self.union_types = set()
self.typedefs = {}
self.callback_functions = {}
@@ -229,12 +229,16 @@
'component_dir': idl_filename_to_component(idl_filename),
'full_path': os.path.realpath(idl_filename),
}
- # Check enum duplication.
- for enum_name in definitions.enumerations.keys():
- for defined_enum in self.enumerations:
- if defined_enum.name == enum_name:
- raise Exception('Enumeration %s has multiple definitions' % enum_name)
- self.enumerations.update(definitions.enumerations.values())
+
+ # Include more information about the enum for Cobalt.
+ for enum_name, values in definitions.enumerations.iteritems():
+ # Check enum duplication.
+ if self.enumerations.has_key(enum_name):
+ raise Exception('Enumeration %s has multiple definitions' % enum_name)
+ self.enumerations[enum_name] = {
+ 'values': values,
+ 'full_path': os.path.realpath(idl_filename),
+ }
if definitions.interfaces:
definition = next(definitions.interfaces.itervalues())
@@ -342,8 +346,7 @@
"""Returns component wide information as a dict."""
return {
'callback_functions': self.callback_functions,
- 'enumerations': dict((enum.name, enum.values)
- for enum in self.enumerations),
+ 'enumerations': self.enumerations,
'typedefs': self.typedefs,
'union_types': self.union_types,
}
diff --git a/src/third_party/blink/Source/bindings/scripts/interface_dependency_resolver.py b/src/third_party/blink/Source/bindings/scripts/interface_dependency_resolver.py
index 5a032ed..1a8a7b0 100644
--- a/src/third_party/blink/Source/bindings/scripts/interface_dependency_resolver.py
+++ b/src/third_party/blink/Source/bindings/scripts/interface_dependency_resolver.py
@@ -159,7 +159,9 @@
# Sort so order consistent, so can compare output from run to run.
for dependency_idl_filename in sorted(dependency_idl_filenames):
dependency_definitions = reader.read_idl_file(dependency_idl_filename)
- dependency_component = idl_filename_to_component(dependency_idl_filename)
+ # For Cobalt, always use the component where |definitions| is located
+ # so interfaces will get merged together.
+ dependency_component = component
dependency_interface = next(dependency_definitions.interfaces.itervalues())
dependency_interface_basename, _ = os.path.splitext(os.path.basename(dependency_idl_filename))
diff --git a/src/third_party/dlmalloc/dlmalloc.gyp b/src/third_party/dlmalloc/dlmalloc.gyp
index ee777e0..64c0743 100644
--- a/src/third_party/dlmalloc/dlmalloc.gyp
+++ b/src/third_party/dlmalloc/dlmalloc.gyp
@@ -13,7 +13,7 @@
'include_dirs': [
],
'conditions': [
- ['target_arch=="xb1" or target_arch=="xb360"', {
+ ['target_os=="win"', {
# Compile dlmalloc.c as C++ on MSVC.
'msvs_settings': {
'VCCLCompilerTool': {
diff --git a/src/third_party/icu/source/common/putil.cpp b/src/third_party/icu/source/common/putil.cpp
index 7743792..8bed6aa 100644
--- a/src/third_party/icu/source/common/putil.cpp
+++ b/src/third_party/icu/source/common/putil.cpp
@@ -129,7 +129,7 @@
*/
#include <time.h>
-#if !U_PLATFORM_USES_ONLY_WIN32_API
+#if !U_PLATFORM_USES_ONLY_WIN32_API && !defined(STARBOARD)
#include <sys/time.h>
#endif
diff --git a/src/third_party/mozjs-45/js/src/vm/TraceLogging.cpp b/src/third_party/mozjs-45/js/src/vm/TraceLogging.cpp
index ce7acc6..6248c5d 100644
--- a/src/third_party/mozjs-45/js/src/vm/TraceLogging.cpp
+++ b/src/third_party/mozjs-45/js/src/vm/TraceLogging.cpp
@@ -83,6 +83,8 @@
#endif // defined(MOZ_HAVE_RDTSC)
+#if defined(JS_TRACE_LOGGING)
+
class AutoTraceLoggerThreadStateLock
{
TraceLoggerThreadState* logging;
@@ -1029,3 +1031,5 @@
return *this;
}
+
+#endif // defined(JS_TRACE_LOGGING)
diff --git a/src/third_party/mozjs-45/js/src/vm/TraceLogging.h b/src/third_party/mozjs-45/js/src/vm/TraceLogging.h
index 270478c..d09609c 100644
--- a/src/third_party/mozjs-45/js/src/vm/TraceLogging.h
+++ b/src/third_party/mozjs-45/js/src/vm/TraceLogging.h
@@ -111,7 +111,11 @@
return !!payload_;
}
+#if defined(JS_TRACE_LOGGING)
TraceLoggerEvent& operator=(const TraceLoggerEvent& other);
+#else
+ TraceLoggerEvent& operator=(const TraceLoggerEvent& other) { return *this; }
+#endif
TraceLoggerEvent(const TraceLoggerEvent& event) = delete;
};
diff --git a/src/third_party/mozjs-45/mozjs-45.gyp b/src/third_party/mozjs-45/mozjs-45.gyp
index a3d1941..d082045 100644
--- a/src/third_party/mozjs-45/mozjs-45.gyp
+++ b/src/third_party/mozjs-45/mozjs-45.gyp
@@ -61,6 +61,7 @@
'type': 'static_library',
'cflags': [
'-Wno-invalid-offsetof',
+ '-Wno-undefined-var-template',
'-Wno-unused-function',
'-include <(DEPTH)/third_party/mozjs-45/cobalt_config/include/js-confdefs.h',
],
@@ -78,6 +79,7 @@
],
'cflags': [
'-Wno-invalid-offsetof',
+ '-Wno-undefined-var-template',
'-Wno-unused-function',
'-include <(DEPTH)/third_party/mozjs-45/cobalt_config/include/js-confdefs.h',
],
@@ -99,6 +101,11 @@
'JS_NUNBOX32=1',
],
}],
+ ['cobalt_config != "gold"', {
+ 'defines': [
+ 'JS_TRACE_LOGGING=1',
+ ],
+ }],
],
},
'include_dirs': [
diff --git a/src/third_party/openssl/openssl/crypto/des/des_locl.h b/src/third_party/openssl/openssl/crypto/des/des_locl.h
index 3ce8268..5833de0 100644
--- a/src/third_party/openssl/openssl/crypto/des/des_locl.h
+++ b/src/third_party/openssl/openssl/crypto/des/des_locl.h
@@ -77,7 +77,7 @@
# if !defined(OPENSSL_SYS_VMS) || defined(__DECC)
# ifdef OPENSSL_UNISTD
# include OPENSSL_UNISTD
-# else
+# elif !defined(OPENSSL_SYS_STARBOARD)
# include <unistd.h>
# endif
# include <math.h>
diff --git a/src/third_party/openssl/openssl/crypto/evp/e_aes.c b/src/third_party/openssl/openssl/crypto/evp/e_aes.c
index dfa60ce..c81005f 100644
--- a/src/third_party/openssl/openssl/crypto/evp/e_aes.c
+++ b/src/third_party/openssl/openssl/crypto/evp/e_aes.c
@@ -64,6 +64,10 @@
# include "modes_lcl.h"
# include <openssl/rand.h>
+#if defined(OPENSSL_SYS_STARBOARD)
+# include "starboard/cryptography.h"
+#endif
+
typedef struct {
AES_KEY ks;
block128_f block;
@@ -108,6 +112,235 @@
# define MAXBITCHUNK ((size_t)1<<(sizeof(size_t)*8-4))
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+static bool sb_translate_mode(int flags, SbCryptographyBlockCipherMode *mode) {
+ switch (flags & EVP_CIPH_MODE) {
+ case EVP_CIPH_CBC_MODE:
+ *mode = kSbCryptographyBlockCipherModeCbc;
+ return true;
+ case EVP_CIPH_CFB_MODE:
+ *mode = kSbCryptographyBlockCipherModeCfb;
+ return true;
+ case EVP_CIPH_CTR_MODE:
+ *mode = kSbCryptographyBlockCipherModeCtr;
+ return true;
+ case EVP_CIPH_ECB_MODE:
+ *mode = kSbCryptographyBlockCipherModeEcb;
+ return true;
+ case EVP_CIPH_OFB_MODE:
+ *mode = kSbCryptographyBlockCipherModeOfb;
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static inline bool sb_has_stream(EVP_CIPHER_CTX *context) {
+ return SbCryptographyIsTransformerValid(context->stream_transformer);
+}
+
+static inline bool sb_gcm_has_stream(GCM128_CONTEXT *context) {
+ return SbCryptographyIsTransformerValid(context->gcm_transformer);
+}
+
+static int sb_init_key(EVP_CIPHER_CTX* context,
+ const unsigned char* key,
+ const unsigned char* initialization_vector,
+ int encrypt) {
+ SbCryptographyDirection direction =
+ (encrypt ? kSbCryptographyDirectionEncode :
+ kSbCryptographyDirectionDecode);
+
+ SbCryptographyBlockCipherMode mode;
+ if (!sb_translate_mode(context->cipher->flags, &mode)) {
+ return 0;
+ }
+
+ context->stream_transformer =
+ SbCryptographyCreateTransformer(
+ kSbCryptographyAlgorithmAes,
+ context->cipher->block_size * 8,
+ direction,
+ mode,
+ initialization_vector,
+ context->cipher->iv_len,
+ key,
+ context->cipher->key_len);
+
+ return sb_has_stream(context) ? 1 : 0;
+}
+
+static inline int sb_cipher(EVP_CIPHER_CTX *context,
+ unsigned char *out,
+ const unsigned char *in,
+ size_t len) {
+ if (!sb_has_stream(context)) {
+ return 0;
+ }
+
+ int result = SbCryptographyTransform(context->stream_transformer,
+ in,
+ len,
+ out);
+ return (result == len);
+}
+
+static bool sb_gcm_init(GCM128_CONTEXT *context, const void *key, int key_length,
+ int encrypt) {
+ SbCryptographyDirection direction =
+ (encrypt ? kSbCryptographyDirectionEncode :
+ kSbCryptographyDirectionDecode);
+ context->gcm_transformer =
+ SbCryptographyCreateTransformer(
+ kSbCryptographyAlgorithmAes,
+ 128,
+ direction,
+ kSbCryptographyBlockCipherModeGcm,
+ NULL,
+ 0,
+ key,
+ key_length);
+ return sb_gcm_has_stream(context);
+}
+
+static inline void sb_gcm_setiv(GCM128_CONTEXT *context,
+ const unsigned char *initialization_vector,
+ size_t initialization_vector_size) {
+ if (sb_gcm_has_stream(context)) {
+ SbCryptographySetInitializationVector(context->gcm_transformer,
+ initialization_vector,
+ initialization_vector_size);
+ return;
+ }
+
+ CRYPTO_gcm128_setiv(context, initialization_vector,
+ initialization_vector_size);
+}
+
+static inline int sb_gcm_aad(GCM128_CONTEXT *context,
+ const unsigned char *data,
+ size_t data_size) {
+ if (sb_gcm_has_stream(context)) {
+ return SbCryptographySetAuthenticatedData(context->gcm_transformer,
+ data, data_size) ? 0 : -1;
+ }
+
+ return CRYPTO_gcm128_aad(context, data, data_size);
+}
+
+static inline int sb_gcm_encrypt(GCM128_CONTEXT *context,
+ const unsigned char *in, unsigned char *out,
+ size_t len) {
+ if (sb_gcm_has_stream(context)) {
+ int result =
+ SbCryptographyTransform(context->gcm_transformer, in, len, out);
+ return result == len ? 0 : -1;
+ }
+
+ return CRYPTO_gcm128_encrypt(context, in, out, len);
+}
+
+static inline int sb_gcm_decrypt(GCM128_CONTEXT *context,
+ const unsigned char *in, unsigned char *out,
+ size_t len) {
+ if (sb_gcm_has_stream(context)) {
+ int result =
+ SbCryptographyTransform(context->gcm_transformer, in, len, out);
+ return result == len ? 0 : -1;
+ }
+
+ return CRYPTO_gcm128_decrypt(context, in, out, len);
+}
+
+static inline int sb_gcm_finish(GCM128_CONTEXT *context,
+ const unsigned char *tag, size_t len) {
+ if (sb_gcm_has_stream(context)) {
+ unsigned char actual_tag[16];
+ SbCryptographyGetTag(context->gcm_transformer, actual_tag, 16);
+ return SbMemoryCompare(tag, actual_tag, len) == 0 ? 0 : -1;
+ }
+
+ return CRYPTO_gcm128_finish(context, tag, len);
+}
+
+static inline void sb_gcm_tag(GCM128_CONTEXT *context,
+ unsigned char *tag, size_t len) {
+ if (sb_gcm_has_stream(context)) {
+ SbCryptographyGetTag(context->gcm_transformer, tag, len);
+ return;
+ }
+
+ CRYPTO_gcm128_tag(context, tag, len);
+}
+
+#define SB_TRY_INIT(context, key, iv, encrypt) \
+ if (sb_init_key(context, key, iv, encrypt)) { \
+ return 1; \
+ }
+
+#define SB_TRY_CIPHER(context, out, in, len) \
+ if (sb_cipher(context, out, in, len)) { \
+ return 1; \
+ }
+
+#else // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+
+static inline bool sb_has_stream(EVP_CIPHER_CTX *context) {
+ return false;
+}
+
+static inline bool sb_gcm_has_stream(GCM128_CONTEXT *context) {
+ return false;
+}
+
+static bool sb_gcm_init(GCM128_CONTEXT *context, const void *key, int key_length,
+ int encrypt) {
+ return false;
+}
+
+static inline void sb_gcm_setiv(GCM128_CONTEXT *context,
+ const unsigned char *initialization_vector,
+ size_t initialization_vector_size) {
+ CRYPTO_gcm128_setiv(context, initialization_vector,
+ initialization_vector_size);
+}
+
+static inline int sb_gcm_aad(GCM128_CONTEXT *context,
+ const unsigned char *data,
+ size_t data_size) {
+ return CRYPTO_gcm128_aad(context, data, data_size);
+}
+
+static inline int sb_gcm_encrypt(GCM128_CONTEXT *context,
+ const unsigned char *in, unsigned char *out,
+ size_t len) {
+ return CRYPTO_gcm128_encrypt(context, in, out, len);
+}
+
+static inline int sb_gcm_decrypt(GCM128_CONTEXT *context,
+ const unsigned char *in, unsigned char *out,
+ size_t len) {
+ return CRYPTO_gcm128_decrypt(context, in, out, len);
+}
+
+static inline int sb_gcm_finish(GCM128_CONTEXT *context,
+ const unsigned char *tag, size_t len) {
+ return CRYPTO_gcm128_finish(context, tag, len);
+}
+
+static inline void sb_gcm_tag(GCM128_CONTEXT *context, unsigned char *tag,
+ size_t len) {
+ CRYPTO_gcm128_tag(context, tag, len);
+}
+
+#define SB_TRY_INIT(context, key, iv, encrypt)
+
+#define SB_TRY_CIPHER(context, out, in, len)
+
+#endif // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+
# ifdef VPAES_ASM
int vpaes_set_encrypt_key(const unsigned char *userKey, int bits,
AES_KEY *key);
@@ -479,6 +712,8 @@
int ret, mode;
EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
+ SB_TRY_INIT(ctx, key, iv, enc);
+
mode = ctx->cipher->flags & EVP_CIPH_MODE;
if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE)
&& !enc)
@@ -542,6 +777,8 @@
{
EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
+ SB_TRY_CIPHER(ctx, out, in, len);
+
if (dat->stream.cbc)
(*dat->stream.cbc) (in, out, len, &dat->ks, ctx->iv, ctx->encrypt);
else if (ctx->encrypt)
@@ -559,6 +796,8 @@
size_t i;
EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
+ SB_TRY_CIPHER(ctx, out, in, len);
+
if (len < bl)
return 1;
@@ -573,6 +812,8 @@
{
EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
+ SB_TRY_CIPHER(ctx, out, in, len);
+
CRYPTO_ofb128_encrypt(in, out, len, &dat->ks,
ctx->iv, &ctx->num, dat->block);
return 1;
@@ -583,6 +824,8 @@
{
EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
+ SB_TRY_CIPHER(ctx, out, in, len);
+
CRYPTO_cfb128_encrypt(in, out, len, &dat->ks,
ctx->iv, &ctx->num, ctx->encrypt, dat->block);
return 1;
@@ -593,6 +836,8 @@
{
EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
+ SB_TRY_CIPHER(ctx, out, in, len);
+
CRYPTO_cfb128_8_encrypt(in, out, len, &dat->ks,
ctx->iv, &ctx->num, ctx->encrypt, dat->block);
return 1;
@@ -603,6 +848,8 @@
{
EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
+ SB_TRY_CIPHER(ctx, out, in, len);
+
if (ctx->flags & EVP_CIPH_FLAG_LENGTH_BITS) {
CRYPTO_cfb128_1_encrypt(in, out, len, &dat->ks,
ctx->iv, &ctx->num, ctx->encrypt, dat->block);
@@ -627,6 +874,8 @@
unsigned int num = ctx->num;
EVP_AES_KEY *dat = (EVP_AES_KEY *) ctx->cipher_data;
+ SB_TRY_CIPHER(ctx, out, in, len);
+
if (dat->stream.ctr)
CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks,
ctx->iv, ctx->buf, &num, dat->stream.ctr);
@@ -644,6 +893,11 @@
static int aes_gcm_cleanup(EVP_CIPHER_CTX *c)
{
EVP_AES_GCM_CTX *gctx = c->cipher_data;
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+ SbCryptographyDestroyTransformer(gctx->gcm.gcm_transformer);
+ SbCryptographyDestroyTransformer(gctx->gcm.ctr_transformer);
+ SbCryptographyDestroyTransformer(gctx->gcm.ecb_transformer);
+#endif // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
OPENSSL_cleanse(&gctx->gcm, sizeof(gctx->gcm));
if (gctx->iv != c->iv)
OPENSSL_free(gctx->iv);
@@ -669,6 +923,7 @@
static int aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
{
EVP_AES_GCM_CTX *gctx = c->cipher_data;
+
switch (type) {
case EVP_CTRL_INIT:
gctx->key_set = 0;
@@ -678,6 +933,12 @@
gctx->taglen = -1;
gctx->iv_gen = 0;
gctx->tls_aad_len = -1;
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+ gctx->ctr = NULL;
+ gctx->gcm.gcm_transformer = kSbCryptographyInvalidTransformer;
+ gctx->gcm.ctr_transformer = kSbCryptographyInvalidTransformer;
+ gctx->gcm.ecb_transformer = kSbCryptographyInvalidTransformer;
+#endif
return 1;
case EVP_CTRL_GCM_SET_IVLEN:
@@ -735,7 +996,7 @@
case EVP_CTRL_GCM_IV_GEN:
if (gctx->iv_gen == 0 || gctx->key_set == 0)
return 0;
- CRYPTO_gcm128_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
+ sb_gcm_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
if (arg <= 0 || arg > gctx->ivlen)
arg = gctx->ivlen;
OPENSSL_port_memcpy(ptr, gctx->iv + gctx->ivlen - arg, arg);
@@ -751,7 +1012,7 @@
if (gctx->iv_gen == 0 || gctx->key_set == 0 || c->encrypt)
return 0;
OPENSSL_port_memcpy(gctx->iv + gctx->ivlen - arg, ptr, arg);
- CRYPTO_gcm128_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
+ sb_gcm_setiv(&gctx->gcm, gctx->iv, gctx->ivlen);
gctx->iv_set = 1;
return 1;
@@ -810,32 +1071,38 @@
do {
# ifdef BSAES_CAPABLE
if (BSAES_CAPABLE) {
- AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
- CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
- (block128_f) AES_encrypt);
- gctx->ctr = (ctr128_f) bsaes_ctr32_encrypt_blocks;
+ if (!sb_gcm_init(&gctx->gcm, key, ctx->key_len, enc)) {
+ AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
+ CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+ (block128_f) AES_encrypt);
+ gctx->ctr = (ctr128_f) bsaes_ctr32_encrypt_blocks;
+ }
break;
} else
# endif
# ifdef VPAES_CAPABLE
if (VPAES_CAPABLE) {
- vpaes_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
- CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
- (block128_f) vpaes_encrypt);
- gctx->ctr = NULL;
+ if (!sb_gcm_init(&gctx->gcm, key, ctx->key_len, enc)) {
+ vpaes_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
+ CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+ (block128_f) vpaes_encrypt);
+ gctx->ctr = NULL;
+ }
break;
} else
# endif
(void)0; /* terminate potentially open 'else' */
- AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
- CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
- (block128_f) AES_encrypt);
+ if (!sb_gcm_init(&gctx->gcm, key, ctx->key_len, enc)) {
+ AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks);
+ CRYPTO_gcm128_init(&gctx->gcm, &gctx->ks,
+ (block128_f) AES_encrypt);
# ifdef AES_CTR_ASM
- gctx->ctr = (ctr128_f) AES_ctr32_encrypt;
+ gctx->ctr = (ctr128_f) AES_ctr32_encrypt;
# else
- gctx->ctr = NULL;
+ gctx->ctr = NULL;
# endif
+ }
} while (0);
/*
@@ -844,14 +1111,14 @@
if (iv == NULL && gctx->iv_set)
iv = gctx->iv;
if (iv) {
- CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+ sb_gcm_setiv(&gctx->gcm, iv, gctx->ivlen);
gctx->iv_set = 1;
}
gctx->key_set = 1;
} else {
/* If key set use IV, otherwise copy */
if (gctx->key_set)
- CRYPTO_gcm128_setiv(&gctx->gcm, iv, gctx->ivlen);
+ sb_gcm_setiv(&gctx->gcm, iv, gctx->ivlen);
else
OPENSSL_port_memcpy(gctx->iv, iv, gctx->ivlen);
gctx->iv_set = 1;
@@ -885,7 +1152,7 @@
EVP_GCM_TLS_EXPLICIT_IV_LEN, out) <= 0)
goto err;
/* Use saved AAD */
- if (CRYPTO_gcm128_aad(&gctx->gcm, ctx->buf, gctx->tls_aad_len))
+ if (sb_gcm_aad(&gctx->gcm, ctx->buf, gctx->tls_aad_len))
goto err;
/* Fix buffer and length to point to payload */
in += EVP_GCM_TLS_EXPLICIT_IV_LEN;
@@ -898,12 +1165,12 @@
in, out, len, gctx->ctr))
goto err;
} else {
- if (CRYPTO_gcm128_encrypt(&gctx->gcm, in, out, len))
+ if (sb_gcm_encrypt(&gctx->gcm, in, out, len))
goto err;
}
out += len;
/* Finally write tag */
- CRYPTO_gcm128_tag(&gctx->gcm, out, EVP_GCM_TLS_TAG_LEN);
+ sb_gcm_tag(&gctx->gcm, out, EVP_GCM_TLS_TAG_LEN);
rv = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
} else {
/* Decrypt */
@@ -912,11 +1179,11 @@
in, out, len, gctx->ctr))
goto err;
} else {
- if (CRYPTO_gcm128_decrypt(&gctx->gcm, in, out, len))
+ if (sb_gcm_decrypt(&gctx->gcm, in, out, len))
goto err;
}
/* Retrieve tag */
- CRYPTO_gcm128_tag(&gctx->gcm, ctx->buf, EVP_GCM_TLS_TAG_LEN);
+ sb_gcm_tag(&gctx->gcm, ctx->buf, EVP_GCM_TLS_TAG_LEN);
/* If tag mismatch wipe buffer */
if (CRYPTO_memcmp(ctx->buf, in + len, EVP_GCM_TLS_TAG_LEN)) {
OPENSSL_cleanse(out, len);
@@ -946,7 +1213,7 @@
return -1;
if (in) {
if (out == NULL) {
- if (CRYPTO_gcm128_aad(&gctx->gcm, in, len))
+ if (sb_gcm_aad(&gctx->gcm, in, len))
return -1;
} else if (ctx->encrypt) {
if (gctx->ctr) {
@@ -954,7 +1221,7 @@
in, out, len, gctx->ctr))
return -1;
} else {
- if (CRYPTO_gcm128_encrypt(&gctx->gcm, in, out, len))
+ if (sb_gcm_encrypt(&gctx->gcm, in, out, len))
return -1;
}
} else {
@@ -963,7 +1230,7 @@
in, out, len, gctx->ctr))
return -1;
} else {
- if (CRYPTO_gcm128_decrypt(&gctx->gcm, in, out, len))
+ if (sb_gcm_decrypt(&gctx->gcm, in, out, len))
return -1;
}
}
@@ -972,12 +1239,12 @@
if (!ctx->encrypt) {
if (gctx->taglen < 0)
return -1;
- if (CRYPTO_gcm128_finish(&gctx->gcm, ctx->buf, gctx->taglen) != 0)
+ if (sb_gcm_finish(&gctx->gcm, ctx->buf, gctx->taglen) != 0)
return -1;
gctx->iv_set = 0;
return 0;
}
- CRYPTO_gcm128_tag(&gctx->gcm, ctx->buf, 16);
+ sb_gcm_tag(&gctx->gcm, ctx->buf, 16);
gctx->taglen = 16;
/* Don't reuse the IV */
gctx->iv_set = 0;
diff --git a/src/third_party/openssl/openssl/crypto/evp/evp.h b/src/third_party/openssl/openssl/crypto/evp/evp.h
index 6cf98ac..6e50fa7 100644
--- a/src/third_party/openssl/openssl/crypto/evp/evp.h
+++ b/src/third_party/openssl/openssl/crypto/evp/evp.h
@@ -75,6 +75,10 @@
# include <openssl/bio.h>
# endif
+#if defined(OPENSSL_SYS_STARBOARD)
+# include "starboard/cryptography.h"
+#endif // defined(STARBOARD)
+
/*-
#define EVP_RC2_KEY_SIZE 16
#define EVP_RC4_KEY_SIZE 16
@@ -432,6 +436,15 @@
int buf_len; /* number we have left */
unsigned char oiv[EVP_MAX_IV_LENGTH]; /* original iv */
unsigned char iv[EVP_MAX_IV_LENGTH]; /* working iv */
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+ // A handle to a transformer for the full stream cipher. Intermediate state
+ // is kept internally to the transformer, so fields like |iv| and |num| are
+ // not used.
+ //
+ // This is not used for GCM. The GCM transformer is inside the GCM context,
+ // which is stored as the |cipher_data| for the GCM cipher.
+ SbCryptographyTransformer stream_transformer;
+#endif // defined(STARBOARD) && SB_API_VERSION >= 4
unsigned char buf[EVP_MAX_BLOCK_LENGTH]; /* saved partial block */
int num; /* used by cfb/ofb/ctr mode */
void *app_data; /* application stuff */
diff --git a/src/third_party/openssl/openssl/crypto/evp/evp_enc.c b/src/third_party/openssl/openssl/crypto/evp/evp_enc.c
index 5cd2037..3021e52 100644
--- a/src/third_party/openssl/openssl/crypto/evp/evp_enc.c
+++ b/src/third_party/openssl/openssl/crypto/evp/evp_enc.c
@@ -72,6 +72,10 @@
#endif
#include "evp_locl.h"
+#if defined(OPENSSL_SYS_STARBOARD)
+#include "starboard/cryptography.h"
+#endif // defined(OPENSSL_SYS_STARBOARD)
+
#ifdef OPENSSL_FIPS
# define M_do_cipher(ctx, out, in, inl) FIPS_cipher(ctx, out, in, inl)
#else
@@ -544,6 +548,9 @@
int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *c)
{
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+ SbCryptographyDestroyTransformer(c->stream_transformer);
+#endif // defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
#ifndef OPENSSL_FIPS
if (c->cipher != NULL) {
if (c->cipher->cleanup && !c->cipher->cleanup(c))
diff --git a/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h b/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
index 296849b..54febc9 100644
--- a/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
+++ b/src/third_party/openssl/openssl/crypto/modes/modes_lcl.h
@@ -6,6 +6,11 @@
*/
#include <openssl/modes.h>
+#include <openssl/opensslconf.h>
+
+#if defined(OPENSSL_SYS_STARBOARD)
+#include "starboard/cryptography.h"
+#endif
#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
typedef __int64 i64;
@@ -115,6 +120,23 @@
unsigned int mres, ares;
block128_f block;
void *key;
+
+#if defined(OPENSSL_SYS_STARBOARD) && SB_API_VERSION >= 4
+ // A handle to a HW-accelerated transformer for the GCM block cipher
+ // mode. If valid, then this is used instead of any other state in the
+ // context.
+ SbCryptographyTransformer gcm_transformer;
+
+ // A handle to a HW-accelerated transformer for the CTR block cipher
+ // mode. If valid, then the software GCM code will wrap the hardware CTR
+ // stream.
+ SbCryptographyTransformer ctr_transformer;
+
+ // A handle to a HW-accelerated transformer for the ECB block cipher
+ // mode. If valid, then the software GCM code will wrap the hardware ECB
+ // block cipher.
+ SbCryptographyTransformer ecb_transformer;
+#endif
};
struct xts128_context {
diff --git a/src/third_party/openssl/openssl/ssl/dtls1.h b/src/third_party/openssl/openssl/ssl/dtls1.h
index 8a6ad50..950cffb 100644
--- a/src/third_party/openssl/openssl/ssl/dtls1.h
+++ b/src/third_party/openssl/openssl/ssl/dtls1.h
@@ -60,6 +60,8 @@
#ifndef HEADER_DTLS1_H
# define HEADER_DTLS1_H
+#include "build/build_config.h"
+
# include <openssl/buffer.h>
# include <openssl/pqueue.h>
# ifdef OPENSSL_SYS_VMS
@@ -74,7 +76,7 @@
# else
# if defined(OPENSSL_SYS_VXWORKS)
# include <sys/times.h>
-# else
+# elif !defined(OS_STARBOARD)
# include <sys/time.h>
# endif
# endif
diff --git a/src/third_party/skia/gyp/gpu.gypi b/src/third_party/skia/gyp/gpu.gypi
index eaaf5d0..e5827cb 100644
--- a/src/third_party/skia/gyp/gpu.gypi
+++ b/src/third_party/skia/gyp/gpu.gypi
@@ -7,8 +7,6 @@
#
{
'defines': [
- 'COBALT_SKIA_GLYPH_ATLAS_WIDTH=<(skia_glyph_atlas_width)',
- 'COBALT_SKIA_GLYPH_ATLAS_HEIGHT=<(skia_glyph_atlas_height)',
],
'variables': {
'skgpu_sources': [
diff --git a/src/third_party/skia/include/core/SkMath.h b/src/third_party/skia/include/core/SkMath.h
index 014f014..625f5c7 100644
--- a/src/third_party/skia/include/core/SkMath.h
+++ b/src/third_party/skia/include/core/SkMath.h
@@ -10,6 +10,8 @@
#ifndef SkMath_DEFINED
#define SkMath_DEFINED
+#include "build/build_config.h"
+
#include "SkTypes.h"
// 64bit -> 32bit utilities
@@ -72,7 +74,7 @@
int SkCLZ_portable(uint32_t);
#ifndef SkCLZ
- #if defined(_MSC_VER) && _MSC_VER >= 1400
+ #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(OS_STARBOARD)
#include <intrin.h>
static inline int SkCLZ(uint32_t mask) {
diff --git a/src/third_party/skia/include/gpu/GrContext.h b/src/third_party/skia/include/gpu/GrContext.h
index 45cd599..8450152 100644
--- a/src/third_party/skia/include/gpu/GrContext.h
+++ b/src/third_party/skia/include/gpu/GrContext.h
@@ -62,7 +62,9 @@
/**
* Creates a GrContext for a backend context.
*/
- static GrContext* Create(GrBackend, GrBackendContext, const Options* opts = NULL);
+ static GrContext* Create(GrBackend, GrBackendContext,
+ int atlas_tex_width, int atlas_tex_height,
+ const Options* opts = NULL);
virtual ~GrContext();
@@ -1029,9 +1031,14 @@
int fMaxTextureSizeOverride;
+ // The size of the glyph atlas texture.
+ int atlas_texture_width;
+ int atlas_texture_height;
+
const Options fOptions;
- GrContext(const Options&); // init must be called after the constructor.
+ // init must be called after the constructor.
+ GrContext(int atlas_tex_width, int atlas_tex_height, const Options&);
bool init(GrBackend, GrBackendContext);
void setupDrawBuffer();
diff --git a/src/third_party/skia/src/gpu/GrContext.cpp b/src/third_party/skia/src/gpu/GrContext.cpp
index ad1b4c4..32a530e 100755
--- a/src/third_party/skia/src/gpu/GrContext.cpp
+++ b/src/third_party/skia/src/gpu/GrContext.cpp
@@ -86,12 +86,14 @@
};
GrContext* GrContext::Create(GrBackend backend, GrBackendContext backendContext,
+ int atlas_tex_width, int atlas_tex_height,
const Options* opts) {
- GrContext* context;
- if (NULL == opts) {
- context = SkNEW_ARGS(GrContext, (Options()));
- } else {
- context = SkNEW_ARGS(GrContext, (*opts));
+
+ GrContext* context;
+ if (NULL == opts) {
+ context = SkNEW_ARGS(GrContext, (atlas_tex_width, atlas_tex_height, Options()));
+ } else {
+ context = SkNEW_ARGS(GrContext, (atlas_tex_width, atlas_tex_height, *opts));
}
if (context->init(backend, backendContext)) {
@@ -102,7 +104,11 @@
}
}
-GrContext::GrContext(const Options& opts) : fOptions(opts) {
+GrContext::GrContext(int atlas_tex_width, int atlas_tex_height,
+ const Options& opts) :
+ atlas_texture_width(atlas_tex_width),
+ atlas_texture_height(atlas_tex_height),
+ fOptions(opts) {
fDrawState = NULL;
fGpu = NULL;
fClip = NULL;
@@ -121,7 +127,8 @@
fMaxTextureSizeOverride = 1 << 20;
}
-bool GrContext::init(GrBackend backend, GrBackendContext backendContext) {
+bool GrContext::init(GrBackend backend,
+ GrBackendContext backendContext) {
SkASSERT(NULL == fGpu);
fGpu = GrGpu::Create(backend, backendContext, this);
@@ -137,7 +144,9 @@
fResourceCache->setOverbudgetCallback(OverbudgetCB, this);
fResourceCache2 = SkNEW(GrResourceCache2);
- fFontCache = SkNEW_ARGS(GrFontCache, (fGpu));
+ fFontCache = SkNEW_ARGS(GrFontCache, (atlas_texture_width,
+ atlas_texture_height,
+ fGpu));
fLayerCache.reset(SkNEW_ARGS(GrLayerCache, (this)));
diff --git a/src/third_party/skia/src/gpu/GrTextStrike.cpp b/src/third_party/skia/src/gpu/GrTextStrike.cpp
index ef1bf64..74952e6 100644
--- a/src/third_party/skia/src/gpu/GrTextStrike.cpp
+++ b/src/third_party/skia/src/gpu/GrTextStrike.cpp
@@ -17,11 +17,6 @@
#if defined(COBALT)
-// On Cobalt we would like to avoid re-rasterizing glyphs as much as possible,
-// so increase the default atlas size.
-#define GR_ATLAS_TEXTURE_WIDTH COBALT_SKIA_GLYPH_ATLAS_WIDTH
-#define GR_ATLAS_TEXTURE_HEIGHT COBALT_SKIA_GLYPH_ATLAS_HEIGHT
-
// On Cobalt, not being able to fit glyphs into the atlas is a big penalty,
// since its software rendering is not optimized. Increase the plot size
// to allow it to accommodate larger glyphs and avoid this situation as
@@ -31,23 +26,22 @@
#else
-#define GR_ATLAS_TEXTURE_WIDTH 1024
-#define GR_ATLAS_TEXTURE_HEIGHT 2048
-
#define GR_PLOT_WIDTH 256
#define GR_PLOT_HEIGHT 256
#endif // defined(COBALT)
-#define GR_NUM_PLOTS_X (GR_ATLAS_TEXTURE_WIDTH / GR_PLOT_WIDTH)
-#define GR_NUM_PLOTS_Y (GR_ATLAS_TEXTURE_HEIGHT / GR_PLOT_HEIGHT)
-
#define FONT_CACHE_STATS 0
#if FONT_CACHE_STATS
static int g_PurgeCount = 0;
#endif
-GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
+GrFontCache::GrFontCache(int atlas_texture_width,
+ int atlas_texture_height,
+ GrGpu* gpu)
+ : atlas_texture_width_(atlas_texture_width),
+ atlas_texture_height_(atlas_texture_height),
+ fGpu(gpu) {
gpu->ref();
for (int i = 0; i < kAtlasCount; ++i) {
fAtlases[i] = NULL;
@@ -101,12 +95,12 @@
GrPixelConfig config = mask_format_to_pixel_config(format);
int atlasIndex = mask_format_to_atlas_index(format);
if (NULL == fAtlases[atlasIndex]) {
- SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
- GR_ATLAS_TEXTURE_HEIGHT);
+ SkISize textureSize = SkISize::Make(atlas_texture_width_,
+ atlas_texture_height_);
fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrTextureFlags,
textureSize,
- GR_NUM_PLOTS_X,
- GR_NUM_PLOTS_Y,
+ num_plots_x(),
+ num_plots_y(),
true));
}
GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
@@ -365,3 +359,12 @@
glyph->fPlot = plot;
return true;
}
+
+
+int GrFontCache::num_plots_x() const {
+ return (atlas_texture_width_ / GR_PLOT_WIDTH);
+}
+
+int GrFontCache::num_plots_y() const {
+ return (atlas_texture_height_ / GR_PLOT_HEIGHT);
+}
diff --git a/src/third_party/skia/src/gpu/GrTextStrike.h b/src/third_party/skia/src/gpu/GrTextStrike.h
index 401bd73..06a21df 100644
--- a/src/third_party/skia/src/gpu/GrTextStrike.h
+++ b/src/third_party/skia/src/gpu/GrTextStrike.h
@@ -80,7 +80,9 @@
class GrFontCache {
public:
- GrFontCache(GrGpu*);
+ GrFontCache(int atlas_texture_width,
+ int atlas_texture_height,
+ GrGpu*);
~GrFontCache();
inline GrTextStrike* getStrike(GrFontScaler*, bool useDistanceField);
@@ -133,6 +135,12 @@
GrTextStrike* generateStrike(GrFontScaler*);
inline void detachStrikeFromList(GrTextStrike*);
void purgeStrike(GrTextStrike* strike);
+
+ int num_plots_x() const;
+ int num_plots_y() const;
+
+ int atlas_texture_width_;
+ int atlas_texture_height_;
};
#endif
diff --git a/src/third_party/skia/src/ports/SkFontHost_FreeType_common.h b/src/third_party/skia/src/ports/SkFontHost_FreeType_common.h
index f093dba..ad25d3e 100644
--- a/src/third_party/skia/src/ports/SkFontHost_FreeType_common.h
+++ b/src/third_party/skia/src/ports/SkFontHost_FreeType_common.h
@@ -13,8 +13,11 @@
#include "SkScalerContext.h"
#include "SkTypeface.h"
-#include <ft2build.h>
-#include FT_FREETYPE_H
+// These are forward declared to avoid pimpl but also hide the FreeType implementation.
+typedef struct FT_LibraryRec_* FT_Library;
+typedef struct FT_FaceRec_* FT_Face;
+typedef struct FT_StreamRec_* FT_Stream;
+typedef signed long FT_Pos;
#ifdef SK_DEBUG
#define SkASSERT_CONTINUE(pred) \
diff --git a/src/third_party/super_fast_hash/super_fast_hash.cc b/src/third_party/super_fast_hash/super_fast_hash.cc
index 6618791..ecd1177 100644
--- a/src/third_party/super_fast_hash/super_fast_hash.cc
+++ b/src/third_party/super_fast_hash/super_fast_hash.cc
@@ -85,4 +85,4 @@
hash += hash >> 6;
return hash;
-}
\ No newline at end of file
+}
diff --git a/src/tools/clang/scripts/update.py b/src/tools/clang/scripts/update.py
index 617d863..ddd9098 100755
--- a/src/tools/clang/scripts/update.py
+++ b/src/tools/clang/scripts/update.py
@@ -405,12 +405,70 @@
% (version_out, VERSION))
sys.exit(1)
+# Returns true if there is no stdout/stderr
+# Or if there is a stdout/stderr and the user types "Y" or "y".
+def AskIfWantToWipeOutFolder():
+ if sys.stdin.isatty() and (sys.stdout.isatty() or sys.stderr.isatty()):
+ # Prompt the user, if there's a TTY.
+ reply = raw_input('clang was updated. You need to do a clean build. Want me'
+ ' (update.py) to clean out your out/ folder? [yN]');
+ if reply == "y" or reply == "Y":
+ return True
+
+ return False
+
+ return True
+
+
+# Clobber the out/ folder for configs that use clang.
+def ClobberOutFolderForClang():
+ should_wipe_out_folder = None
+
+ directory_name_of_current_file = os.path.dirname(os.path.abspath(__file__))
+ out_dir_with_dots = os.path.join(directory_name_of_current_file, '..', '..',
+ '..', 'out')
+ out_dir = os.path.abspath(out_dir_with_dots)
+
+ if not os.path.exists(out_dir):
+ return
+
+ out_subdirs = [os.path.join(out_dir, d)
+ for d in os.listdir(out_dir)]
+ for build_folder in filter(os.path.isdir, out_subdirs):
+ if not os.path.exists(os.path.join(build_folder, 'build.ninja')):
+ continue
+
+ package_version_filename = os.path.join(build_folder, 'cr_build_revision')
+ package_version = None
+ try:
+ with open(package_version_filename, 'r') as f:
+ package_version = f.read()
+ except IOError:
+ # File does not exist
+ pass
+
+ if package_version == PACKAGE_VERSION:
+ continue
+
+ if should_wipe_out_folder is None:
+ should_wipe_out_folder = AskIfWantToWipeOutFolder()
+
+ if should_wipe_out_folder is False:
+ break
+
+ RunCommand(['ninja', '-C', build_folder, '-t', 'clean'])
+
+ with open(package_version_filename, 'w') as f:
+ f.write(PACKAGE_VERSION)
+
def UpdateClang(args):
# Required for LTO, which is used when is_official_build = true.
need_gold_plugin = sys.platform.startswith('linux')
+ ClobberOutFolderForClang()
+
if ReadStampFile(
STAMP_FILE) == PACKAGE_VERSION and not args.force_local_build:
if not need_gold_plugin or os.path.exists(
diff --git a/src/tools/gyp/pylib/gyp/generator/ninja.py b/src/tools/gyp/pylib/gyp/generator/ninja.py
index 7c01386..f05c6a9 100755
--- a/src/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/src/tools/gyp/pylib/gyp/generator/ninja.py
@@ -576,11 +576,7 @@
return '%s %s: %s' % (verb, self.name, fallback)
def IsCygwinRule(self, action):
- if platform.system() == 'Linux':
- return False
- elif self.flavor == 'win':
- return self.msvs_settings.IsRuleRunUnderCygwin(action)
- elif self.flavor in ['ps3', 'xb1', 'ps4'] :
+ if self.flavor in ['ps3', 'ps4']:
return str(action.get('msvs_cygwin_shell', 1)) != '0'
return False
@@ -1675,7 +1671,7 @@
if flavor in ['win', 'xb1']:
master_ninja.variable('ld', ld)
master_ninja.variable('idl', 'midl.exe')
- master_ninja.variable('ar', 'lib.exe')
+ master_ninja.variable('ar', os.environ.get('AR', 'ar'))
master_ninja.variable('rc', 'rc.exe')
master_ninja.variable('asm', 'ml.exe')
master_ninja.variable('mt', 'mt.exe')
diff --git a/src/tools/gyp/pylib/gyp/msvs_emulation.py b/src/tools/gyp/pylib/gyp/msvs_emulation.py
index 4f75aa3..cf81dc9 100755
--- a/src/tools/gyp/pylib/gyp/msvs_emulation.py
+++ b/src/tools/gyp/pylib/gyp/msvs_emulation.py
@@ -618,7 +618,7 @@
def midl(name, default=None):
return self.ConvertVSMacros(midl_get(name, default=default),
config=config)
- if config.startswith('XB1'):
+ if config.startswith('xb1'):
tlb = ''
header = midl('HeaderFileName', default='${root}.h')
dlldata = ''
@@ -634,7 +634,7 @@
# Note that .tlb is not included in the outputs as it is not always
# generated depending on the content of the input idl file.
outdir = midl('OutputDirectory', default='')
- if config.startswith('XB1'):
+ if config.startswith('xb1'):
output = [header]
else:
output = [header, dlldata, iid, proxy]
@@ -643,9 +643,9 @@
('dlldata', dlldata),
('iid', iid),
('proxy', proxy)]
- if config.startswith('XB1'):
- metadata_dir = '"%s%s"' % (os.environ.get('DurangoXDK'),
- 'adk\\references\\commonconfiguration\\neutral')
+ if config.startswith('xb1'):
+ metadata_dir = '"%s%s"' % ('C:\\Program Files (x86)\\Windows Kits\\10\\',
+ 'UnionMetadata')
flags = ['/env', 'x64', '/W1', '/char', 'signed', '/enum_class',
'/metadata_dir', metadata_dir, '/notlb', '/winrt']
else:
@@ -829,17 +829,13 @@
'lbshell', 'build', 'platforms', 'DurangoVars.cmd'),
'ADK'
]
- # Using cygwin python so there is a bit of wrapping done to get the
- # dos environment via set:
+ # Get the dos environment via set:
# Use cmd /c to execute under native windows command
args_cmd = 'cmd /c '
- # Convert the cygwin path i.e. /cygdrive/c .. to C:\ ..
- args_bat = '\"`cygpath -d \'' + vs_args[0] + '\'` '
- # Create a list of the remaining arguments to the bat file
- args_args = ' '.join(vs_args[1:])
- args_set = ' && set\"'
+ args_set = '\"set\"'
- args = args_cmd + args_bat + args_args + args_set
+ args = args_cmd + args_set
+
popen = subprocess.Popen(
args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
variables, _ = popen.communicate()