Import Cobalt 13.93830
diff --git a/src/cobalt/base/tokens.h b/src/cobalt/base/tokens.h
index 9473e36..891c4a6 100644
--- a/src/cobalt/base/tokens.h
+++ b/src/cobalt/base/tokens.h
@@ -43,6 +43,7 @@
MacroOpWithNameOnly(animationend) \
MacroOpWithNameOnly(assertive) \
MacroOpWithNameOnly(attributes) \
+ MacroOpWithNameOnly(beforeunload) \
MacroOpWithNameOnly(blur) \
MacroOpWithNameOnly(boundary) \
MacroOpWithNameOnly(canplay) \
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 9f855ca..2ac5506 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -93,16 +93,16 @@
#endif // ENABLE_REMOTE_DEBUGGING
#if defined(ENABLE_WEBDRIVER)
-#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
int GetWebDriverPort() {
// The default port on which the webdriver server should listen for incoming
// connections.
#if defined(SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT)
const int kDefaultWebDriverPort = SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT;
#else
- const int kDefaultWebDriverPort = 9515;
+ const int kDefaultWebDriverPort = 4444;
#endif // defined(SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT)
int webdriver_port = kDefaultWebDriverPort;
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kWebDriverPort)) {
if (!base::StringToInt(
@@ -114,22 +114,24 @@
webdriver_port = kDefaultWebDriverPort;
}
}
+#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
return webdriver_port;
}
std::string GetWebDriverListenIp() {
- // The default port on which the webdriver server should listen for incoming
+ // The default IP on which the webdriver server should listen for incoming
// connections.
std::string webdriver_listen_ip =
webdriver::WebDriverModule::kDefaultListenIp;
+#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kWebDriverListenIp)) {
webdriver_listen_ip =
command_line->GetSwitchValueASCII(switches::kWebDriverListenIp);
}
+#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
return webdriver_listen_ip;
}
-#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
#endif // ENABLE_WEBDRIVER
GURL GetInitialURL() {
@@ -625,7 +627,12 @@
#if defined(ENABLE_WEBDRIVER)
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
- if (command_line->HasSwitch(switches::kEnableWebDriver)) {
+ bool create_webdriver_module =
+ !command_line->HasSwitch(switches::kDisableWebDriver);
+#else
+ bool create_webdriver_module = true;
+#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
+ if (create_webdriver_module) {
web_driver_module_.reset(new webdriver::WebDriverModule(
GetWebDriverPort(), GetWebDriverListenIp(),
base::Bind(&BrowserModule::CreateSessionDriver,
@@ -636,7 +643,6 @@
base::Unretained(browser_module_.get())),
base::Bind(&Application::Quit, base::Unretained(this))));
}
-#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
#endif // ENABLE_WEBDRIVER
#if defined(ENABLE_REMOTE_DEBUGGING)
@@ -724,6 +730,9 @@
case kSbEventTypeUnpause:
case kSbEventTypeSuspend:
case kSbEventTypeResume:
+#if SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION
+ case kSbEventTypeLowMemory:
+#endif // SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION
OnApplicationEvent(starboard_event->type);
break;
case kSbEventTypeNetworkConnect:
@@ -739,11 +748,9 @@
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;
@@ -815,6 +822,13 @@
browser_module_->Resume();
DLOG(INFO) << "Finished resuming.";
break;
+#if SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION
+ case kSbEventTypeLowMemory:
+ DLOG(INFO) << "Got low memory event.";
+ browser_module_->ReduceMemory();
+ DLOG(INFO) << "Finished reducing memory usage.";
+ break;
+#endif // SB_API_VERSION >= SB_LOW_MEMORY_EVENT_API_VERSION
default:
NOTREACHED() << "Unexpected event type: " << event_type;
return;
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index cf2ba72..41f4937 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -65,6 +65,7 @@
'../dom/comment.idl',
'../dom/console.idl',
'../dom/crypto.idl',
+ '../dom/custom_event.idl',
'../dom/data_view.idl',
'../dom/device_orientation_event.idl',
'../dom/document.idl',
@@ -79,6 +80,7 @@
'../dom/dom_string_map.idl',
'../dom/dom_token_list.idl',
'../dom/element.idl',
+ '../dom/error_event.idl',
'../dom/event.idl',
'../dom/event_listener.idl',
'../dom/event_target.idl',
@@ -219,9 +221,11 @@
'../audio/audio_node_channel_count_mode.idl',
'../audio/audio_node_channel_interpretation.idl',
'../dom/blob_property_bag.idl',
+ '../dom/custom_event_init.idl',
'../dom/device_orientation_event_init.idl',
'../dom/document_ready_state.idl',
'../dom/dom_parser_supported_type.idl',
+ '../dom/error_event_init.idl',
'../dom/event_init.idl',
'../dom/event_modifier_init.idl',
'../dom/focus_event_init.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index a9eb520..cfc4a45 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -111,8 +111,13 @@
// TODO: Subscribe to viewport size changes.
+const int kMainWebModuleZIndex = 1;
+const int kSplashScreenZIndex = 2;
+
#if defined(ENABLE_DEBUG_CONSOLE)
+const int kDebugConsoleZIndex = 3;
+
const char kFuzzerToggleCommand[] = "fuzzer_toggle";
const char kFuzzerToggleCommandShortHelp[] = "Toggles the input fuzzer on/off.";
const char kFuzzerToggleCommandLongHelp[] =
@@ -211,9 +216,7 @@
storage_manager_(make_scoped_ptr(new StorageUpgradeHandler(url))
.PassAs<storage::StorageManager::UpgradeHandler>(),
options_.storage_manager_options),
-#if defined(OS_STARBOARD)
is_rendered_(false),
-#endif // OS_STARBOARD
#if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
array_buffer_allocator_(
new ResourceProviderArrayBufferAllocator(GetResourceProvider())),
@@ -251,7 +254,8 @@
#endif
will_quit_(false),
application_state_(initial_application_state),
- splash_screen_cache_(new SplashScreenCache()) {
+ splash_screen_cache_(new SplashScreenCache()),
+ produced_render_tree_(false) {
#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
SbCoreDumpRegisterHandler(BrowserModule::CoreDumpHandler, this);
on_error_triggered_count_ = 0;
@@ -296,9 +300,7 @@
OnFuzzerToggle(std::string());
}
if (command_line->HasSwitch(switches::kSuspendFuzzer)) {
-#if SB_API_VERSION >= 4
suspend_fuzzer_.emplace();
-#endif
}
#endif // ENABLE_DEBUG_CONSOLE && ENABLE_DEBUG_COMMAND_LINE_SWITCHES
@@ -403,13 +405,18 @@
base::optional<std::string> key = SplashScreenCache::GetKeyForStartUrl(url);
if (fallback_splash_screen_url_ ||
(key && splash_screen_cache_->IsSplashScreenCached(*key))) {
+ // Create the splash screen layer.
+ splash_screen_layer_ =
+ render_tree_combiner_->CreateLayer(kSplashScreenZIndex);
+
splash_screen_.reset(new SplashScreen(
application_state_,
- base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
+ base::Bind(&BrowserModule::QueueOnSplashScreenRenderTreeProduced,
base::Unretained(this)),
&network_module_, viewport_size, GetResourceProvider(),
kLayoutMaxRefreshFrequencyInHz, *fallback_splash_screen_url_, url,
- splash_screen_cache_.get()));
+ splash_screen_cache_.get(),
+ base::Bind(&BrowserModule::DestroySplashScreen, weak_this_)));
lifecycle_observers_.AddObserver(splash_screen_.get());
}
@@ -473,8 +480,6 @@
return;
}
- DestroySplashScreen();
-
// This log is relied on by the webdriver benchmark tests, so it shouldn't be
// changed unless the corresponding benchmark logic is changed as well.
LOG(INFO) << "Loaded WebModule";
@@ -519,26 +524,67 @@
base::Bind(&BrowserModule::ProcessRenderTreeSubmissionQueue, weak_this_));
}
+void BrowserModule::QueueOnSplashScreenRenderTreeProduced(
+ const browser::WebModule::LayoutResults& layout_results) {
+ TRACE_EVENT0("cobalt::browser",
+ "BrowserModule::QueueOnSplashScreenRenderTreeProduced()");
+ render_tree_submission_queue_.AddMessage(
+ base::Bind(&BrowserModule::OnSplashScreenRenderTreeProduced,
+ base::Unretained(this), layout_results));
+ self_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&BrowserModule::ProcessRenderTreeSubmissionQueue, weak_this_));
+}
+
void BrowserModule::OnRenderTreeProduced(
const browser::WebModule::LayoutResults& layout_results) {
TRACE_EVENT0("cobalt::browser", "BrowserModule::OnRenderTreeProduced()");
DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+
+ if (splash_screen_ && !produced_render_tree_) {
+ splash_screen_->Shutdown();
+ }
+ produced_render_tree_ = true;
+
if (application_state_ == base::kApplicationStatePreloading ||
- !render_tree_combiner_) {
+ !render_tree_combiner_ || !main_web_module_layer_) {
+ return;
+ }
+ renderer::Submission renderer_submission(layout_results.render_tree,
+ layout_results.layout_time);
+ renderer_submission.on_rasterized_callback = base::Bind(
+ &BrowserModule::OnRendererSubmissionRasterized, base::Unretained(this));
+ main_web_module_layer_->Submit(renderer_submission, true /* receive_time */);
+
+#if defined(ENABLE_SCREENSHOT)
+ screen_shot_writer_->SetLastPipelineSubmission(renderer::Submission(
+ layout_results.render_tree, layout_results.layout_time));
+#endif
+}
+
+void BrowserModule::OnSplashScreenRenderTreeProduced(
+ const browser::WebModule::LayoutResults& layout_results) {
+ TRACE_EVENT0("cobalt::browser",
+ "BrowserModule::OnSplashScreenRenderTreeProduced()");
+ DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+
+ if (application_state_ == base::kApplicationStatePreloading ||
+ !render_tree_combiner_ || !splash_screen_layer_) {
return;
}
renderer::Submission renderer_submission(layout_results.render_tree,
layout_results.layout_time);
-#if defined(OS_STARBOARD)
renderer_submission.on_rasterized_callback = base::Bind(
&BrowserModule::OnRendererSubmissionRasterized, base::Unretained(this));
-#endif // OS_STARBOARD
- render_tree_combiner_->UpdateMainRenderTree(renderer_submission);
+ splash_screen_layer_->Submit(renderer_submission, false /* receive_time */);
#if defined(ENABLE_SCREENSHOT)
- screen_shot_writer_->SetLastPipelineSubmission(renderer::Submission(
- layout_results.render_tree, layout_results.layout_time));
+// TODO: write screen shot using render_tree_combinder_ (to combine
+// splash screen and main web_module). Consider when the splash
+// screen is overlaid on top of the main web module render tree, and
+// a screenshot is taken : there will be a race condition on which
+// web module update their render tree last.
#endif
}
@@ -549,11 +595,7 @@
}
#endif
-#if defined(OS_STARBOARD)
SbSystemRequestStop(0);
-#else
- LOG(WARNING) << "window.close() is not supported on this platform.";
-#endif
}
void BrowserModule::OnWindowMinimize() {
@@ -563,11 +605,7 @@
}
#endif
-#if defined(OS_STARBOARD) && SB_API_VERSION >= 4
SbSystemRequestSuspend();
-#else
- LOG(WARNING) << "window.minimize() is not supported on this platform.";
-#endif
}
#if defined(ENABLE_DEBUG_CONSOLE)
@@ -632,16 +670,16 @@
"BrowserModule::OnDebugConsoleRenderTreeProduced()");
DCHECK_EQ(MessageLoop::current(), self_message_loop_);
if (application_state_ == base::kApplicationStatePreloading ||
- !render_tree_combiner_) {
+ !render_tree_combiner_ || !debug_console_layer_) {
return;
}
if (debug_console_->GetMode() == debug::DebugHub::kDebugConsoleOff) {
- render_tree_combiner_->UpdateDebugConsoleRenderTree(base::nullopt);
+ debug_console_layer_->Submit(base::nullopt);
return;
}
- render_tree_combiner_->UpdateDebugConsoleRenderTree(renderer::Submission(
+ debug_console_layer_->Submit(renderer::Submission(
layout_results.render_tree, layout_results.layout_time));
}
@@ -804,9 +842,15 @@
void BrowserModule::DestroySplashScreen() {
TRACE_EVENT0("cobalt::browser", "BrowserModule::DestroySplashScreen()");
+ if (MessageLoop::current() != self_message_loop_) {
+ self_message_loop_->PostTask(
+ FROM_HERE, base::Bind(&BrowserModule::DestroySplashScreen, weak_this_));
+ return;
+ }
if (splash_screen_) {
lifecycle_observers_.RemoveObserver(splash_screen_.get());
}
+ splash_screen_layer_.reset(NULL);
splash_screen_.reset(NULL);
}
@@ -912,6 +956,22 @@
application_state_ = base::kApplicationStatePaused;
}
+void BrowserModule::ReduceMemory() {
+ if (splash_screen_) {
+ splash_screen_->ReduceMemory();
+ }
+
+#if defined(ENABLE_DEBUG_CONSOLE)
+ if (debug_console_) {
+ debug_console_->ReduceMemory();
+ }
+#endif // defined(ENABLE_DEBUG_CONSOLE)
+
+ if (web_module_) {
+ web_module_->ReduceMemory();
+ }
+}
+
void BrowserModule::CheckMemory(
const int64_t& used_cpu_memory,
const base::optional<int64_t>& used_gpu_memory) {
@@ -923,7 +983,6 @@
used_gpu_memory);
}
-#if defined(OS_STARBOARD)
void BrowserModule::OnRendererSubmissionRasterized() {
TRACE_EVENT0("cobalt::browser",
"BrowserModule::OnRendererSubmissionRasterized()");
@@ -933,7 +992,6 @@
SbSystemHideSplashScreen();
}
}
-#endif // OS_STARBOARD
#if defined(COBALT_CHECK_RENDER_TIMEOUT)
void BrowserModule::OnPollForRenderTimeout(const GURL& url) {
@@ -1021,11 +1079,14 @@
render_tree_combiner_.reset(
new RenderTreeCombiner(renderer_module_.get(), GetViewportSize()));
-
- // Always render the debug console. It will draw nothing if disabled.
- // This setting is ignored if ENABLE_DEBUG_CONSOLE is not defined.
- // TODO: Render tree combiner should probably be refactored.
- render_tree_combiner_->set_render_debug_console(true);
+ // Create the main web module layer.
+ main_web_module_layer_ =
+ render_tree_combiner_->CreateLayer(kMainWebModuleZIndex);
+// Create the debug console layer.
+#if defined(ENABLE_DEBUG_CONSOLE)
+ debug_console_layer_ =
+ render_tree_combiner_->CreateLayer(kDebugConsoleZIndex);
+#endif
#if defined(ENABLE_SCREENSHOT)
screen_shot_writer_.reset(new ScreenShotWriter(renderer_module_->pipeline()));
@@ -1085,8 +1146,14 @@
// Clear out the render tree combiner so that it doesn't hold on to any
// render tree resources either.
- if (render_tree_combiner_) {
- render_tree_combiner_->Reset();
+ if (main_web_module_layer_) {
+ main_web_module_layer_->Reset();
+ }
+ if (splash_screen_layer_) {
+ splash_screen_layer_->Reset();
+ }
+ if (debug_console_layer_) {
+ debug_console_layer_->Reset();
}
#if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index ba3861d..386eb0e 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -142,6 +142,10 @@
void Suspend();
void Resume();
+ // Attempt to reduce overall memory consumption. Called in response to a
+ // system indication that memory usage is nearing a critical level.
+ void ReduceMemory();
+
void CheckMemory(const int64_t& used_cpu_memory,
const base::optional<int64_t>& used_gpu_memory);
@@ -173,6 +177,13 @@
void OnRenderTreeProduced(
const browser::WebModule::LayoutResults& layout_results);
+ // Glue function to deal with the production of the splash screen render tree,
+ // and will manage handing it off to the renderer.
+ void QueueOnSplashScreenRenderTreeProduced(
+ const browser::WebModule::LayoutResults& layout_results);
+ void OnSplashScreenRenderTreeProduced(
+ const browser::WebModule::LayoutResults& layout_results);
+
// Saves/loads the debug console mode to/from local storage so we can
// persist the user's preference.
void SaveDebugConsoleMode();
@@ -357,8 +368,11 @@
// Sets up the network component for requesting internet resources.
network::NetworkModule network_module_;
- // Manages the two render trees, combines and renders them.
+ // Manages the three render trees, combines and renders them.
scoped_ptr<RenderTreeCombiner> render_tree_combiner_;
+ scoped_ptr<RenderTreeCombiner::Layer> main_web_module_layer_;
+ scoped_ptr<RenderTreeCombiner::Layer> debug_console_layer_;
+ scoped_ptr<RenderTreeCombiner::Layer> splash_screen_layer_;
#if defined(ENABLE_SCREENSHOT)
// Helper object to create screen shots of the last layout tree.
@@ -459,6 +473,9 @@
// The splash screen cache.
scoped_ptr<SplashScreenCache> splash_screen_cache_;
+
+ // Whether or not the main WebModule has produced any render trees yet.
+ bool produced_render_tree_;
};
} // namespace browser
diff --git a/src/cobalt/browser/debug_console.h b/src/cobalt/browser/debug_console.h
index 9ecbef5..6ed291d 100644
--- a/src/cobalt/browser/debug_console.h
+++ b/src/cobalt/browser/debug_console.h
@@ -79,6 +79,8 @@
web_module_->Resume(resource_provider);
}
+ void ReduceMemory() { web_module_->ReduceMemory(); }
+
private:
void OnError(const GURL& /* url */, const std::string& error) {
LOG(ERROR) << error;
diff --git a/src/cobalt/browser/debug_console/console_values.js b/src/cobalt/browser/debug_console/console_values.js
index 2ed232e..444ca48 100644
--- a/src/cobalt/browser/debug_console/console_values.js
+++ b/src/cobalt/browser/debug_console/console_values.js
@@ -20,8 +20,9 @@
this.DEFAULT_KEY = 'default';
// Reduced space-separated list of CVal prefixes to display at start-up.
this.DEFAULT_ACTIVE_SET =
- 'Cobalt DevTools Memory.CPU Memory.MainWebModule Memory.JS Memory.Font ' +
- 'Event.Duration.MainWebModule.KeyDown Renderer.Rasterize.Duration';
+ 'Cobalt DevTools WebDriver Memory.CPU Memory.MainWebModule Memory.JS ' +
+ 'Memory.Font Event.Duration.MainWebModule.KeyDown ' +
+ 'Renderer.Rasterize.Duration';
var names = window.debugHub.getConsoleValueNames();
this.allCVals = names.split(' ');
diff --git a/src/cobalt/browser/memory_tracker/tool/tool_impl.cc b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
index f2a92bb..65de06f 100644
--- a/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
+++ b/src/cobalt/browser/memory_tracker/tool/tool_impl.cc
@@ -30,7 +30,6 @@
#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"
diff --git a/src/cobalt/browser/render_tree_combiner.cc b/src/cobalt/browser/render_tree_combiner.cc
index 171fbc3..78fe4be 100644
--- a/src/cobalt/browser/render_tree_combiner.cc
+++ b/src/cobalt/browser/render_tree_combiner.cc
@@ -14,99 +14,97 @@
#include "cobalt/browser/render_tree_combiner.h"
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/optional.h"
+#include "base/time.h"
#include "cobalt/render_tree/composition_node.h"
#include "cobalt/render_tree/rect_node.h"
+#include "cobalt/renderer/renderer_module.h"
+#include "cobalt/renderer/submission.h"
namespace cobalt {
namespace browser {
-#if defined(ENABLE_DEBUG_CONSOLE)
+RenderTreeCombiner::Layer::Layer(RenderTreeCombiner* render_tree_combiner)
+ : render_tree_combiner_(render_tree_combiner),
+ render_tree_(base::nullopt),
+ receipt_time_(base::nullopt) {}
+
+RenderTreeCombiner::Layer::~Layer() {
+ DCHECK(render_tree_combiner_);
+ render_tree_combiner_->RemoveLayer(this);
+}
+
+void RenderTreeCombiner::Layer::Submit(
+ const base::optional<renderer::Submission>& render_tree_submission,
+ bool receive_time) {
+ render_tree_ = render_tree_submission;
+ if (receive_time) {
+ receipt_time_ = base::TimeTicks::HighResNow();
+ } else {
+ receipt_time_ = base::nullopt;
+ }
+ DCHECK(render_tree_combiner_);
+ render_tree_combiner_->SubmitToRenderer();
+}
+
RenderTreeCombiner::RenderTreeCombiner(
renderer::RendererModule* renderer_module, const math::Size& viewport_size)
- : render_debug_console_(true),
- renderer_module_(renderer_module),
- viewport_size_(viewport_size) {}
+ : renderer_module_(renderer_module), viewport_size_(viewport_size) {}
-RenderTreeCombiner::~RenderTreeCombiner() {}
+scoped_ptr<RenderTreeCombiner::Layer> RenderTreeCombiner::CreateLayer(
+ int z_index) {
+ if (layers_.count(z_index) > 0) {
+ return scoped_ptr<RenderTreeCombiner::Layer>(NULL);
+ }
+ RenderTreeCombiner::Layer* layer = new Layer(this);
+ layers_[z_index] = layer;
-void RenderTreeCombiner::Reset() {
- main_render_tree_ = base::nullopt;
- debug_console_render_tree_ = base::nullopt;
- main_render_tree_receipt_time_ = base::nullopt;
+ return scoped_ptr<RenderTreeCombiner::Layer>(layers_[z_index]);
}
-void RenderTreeCombiner::UpdateMainRenderTree(
- const renderer::Submission& render_tree_submission) {
- main_render_tree_ = render_tree_submission;
- main_render_tree_receipt_time_ = base::TimeTicks::HighResNow();
- SubmitToRenderer();
-}
-
-void RenderTreeCombiner::UpdateDebugConsoleRenderTree(
- const base::optional<renderer::Submission>& render_tree_submission) {
- debug_console_render_tree_ = render_tree_submission;
- SubmitToRenderer();
+void RenderTreeCombiner::RemoveLayer(const Layer* layer) {
+ for (auto it = layers_.begin(); it != layers_.end(); /* no increment */) {
+ if (it->second == layer) {
+ it = layers_.erase(it);
+ } else {
+ ++it;
+ }
+ }
}
void RenderTreeCombiner::SubmitToRenderer() {
- if (render_debug_console_ && debug_console_render_tree_) {
- if (main_render_tree_) {
- render_tree::CompositionNode::Builder builder;
- builder.AddChild(main_render_tree_->render_tree);
- builder.AddChild(debug_console_render_tree_->render_tree);
- scoped_refptr<render_tree::Node> combined_tree =
- new render_tree::CompositionNode(builder);
+ render_tree::CompositionNode::Builder builder;
- // Setup time to be based off of the main submitted tree only.
- // TODO: Setup a "layers" interface on the Pipeline so that
- // trees can be combined and animated there, properly.
- renderer::Submission combined_submission(*main_render_tree_);
- combined_submission.render_tree = combined_tree;
- combined_submission.time_offset =
- main_render_tree_->time_offset +
- (base::TimeTicks::HighResNow() - *main_render_tree_receipt_time_);
-
- renderer_module_->pipeline()->Submit(combined_submission);
- } else {
- // If we are rendering the debug console by itself, give it a solid black
- // background to it.
- render_tree::CompositionNode::Builder builder;
- builder.AddChild(new render_tree::RectNode(
- math::RectF(viewport_size_),
- scoped_ptr<render_tree::Brush>(new render_tree::SolidColorBrush(
- render_tree::ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)))));
- builder.AddChild(debug_console_render_tree_->render_tree);
-
- renderer::Submission combined_submission(*debug_console_render_tree_);
- combined_submission.render_tree =
- new render_tree::CompositionNode(builder);
- renderer_module_->pipeline()->Submit(combined_submission);
+ // Add children for all layers in order.
+ base::optional<renderer::Submission> first_tree = base::nullopt;
+ base::optional<renderer::Submission> combined_submission = base::nullopt;
+ for (auto it = layers_.begin(); it != layers_.end(); ++it) {
+ RenderTreeCombiner::Layer* layer = it->second;
+ if (layer->render_tree_) {
+ builder.AddChild(layer->render_tree_->render_tree);
+ first_tree = layer->render_tree_;
+ // Make the combined submission with the first receipt_time_ we find.
+ if (!combined_submission && layer->receipt_time_) {
+ combined_submission = renderer::Submission(*layer->render_tree_);
+ combined_submission->time_offset =
+ layer->render_tree_->time_offset +
+ (base::TimeTicks::HighResNow() - *layer->receipt_time_);
+ }
}
- } else if (main_render_tree_) {
- renderer_module_->pipeline()->Submit(*main_render_tree_);
}
+ if (!first_tree) {
+ return;
+ }
+ if (!combined_submission) {
+ // None of the layers store the time.
+ combined_submission = renderer::Submission(*first_tree);
+ }
+
+ combined_submission->render_tree = new render_tree::CompositionNode(builder);
+ renderer_module_->pipeline()->Submit(*combined_submission);
}
-#else // ENABLE_DEBUG_CONSOLE
-RenderTreeCombiner::RenderTreeCombiner(
- renderer::RendererModule* renderer_module, const math::Size& viewport_size)
- : renderer_module_(renderer_module) {
- UNREFERENCED_PARAMETER(viewport_size);
-}
-
-RenderTreeCombiner::~RenderTreeCombiner() {}
-
-void RenderTreeCombiner::Reset() {}
-
-void RenderTreeCombiner::UpdateMainRenderTree(
- const renderer::Submission& render_tree_submission) {
- renderer_module_->pipeline()->Submit(render_tree_submission);
-}
-
-void RenderTreeCombiner::UpdateDebugConsoleRenderTree(
- const base::optional<renderer::Submission>& render_tree_submission) {
- UNREFERENCED_PARAMETER(render_tree_submission);
-}
-#endif // ENABLE_DEBUG_CONSOLE
-
} // namespace browser
} // namespace cobalt
diff --git a/src/cobalt/browser/render_tree_combiner.h b/src/cobalt/browser/render_tree_combiner.h
index 76b1a22..65f3127 100644
--- a/src/cobalt/browser/render_tree_combiner.h
+++ b/src/cobalt/browser/render_tree_combiner.h
@@ -15,49 +15,72 @@
#ifndef COBALT_BROWSER_RENDER_TREE_COMBINER_H_
#define COBALT_BROWSER_RENDER_TREE_COMBINER_H_
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/optional.h"
+#include "base/time.h"
#include "cobalt/renderer/renderer_module.h"
#include "cobalt/renderer/submission.h"
namespace cobalt {
namespace browser {
-// Combines the main and debug console render trees. Caches the individual
-// trees as they are produced. Re-renders when either tree changes.
-// This class is only fully implemented when ENABLE_DEBUG_CONSOLE is defined,
-// otherwise (e.g. in release builds) a stub implementation is used.
+// Combines rendering layers (such as the main, splash screen, and
+// debug console). Caches the individual trees as they are produced.
+// Re-renders when any tree changes.
class RenderTreeCombiner {
public:
+ // Layer represents the render layer corresponding to the main web
+ // module, the splash screen, or the debug console and are used to
+ // create and submit a combined tree to the RendererModule's
+ // pipeline. Layers are combined in order of the |z_index| specifed
+ // at the Layers' creation. The RenderTreeCombiner stores pointers
+ // to Layers. The Layers are owned by the caller of
+ // RenderTreeCombiner::CreateLayer.
+ class Layer {
+ public:
+ ~Layer();
+
+ void Reset() {
+ render_tree_ = base::nullopt;
+ receipt_time_ = base::nullopt;
+ }
+
+ // Submit render tree to the layer, and specify whether the time
+ // received should be stored.
+ void Submit(
+ const base::optional<renderer::Submission>& render_tree_submission,
+ bool receive_time = false);
+
+ private:
+ friend class RenderTreeCombiner;
+
+ explicit Layer(RenderTreeCombiner* render_tree_combiner = NULL);
+
+ RenderTreeCombiner* render_tree_combiner_;
+
+ base::optional<renderer::Submission> render_tree_;
+ base::optional<base::TimeTicks> receipt_time_;
+ };
+
explicit RenderTreeCombiner(renderer::RendererModule* renderer_module,
const math::Size& viewport_size);
- ~RenderTreeCombiner();
+ ~RenderTreeCombiner() {}
- void Reset();
-
- // Update the main web module render tree.
- void UpdateMainRenderTree(const renderer::Submission& render_tree_submission);
-
- // Update the debug console render tree.
- void UpdateDebugConsoleRenderTree(
- const base::optional<renderer::Submission>& render_tree_submission);
-
-#if defined(ENABLE_DEBUG_CONSOLE)
- bool render_debug_console() const { return render_debug_console_; }
- void set_render_debug_console(bool render_debug_console) {
- render_debug_console_ = render_debug_console;
- }
-#else // ENABLE_DEBUG_CONSOLE
- bool render_debug_console() const { return false; }
- void set_render_debug_console(bool render_debug_console) {
- UNREFERENCED_PARAMETER(render_debug_console);
- }
-#endif // ENABLE_DEBUG_CONSOLE
+ // Create a Layer with a given |z_index|. If a Layer already exists
+ // at |z_index|, return NULL, and no Layer is created.
+ scoped_ptr<Layer> CreateLayer(int z_index);
private:
-#if defined(ENABLE_DEBUG_CONSOLE)
- // Combines the two cached render trees (main/debug) and renders the result.
- void SubmitToRenderer();
+ // The layers keyed on their z_index.
+ std::map<int, Layer*> layers_;
- bool render_debug_console_;
+ // Removes a layer from |layers_|. Called by the Layer destructor.
+ void RemoveLayer(const Layer* layer);
+
+ // Combines the cached render trees and renders the result.
+ void SubmitToRenderer();
// Local reference to the render pipeline, so we can submit the combined tree.
// Reference counted pointer not necessary here.
@@ -65,22 +88,6 @@
// The size of the output viewport.
math::Size viewport_size_;
-
- // Local references to the main and debug console render trees/animation maps
- // so we can combine them.
- base::optional<renderer::Submission> main_render_tree_;
-
- // This is the time that we received the last main render tree submission.
- // used so that we know what time to forward the submission to the pipeline
- // with.
- base::optional<base::TimeTicks> main_render_tree_receipt_time_;
-
- // The debug console render tree submission.
- base::optional<renderer::Submission> debug_console_render_tree_;
-#else // ENABLE_DEBUG_CONSOLE
- // Use this local reference even in release builds to submit the main tree.
- renderer::RendererModule* renderer_module_;
-#endif // ENABLE_DEBUG_CONSOLE
};
} // namespace browser
diff --git a/src/cobalt/browser/splash_screen.cc b/src/cobalt/browser/splash_screen.cc
index 8c78ce2..2999b41 100644
--- a/src/cobalt/browser/splash_screen.cc
+++ b/src/cobalt/browser/splash_screen.cc
@@ -17,25 +17,51 @@
#include <string>
#include "base/bind.h"
+#include "base/callback.h"
+#include "base/cancelable_callback.h"
#include "base/threading/platform_thread.h"
+#include "base/time.h"
#include "cobalt/browser/splash_screen_cache.h"
#include "cobalt/loader/cache_fetcher.h"
namespace cobalt {
namespace browser {
+namespace {
-SplashScreen::SplashScreen(base::ApplicationState initial_application_state,
- 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 GURL& fallback_splash_screen_url,
- const GURL& initial_main_web_module_url,
- SplashScreenCache* splash_screen_cache)
+const int kSplashShutdownSeconds = 2;
+
+void PostCallbackToMessageLoop(const base::Closure& callback,
+ MessageLoop* message_loop) {
+ DCHECK(message_loop);
+ message_loop->PostTask(FROM_HERE, callback);
+}
+
+// TODO: consolidate definitions of BindToLoop / BindToCurrentLoop
+// from here and media in base.
+base::Closure BindToLoop(const base::Closure& callback,
+ MessageLoop* message_loop) {
+ return base::Bind(&PostCallbackToMessageLoop, callback, message_loop);
+}
+
+void OnError(const GURL& /* url */, const std::string& error) {
+ LOG(ERROR) << error;
+}
+
+} // namespace
+
+SplashScreen::SplashScreen(
+ base::ApplicationState initial_application_state,
+ 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 GURL& fallback_splash_screen_url,
+ const GURL& initial_main_web_module_url,
+ SplashScreenCache* splash_screen_cache,
+ const base::Callback<void()>& on_splash_screen_shutdown_complete)
: render_tree_produced_callback_(render_tree_produced_callback),
- is_ready_(true, false) {
+ self_message_loop_(MessageLoop::current()),
+ on_splash_screen_shutdown_complete_(on_splash_screen_shutdown_complete) {
WebModule::Options web_module_options;
web_module_options.name = "SplashScreenWebModule";
@@ -57,11 +83,14 @@
web_module_options.splash_screen_cache = splash_screen_cache;
}
+ base::Callback<void()> on_window_close(
+ BindToLoop(on_splash_screen_shutdown_complete, self_message_loop_));
+
+ web_module_options.on_before_unload_fired_but_not_handled = on_window_close;
+
web_module_.reset(new WebModule(
- url_to_pass, initial_application_state,
- base::Bind(&SplashScreen::OnRenderTreeProduced, base::Unretained(this)),
- base::Bind(&SplashScreen::OnError, base::Unretained(this)),
- base::Bind(&SplashScreen::OnWindowClosed, base::Unretained(this)),
+ url_to_pass, initial_application_state, render_tree_produced_callback_,
+ base::Bind(&OnError), on_window_close,
base::Closure(), // window_minimize_callback
&stub_media_module_, network_module, window_dimensions,
1.f /*video_pixel_ratio*/, resource_provider, layout_refresh_rate,
@@ -69,23 +98,23 @@
}
SplashScreen::~SplashScreen() {
+ DCHECK_EQ(MessageLoop::current(), self_message_loop_);
// Destroy the web module first to prevent our callbacks from being called
// (from another thread) while member objects are being destroyed.
web_module_.reset();
+ // Cancel any pending run of the splash screen shutdown callback.
+ on_splash_screen_shutdown_complete_.Cancel();
}
-void SplashScreen::WaitUntilReady() {
- is_ready_.Wait();
-}
-
-void SplashScreen::OnRenderTreeProduced(
- const browser::WebModule::LayoutResults& layout_results) {
- is_ready_.Signal();
- render_tree_produced_callback_.Run(layout_results);
-}
-
-void SplashScreen::OnWindowClosed() {
- is_ready_.Signal();
+void SplashScreen::Shutdown() {
+ DCHECK_EQ(MessageLoop::current(), self_message_loop_);
+ DCHECK(web_module_);
+ if (!on_splash_screen_shutdown_complete_.callback().is_null()) {
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, on_splash_screen_shutdown_complete_.callback(),
+ base::TimeDelta::FromSeconds(kSplashShutdownSeconds));
+ }
+ web_module_->InjectBeforeUnloadEvent();
}
} // namespace browser
diff --git a/src/cobalt/browser/splash_screen.h b/src/cobalt/browser/splash_screen.h
index 0e07915..73db062 100644
--- a/src/cobalt/browser/splash_screen.h
+++ b/src/cobalt/browser/splash_screen.h
@@ -33,16 +33,17 @@
//
class SplashScreen : public LifecycleObserver {
public:
- SplashScreen(base::ApplicationState initial_application_state,
- 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 GURL& fallback_splash_screen_url,
- const GURL& initial_main_web_module_url,
- cobalt::browser::SplashScreenCache* splash_screen_cache);
+ SplashScreen(
+ base::ApplicationState initial_application_state,
+ 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 GURL& fallback_splash_screen_url,
+ const GURL& initial_main_web_module_url,
+ cobalt::browser::SplashScreenCache* splash_screen_cache,
+ const base::Callback<void()>& on_splash_screen_shutdown_complete);
~SplashScreen();
void SetSize(const math::Size& window_dimensions, float video_pixel_ratio) {
@@ -61,29 +62,32 @@
web_module_->Resume(resource_provider);
}
- // Block the caller until the splash screen is ready to be rendered.
- void WaitUntilReady();
+ void ReduceMemory() { web_module_->ReduceMemory(); }
+
+ // This dispatches event beforeunload in the WebModule. If
+ // beforeunload has any handlers or listeners, Shutdown waits for
+ // window.close to be called or a maximum of kSplashShutdownSeconds
+ // before running |on_splash_screen_shutdown_complete_|. If beforeunload has
+ // no handlers, |on_splash_screen_shutdown_complete_| is run immediately.
+ void Shutdown();
private:
- void OnRenderTreeProduced(
- const browser::WebModule::LayoutResults& layout_results);
-
- void OnError(const GURL& /* url */, const std::string& error) {
- is_ready_.Signal();
- LOG(ERROR) << error;
- }
-
+ // Run when window.close() is called by the WebModule.
void OnWindowClosed();
+ void OnWindowClosedInternal();
media::MediaModuleStub stub_media_module_;
WebModule::OnRenderTreeProducedCallback render_tree_produced_callback_;
- // Signalled once the splash screen has produced its first render tree or
- // an error occurred.
- base::WaitableEvent is_ready_;
-
scoped_ptr<WebModule> web_module_;
+
+ // The splash screen runs on this message loop.
+ MessageLoop* const self_message_loop_;
+
+ // This is called by Shutdown (via window.close) or after
+ // the time limit has been exceeded.
+ base::CancelableClosure on_splash_screen_shutdown_complete_;
};
} // namespace browser
diff --git a/src/cobalt/browser/suspend_fuzzer.cc b/src/cobalt/browser/suspend_fuzzer.cc
index b94158b..51a7265 100644
--- a/src/cobalt/browser/suspend_fuzzer.cc
+++ b/src/cobalt/browser/suspend_fuzzer.cc
@@ -43,20 +43,13 @@
void SuspendFuzzer::DoStep() {
DCHECK(MessageLoop::current() == thread_.message_loop());
-#if SB_API_VERSION < 4
- NOTREACHED() << "Cannot run suspend_fuzzer on SB_API_VERSION < 4.";
-#endif
if (step_type_ == kShouldRequestSuspend) {
SB_DLOG(INFO) << "suspend_fuzzer: Requesting suspend.";
-#if SB_API_VERSION >= 4
SbSystemRequestSuspend();
-#endif
step_type_ = kShouldRequestUnpause;
} else if (step_type_ == kShouldRequestUnpause) {
SB_DLOG(INFO) << "suspend_fuzzer: Requesting unpause.";
-#if SB_API_VERSION >= 4
SbSystemRequestUnpause();
-#endif
step_type_ = kShouldRequestSuspend;
} else {
NOTREACHED();
diff --git a/src/cobalt/browser/suspend_fuzzer.h b/src/cobalt/browser/suspend_fuzzer.h
index 4aa97cc..d1681cf 100644
--- a/src/cobalt/browser/suspend_fuzzer.h
+++ b/src/cobalt/browser/suspend_fuzzer.h
@@ -22,7 +22,7 @@
namespace browser {
// Repeatedly switch off between calling |SbSystemRequestSuspend| and
-// |SbSystemRequestUnpause|, or just no-op if on an SB_API_VERSION < 4.
+// |SbSystemRequestUnpause|.
class SuspendFuzzer {
public:
SuspendFuzzer();
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 2c39eed..11e98c3 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -34,12 +34,12 @@
// Switches different debug console modes: on | hud | off
const char kDebugConsoleMode[] = "debug_console";
+// Do not create the WebDriver server.
+const char kDisableWebDriver[] = "disable_webdriver";
+
// Disable webm/vp9.
const char kDisableWebmVp9[] = "disable_webm_vp9";
-// Create WebDriver server.
-const char kEnableWebDriver[] = "enable_webdriver";
-
// Additional base directory for accessing web files via file://.
const char kExtraWebFileDir[] = "web_file_path";
@@ -83,8 +83,7 @@
const char kStubImageDecoder[] = "stub_image_decoder";
// If this flag is set, alternating calls to |SbSystemRequestSuspend| and
-// |SbSystemRequestUnpause| will be made periodically. Requires
-// SB_API_VERSION >= 4, and will otherwise just no-op.
+// |SbSystemRequestUnpause| will be made periodically.
const char kSuspendFuzzer[] = "suspend_fuzzer";
// If this is set, then a trace (see base/debug/trace_eventh.h) is started on
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 4470c4e..4dc02ef 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -24,8 +24,8 @@
extern const char kAudioDecoderStub[];
extern const char kCspMode[];
extern const char kDebugConsoleMode[];
+extern const char kDisableWebDriver[];
extern const char kDisableWebmVp9[];
-extern const char kEnableWebDriver[];
extern const char kExtraWebFileDir[];
extern const char kFakeMicrophone[];
extern const char kIgnoreCertificateErrors[];
diff --git a/src/cobalt/browser/testdata/splash_screen/beforeunload.html b/src/cobalt/browser/testdata/splash_screen/beforeunload.html
new file mode 100644
index 0000000..5b6ecf9
--- /dev/null
+++ b/src/cobalt/browser/testdata/splash_screen/beforeunload.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+.box {
+ width: 100px;
+ height: 100px;
+ background-color: red;
+ color : yellow;
+ font-size: 20px;
+ left: 0px;
+ top: 0px;
+ position: absolute;
+ transition: background-color .25s;
+ transition-timing-function: ease;
+}
+.box1{
+ width: 100px;
+ height: 100px;
+ background-color: blue;
+ color: yellow;
+ font-size: 20px;
+ left: 0px;
+ top: 0px;
+ position:absolute;
+ transition: background-color .25s;
+ transition-timing-function: ease;
+}
+ </style>
+ </head>
+<body style="background-color: #1f52a5;">
+<div class="box" id="box">Sample</div>
+<div id="immediately" style="display:block;">
+THIS SHOWS IMMEDIATELY
+</div>
+
+<div id="beforeUnload" style="display:none;">
+THIS SHOWS AT BEFOREUNLOAD
+</div>
+
+<div id="transitionEnd" style="display:none;">
+THIS SHOWS AT TRANSITIONEND
+</div>
+
+
+<script>
+ console.log('Running the script in beforeunload.html');
+
+ function updateTransition() {
+ var el = document.getElementById("box");
+ if (el.className == "box") {
+ el.className = "box1";
+ } else {
+ el.className = "box";
+ }
+ }
+
+ function transitionEndFunction() {
+ console.log("transitionend event");
+ // Set this to true to simulate an unresponsive splash screen which does
+ // not call window.close().
+ var unresponsive = true;
+ // This style change is only shown if unresponsive.
+ document.getElementById("transitionEnd").style.display="block";
+ // Comment this out to test out an unresponsive
+ // window.ontransitionend function.
+ if (!unresponsive) {
+ window.close();
+ }
+ }
+ // Either event handler or event listeners should work.
+ window.ontransitionend = transitionEndFunction;
+ //window.addEventListener("transitionend", transitionEndFunction, true);
+
+ function beforeUnloadFunction() {
+ console.log("beforeunload event");
+ document.getElementById("immediately").style.display="none";
+ document.getElementById("beforeUnload").style.display="block";
+ window.updateTransition();
+ // Returning a string shows a confirmation dialog in Chrome.
+ return "returning text is futile";
+ };
+ // Either event handler or event listeners should work.
+ // window.onbeforeunload = beforeUnloadFunction;
+ window.addEventListener("beforeunload", beforeUnloadFunction, true);
+
+ console.log("Ran the script in beforeunload.html");
+</script>
+
+</body>
+</html>
diff --git a/src/cobalt/browser/testdata/splash_screen/block_render_tree_head_body_display_none.html b/src/cobalt/browser/testdata/splash_screen/block_render_tree_head_body_display_none.html
new file mode 100644
index 0000000..f481c4d
--- /dev/null
+++ b/src/cobalt/browser/testdata/splash_screen/block_render_tree_head_body_display_none.html
@@ -0,0 +1,441 @@
+<!DOCTYPE html>
+<html>
+
+<head style="display : none">
+ <meta http-equiv="Content-Security-Policy" content="
+ default-src 'unsafe-inline';
+ style-src 'unsafe-inline';
+ script-src 'unsafe-inline';">
+</head>
+
+<script>
+ window.setTimeout(function() {
+ document.getElementsByTagName('body')[0].style.display = 'block';
+ }, 5000);
+</script>
+
+<body style="background-color: #1f52a5; display: none">
+<h1>Heading</h1>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+</body>
+</html>
diff --git a/src/cobalt/browser/testdata/splash_screen/block_render_tree_html_display_none.html b/src/cobalt/browser/testdata/splash_screen/block_render_tree_html_display_none.html
new file mode 100644
index 0000000..d5f4552
--- /dev/null
+++ b/src/cobalt/browser/testdata/splash_screen/block_render_tree_html_display_none.html
@@ -0,0 +1,440 @@
+<!DOCTYPE html>
+<html style="display: none">
+
+<head>
+ <meta http-equiv="Content-Security-Policy" content="
+ default-src 'unsafe-inline';
+ style-src 'unsafe-inline';
+ script-src 'unsafe-inline';">
+</head>
+
+<script>
+ window.setTimeout(function() {
+ document.getElementsByTagName('html')[0].style.display = 'block';
+ }, 5000);
+</script>
+<body style="background-color: #1f52a5;">
+<h1>Heading</h1>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+interdum maximus finibus. Ut fermentum malesuada commodo. Sed
+faucibus, sapien a mattis lobortis, magna ante efficitur mauris, quis
+sodales nibh diam nec quam. Vestibulum magna libero, tincidunt non
+erat sed, molestie pulvinar ex. Maecenas semper blandit elit, id
+suscipit nulla venenatis pretium. Integer accumsan porta felis, vitae
+placerat urna accumsan vel. Aliquam eu aliquet mi. Aenean tincidunt
+eros lectus, sit amet efficitur orci ultrices at. Morbi lobortis ex
+quis luctus rutrum. In nulla velit, elementum vitae turpis vitae,
+finibus varius massa. Morbi id libero faucibus, tempus eros et,
+ullamcorper ipsum. Sed eleifend finibus bibendum. Nullam ut nunc nec
+lacus posuere dignissim. Nunc sollicitudin vitae augue id
+vulputate. Ut ac nibh gravida, volutpat est ac, facilisis neque.</p>
+
+<p>Nam dictum leo massa, non posuere dui bibendum id. Morbi sagittis est
+non est laoreet, a sollicitudin felis aliquet. Ut cursus vel leo a
+efficitur. Proin ut pellentesque sapien, vel maximus dui. Suspendisse
+eu felis eget leo elementum efficitur. Class aptent taciti sociosqu ad
+litora torquent per conubia nostra, per inceptos himenaeos. Fusce
+lobortis velit in elit pellentesque, ut auctor ipsum dignissim. Sed
+aliquet eleifend convallis. Duis mollis, dolor sed rutrum mollis,
+augue eros dignissim erat, eu dapibus augue turpis ac sapien. Morbi at
+volutpat odio, at molestie risus. Nulla quis nulla et magna vestibulum
+euismod. Praesent suscipit quam elit, non luctus turpis rutrum
+faucibus.</p>
+
+<p>Morbi feugiat lacus rhoncus, dignissim velit nec, dignissim
+lorem. Aliquam erat volutpat. Mauris semper dictum tempus. Nulla ex
+ligula, malesuada in ornare sed, euismod vitae massa. Etiam quis erat
+quis nisl facilisis suscipit. Mauris placerat ante et auctor
+fermentum. Donec tincidunt justo sem, ullamcorper vulputate nisl
+commodo a. Vestibulum quis ex non elit porttitor semper eget quis
+tortor. Suspendisse mattis neque non elementum scelerisque. Nulla
+facilisi. Nulla non felis et justo feugiat elementum. Aenean sodales
+turpis at erat eleifend lacinia. Proin eleifend volutpat purus id
+mollis. Proin vel tellus faucibus, sagittis libero at, lobortis
+odio. Praesent quam mauris, auctor vel velit eu, convallis molestie
+nisi. Pellentesque in nunc at orci ultrices vehicula.</p>
+
+<p>Praesent nibh lectus, efficitur sed risus in, rutrum tristique
+arcu. Curabitur non efficitur elit. Phasellus eget odio iaculis,
+molestie dui eget, venenatis erat. Nulla luctus facilisis lectus, nec
+dapibus tortor rhoncus vel. Donec nec arcu elit. Nullam ut faucibus
+purus, sed ultricies diam. Pellentesque at finibus ipsum. Vestibulum
+egestas dignissim nisl, ac rhoncus risus finibus sit amet. Donec non
+feugiat ante. Donec vehicula dui a lorem imperdiet, a tempus diam
+pulvinar. Nullam congue efficitur justo, non posuere ligula sodales
+in. Ut a urna ornare, ultrices velit in, pellentesque
+lorem. Vestibulum ante ipsum primis in faucibus orci luctus et
+ultrices posuere cubilia Curae;</p>
+
+<p>Orci varius natoque penatibus et magnis dis parturient montes,
+nascetur ridiculus mus. Morbi maximus quis magna et aliquet. Nam
+bibendum fermentum tempus. Praesent iaculis tortor metus, at
+vestibulum ipsum hendrerit mattis. Proin fringilla nisl sit amet
+tincidunt blandit. Interdum et malesuada fames ac ante ipsum primis in
+faucibus. Phasellus vel lectus leo. Curabitur fringilla, arcu non
+posuere viverra, urna metus blandit augue, convallis mattis tortor dui
+vel arcu. In sit amet metus vitae ex rhoncus hendrerit.</p>
+
+</body>
+</html>
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 87fc9f2..729697f 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -54,6 +54,7 @@
#include "cobalt/loader/image/animated_image_tracker.h"
#include "cobalt/media_session/media_session_client.h"
#include "cobalt/page_visibility/visibility_state.h"
+#include "cobalt/script/error_report.h"
#include "cobalt/script/javascript_engine.h"
#include "cobalt/storage/storage_manager.h"
#include "starboard/accessibility.h"
@@ -143,6 +144,12 @@
void InjectWheelEvent(scoped_refptr<dom::Element> element, base::Token type,
const dom::WheelEventInit& event);
+ // Called to inject a beforeunload event into the web module. If
+ // this event is not handled by the web application,
+ // on_before_unload_fired_but_not_handled will be called. The event
+ // is not directed at a specific element.
+ void InjectBeforeUnloadEvent();
+
// Called to execute JavaScript in this WebModule. Sets the |result|
// output parameter and signals |got_result|.
void ExecuteJavascript(const std::string& script_utf8,
@@ -197,8 +204,10 @@
void Unpause();
void Resume(render_tree::ResourceProvider* resource_provider);
- void ReportScriptError(const base::SourceLocation& source_location,
- const std::string& error_message);
+ void ReduceMemory();
+
+ void LogScriptError(const base::SourceLocation& source_location,
+ const std::string& error_message);
private:
class DocumentLoadedObserver;
@@ -233,6 +242,10 @@
error_callback_.Run(window_->location()->url(), error);
}
+ // Report an error encountered while running JS.
+ // Returns whether or not the error was handled.
+ bool ReportScriptError(const script::ErrorReport& error_report);
+
// Inject the DOM event object into the window or the element.
void InjectInputEvent(scoped_refptr<dom::Element> element,
const scoped_refptr<dom::Event>& event);
@@ -358,6 +371,8 @@
scoped_ptr<media_session::MediaSessionClient> media_session_client_;
scoped_ptr<layout::TopmostEventTarget> topmost_event_target_;
+
+ base::Closure on_before_unload_fired_but_not_handled;
};
class WebModule::Impl::DocumentLoadedObserver : public dom::DocumentObserver {
@@ -422,6 +437,9 @@
base::Unretained(data.options.splash_screen_cache));
}
+ on_before_unload_fired_but_not_handled =
+ data.options.on_before_unload_fired_but_not_handled;
+
fetcher_factory_.reset(new loader::FetcherFactory(
data.network_module, data.options.extra_web_file_dir,
dom::URL::MakeBlobResolverCallback(blob_registry_.get()),
@@ -475,7 +493,7 @@
#if defined(COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING)
script::JavaScriptEngine::ErrorHandler error_handler =
- base::Bind(&WebModule::Impl::ReportScriptError, base::Unretained(this));
+ base::Bind(&WebModule::Impl::LogScriptError, base::Unretained(this));
javascript_engine_->RegisterErrorHandler(error_handler);
#endif
@@ -578,6 +596,9 @@
base::Bind(&dom::CspDelegate::ReportEval,
base::Unretained(window_->document()->csp_delegate())));
+ global_environment_->SetReportErrorCallback(
+ base::Bind(&WebModule::Impl::ReportScriptError, base::Unretained(this)));
+
InjectCustomWindowAttributes(data.options.injected_window_attributes);
if (!data.options.loaded_callbacks.empty()) {
@@ -594,6 +615,8 @@
DCHECK(is_running_);
is_running_ = false;
global_environment_->SetReportEvalCallback(base::Closure());
+ global_environment_->SetReportErrorCallback(
+ script::GlobalEnvironment::ReportErrorCallback());
window_->DispatchEvent(new dom::Event(base::Tokens::unload()));
document_load_observer_.reset();
media_session_client_.reset();
@@ -750,6 +773,14 @@
}
}
+bool WebModule::Impl::ReportScriptError(
+ const script::ErrorReport& error_report) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(is_running_);
+ DCHECK(window_);
+ return window_->ReportScriptError(error_report);
+}
+
#if defined(ENABLE_WEBDRIVER)
void WebModule::Impl::CreateWindowDriver(
const webdriver::protocol::WindowId& window_id,
@@ -925,7 +956,23 @@
SetApplicationState(base::kApplicationStatePaused);
}
-void WebModule::Impl::ReportScriptError(
+void WebModule::Impl::ReduceMemory() {
+ TRACE_EVENT0("cobalt::browser", "WebModule::Impl::ReduceMemory()");
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!is_running_) {
+ return;
+ }
+
+ PurgeResourceCaches();
+ window_->document()->PurgeCachedResources();
+
+ // Force garbage collection in |javascript_engine_|.
+ if (javascript_engine_) {
+ javascript_engine_->CollectGarbage();
+ }
+}
+
+void WebModule::Impl::LogScriptError(
const base::SourceLocation& source_location,
const std::string& error_message) {
std::string file_name =
@@ -944,6 +991,15 @@
SbLogRaw(ss.str().c_str());
}
+void WebModule::Impl::InjectBeforeUnloadEvent() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (window_ && window_->HasEventListener(base::Tokens::beforeunload())) {
+ window_->DispatchEvent(new dom::Event(base::Tokens::beforeunload()));
+ } else if (!on_before_unload_fired_but_not_handled.is_null()) {
+ on_before_unload_fired_but_not_handled.Run();
+ }
+}
+
void WebModule::Impl::PurgeResourceCaches() {
image_cache_->Purge();
remote_typeface_cache_->Purge();
@@ -1097,6 +1153,15 @@
scoped_refptr<dom::Element>(), type, event));
}
+void WebModule::InjectBeforeUnloadEvent() {
+ TRACE_EVENT0("cobalt::browser", "WebModule::InjectBeforeUnloadEvent()");
+ DCHECK(message_loop());
+ DCHECK(impl_);
+ message_loop()->PostTask(FROM_HERE,
+ base::Bind(&WebModule::Impl::InjectBeforeUnloadEvent,
+ base::Unretained(impl_.get())));
+}
+
std::string WebModule::ExecuteJavascript(
const std::string& script_utf8,
const base::SourceLocation& script_location,
@@ -1287,6 +1352,17 @@
base::Unretained(impl_.get()), resource_provider));
}
+void WebModule::ReduceMemory() {
+ // Must only be called by a thread external from the WebModule thread.
+ DCHECK_NE(MessageLoop::current(), message_loop());
+
+ // We block here so that we block the Low Memory event handler until we have
+ // reduced our memory consumption.
+ message_loop()->PostBlockingTask(
+ FROM_HERE, base::Bind(&WebModule::Impl::ReduceMemory,
+ base::Unretained(impl_.get())));
+}
+
void WebModule::Impl::HandlePointerEvents() {
TRACE_EVENT0("cobalt::browser", "WebModule::Impl::HandlePointerEvents");
const scoped_refptr<dom::Document>& document = window_->document();
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index b296d6f..ba23707 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -185,6 +185,14 @@
// The splash screen cache object, owned by the BrowserModule.
SplashScreenCache* splash_screen_cache;
+ // The beforeunload event can give a web page a chance to shut
+ // itself down softly and ultimately call window.close(), however
+ // if it is not handled by the web application, we indicate this
+ // situation externally by calling this callback, so that if the
+ // beforeunload event was generated it can be known that there is
+ // no window.close() call pending.
+ base::Closure on_before_unload_fired_but_not_handled;
+
// Whether or not the WebModule is allowed to fetch from cache via
// h5vcc-cache://.
bool can_fetch_cache;
@@ -222,6 +230,11 @@
// represents the event name, for example 'wheel'.
void InjectWheelEvent(base::Token type, const dom::WheelEventInit& event);
+ // Call this to inject a beforeunload event into the web module. If
+ // this event is not handled by the web application,
+ // on_before_unload_fired_but_not_handled will be called.
+ void InjectBeforeUnloadEvent();
+
// Call this to execute Javascript code in this web module. The calling
// thread will block until the JavaScript has executed and the output results
// are available.
@@ -262,6 +275,10 @@
void Suspend() OVERRIDE;
void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE;
+ // Attempt to reduce overall memory consumption. Called in response to a
+ // system indication that memory usage is nearing a critical level.
+ void ReduceMemory();
+
private:
// Data required to construct a WebModule, initialized in the constructor and
// passed to |Initialize|.
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 3c54c85..248bc70 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-90790
\ No newline at end of file
+93830
\ No newline at end of file
diff --git a/src/cobalt/build/config/base.gypi b/src/cobalt/build/config/base.gypi
index a3243c1..02c5ec2 100644
--- a/src/cobalt/build/config/base.gypi
+++ b/src/cobalt/build/config/base.gypi
@@ -790,10 +790,9 @@
# Clients must copy over all content; to avoid having to copy over extra data, we
# omit the test data
'conditions': [
- ['cobalt_config != "gold" and cobalt_enable_lib == 0', {
+ ['cobalt_config != "gold"', {
'variables' : {
'cobalt_copy_debug_console': 1,
- 'cobalt_copy_test_data': 1,
'enable_about_scheme': 1,
'enable_fake_microphone': 1,
'enable_file_scheme': 1,
@@ -807,7 +806,6 @@
{
'variables' : {
'cobalt_copy_debug_console': 0,
- 'cobalt_copy_test_data': 0,
'enable_about_scheme': 0,
'enable_fake_microphone': 0,
'enable_file_scheme': 0,
@@ -818,5 +816,15 @@
'sb_allows_memory_tracking': 0,
},
}],
+ ['cobalt_config != "gold" and cobalt_enable_lib == 0', {
+ 'variables' : {
+ 'cobalt_copy_test_data': 1,
+ },
+ },
+ {
+ 'variables' : {
+ 'cobalt_copy_test_data': 0,
+ },
+ }],
],
}
diff --git a/src/cobalt/build/config/starboard.py b/src/cobalt/build/config/starboard.py
index ecba441..2af50cd 100644
--- a/src/cobalt/build/config/starboard.py
+++ b/src/cobalt/build/config/starboard.py
@@ -81,9 +81,6 @@
# Cobalt uses OpenSSL on all platforms.
'use_openssl': 1,
'clang': use_clang,
- # Cobalt relies on the Starboard implementation for DRM on all Starboard
- # platforms.
- 'use_widevine': 0,
# Whether to build with clang's Address Sanitizer instrumentation.
'use_asan': use_asan,
# Whether to build with clang's Thread Sanitizer instrumentation.
diff --git a/src/cobalt/debug/debug_web_server.cc b/src/cobalt/debug/debug_web_server.cc
index 6ffd152..baa0fb9 100644
--- a/src/cobalt/debug/debug_web_server.cc
+++ b/src/cobalt/debug/debug_web_server.cc
@@ -30,12 +30,7 @@
#include "net/base/net_errors.h"
#include "net/base/tcp_listen_socket.h"
#include "net/server/http_server_request_info.h"
-
-#if defined(__LB_SHELL__)
-#include "lb_network_helpers.h" // NOLINT[build/include]
-#elif defined(OS_STARBOARD)
#include "starboard/socket.h"
-#endif
namespace cobalt {
namespace debug {
@@ -84,7 +79,6 @@
net::IPEndPoint ip_addr;
SbSocketAddress local_ip;
SbMemorySet(&local_ip, 0, sizeof(local_ip));
-#if SB_API_VERSION >= 4
bool result = false;
// Prefer IPv4 addresses, as they're easier to type for debugging.
@@ -109,19 +103,6 @@
DLOG(WARNING) << "Unable to get a local interface address.";
return base::nullopt;
}
-#else
- bool result = SbSocketGetLocalInterfaceAddress(&local_ip);
- if (!result) {
- DLOG(WARNING) << "Unable to get a local interface address.";
- return base::nullopt;
- }
-
- result = ip_addr.FromSbSocketAddress(&local_ip);
- if (!result) {
- LOG(WARNING) << "Got invalid local interface address.";
- return base::nullopt;
- }
-#endif // SB_API_VERSION >= 4
return ip_addr.ToStringWithoutPort();
}
diff --git a/src/cobalt/dom/custom_event.h b/src/cobalt/dom/custom_event.h
new file mode 100644
index 0000000..615eb6a
--- /dev/null
+++ b/src/cobalt/dom/custom_event.h
@@ -0,0 +1,76 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_DOM_CUSTOM_EVENT_H_
+#define COBALT_DOM_CUSTOM_EVENT_H_
+
+#include <string>
+
+#include "cobalt/dom/custom_event_init.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/script/value_handle.h"
+
+namespace cobalt {
+namespace dom {
+
+// Events using the CustomEvent interface can be used to carry custom data.
+// https://www.w3.org/TR/2015/REC-dom-20151119/#customevent
+class CustomEvent : public Event {
+ public:
+ explicit CustomEvent(const std::string& type) : Event(type) {}
+ CustomEvent(const std::string& type, const CustomEventInit& init_dict)
+ : Event(type, init_dict) {
+ set_detail(init_dict.detail());
+ }
+
+ // Creates an event with its "initialized flag" unset.
+ explicit CustomEvent(UninitializedFlag uninitialized_flag)
+ : Event(uninitialized_flag) {}
+
+ // Web API: CustomEvent
+ //
+ void InitCustomEvent(const std::string& type, bool bubbles, bool cancelable,
+ const script::ValueHandleHolder& detail) {
+ InitEvent(type, bubbles, cancelable);
+ set_detail(&detail);
+ }
+
+ void set_detail(const script::ValueHandleHolder* detail) {
+ if (detail) {
+ detail_.reset(new script::ValueHandleHolder::Reference(this, *detail));
+ } else {
+ detail_.reset();
+ }
+ }
+
+ const script::ValueHandleHolder* detail() const {
+ if (!detail_) {
+ return NULL;
+ }
+
+ return &(detail_->referenced_value());
+ }
+
+ DEFINE_WRAPPABLE_TYPE(CustomEvent);
+
+ protected:
+ ~CustomEvent() OVERRIDE {}
+
+ scoped_ptr<script::ValueHandleHolder::Reference> detail_;
+};
+
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_CUSTOM_EVENT_H_
diff --git a/src/cobalt/dom/custom_event.idl b/src/cobalt/dom/custom_event.idl
new file mode 100644
index 0000000..cd4f555
--- /dev/null
+++ b/src/cobalt/dom/custom_event.idl
@@ -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.
+
+// https://www.w3.org/TR/2015/REC-dom-20151119/#customevent
+[Constructor(DOMString type, optional CustomEventInit eventInitDict)]
+interface CustomEvent : Event {
+ readonly attribute any detail;
+
+ void initCustomEvent(DOMString type,
+ boolean bubbles,
+ boolean cancelable,
+ any detail);
+};
diff --git a/src/cobalt/dom/custom_event_init.idl b/src/cobalt/dom/custom_event_init.idl
new file mode 100644
index 0000000..2967077
--- /dev/null
+++ b/src/cobalt/dom/custom_event_init.idl
@@ -0,0 +1,19 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/dom/#customeventinit
+
+dictionary CustomEventInit : EventInit {
+ any detail = null;
+};
diff --git a/src/cobalt/dom/custom_event_test.cc b/src/cobalt/dom/custom_event_test.cc
new file mode 100644
index 0000000..d8b4c42
--- /dev/null
+++ b/src/cobalt/dom/custom_event_test.cc
@@ -0,0 +1,198 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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/custom_event.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/css_parser/parser.h"
+#include "cobalt/dom/custom_event_init.h"
+#include "cobalt/dom/local_storage_database.h"
+#include "cobalt/dom/testing/gtest_workarounds.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/dom_parser/parser.h"
+#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/media/media_module_stub.h"
+#include "cobalt/media_session/media_session.h"
+#include "cobalt/network/network_module.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/javascript_engine.h"
+#include "cobalt/script/source_code.h"
+#include "cobalt/script/testing/fake_script_value.h"
+#include "cobalt/script/value_handle.h"
+#include "cobalt/script/wrappable.h"
+#include "nb/pointer_arithmetic.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace dom {
+
+using ::cobalt::script::testing::FakeScriptValue;
+
+class MockErrorCallback : public base::Callback<void(const std::string&)> {
+ public:
+ MOCK_METHOD1(Run, void(const std::string&));
+};
+
+namespace {
+class CustomEventTest : public ::testing::Test {
+ public:
+ CustomEventTest()
+ : environment_settings_(new script::EnvironmentSettings),
+ message_loop_(MessageLoop::TYPE_DEFAULT),
+ css_parser_(css_parser::Parser::Create()),
+ dom_parser_(new dom_parser::Parser(mock_error_callback_)),
+ fetcher_factory_(new loader::FetcherFactory(&network_module_)),
+ local_storage_database_(NULL),
+ stub_media_module_(new media::MediaModuleStub()),
+ url_("about:blank"),
+ window_(new Window(
+ 1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
+ dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
+ NULL, NULL, &local_storage_database_, stub_media_module_.get(),
+ stub_media_module_.get(), NULL, NULL, NULL, NULL, NULL, url_, "",
+ "en-US", base::Callback<void(const GURL&)>(),
+ base::Bind(&MockErrorCallback::Run,
+ base::Unretained(&mock_error_callback_)),
+ NULL, network_bridge::PostSender(),
+ std::string() /* default security policy */, kCspEnforcementEnable,
+ base::Closure() /* csp_policy_changed */,
+ base::Closure() /* ran_animation_frame_callbacks */,
+ base::Closure() /* window_close */,
+ base::Closure() /* window_minimize */, NULL, NULL)) {
+ engine_ = script::JavaScriptEngine::CreateEngine(
+ script::JavaScriptEngine::Options());
+ global_environment_ = engine_->CreateGlobalEnvironment();
+ global_environment_->CreateGlobalObject(window_,
+ environment_settings_.get());
+ }
+
+ bool EvaluateScript(const std::string& js_code, std::string* result);
+
+ private:
+ scoped_ptr<script::JavaScriptEngine> engine_;
+ scoped_refptr<script::GlobalEnvironment> global_environment_;
+
+ const scoped_ptr<script::EnvironmentSettings> environment_settings_;
+ MessageLoop message_loop_;
+ MockErrorCallback mock_error_callback_;
+ scoped_ptr<css_parser::Parser> css_parser_;
+ scoped_ptr<dom_parser::Parser> dom_parser_;
+ network::NetworkModule network_module_;
+ scoped_ptr<loader::FetcherFactory> fetcher_factory_;
+ dom::LocalStorageDatabase local_storage_database_;
+ scoped_ptr<media::MediaModule> stub_media_module_;
+ GURL url_;
+ const scoped_refptr<Window> window_;
+};
+
+bool CustomEventTest::EvaluateScript(const std::string& js_code,
+ std::string* result) {
+ DCHECK(global_environment_);
+ DCHECK(result);
+ scoped_refptr<script::SourceCode> source_code =
+ script::SourceCode::CreateSourceCode(
+ js_code, base::SourceLocation(__FILE__, __LINE__, 1));
+
+ global_environment_->EnableEval();
+ global_environment_->SetReportEvalCallback(base::Closure());
+ bool succeeded = global_environment_->EvaluateScript(source_code, result);
+ return succeeded;
+}
+} // namespace
+
+TEST_F(CustomEventTest, ConstructorWithEventTypeString) {
+ scoped_refptr<CustomEvent> event = new CustomEvent("mytestevent");
+
+ EXPECT_EQ("mytestevent", event->type());
+ EXPECT_EQ(NULL, event->target());
+ EXPECT_EQ(NULL, event->current_target());
+ EXPECT_EQ(Event::kNone, event->event_phase());
+ EXPECT_FALSE(event->bubbles());
+ EXPECT_FALSE(event->cancelable());
+ EXPECT_FALSE(event->default_prevented());
+ EXPECT_FALSE(event->IsBeingDispatched());
+ EXPECT_FALSE(event->propagation_stopped());
+ EXPECT_FALSE(event->immediate_propagation_stopped());
+ EXPECT_EQ(NULL, event->detail());
+}
+
+TEST_F(CustomEventTest, ConstructorWithEventTypeAndDefaultInitDict) {
+ CustomEventInit init;
+ scoped_refptr<CustomEvent> event = new CustomEvent("mytestevent", init);
+
+ EXPECT_EQ("mytestevent", event->type());
+ EXPECT_EQ(NULL, event->target());
+ EXPECT_EQ(NULL, event->current_target());
+ EXPECT_EQ(Event::kNone, event->event_phase());
+ EXPECT_FALSE(event->bubbles());
+ EXPECT_FALSE(event->cancelable());
+ EXPECT_FALSE(event->default_prevented());
+ EXPECT_FALSE(event->IsBeingDispatched());
+ EXPECT_FALSE(event->propagation_stopped());
+ EXPECT_FALSE(event->immediate_propagation_stopped());
+ EXPECT_EQ(NULL, event->detail());
+}
+
+TEST_F(CustomEventTest, ConstructorWithEventTypeAndCustomInitDict) {
+ std::string result;
+ bool success = EvaluateScript(
+ "var event = new CustomEvent('dog', "
+ " {'bubbles':true, "
+ " 'cancelable':true, "
+ " 'detail':{'cobalt':'rulez'}});"
+ "if (event.type == 'dog' &&"
+ " event.bubbles == true &&"
+ " event.cancelable == true) "
+ " event.detail.cobalt;",
+ &result);
+ EXPECT_EQ("rulez", result);
+
+ if (!success) {
+ DLOG(ERROR) << "Failed to evaluate test: "
+ << "\"" << result << "\"";
+ } else {
+ LOG(INFO) << "Test result : "
+ << "\"" << result << "\"";
+ }
+}
+
+TEST_F(CustomEventTest, InitCustomEvent) {
+ std::string result;
+ bool success = EvaluateScript(
+ "var event = new CustomEvent('cat');\n"
+ "event.initCustomEvent('dog', true, true, {cobalt:'rulez'});"
+ "if (event.type == 'dog' &&"
+ " event.detail &&"
+ " event.bubbles == true &&"
+ " event.cancelable == true) "
+ " event.detail.cobalt;",
+ &result);
+ EXPECT_EQ("rulez", result);
+
+ if (!success) {
+ DLOG(ERROR) << "Failed to evaluate test: "
+ << "\"" << result << "\"";
+ } else {
+ LOG(INFO) << "Test result : "
+ << "\"" << result << "\"";
+ }
+}
+
+} // namespace dom
+} // namespace cobalt
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index ff262c6..ec42f43 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -34,6 +34,7 @@
#include "cobalt/dom/comment.h"
#include "cobalt/dom/csp_delegate.h"
#include "cobalt/dom/csp_delegate_factory.h"
+#include "cobalt/dom/custom_event.h"
#include "cobalt/dom/dom_exception.h"
#include "cobalt/dom/dom_implementation.h"
#include "cobalt/dom/element.h"
@@ -240,6 +241,8 @@
} else if (base::strcasecmp(interface_name.c_str(), "uievent") == 0 ||
base::strcasecmp(interface_name.c_str(), "uievents") == 0) {
return new UIEvent(Event::Uninitialized);
+ } else if (base::strcasecmp(interface_name.c_str(), "customevent") == 0) {
+ return new CustomEvent(Event::Uninitialized);
}
DOMException::Raise(
diff --git a/src/cobalt/dom/document_test.cc b/src/cobalt/dom/document_test.cc
index 2a4ee06..7e4c391 100644
--- a/src/cobalt/dom/document_test.cc
+++ b/src/cobalt/dom/document_test.cc
@@ -19,6 +19,7 @@
#include "cobalt/cssom/css_style_sheet.h"
#include "cobalt/dom/attr.h"
#include "cobalt/dom/comment.h"
+#include "cobalt/dom/custom_event.h"
#include "cobalt/dom/dom_exception.h"
#include "cobalt/dom/dom_implementation.h"
#include "cobalt/dom/dom_stat_tracker.h"
@@ -172,6 +173,19 @@
EXPECT_FALSE(event->initialized_flag());
}
+TEST_F(DocumentTest, CreateEventCustomEvent) {
+ StrictMock<MockExceptionState> exception_state;
+ scoped_refptr<script::ScriptException> exception;
+ scoped_refptr<Document> document = new Document(&html_element_context_);
+
+ // Create an Event, the name is case insensitive.
+ scoped_refptr<Event> event =
+ document->CreateEvent("CuStOmEvEnT", &exception_state);
+ EXPECT_TRUE(event);
+ EXPECT_FALSE(event->initialized_flag());
+ EXPECT_TRUE(base::polymorphic_downcast<CustomEvent*>(event.get()));
+}
+
TEST_F(DocumentTest, CreateEventUIEvent) {
StrictMock<MockExceptionState> exception_state;
scoped_refptr<script::ScriptException> exception;
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 88ef23d..27a8f29 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -62,6 +62,7 @@
'css_animations_adapter.h',
'css_transitions_adapter.cc',
'css_transitions_adapter.h',
+ 'custom_event.h',
'data_view.cc',
'data_view.h',
'device_orientation_event.cc',
@@ -93,8 +94,10 @@
'dom_token_list.h',
'element.cc',
'element.h',
+ 'error_event.h',
'event.cc',
'event.h',
+ 'event_init.h',
'event_listener.cc',
'event_listener.h',
'event_queue.cc',
@@ -105,6 +108,7 @@
'float64_array.h',
'focus_event.cc',
'focus_event.h',
+ 'focus_event_init.h',
'font_cache.cc',
'font_cache.h',
'font_face.cc',
@@ -164,6 +168,7 @@
'initial_computed_style.h',
'keyboard_event.cc',
'keyboard_event.h',
+ 'keyboard_event_init.h',
'keycode.h',
'keyframes_map_updater.cc',
'keyframes_map_updater.h',
@@ -182,6 +187,7 @@
'mime_type_array.h',
'mouse_event.cc',
'mouse_event.h',
+ 'mouse_event_init.h',
'mutation_observer.cc',
'mutation_observer.h',
'mutation_observer_init.h',
@@ -212,6 +218,7 @@
'plugin_array.h',
'pointer_event.cc',
'pointer_event.h',
+ 'pointer_event_init.h',
'pointer_state.cc',
'pointer_state.h',
'progress_event.cc',
@@ -248,6 +255,7 @@
'typed_array.h',
'ui_event.cc',
'ui_event.h',
+ 'ui_event_init.h',
'ui_event_with_key_state.cc',
'ui_event_with_key_state.h',
'uint8_array.h',
@@ -260,6 +268,7 @@
'video_track_list.h',
'wheel_event.cc',
'wheel_event.h',
+ 'wheel_event_init.h',
'window.cc',
'window.h',
'window_timers.cc',
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index 2c41b83..8cf266a 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -28,6 +28,7 @@
'comment_test.cc',
'crypto_test.cc',
'csp_delegate_test.cc',
+ 'custom_event_test.cc',
'data_view_test.cc',
'document_test.cc',
'document_type_test.cc',
@@ -37,6 +38,7 @@
'dom_string_map_test.cc',
'dom_token_list_test.cc',
'element_test.cc',
+ 'error_event_test.cc',
'event_queue_test.cc',
'event_target_test.cc',
'event_test.cc',
diff --git a/src/cobalt/dom/error_event.h b/src/cobalt/dom/error_event.h
new file mode 100644
index 0000000..d3b3b71
--- /dev/null
+++ b/src/cobalt/dom/error_event.h
@@ -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.
+
+#ifndef COBALT_DOM_ERROR_EVENT_H_
+#define COBALT_DOM_ERROR_EVENT_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/dom/error_event_init.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/script/value_handle.h"
+
+namespace cobalt {
+namespace dom {
+
+// Whenever an uncaught runtime script error occurs in one of the scripts
+// associated with a Document, the user agent must report the error for the
+// relevant script.
+// https://www.w3.org/TR/html5/webappapis.html#errorevent
+class ErrorEvent : public Event {
+ public:
+ explicit ErrorEvent(const std::string& type)
+ : Event(type), lineno_(0), colno_(0) {}
+ ErrorEvent(const std::string& type, const ErrorEventInit& init_dict)
+ : Event(type, init_dict),
+ message_(init_dict.message()),
+ filename_(init_dict.filename()),
+ lineno_(init_dict.lineno()),
+ colno_(init_dict.colno()) {
+ InitError(init_dict);
+ }
+ ErrorEvent(base::Token type, const ErrorEventInit& init_dict)
+ : Event(type, init_dict),
+ message_(init_dict.message()),
+ filename_(init_dict.filename()),
+ lineno_(init_dict.lineno()),
+ colno_(init_dict.colno()) {
+ InitError(init_dict);
+ }
+
+ // Web API: ErrorEvent
+ //
+ std::string message() const { return message_; }
+ std::string filename() const { return filename_; }
+ uint32 lineno() const { return lineno_; }
+ uint32 colno() const { return colno_; }
+
+ const script::ValueHandleHolder* error() const {
+ if (!error_) {
+ return NULL;
+ }
+ return &(error_->referenced_value());
+ }
+
+ DEFINE_WRAPPABLE_TYPE(ErrorEvent);
+
+ protected:
+ ~ErrorEvent() OVERRIDE {}
+
+ private:
+ void InitError(const ErrorEventInit& init_dict) {
+ const script::ValueHandleHolder* error = init_dict.error();
+ if (error) {
+ error_.reset(new script::ValueHandleHolder::Reference(this, *error));
+ }
+ }
+
+ std::string message_;
+ std::string filename_;
+ uint32 lineno_;
+ uint32 colno_;
+ scoped_ptr<script::ValueHandleHolder::Reference> error_;
+};
+
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_ERROR_EVENT_H_
diff --git a/src/cobalt/dom/error_event.idl b/src/cobalt/dom/error_event.idl
new file mode 100644
index 0000000..10c4699
--- /dev/null
+++ b/src/cobalt/dom/error_event.idl
@@ -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.
+
+// https://www.w3.org/TR/html5/webappapis.html#errorevent
+
+[Constructor(DOMString type, optional ErrorEventInit eventInitDict)]
+interface ErrorEvent : Event {
+ readonly attribute DOMString message;
+ readonly attribute DOMString filename;
+ readonly attribute unsigned long lineno;
+ readonly attribute unsigned long colno;
+ readonly attribute any error;
+};
diff --git a/src/cobalt/dom/error_event_init.idl b/src/cobalt/dom/error_event_init.idl
new file mode 100644
index 0000000..77e80c0
--- /dev/null
+++ b/src/cobalt/dom/error_event_init.idl
@@ -0,0 +1,23 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://www.w3.org/TR/html5/webappapis.html#erroreventinit
+
+dictionary ErrorEventInit : EventInit {
+ DOMString message = "";
+ DOMString filename = "";
+ unsigned long lineno = 0;
+ unsigned long colno = 0;
+ any error = null;
+};
diff --git a/src/cobalt/dom/error_event_test.cc b/src/cobalt/dom/error_event_test.cc
new file mode 100644
index 0000000..b8ead45
--- /dev/null
+++ b/src/cobalt/dom/error_event_test.cc
@@ -0,0 +1,191 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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/error_event.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/css_parser/parser.h"
+#include "cobalt/dom/error_event_init.h"
+#include "cobalt/dom/local_storage_database.h"
+#include "cobalt/dom/testing/gtest_workarounds.h"
+#include "cobalt/dom/window.h"
+#include "cobalt/dom_parser/parser.h"
+#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/media/media_module_stub.h"
+#include "cobalt/media_session/media_session.h"
+#include "cobalt/network/network_module.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/javascript_engine.h"
+#include "cobalt/script/source_code.h"
+#include "cobalt/script/testing/fake_script_value.h"
+#include "cobalt/script/value_handle.h"
+#include "cobalt/script/wrappable.h"
+#include "nb/pointer_arithmetic.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace dom {
+
+using ::cobalt::script::testing::FakeScriptValue;
+
+class MockErrorCallback : public base::Callback<void(const std::string&)> {
+ public:
+ MOCK_METHOD1(Run, void(const std::string&));
+};
+
+namespace {
+class ErrorEventTest : public ::testing::Test {
+ public:
+ ErrorEventTest()
+ : environment_settings_(new script::EnvironmentSettings),
+ message_loop_(MessageLoop::TYPE_DEFAULT),
+ css_parser_(css_parser::Parser::Create()),
+ dom_parser_(new dom_parser::Parser(mock_error_callback_)),
+ fetcher_factory_(new loader::FetcherFactory(&network_module_)),
+ local_storage_database_(NULL),
+ stub_media_module_(new media::MediaModuleStub()),
+ url_("about:blank"),
+ window_(new Window(
+ 1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
+ dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
+ NULL, NULL, &local_storage_database_, stub_media_module_.get(),
+ stub_media_module_.get(), NULL, NULL, NULL, NULL, NULL, url_, "",
+ "en-US", base::Callback<void(const GURL&)>(),
+ base::Bind(&MockErrorCallback::Run,
+ base::Unretained(&mock_error_callback_)),
+ NULL, network_bridge::PostSender(),
+ std::string() /* default security policy */, kCspEnforcementEnable,
+ base::Closure() /* csp_policy_changed */,
+ base::Closure() /* ran_animation_frame_callbacks */,
+ base::Closure() /* window_close */,
+ base::Closure() /* window_minimize */, NULL, NULL)) {
+ engine_ = script::JavaScriptEngine::CreateEngine(
+ script::JavaScriptEngine::Options());
+ global_environment_ = engine_->CreateGlobalEnvironment();
+ global_environment_->CreateGlobalObject(window_,
+ environment_settings_.get());
+ }
+
+ bool EvaluateScript(const std::string& js_code, std::string* result);
+
+ private:
+ scoped_ptr<script::JavaScriptEngine> engine_;
+ scoped_refptr<script::GlobalEnvironment> global_environment_;
+
+ const scoped_ptr<script::EnvironmentSettings> environment_settings_;
+ MessageLoop message_loop_;
+ MockErrorCallback mock_error_callback_;
+ scoped_ptr<css_parser::Parser> css_parser_;
+ scoped_ptr<dom_parser::Parser> dom_parser_;
+ network::NetworkModule network_module_;
+ scoped_ptr<loader::FetcherFactory> fetcher_factory_;
+ dom::LocalStorageDatabase local_storage_database_;
+ scoped_ptr<media::MediaModule> stub_media_module_;
+ GURL url_;
+ const scoped_refptr<Window> window_;
+};
+
+bool ErrorEventTest::EvaluateScript(const std::string& js_code,
+ std::string* result) {
+ DCHECK(global_environment_);
+ DCHECK(result);
+ scoped_refptr<script::SourceCode> source_code =
+ script::SourceCode::CreateSourceCode(
+ js_code, base::SourceLocation(__FILE__, __LINE__, 1));
+
+ global_environment_->EnableEval();
+ global_environment_->SetReportEvalCallback(base::Closure());
+ bool succeeded = global_environment_->EvaluateScript(source_code, result);
+ return succeeded;
+}
+} // namespace
+
+TEST_F(ErrorEventTest, ConstructorWithEventTypeString) {
+ scoped_refptr<ErrorEvent> event = new ErrorEvent("mytestevent");
+
+ EXPECT_EQ("mytestevent", event->type());
+ EXPECT_EQ(NULL, event->target());
+ EXPECT_EQ(NULL, event->current_target());
+ EXPECT_EQ(Event::kNone, event->event_phase());
+ EXPECT_FALSE(event->bubbles());
+ EXPECT_FALSE(event->cancelable());
+ EXPECT_FALSE(event->default_prevented());
+ EXPECT_FALSE(event->IsBeingDispatched());
+ EXPECT_FALSE(event->propagation_stopped());
+ EXPECT_FALSE(event->immediate_propagation_stopped());
+ EXPECT_EQ("", event->message());
+ EXPECT_EQ("", event->filename());
+ EXPECT_EQ(0, event->lineno());
+ EXPECT_EQ(0, event->colno());
+ EXPECT_EQ(NULL, event->error());
+}
+
+TEST_F(ErrorEventTest, ConstructorWithEventTypeAndDefaultInitDict) {
+ ErrorEventInit init;
+ scoped_refptr<ErrorEvent> event = new ErrorEvent("mytestevent", init);
+
+ EXPECT_EQ("mytestevent", event->type());
+ EXPECT_EQ(NULL, event->target());
+ EXPECT_EQ(NULL, event->current_target());
+ EXPECT_EQ(Event::kNone, event->event_phase());
+ EXPECT_FALSE(event->bubbles());
+ EXPECT_FALSE(event->cancelable());
+ EXPECT_FALSE(event->default_prevented());
+ EXPECT_FALSE(event->IsBeingDispatched());
+ EXPECT_FALSE(event->propagation_stopped());
+ EXPECT_FALSE(event->immediate_propagation_stopped());
+ EXPECT_EQ("", event->message());
+ EXPECT_EQ("", event->filename());
+ EXPECT_EQ(0, event->lineno());
+ EXPECT_EQ(0, event->colno());
+ EXPECT_EQ(NULL, event->error());
+}
+
+TEST_F(ErrorEventTest, ConstructorWithEventTypeAndErrorInitDict) {
+ std::string result;
+ bool success = EvaluateScript(
+ "var event = new ErrorEvent('dog', "
+ " {'cancelable':true, "
+ " 'message':'error_message', "
+ " 'filename':'error_filename', "
+ " 'lineno':100, "
+ " 'colno':50, "
+ " 'error':{'cobalt':'rulez'}});"
+ "if (event.type == 'dog' &&"
+ " event.bubbles == false &&"
+ " event.cancelable == true &&"
+ " event.message == 'error_message' &&"
+ " event.filename == 'error_filename' &&"
+ " event.lineno == 100 &&"
+ " event.colno == 50) "
+ " event.error.cobalt;",
+ &result);
+ EXPECT_EQ("rulez", result);
+
+ if (!success) {
+ DLOG(ERROR) << "Failed to evaluate test: "
+ << "\"" << result << "\"";
+ } else {
+ LOG(INFO) << "Test result : "
+ << "\"" << result << "\"";
+ }
+}
+
+} // namespace dom
+} // namespace cobalt
diff --git a/src/cobalt/dom/event_target.cc b/src/cobalt/dom/event_target.cc
index ca6593c..b6a3a8c 100644
--- a/src/cobalt/dom/event_target.cc
+++ b/src/cobalt/dom/event_target.cc
@@ -78,7 +78,6 @@
DCHECK(event->initialized_flag());
TRACE_EVENT1("cobalt::dom", "EventTarget::DispatchEvent", "event",
event->type().c_str());
-
if (!event || event->IsBeingDispatched() || !event->initialized_flag()) {
return false;
}
@@ -223,6 +222,18 @@
new EventListenerInfo(type, this, listener, use_capture, listener_type));
}
+bool EventTarget::HasEventListener(base::Token type) {
+ TRACK_MEMORY_SCOPE("DOM");
+
+ for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
+ iter != event_listener_infos_.end(); ++iter) {
+ if ((*iter)->type == type) {
+ return true;
+ }
+ }
+ return false;
+}
+
EventTarget::EventListenerInfo::EventListenerInfo(
base::Token type, EventTarget* const event_target,
const EventListenerScriptValue& listener, bool use_capture,
diff --git a/src/cobalt/dom/event_target.h b/src/cobalt/dom/event_target.h
index 896667c..4e269f3 100644
--- a/src/cobalt/dom/event_target.h
+++ b/src/cobalt/dom/event_target.h
@@ -78,6 +78,9 @@
const tracked_objects::Location& location, base::Token event_name,
const base::Closure& dispatched_callback);
+ // Check if target has event listener (atrtibute or not attribute).
+ bool HasEventListener(base::Token type);
+
// Web API: GlobalEventHandlers (implements)
// Many objects can have event handlers specified. These act as non-capture
// event listeners for the object on which they are specified.
@@ -338,6 +341,20 @@
SetAttributeEventListener(base::Tokens::timeupdate(), event_listener);
}
+ const EventListenerScriptValue* onbeforeunload() {
+ return GetAttributeEventListener(base::Tokens::beforeunload());
+ }
+ void set_onbeforeunload(const EventListenerScriptValue& event_listener) {
+ SetAttributeEventListener(base::Tokens::beforeunload(), event_listener);
+ }
+
+ const EventListenerScriptValue* ontransitionend() {
+ return GetAttributeEventListener(base::Tokens::transitionend());
+ }
+ void set_ontransitionend(const EventListenerScriptValue& event_listener) {
+ SetAttributeEventListener(base::Tokens::transitionend(), event_listener);
+ }
+
const EventListenerScriptValue* onunload() {
return GetAttributeEventListener(base::Tokens::unload());
}
diff --git a/src/cobalt/dom/global_event_handlers.idl b/src/cobalt/dom/global_event_handlers.idl
index 372f2f7..df852fc 100644
--- a/src/cobalt/dom/global_event_handlers.idl
+++ b/src/cobalt/dom/global_event_handlers.idl
@@ -44,6 +44,8 @@
attribute EventHandler onresize;
+ attribute EventHandler ontransitionend;
+
// Extensions for the Pointer Events recommendation.
// https://www.w3.org/TR/2015/REC-pointerevents-20150224/#extensions-to-the-globaleventhandlers-interface
attribute EventHandler ongotpointercapture;
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index e62c335..5806cfc 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -272,6 +272,10 @@
// https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty
bool CanbeDesignatedByPointerIfDisplayed() const;
+ // Returns true if this node and all of its ancestors do NOT have display set
+ // to 'none'.
+ bool IsDisplayed() const;
+
DEFINE_WRAPPABLE_TYPE(HTMLElement);
protected:
@@ -330,10 +334,6 @@
// Purge the cached background images on only this node.
void PurgeCachedBackgroundImages();
- // Returns true if this node and all of its ancestors do NOT have display set
- // to 'none'.
- bool IsDisplayed() const;
-
bool locked_for_focus_;
// The directionality of the html element is determined by the 'dir'
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 0a4a3a7..51200ba 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -207,6 +207,12 @@
std::string HTMLMediaElement::CanPlayType(const std::string& mime_type,
const std::string& key_system) {
+ if (!html_element_context()->can_play_type_handler()) {
+ DLOG(ERROR) << __FUNCTION__ << "(" << mime_type << ", " << key_system
+ << "): Media playback in PRELOADING is not supported.";
+ return "";
+ }
+
#if defined(COBALT_MEDIA_SOURCE_2016)
DLOG_IF(ERROR, !key_system.empty())
<< "CanPlayType() only accepts one parameter but (" << key_system
@@ -732,6 +738,11 @@
}
}
+ if (!html_element_context()->web_media_player_factory()) {
+ DLOG(ERROR) << "Media playback in PRELOADING is not supported.";
+ return;
+ }
+
player_ =
html_element_context()->web_media_player_factory()->CreateWebMediaPlayer(
this);
@@ -1517,7 +1528,7 @@
EndProcessingMediaPlayerCallback();
}
-void HTMLMediaElement::TimeChanged() {
+void HTMLMediaElement::TimeChanged(bool eos_played) {
DCHECK(player_);
if (!player_) {
return;
@@ -1543,8 +1554,9 @@
// When the current playback position reaches the end of the media resource
// when the direction of playback is forwards, then the user agent must follow
// these steps:
- if (!SbDoubleIsNan(dur) && (0.0f != dur) && now >= dur &&
- playback_rate_ > 0) {
+ eos_played |=
+ !SbDoubleIsNan(dur) && (0.0f != dur) && now >= dur && playback_rate_ > 0;
+ if (eos_played) {
// If the media element has a loop attribute specified and does not have a
// current media controller,
if (loop()) {
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index b3d1f0d..31314e3 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -231,7 +231,7 @@
// WebMediaPlayerClient methods
void NetworkStateChanged() OVERRIDE;
void ReadyStateChanged() OVERRIDE;
- void TimeChanged() OVERRIDE;
+ void TimeChanged(bool eos_played) OVERRIDE;
void DurationChanged() OVERRIDE;
void OutputModeChanged() OVERRIDE;
void PlaybackStateChanged() OVERRIDE;
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 0d4e62e..fab5c8c 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -28,6 +28,8 @@
#include "cobalt/dom/document.h"
#include "cobalt/dom/dom_settings.h"
#include "cobalt/dom/element.h"
+#include "cobalt/dom/error_event.h"
+#include "cobalt/dom/error_event_init.h"
#include "cobalt/dom/event.h"
#include "cobalt/dom/history.h"
#include "cobalt/dom/html_element.h"
@@ -111,6 +113,7 @@
height_(height),
device_pixel_ratio_(device_pixel_ratio),
is_resize_event_pending_(false),
+ is_reporting_script_error_(false),
#if defined(ENABLE_TEST_RUNNER)
test_runner_(new TestRunner()),
#endif // ENABLE_TEST_RUNNER
@@ -430,6 +433,65 @@
html_element_context_->page_visibility_state()->SetApplicationState(state);
}
+bool Window::ReportScriptError(const script::ErrorReport& error_report) {
+ // Runtime script errors: when the user agent is required to report an error
+ // for a particular script, it must run these steps, after which the error is
+ // either handled or not handled:
+ // https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors
+
+ // 1. If target is in error reporting mode, then abort these steps; the error
+ // is not handled.
+ if (is_reporting_script_error_) {
+ return false;
+ }
+
+ // 2. Let target be in error reporting mode.
+ is_reporting_script_error_ = true;
+
+ // 7. Let event be a new trusted ErrorEvent object that does not bubble but is
+ // cancelable, and which has the event name error.
+ // NOTE: Cobalt does not currently support trusted events.
+ ErrorEventInit error_event_init;
+ error_event_init.set_bubbles(false);
+ error_event_init.set_cancelable(true);
+
+ if (error_report.is_muted) {
+ // 6. If script has muted errors, then set message to "Script error.", set
+ // location to the empty string, set line and col to 0, and set error
+ // object to null.
+ error_event_init.set_message("Script error.");
+ error_event_init.set_filename("");
+ error_event_init.set_lineno(0);
+ error_event_init.set_colno(0);
+ error_event_init.set_error(NULL);
+ } else {
+ // 8. Initialize event's message attribute to message.
+ error_event_init.set_message(error_report.message);
+ // 9. Initialize event's filename attribute to location.
+ error_event_init.set_filename(error_report.filename);
+ // 10. Initialize event's lineno attribute to line.
+ error_event_init.set_lineno(error_report.line_number);
+ // 11. Initialize event's colno attribute to col.
+ error_event_init.set_colno(error_report.column_number);
+ // 12. Initialize event's error attribute to error object.
+ error_event_init.set_error(error_report.error ? error_report.error.get()
+ : NULL);
+ }
+
+ scoped_refptr<ErrorEvent> error_event(
+ new ErrorEvent(base::Tokens::error(), error_event_init));
+
+ // 13. Dispatch event at target.
+ DispatchEvent(error_event);
+
+ // 14. Let target no longer be in error reporting mode.
+ is_reporting_script_error_ = false;
+
+ // 15. If event was canceled, then the error is handled. Otherwise, the error
+ // is not handled.
+ return error_event->default_prevented();
+}
+
void Window::SetSynchronousLayoutCallback(
const base::Closure& synchronous_layout_callback) {
document_->set_synchronous_layout_callback(synchronous_layout_callback);
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index e3a3f83..9cd4cb6 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -53,6 +53,7 @@
#include "cobalt/page_visibility/page_visibility_state.h"
#include "cobalt/script/callback_function.h"
#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/error_report.h"
#include "cobalt/script/execution_state.h"
#include "cobalt/script/script_runner.h"
#include "cobalt/script/script_value_factory.h"
@@ -310,6 +311,11 @@
// precipitate events to be dispatched.
void SetApplicationState(base::ApplicationState state);
+ // Performs the steps specified for runtime script errors:
+ // https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors
+ // Returns whether or not the script was handled.
+ bool ReportScriptError(const script::ErrorReport& error_report);
+
// page_visibility::PageVisibilityState::Observer implementation.
void OnWindowFocusChanged(bool has_focus) OVERRIDE;
void OnVisibilityStateChanged(
@@ -347,6 +353,11 @@
// visibility state changes to visible.
bool is_resize_event_pending_;
+ // Whether or not the window is currently reporting a script error. This is
+ // used to prevent infinite recursion, because reporting the error causes an
+ // event to be dispatched, which can generate a new script error.
+ bool is_reporting_script_error_;
+
#if defined(ENABLE_TEST_RUNNER)
scoped_refptr<TestRunner> test_runner_;
#endif // ENABLE_TEST_RUNNER
diff --git a/src/cobalt/dom/window_event_handlers.idl b/src/cobalt/dom/window_event_handlers.idl
index 2cda344..bc2bb42 100644
--- a/src/cobalt/dom/window_event_handlers.idl
+++ b/src/cobalt/dom/window_event_handlers.idl
@@ -17,4 +17,5 @@
[NoInterfaceObject]
interface WindowEventHandlers {
attribute EventHandler onunload;
+ attribute EventHandler onbeforeunload;
};
diff --git a/src/cobalt/h5vcc/h5vcc_accessibility.cc b/src/cobalt/h5vcc/h5vcc_accessibility.cc
index 2f9ddb7..7ab5164 100644
--- a/src/cobalt/h5vcc/h5vcc_accessibility.cc
+++ b/src/cobalt/h5vcc/h5vcc_accessibility.cc
@@ -42,7 +42,6 @@
#if SB_HAS(SPEECH_SYNTHESIS)
bool IsTextToSpeechEnabled() {
-#if SB_API_VERSION >= 4
// Check if the tts feature is enabled in Starboard.
SbAccessibilityTextToSpeechSettings tts_settings = {0};
// Check platform settings.
@@ -50,7 +49,7 @@
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)
@@ -102,7 +101,6 @@
}
bool H5vccAccessibility::high_contrast_text() const {
-#if SB_API_VERSION >= 4
SbAccessibilityDisplaySettings settings;
SbMemorySet(&settings, 0, sizeof(settings));
@@ -111,13 +109,9 @@
}
return settings.is_high_contrast_text_enabled;
-#else // SB_API_VERSION >= 4
- return false;
-#endif // SB_API_VERSION >= 4
}
bool H5vccAccessibility::text_to_speech() const {
-#if SB_API_VERSION >= 4
SbAccessibilityTextToSpeechSettings settings;
SbMemorySet(&settings, 0, sizeof(settings));
@@ -127,9 +121,6 @@
return settings.has_text_to_speech_setting &&
settings.is_text_to_speech_enabled;
-#else // SB_API_VERSION >= 4
- return false;
-#endif // SB_API_VERSION >= 4
}
void H5vccAccessibility::AddHighContrastTextListener(
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index b1e6b70..34a3853 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -650,10 +650,10 @@
RenderAndAnimateOverflow(padding_rounded_corners, border_node,
&animate_node_builder, border_box_offset);
}
- border_node = RenderAndAnimateOpacity(border_node, &animate_node_builder,
- opacity, opacity_animated);
border_node = RenderAndAnimateTransform(border_node, &animate_node_builder,
border_box_offset);
+ border_node = RenderAndAnimateOpacity(border_node, &animate_node_builder,
+ opacity, opacity_animated);
cached_render_tree_node_info_->node_ =
animate_node_builder.empty()
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index 5f9c536..dc00344 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -64,7 +64,6 @@
scoped_refptr<render_tree::Image> GetVideoFrame(
const scoped_refptr<ShellVideoFrameProvider>& frame_provider,
render_tree::ResourceProvider* resource_provider) {
-#if SB_API_VERSION >= 4
SbDecodeTarget decode_target = frame_provider->GetCurrentSbDecodeTarget();
if (SbDecodeTargetIsValid(decode_target)) {
#if SB_HAS(GRAPHICS)
@@ -72,12 +71,8 @@
#else // SB_HAS(GRAPHICS)
UNREFERENCED_PARAMETER(resource_provider);
return NULL;
-#endif
+#endif // SB_HAS(GRAPHICS)
} else {
-#else // SB_API_VERSION >= 4
- UNREFERENCED_PARAMETER(resource_provider);
- {
-#endif
DCHECK(frame_provider);
scoped_refptr<VideoFrame> video_frame = frame_provider->GetCurrentFrame();
if (video_frame && video_frame->texture_id()) {
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc
index e9b8319..7f0cc65 100644
--- a/src/cobalt/layout/layout_manager.cc
+++ b/src/cobalt/layout/layout_manager.cc
@@ -24,7 +24,9 @@
#include "base/timer.h"
#include "cobalt/cssom/cascade_precedence.h"
#include "cobalt/dom/camera_3d.h"
+#include "cobalt/dom/html_body_element.h"
#include "cobalt/dom/html_element_context.h"
+#include "cobalt/dom/html_head_element.h"
#include "cobalt/dom/html_html_element.h"
#include "cobalt/layout/benchmark_stat_names.h"
#include "cobalt/layout/block_formatting_block_container_box.h"
@@ -77,6 +79,8 @@
const OnLayoutCallback on_layout_callback_;
const LayoutTrigger layout_trigger_;
+ bool produced_render_tree_;
+
// Setting these flags triggers an update of the layout box tree and the
// generation of a new render tree at a regular interval (e.g. 60Hz). Events
// such as DOM mutations cause them to be set to true. While the render tree
@@ -167,6 +171,7 @@
on_render_tree_produced_callback_(on_render_tree_produced),
on_layout_callback_(on_layout),
layout_trigger_(layout_trigger),
+ produced_render_tree_(false),
are_computed_styles_and_box_tree_dirty_(true),
is_render_tree_pending_(
StringPrintf("%s.Layout.IsRenderTreePending", name.c_str()), true,
@@ -366,11 +371,32 @@
are_computed_styles_and_box_tree_dirty_ = false;
}
+ // If no render tree has been produced yet, check if html, head, and
+ // body display should block the first render tree.
+ if (!produced_render_tree_) {
+ bool displayed_html = document->html()->IsDisplayed();
+ if (!displayed_html) {
+ return;
+ }
+ bool displayed_head = true;
+ if (document->head()) {
+ displayed_head = document->head();
+ }
+ bool displayed_body = true;
+ if (document->body()) {
+ displayed_body = document->body();
+ }
+ if (!displayed_head && !displayed_body) {
+ return;
+ }
+ }
+
scoped_refptr<render_tree::Node> render_tree_root =
layout::GenerateRenderTreeFromBoxTree(used_style_provider_.get(),
layout_stat_tracker_,
&initial_containing_block_);
bool run_on_render_tree_produced_callback = true;
+ produced_render_tree_ = true;
#if defined(ENABLE_TEST_RUNNER)
if (layout_trigger_ == kTestRunnerMode &&
window_->test_runner()->should_wait()) {
diff --git a/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements-expected.png b/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements-expected.png
deleted file mode 100644
index b16b41a..0000000
--- a/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements.html b/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements.html
deleted file mode 100644
index 4d9cc35..0000000
--- a/src/cobalt/layout_tests/testdata/cobalt/display_none_set_to_all_elements.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!--
- | Applying display: none to all elements.
- -->
-<html>
-<head></head>
-<body></body>
-<style>
-* {
- display: none;
-}
-</style>
-
-</html>
diff --git a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
index 03292a0..9509933 100644
--- a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
@@ -4,7 +4,6 @@
changing-css-text-triggers-layout
cobalt-oxide, file:///cobalt/browser/testdata/cobalt-oxide/cobalt-oxide.html
console-trace-should-not-crash
-display_none_set_to_all_elements
divs-with-background-color-and-text
fixed-width-divs-with-background-color
font-weight
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 13a4626..87dab78 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
@@ -170,7 +170,7 @@
send-redirect-infinite.htm,PASS
send-redirect-infinite-sync.htm,FAIL
send-redirect-no-location.htm,FAIL
-send-redirect-to-cors.htm,DISABLE
+send-redirect-to-cors.htm,PASS
send-redirect-to-non-cors.htm,FAIL
send-response-event-order.htm,FAIL
send-response-upload-event-loadend.htm,PASS
diff --git a/src/cobalt/loader/error_fetcher.cc b/src/cobalt/loader/error_fetcher.cc
new file mode 100644
index 0000000..caf00e2
--- /dev/null
+++ b/src/cobalt/loader/error_fetcher.cc
@@ -0,0 +1,37 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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/error_fetcher.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+
+namespace cobalt {
+namespace loader {
+
+ErrorFetcher::ErrorFetcher(Handler* handler, const std::string& error_message)
+ : Fetcher(handler),
+ error_message_(error_message),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&ErrorFetcher::Fetch, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ErrorFetcher::Fetch() {
+ handler()->OnError(this, error_message_);
+}
+
+} // namespace loader
+} // namespace cobalt
diff --git a/src/cobalt/loader/error_fetcher.h b/src/cobalt/loader/error_fetcher.h
new file mode 100644
index 0000000..8f7738e
--- /dev/null
+++ b/src/cobalt/loader/error_fetcher.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_LOADER_ERROR_FETCHER_H_
+#define COBALT_LOADER_ERROR_FETCHER_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "cobalt/loader/fetcher.h"
+
+namespace cobalt {
+namespace loader {
+
+// Always returns an error.
+class ErrorFetcher : public Fetcher {
+ public:
+ ErrorFetcher(Handler* handler, const std::string& error_message_);
+
+ private:
+ void Fetch();
+
+ std::string error_message_;
+ base::WeakPtrFactory<ErrorFetcher> weak_ptr_factory_;
+};
+
+} // namespace loader
+} // namespace cobalt
+
+#endif // COBALT_LOADER_ERROR_FETCHER_H_
diff --git a/src/cobalt/loader/fetcher_factory.cc b/src/cobalt/loader/fetcher_factory.cc
index 1e70cf4..9776365 100644
--- a/src/cobalt/loader/fetcher_factory.cc
+++ b/src/cobalt/loader/fetcher_factory.cc
@@ -14,6 +14,7 @@
#include "cobalt/loader/fetcher_factory.h"
+#include <sstream>
#include <string>
#include "base/bind.h"
@@ -25,6 +26,7 @@
#include "cobalt/loader/blob_fetcher.h"
#include "cobalt/loader/cache_fetcher.h"
#include "cobalt/loader/embedded_fetcher.h"
+#include "cobalt/loader/error_fetcher.h"
#include "cobalt/loader/file_fetcher.h"
#include "cobalt/loader/net_fetcher.h"
#include "cobalt/network/network_module.h"
@@ -37,6 +39,7 @@
const char kAboutScheme[] = "about";
#endif
+#if defined(COBALT_ENABLE_FILE_SCHEME)
bool FileURLToFilePath(const GURL& url, FilePath* file_path) {
DCHECK(url.is_valid() && url.SchemeIsFile());
std::string path = url.path();
@@ -45,6 +48,7 @@
*file_path = FilePath(path);
return !file_path->empty();
}
+#endif
std::string ClipUrl(const GURL& url, size_t length) {
const std::string& spec = url.possibly_invalid_spec();
@@ -94,59 +98,67 @@
scoped_ptr<Fetcher> FetcherFactory::CreateSecureFetcher(
const GURL& url, const csp::SecurityCallback& url_security_callback,
Fetcher::Handler* handler) {
+ DLOG(INFO) << "Fetching: " << ClipUrl(url, 60);
+
if (!url.is_valid()) {
- LOG(ERROR) << "URL is invalid: " << url;
- return scoped_ptr<Fetcher>(NULL);
+ std::stringstream error_message;
+ error_message << "URL is invalid: " << url;
+ return scoped_ptr<Fetcher>(new ErrorFetcher(handler, error_message.str()));
}
- DLOG(INFO) << "Fetching: " << ClipUrl(url, 60);
- scoped_ptr<Fetcher> fetcher;
+ if ((url.SchemeIs("https") || url.SchemeIs("http") ||
+ url.SchemeIs("data")) &&
+ network_module_) {
+ NetFetcher::Options options;
+ return scoped_ptr<Fetcher>(new NetFetcher(url, url_security_callback,
+ handler, network_module_,
+ options));
+ }
+
+ if (url.SchemeIs("blob") && !blob_resolver_.is_null()) {
+ return scoped_ptr<Fetcher>(new BlobFetcher(url, handler, blob_resolver_));
+ }
+
if (url.SchemeIs(kEmbeddedScheme)) {
EmbeddedFetcher::Options options;
- fetcher.reset(
- new EmbeddedFetcher(url, url_security_callback, handler, options));
- } else if (url.SchemeIsFile()) {
+ return scoped_ptr<Fetcher>(new EmbeddedFetcher(url, url_security_callback,
+ handler, options));
+ }
+
+ // h5vcc-cache: scheme requires read_cache_callback_ which is not available
+ // in the main WebModule.
+ if (url.SchemeIs(kCacheScheme) && !read_cache_callback_.is_null()) {
+ return scoped_ptr<Fetcher>(new CacheFetcher(url, url_security_callback,
+ handler,
+ read_cache_callback_));
+ }
+
+#if defined(COBALT_ENABLE_FILE_SCHEME)
+ if (url.SchemeIsFile()) {
FilePath file_path;
- if (FileURLToFilePath(url, &file_path)) {
- FileFetcher::Options options;
- options.message_loop_proxy = file_thread_.message_loop_proxy();
- options.extra_search_dir = extra_search_dir_;
- fetcher.reset(new FileFetcher(file_path, handler, options));
- } else {
- LOG(ERROR) << "File URL cannot be converted to file path: " << url;
- }
- }
-#if defined(ENABLE_ABOUT_SCHEME)
- else if (url.SchemeIs(kAboutScheme)) { // NOLINT(readability/braces)
- fetcher.reset(new AboutFetcher(handler));
- }
-#endif
- else if (url.SchemeIs("blob")) { // NOLINT(readability/braces)
- if (!blob_resolver_.is_null()) {
- fetcher.reset(new BlobFetcher(url, handler, blob_resolver_));
- } else {
- LOG(ERROR) << "Fetcher factory not provided the blob registry, "
- "could not fetch the URL: "
- << url;
- }
- } else if (url.SchemeIs(kCacheScheme)) {
- if (read_cache_callback_.is_null()) {
- LOG(ERROR) << "read_cache_callback_ must be provided to CacheFetcher for "
- "accessing h5vcc-cache:// . This is not available in the "
- "main WebModule.";
- DCHECK(!read_cache_callback_.is_null());
- return fetcher.Pass();
+ if (!FileURLToFilePath(url, &file_path)) {
+ std::stringstream error_message;
+ error_message << "File URL cannot be converted to file path: " << url;
+ return scoped_ptr<Fetcher>(new ErrorFetcher(handler,
+ error_message.str()));
}
- fetcher.reset(new CacheFetcher(url, url_security_callback, handler,
- read_cache_callback_));
- } else { // NOLINT(readability/braces)
- DCHECK(network_module_) << "Network module required.";
- NetFetcher::Options options;
- fetcher.reset(new NetFetcher(url, url_security_callback, handler,
- network_module_, options));
+ FileFetcher::Options options;
+ options.message_loop_proxy = file_thread_.message_loop_proxy();
+ options.extra_search_dir = extra_search_dir_;
+ return scoped_ptr<Fetcher>(new FileFetcher(file_path, handler, options));
}
- return fetcher.Pass();
+#endif
+
+#if defined(ENABLE_ABOUT_SCHEME)
+ if (url.SchemeIs(kAboutScheme)) {
+ return scoped_ptr<Fetcher>(new AboutFetcher(handler));
+ }
+#endif
+
+ std::stringstream error_message;
+ error_message << "Scheme " << url.scheme() << ": is not supported";
+ return scoped_ptr<Fetcher>(new ErrorFetcher(handler, error_message.str()));
}
} // namespace loader
diff --git a/src/cobalt/loader/fetcher_factory_test.cc b/src/cobalt/loader/fetcher_factory_test.cc
index 7e525e1..e39d0a4 100644
--- a/src/cobalt/loader/fetcher_factory_test.cc
+++ b/src/cobalt/loader/fetcher_factory_test.cc
@@ -14,6 +14,7 @@
#include <string>
+#include "base/optional.h"
#include "base/run_loop.h"
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/loader/file_fetcher.h"
@@ -26,32 +27,32 @@
class StubFetcherHandler : public Fetcher::Handler {
public:
explicit StubFetcherHandler(base::RunLoop* run_loop)
- : fetcher_(NULL), run_loop_(run_loop) {}
+ : run_loop_(run_loop), fetcher_(NULL) {}
// From Fetcher::Handler.
void OnReceived(Fetcher* fetcher, const char* data, size_t size) OVERRIDE {
UNREFERENCED_PARAMETER(data);
UNREFERENCED_PARAMETER(size);
- CheckFetcher(fetcher);
+ CheckSameFetcher(fetcher);
}
void OnDone(Fetcher* fetcher) OVERRIDE {
- CheckFetcher(fetcher);
- if (run_loop_) {
- MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
- }
+ CheckSameFetcher(fetcher);
+ MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
}
- void OnError(Fetcher* fetcher, const std::string& error) OVERRIDE {
- UNREFERENCED_PARAMETER(error);
- CheckFetcher(fetcher);
- if (run_loop_) {
- MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
- }
+ void OnError(Fetcher* fetcher, const std::string& error_message) OVERRIDE {
+ CheckSameFetcher(fetcher);
+ error_message_ = error_message;
+ MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure());
}
Fetcher* fetcher() const { return fetcher_; }
+ const base::optional<std::string>& error_message() const {
+ return error_message_;
+ }
+
private:
- void CheckFetcher(Fetcher* fetcher) {
+ void CheckSameFetcher(Fetcher* fetcher) {
EXPECT_TRUE(fetcher);
if (fetcher_ == NULL) {
fetcher_ = fetcher;
@@ -60,8 +61,9 @@
EXPECT_EQ(fetcher_, fetcher);
}
- Fetcher* fetcher_;
base::RunLoop* run_loop_;
+ Fetcher* fetcher_;
+ base::optional<std::string> error_message_;
};
} // namespace
@@ -79,27 +81,42 @@
};
TEST_F(FetcherFactoryTest, InvalidURL) {
- StubFetcherHandler stub_fetcher_handler(NULL);
+ base::RunLoop run_loop;
+ StubFetcherHandler stub_fetcher_handler(&run_loop);
+
fetcher_ = fetcher_factory_.CreateFetcher(GURL("invalid-url"),
&stub_fetcher_handler);
- EXPECT_FALSE(fetcher_.get());
- EXPECT_FALSE(stub_fetcher_handler.fetcher());
+ EXPECT_TRUE(fetcher_);
+
+ run_loop.Run();
+ EXPECT_EQ(fetcher_.get(), stub_fetcher_handler.fetcher());
+ EXPECT_TRUE(stub_fetcher_handler.error_message().has_engaged());
}
TEST_F(FetcherFactoryTest, EmptyFileURL) {
- StubFetcherHandler stub_fetcher_handler(NULL);
+ base::RunLoop run_loop;
+ StubFetcherHandler stub_fetcher_handler(&run_loop);
+
fetcher_ =
fetcher_factory_.CreateFetcher(GURL("file:///"), &stub_fetcher_handler);
- EXPECT_FALSE(fetcher_.get());
- EXPECT_FALSE(stub_fetcher_handler.fetcher());
+ EXPECT_TRUE(fetcher_);
+
+ run_loop.Run();
+ EXPECT_EQ(fetcher_.get(), stub_fetcher_handler.fetcher());
+ EXPECT_TRUE(stub_fetcher_handler.error_message().has_engaged());
}
TEST_F(FetcherFactoryTest, FileURLCannotConvertToFilePath) {
- StubFetcherHandler stub_fetcher_handler(NULL);
+ base::RunLoop run_loop;
+ StubFetcherHandler stub_fetcher_handler(&run_loop);
+
fetcher_ = fetcher_factory_.CreateFetcher(GURL("file://file.txt"),
&stub_fetcher_handler);
- EXPECT_FALSE(fetcher_.get());
- EXPECT_FALSE(stub_fetcher_handler.fetcher());
+ EXPECT_TRUE(fetcher_);
+
+ run_loop.Run();
+ EXPECT_EQ(fetcher_.get(), stub_fetcher_handler.fetcher());
+ EXPECT_TRUE(stub_fetcher_handler.error_message().has_engaged());
}
TEST_F(FetcherFactoryTest, MultipleCreations) {
diff --git a/src/cobalt/loader/image/image_data_decoder.h b/src/cobalt/loader/image/image_data_decoder.h
index 2d648b5..432942a 100644
--- a/src/cobalt/loader/image/image_data_decoder.h
+++ b/src/cobalt/loader/image/image_data_decoder.h
@@ -46,7 +46,7 @@
}
#if defined(STARBOARD)
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
// Starboard version 3 adds support for hardware accelerated image decoding.
// In order to make use of this feature, subclasses of ImageDataDecoder may
// override this method in order to return an SbDecodeTarget, rather than a
@@ -59,7 +59,7 @@
virtual SbDecodeTarget RetrieveSbDecodeTarget() {
return kSbDecodeTargetInvalid;
}
-#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif // SB_HAS(GRAPHICS)
#endif // defined(STARBOARD)
void DecodeChunk(const uint8* data, size_t size);
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc
index 739c523..50f1699 100644
--- a/src/cobalt/loader/image/image_decoder.cc
+++ b/src/cobalt/loader/image/image_decoder.cc
@@ -29,9 +29,7 @@
#include "cobalt/loader/image/webp_image_decoder.h"
#include "net/base/mime_util.h"
#include "net/http/http_status_code.h"
-#if defined(STARBOARD)
#include "starboard/image.h"
-#endif
namespace cobalt {
namespace loader {
@@ -161,15 +159,13 @@
DCHECK(decoder_);
if (decoder_->FinishWithSuccess()) {
if (!decoder_->has_animation()) {
-#if defined(STARBOARD)
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
SbDecodeTarget target = decoder_->RetrieveSbDecodeTarget();
if (SbDecodeTargetIsValid(target)) {
success_callback_.Run(new StaticImage(
resource_provider_->CreateImageFromSbDecodeTarget(target)));
} else // NOLINT
#endif
-#endif
{
scoped_ptr<render_tree::ImageData> image_data =
decoder_->RetrieveImageData();
@@ -271,8 +267,7 @@
}
namespace {
-#if defined(STARBOARD)
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
const char* GetMimeTypeFromImageType(ImageDecoder::ImageType image_type) {
switch (image_type) {
case ImageDecoder::kImageTypeJPEG:
@@ -331,8 +326,7 @@
}
return scoped_ptr<ImageDataDecoder>();
}
-#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
-#endif // defined(STARBOARD)
+#endif // SB_HAS(GRAPHICS)
scoped_ptr<ImageDataDecoder> CreateImageDecoderFromImageType(
ImageDecoder::ImageType image_type,
@@ -383,12 +377,10 @@
image_type_ = DetermineImageType(signature_cache_.data);
}
-#if defined(STARBOARD)
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
decoder_ =
MaybeCreateStarboardDecoder(mime_type_, image_type_, resource_provider_);
-#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
-#endif // defined(STARBOARD)
+#endif // SB_HAS(GRAPHICS)
if (!decoder_) {
decoder_ = CreateImageDecoderFromImageType(image_type_, resource_provider_);
diff --git a/src/cobalt/loader/image/image_decoder_starboard.cc b/src/cobalt/loader/image/image_decoder_starboard.cc
index a0518c0..1183eea 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_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_TRUE && SB_HAS(GRAPHICS)
namespace cobalt {
namespace loader {
@@ -35,11 +35,11 @@
: ImageDataDecoder(resource_provider),
mime_type_(mime_type),
format_(format),
-#if SB_API_VERSION >= 4
+#if SB_TRUE
provider_(resource_provider->GetSbDecodeTargetGraphicsContextProvider()),
-#else // #if SB_API_VERSION >= 4
+#else // #if SB_TRUE
provider_(resource_provider->GetSbDecodeTargetProvider()),
-#endif // #if SB_API_VERSION >= 4
+#endif // #if SB_TRUE
target_(kSbDecodeTargetInvalid) {
TRACE_EVENT0("cobalt::loader::image",
"ImageDecoderStarboard::ImageDecoderStarboard()");
@@ -75,6 +75,6 @@
} // namespace loader
} // namespace cobalt
-#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif // SB_TRUE && 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 39b6cbe..14e386d 100644
--- a/src/cobalt/loader/image/image_decoder_starboard.h
+++ b/src/cobalt/loader/image/image_decoder_starboard.h
@@ -15,8 +15,6 @@
#ifndef COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_
#define COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_
-#if defined(STARBOARD)
-
#include <string>
#include <vector>
@@ -25,7 +23,7 @@
#include "cobalt/loader/image/image_data_decoder.h"
#include "starboard/decode_target.h"
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
namespace cobalt {
namespace loader {
@@ -51,11 +49,7 @@
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_;
};
@@ -63,8 +57,6 @@
} // namespace loader
} // namespace cobalt
-#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
-
-#endif // defined(STARBOARD)
+#endif // SB_HAS(GRAPHICS)
#endif // COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp
index 2fad2ec..6a07f01 100644
--- a/src/cobalt/loader/loader.gyp
+++ b/src/cobalt/loader/loader.gyp
@@ -28,29 +28,31 @@
'decoder.h',
'embedded_fetcher.cc',
'embedded_fetcher.h',
- 'fetcher.cc',
- 'fetcher.h',
+ 'error_fetcher.cc',
+ 'error_fetcher.h',
'fetcher_factory.cc',
'fetcher_factory.h',
+ 'fetcher.cc',
+ 'fetcher.h',
'file_fetcher.cc',
'file_fetcher.h',
'font/remote_typeface_cache.h',
'font/typeface_decoder.cc',
'font/typeface_decoder.h',
+ 'image/animated_image_tracker.cc',
+ 'image/animated_image_tracker.h',
'image/animated_webp_image.cc',
'image/animated_webp_image.h',
'image/dummy_gif_image_decoder.cc',
'image/dummy_gif_image_decoder.h',
- 'image/image.h',
'image/image_cache.h',
'image/image_data_decoder.cc',
'image/image_data_decoder.h',
- 'image/image_decoder.cc',
- '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/image_decoder.cc',
+ 'image/image_decoder.h',
+ 'image/image.h',
'image/jpeg_image_decoder.cc',
'image/jpeg_image_decoder.h',
'image/png_image_decoder.cc',
@@ -60,11 +62,11 @@
'image/threaded_image_decoder_proxy.h',
'image/webp_image_decoder.cc',
'image/webp_image_decoder.h',
- 'loader.cc',
- 'loader.h',
'loader_factory.cc',
'loader_factory.h',
'loader_types.h',
+ 'loader.cc',
+ 'loader.h',
'mesh/mesh_cache.h',
'mesh/mesh_decoder.cc',
'mesh/mesh_decoder.h',
diff --git a/src/cobalt/media/base/drm_system.h b/src/cobalt/media/base/drm_system.h
index 00dfa4a..b0a26ff 100644
--- a/src/cobalt/media/base/drm_system.h
+++ b/src/cobalt/media/base/drm_system.h
@@ -26,10 +26,6 @@
#include "base/optional.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 {
@@ -97,7 +93,7 @@
,
SessionUpdateKeyStatusesCallback update_key_statuses_callback
#endif // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
- );
+ ); // NOLINT(whitespace/parens)
void set_id(const std::string& id) { id_ = id; }
const SessionUpdateRequestGeneratedCallback&
update_request_generated_callback() const {
@@ -133,7 +129,7 @@
#if SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
SessionUpdateKeyStatusesCallback session_update_key_statuses_callback
#endif // SB_API_VERSION >= SB_DRM_KEY_STATUSES_UPDATE_SUPPORT_API_VERSION
- );
+ ); // NOLINT(whitespace/parens)
private:
// Stores context of |GenerateSessionUpdateRequest|.
diff --git a/src/cobalt/media/base/shell_media_platform.h b/src/cobalt/media/base/shell_media_platform.h
index 73510fd..1e6c711 100644
--- a/src/cobalt/media/base/shell_media_platform.h
+++ b/src/cobalt/media/base/shell_media_platform.h
@@ -63,14 +63,10 @@
return NULL;
}
-#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
// This function is called before the decoder buffer leaves the demuxer and
// is being sent to the media pipeline for decrypting and decoding. The
diff --git a/src/cobalt/media/base/shell_video_frame_provider.h b/src/cobalt/media/base/shell_video_frame_provider.h
index a5bd969..e2b1569 100644
--- a/src/cobalt/media/base/shell_video_frame_provider.h
+++ b/src/cobalt/media/base/shell_video_frame_provider.h
@@ -49,9 +49,7 @@
ShellVideoFrameProvider() : output_mode_(kOutputModeInvalid) {}
-#if SB_API_VERSION >= 4
typedef base::Callback<SbDecodeTarget()> GetCurrentSbDecodeTargetFunction;
-#endif // SB_API_VERSION >= 4
scoped_refptr<VideoFrame> GetCurrentFrame() { return NULL; }
@@ -65,7 +63,6 @@
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
@@ -91,15 +88,12 @@
return get_current_sb_decode_target_function_.Run();
}
}
-#endif // SB_API_VERSION >= 4
private:
mutable base::Lock lock_;
OutputMode output_mode_;
-#if SB_API_VERSION >= 4
GetCurrentSbDecodeTargetFunction get_current_sb_decode_target_function_;
-#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 cb8749c..9cf382a 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -96,11 +96,9 @@
DCHECK(host_);
DCHECK(set_bounds_helper_);
-#if SB_API_VERSION >= 4
output_mode_ = ComputeSbPlayerOutputMode(
MediaVideoCodecToSbMediaVideoCodec(video_config.codec()), drm_system,
prefer_decode_to_texture);
-#endif // SB_API_VERSION >= 4
CreatePlayer();
@@ -118,11 +116,9 @@
ShellMediaPlatform::Instance()->GetVideoFrameProvider()->SetOutputMode(
ShellVideoFrameProvider::kOutputModeInvalid);
-#if SB_API_VERSION >= 4
ShellMediaPlatform::Instance()
->GetVideoFrameProvider()
->ResetGetCurrentSbDecodeTargetFunction();
-#endif // SB_API_VERSION >= 4
if (SbPlayerIsValid(player_)) {
SbPlayerDestroy(player_);
@@ -211,13 +207,9 @@
}
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() {
@@ -231,11 +223,7 @@
}
++ticket_;
-#if SB_API_VERSION < 4
- SbPlayerSetPause(player_, true);
-#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, 0.f);
-#endif // SB_API_VERSION < 4
}
void StarboardPlayer::Seek(base::TimeDelta time) {
@@ -260,11 +248,7 @@
++ticket_;
SbPlayerSeek(player_, TimeDeltaToSbMediaTime(time), ticket_);
seek_pending_ = false;
-#if SB_API_VERSION < 4
- SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif // SB_API_VERSION < 4
}
void StarboardPlayer::SetVolume(float volume) {
@@ -295,11 +279,7 @@
return;
}
-#if SB_API_VERSION < 4
- SbPlayerSetPause(player_, playback_rate == 0.0);
-#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, playback_rate);
-#endif // SB_API_VERSION < 4
}
void StarboardPlayer::GetInfo(uint32* video_frames_decoded,
@@ -349,11 +329,7 @@
DCHECK(SbPlayerIsValid(player_));
-#if SB_API_VERSION < 4
- SbPlayerSetPause(player_, true);
-#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, 0.0);
-#endif // SB_API_VERSION < 4
base::AutoLock auto_lock(lock_);
@@ -391,7 +367,6 @@
}
namespace {
-#if SB_API_VERSION >= 4
ShellVideoFrameProvider::OutputMode ToVideoFrameProviderOutputMode(
SbPlayerOutputMode output_mode) {
switch (output_mode) {
@@ -406,7 +381,6 @@
NOTREACHED();
return ShellVideoFrameProvider::kOutputModeInvalid;
}
-#endif // #if SB_API_VERSION >= 4
} // namespace
void StarboardPlayer::CreatePlayer() {
@@ -446,26 +420,17 @@
SbMediaVideoCodec video_codec =
MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
-#if SB_API_VERSION >= 4
DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
-#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 >= 4
- ,
- output_mode_,
- ShellMediaPlatform::Instance()->GetSbDecodeTargetGraphicsContextProvider()
-#elif SB_API_VERSION >= 3
- ,
- ShellMediaPlatform::Instance()->GetSbDecodeTargetProvider() // provider
-#endif // SB_API_VERSION >= 3
- );
+ player_ = SbPlayerCreate(window_, video_codec, audio_codec,
+ SB_PLAYER_NO_DURATION, drm_system_, &audio_header,
+ &StarboardPlayer::DeallocateSampleCB,
+ &StarboardPlayer::DecoderStatusCB,
+ &StarboardPlayer::PlayerStatusCB, this, output_mode_,
+ ShellMediaPlatform::Instance()
+ ->GetSbDecodeTargetGraphicsContextProvider());
DCHECK(SbPlayerIsValid(player_));
-#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.
@@ -477,10 +442,6 @@
}
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);
@@ -490,14 +451,13 @@
}
}
-#if SB_API_VERSION >= 4
SbDecodeTarget StarboardPlayer::GetCurrentSbDecodeTarget() {
return SbPlayerGetCurrentFrame(player_);
}
+
SbPlayerOutputMode StarboardPlayer::GetSbPlayerOutputMode() {
return output_mode_;
}
-#endif // SB_API_VERSION >= 4
void StarboardPlayer::ClearDecoderBufferCache() {
DCHECK(message_loop_->BelongsToCurrentThread());
@@ -572,11 +532,7 @@
}
SbPlayerSeek(player_, TimeDeltaToSbMediaTime(preroll_timestamp_), ticket_);
SetVolume(volume_);
-#if SB_API_VERSION < 4
- SbPlayerSetPause(player_, playback_rate_ == 0.0);
-#else // SB_API_VERSION < 4
SbPlayerSetPlaybackRate(player_, playback_rate_);
-#endif // SB_API_VERSION < 4
return;
}
host_->OnPlayerStatus(state);
@@ -628,7 +584,6 @@
helper->callback_helper_, sample_buffer));
}
-#if SB_API_VERSION >= 4
// static
SbPlayerOutputMode StarboardPlayer::ComputeSbPlayerOutputMode(
SbMediaVideoCodec codec, SbDrmSystem drm_system,
@@ -658,7 +613,6 @@
return output_mode;
}
-#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 d2c02f1..ad1a31a 100644
--- a/src/cobalt/media/base/starboard_player.h
+++ b/src/cobalt/media/base/starboard_player.h
@@ -73,10 +73,8 @@
void Suspend();
void Resume();
-#if SB_API_VERSION >= 4
SbDecodeTarget GetCurrentSbDecodeTarget();
SbPlayerOutputMode GetSbPlayerOutputMode();
-#endif // SB_API_VERSION >= 4
private:
enum State {
@@ -127,13 +125,11 @@
static void DeallocateSampleCB(SbPlayer player, void* context,
const void* sample_buffer);
-#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 >= 4
// The following variables are initialized in the ctor and never changed.
const scoped_refptr<base::MessageLoopProxy> message_loop_;
@@ -171,10 +167,8 @@
uint32 cached_video_frames_dropped_;
base::TimeDelta preroll_timestamp_;
-#if SB_API_VERSION >= 4
// Keep track of the output mode we are supposed to output to.
SbPlayerOutputMode output_mode_;
-#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 72c4c72..bc6905a 100644
--- a/src/cobalt/media/base/starboard_utils.cc
+++ b/src/cobalt/media/base/starboard_utils.cc
@@ -128,7 +128,6 @@
}
}
-#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 +278,6 @@
return sb_media_color_metadata;
}
-#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 0cb43c1..04196b5 100644
--- a/src/cobalt/media/base/starboard_utils.h
+++ b/src/cobalt/media/base/starboard_utils.h
@@ -39,10 +39,8 @@
SbDrmSampleInfo* drm_info,
SbDrmSubSampleMapping* subsample_mapping);
-#if SB_API_VERSION >= 4
SbMediaColorMetadata MediaToSbMediaColorMetadata(
const WebMColorMetadata& webm_color_metadata);
-#endif
} // namespace media
} // namespace cobalt
diff --git a/src/cobalt/media/player/web_media_player.h b/src/cobalt/media/player/web_media_player.h
index b3b5cb6..beee2aa 100644
--- a/src/cobalt/media/player/web_media_player.h
+++ b/src/cobalt/media/player/web_media_player.h
@@ -194,7 +194,7 @@
public:
virtual void NetworkStateChanged() = 0;
virtual void ReadyStateChanged() = 0;
- virtual void TimeChanged() = 0;
+ virtual void TimeChanged(bool eos_played) = 0;
virtual void DurationChanged() = 0;
virtual void OutputModeChanged() = 0;
virtual void PlaybackStateChanged() = 0;
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index bd0d14b..c8cf80a 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -590,7 +590,8 @@
// Update our paused time.
if (state_.paused) state_.paused_time = pipeline_->GetMediaTime();
- GetClient()->TimeChanged();
+ const bool eos_played = false;
+ GetClient()->TimeChanged(eos_played);
}
void WebMediaPlayerImpl::OnPipelineEnded(PipelineStatus status) {
@@ -599,7 +600,9 @@
OnPipelineError(status);
return;
}
- GetClient()->TimeChanged();
+
+ const bool eos_played = true;
+ GetClient()->TimeChanged(eos_played);
}
void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) {
diff --git a/src/cobalt/media/sandbox/web_media_player_helper.cc b/src/cobalt/media/sandbox/web_media_player_helper.cc
index 58618a2..1909a5c 100644
--- a/src/cobalt/media/sandbox/web_media_player_helper.cc
+++ b/src/cobalt/media/sandbox/web_media_player_helper.cc
@@ -35,7 +35,7 @@
// WebMediaPlayerClient methods
void NetworkStateChanged() OVERRIDE {}
void ReadyStateChanged() OVERRIDE {}
- void TimeChanged() OVERRIDE {}
+ void TimeChanged(bool) OVERRIDE {}
void DurationChanged() OVERRIDE {}
void OutputModeChanged() OVERRIDE {}
void PlaybackStateChanged() OVERRIDE {}
@@ -48,9 +48,8 @@
#endif // defined(COBALT_MEDIA_SOURCE_2016)
std::string SourceURL() const OVERRIDE { return ""; }
#if defined(COBALT_MEDIA_SOURCE_2016)
- void EncryptedMediaInitDataEncountered(EmeInitDataType init_data_type,
- const unsigned char* init_data,
- unsigned init_data_length) OVERRIDE {}
+ void EncryptedMediaInitDataEncountered(EmeInitDataType, const unsigned char*,
+ unsigned) OVERRIDE {}
#endif // defined(COBALT_MEDIA_SOURCE_2016)
};
diff --git a/src/cobalt/media/shell_media_platform_starboard.h b/src/cobalt/media/shell_media_platform_starboard.h
index 0119293..8bb5c4c 100644
--- a/src/cobalt/media/shell_media_platform_starboard.h
+++ b/src/cobalt/media/shell_media_platform_starboard.h
@@ -44,7 +44,6 @@
return video_frame_provider_;
}
-#if SB_API_VERSION >= 4
SbDecodeTargetGraphicsContextProvider*
GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
#if SB_HAS(GRAPHICS)
@@ -53,15 +52,6 @@
return NULL;
#endif // SB_HAS(GRAPHICS)
}
-#elif SB_API_VERSION >= 3
- SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE {
-#if SB_HAS(GRAPHICS)
- return resource_provider_->GetSbDecodeTargetProvider();
-#else // SB_HAS(GRAPHICS)
- return NULL;
-#endif // SB_HAS(GRAPHICS)
- }
-#endif // SB_API_VERSION >= 4
void Suspend() OVERRIDE { resource_provider_ = NULL; }
void Resume(render_tree::ResourceProvider* resource_provider) OVERRIDE {
@@ -134,7 +124,6 @@
const scoped_refptr<DecoderBuffer>& buffer) OVERRIDE;
bool IsOutputProtected() OVERRIDE;
-#if SB_API_VERSION >= 4
SbDecodeTargetGraphicsContextProvider*
GetSbDecodeTargetGraphicsContextProvider() OVERRIDE {
#if SB_HAS(GRAPHICS)
@@ -143,15 +132,6 @@
return NULL;
#endif // SB_HAS(GRAPHICS)
}
-#elif SB_API_VERSION >= 3
- virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() {
-#if SB_HAS(GRAPHICS)
- return resource_provider_->GetSbDecodeTargetProvider();
-#else // SB_HAS(GRAPHICS)
- return NULL;
-#endif // SB_HAS(GRAPHICS)
- }
-#endif // SB_API_VERSION >= 4
void Suspend() OVERRIDE { resource_provider_ = NULL; }
void Resume(
diff --git a/src/cobalt/network/local_network.cc b/src/cobalt/network/local_network.cc
index 725f3e4..964a54e 100644
--- a/src/cobalt/network/local_network.cc
+++ b/src/cobalt/network/local_network.cc
@@ -23,7 +23,6 @@
namespace {
-#if SB_API_VERSION >= 4
bool CompareNBytesOfAddress(const SbSocketAddress& ip,
const SbSocketAddress& source_address,
const SbSocketAddress& netmask,
@@ -57,22 +56,15 @@
}
}
-#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) {
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 c82b19d..3487dd7 100644
--- a/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
+++ b/src/cobalt/network/starboard/user_agent_string_factory_starboard.cc
@@ -41,9 +41,7 @@
case kSbSystemDeviceTypeOverTheTopBox:
case kSbSystemDeviceTypeSetTopBox:
case kSbSystemDeviceTypeTV:
-#if SB_API_VERSION >= 4
case kSbSystemDeviceTypeAndroidTV:
-#endif // SB_API_VERSION >= 4
return true;
case kSbSystemDeviceTypeDesktopPC:
case kSbSystemDeviceTypeUnknown:
@@ -107,11 +105,9 @@
case kSbSystemDeviceTypeTV:
youtube_tv_info_->device_type = YouTubeTVInfo::kTV;
break;
-#if SB_API_VERSION >= 4
case kSbSystemDeviceTypeAndroidTV:
youtube_tv_info_->device_type = YouTubeTVInfo::kAndroidTV;
break;
-#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 7ad310b..8bd7815 100644
--- a/src/cobalt/network/user_agent_string_factory.cc
+++ b/src/cobalt/network/user_agent_string_factory.cc
@@ -39,6 +39,28 @@
#error Unknown build configuration.
#endif
+struct SanitizeReplacements {
+ const char* replace_chars;
+ const char* replace_with;
+} kSanitizeReplacements[] = {
+ { ",", u8"\uFF0C" }, // fullwidth comma
+ { "_", u8"\u2E0F" }, // paragraphos
+ { "/", u8"\u2215" }, // division slash
+ { "(", u8"\uFF08" }, // fullwidth left paren
+ { ")", u8"\uFF09" }, // fullwidth right paren
+};
+
+// Replace reserved characters with Unicode homoglyphs
+std::string Sanitize(const std::string& str) {
+ std::string clean(str);
+ for (size_t i=0; i < arraysize(kSanitizeReplacements); i++) {
+ const SanitizeReplacements* replacement = kSanitizeReplacements + i;
+ ReplaceChars(
+ clean, replacement->replace_chars, replacement->replace_with, &clean);
+ }
+ return clean;
+}
+
} // namespace
std::string UserAgentStringFactory::CreateUserAgentString() {
@@ -66,11 +88,12 @@
if (youtube_tv_info_) {
base::StringAppendF(
&user_agent, ", %s_%s_%s/%s (%s, %s, %s)",
- youtube_tv_info_->network_operator.value_or("").c_str(),
+ Sanitize(youtube_tv_info_->network_operator.value_or("")).c_str(),
CreateDeviceTypeString().c_str(),
- youtube_tv_info_->chipset_model_number.value_or("").c_str(),
- youtube_tv_info_->firmware_version.value_or("").c_str(),
- youtube_tv_info_->brand.c_str(), youtube_tv_info_->model.c_str(),
+ Sanitize(youtube_tv_info_->chipset_model_number.value_or("")).c_str(),
+ Sanitize(youtube_tv_info_->firmware_version.value_or("")).c_str(),
+ Sanitize(youtube_tv_info_->brand).c_str(),
+ Sanitize(youtube_tv_info_->model).c_str(),
CreateConnectionTypeString().c_str());
}
@@ -112,10 +135,8 @@
return "STB";
case YouTubeTVInfo::kTV:
return "TV";
-#if SB_API_VERSION >= 4
case YouTubeTVInfo::kAndroidTV:
return "ATV";
-#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 5869855..7f7686f 100644
--- a/src/cobalt/network/user_agent_string_factory.h
+++ b/src/cobalt/network/user_agent_string_factory.h
@@ -47,9 +47,7 @@
struct YouTubeTVInfo {
enum DeviceType {
kInvalidDeviceType,
-#if SB_API_VERSION >= 4
kAndroidTV,
-#endif // SB_API_VERSION >= 4
kBlueRayDiskPlayer,
kGameConsole,
kOverTheTopBox,
diff --git a/src/cobalt/network/user_agent_string_factory_test.cc b/src/cobalt/network/user_agent_string_factory_test.cc
index 6e3a669..80171b8 100644
--- a/src/cobalt/network/user_agent_string_factory_test.cc
+++ b/src/cobalt/network/user_agent_string_factory_test.cc
@@ -82,23 +82,35 @@
class UserAgentStringFactoryWithYouTubeTVInfo : public UserAgentStringFactory {
public:
UserAgentStringFactoryWithYouTubeTVInfo() {
+ // There are deliberately a variety of underscores, commas, slashes, and
+ // parentheses in the strings below to ensure they get sanitized.
os_name_and_version_ = "GLaDOS 3.11";
youtube_tv_info_ = YouTubeTVInfo();
- youtube_tv_info_->network_operator = "ApertureLaboratories";
+ youtube_tv_info_->network_operator = "Aperture_Science_Innovators";
youtube_tv_info_->device_type = YouTubeTVInfo::kOverTheTopBox;
- youtube_tv_info_->chipset_model_number = "Wheatley";
- youtube_tv_info_->firmware_version = "0.01";
- youtube_tv_info_->brand = "Aperture Science";
+ youtube_tv_info_->chipset_model_number = "P-body/Orange_Atlas/Blue";
+ youtube_tv_info_->firmware_version = "0,01";
+ youtube_tv_info_->brand = "Aperture Science (Labs)";
youtube_tv_info_->model = "GLaDOS";
}
};
+// Look-alike replacements expected from sanitizing fields
+#define COMMA u8"\uFF0C" // fullwidth comma
+#define UNDER u8"\u2E0F" // paragraphos
+#define SLASH u8"\u2215" // division slash
+#define LPAREN u8"\uFF08" // fullwidth left paren
+#define RPAREN u8"\uFF09" // fullwidth right paren
+
TEST(UserAgentStringFactoryTest, WithYouTubeTVInfo) {
std::string user_agent_string =
UserAgentStringFactoryWithYouTubeTVInfo().CreateUserAgentString();
- EXPECT_NE(std::string::npos,
- user_agent_string.find("ApertureLaboratories_OTT_Wheatley/0.01 "
- "(Aperture Science, GLaDOS, )"));
+ const char* tv_info_str =
+ "Aperture" UNDER "Science" UNDER "Innovators"
+ "_OTT_"
+ "P-body" SLASH "Orange" UNDER "Atlas" SLASH "Blue"
+ "/0" COMMA "01 (Aperture Science " LPAREN "Labs" RPAREN ", GLaDOS, )";
+ EXPECT_NE(std::string::npos, user_agent_string.find(tv_info_str));
}
class UserAgentStringFactoryWithWiredConnection
diff --git a/src/cobalt/render_tree/mock_resource_provider.h b/src/cobalt/render_tree/mock_resource_provider.h
index 1d13b6c..d30b9fc 100644
--- a/src/cobalt/render_tree/mock_resource_provider.h
+++ b/src/cobalt/render_tree/mock_resource_provider.h
@@ -94,23 +94,17 @@
#if SB_HAS(GRAPHICS)
-#if SB_API_VERSION >= 3
scoped_refptr<Image> CreateImageFromSbDecodeTarget(SbDecodeTarget target) {
UNREFERENCED_PARAMETER(target);
return NULL;
}
bool SupportsSbDecodeTarget() { return false; }
-#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)
diff --git a/src/cobalt/render_tree/resource_provider.h b/src/cobalt/render_tree/resource_provider.h
index 4a648d1..acbbb21 100644
--- a/src/cobalt/render_tree/resource_provider.h
+++ b/src/cobalt/render_tree/resource_provider.h
@@ -27,9 +27,7 @@
#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"
-#endif // defined(STARBOARD)
namespace cobalt {
namespace render_tree {
@@ -76,7 +74,6 @@
scoped_ptr<ImageData> pixel_data) = 0;
#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.
@@ -85,19 +82,12 @@
// 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;
-#endif // SB__API_VERSION >= 4
#endif // SB_HAS(GRAPHICS)
// Returns a raw chunk of memory that can later be passed into a function like
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h
index aee3f69..397d8cb 100644
--- a/src/cobalt/render_tree/resource_provider_stub.h
+++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -247,30 +247,22 @@
return make_scoped_refptr(new ImageStub(skia_source_data.Pass()));
}
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
scoped_refptr<Image> CreateImageFromSbDecodeTarget(
SbDecodeTarget decode_target) OVERRIDE {
NOTREACHED();
-#if SB_API_VERSION < 4
- SbDecodeTargetDestroy(decode_target);
-#else // 4
SbDecodeTargetRelease(decode_target);
-#endif // 4
return NULL;
}
bool SupportsSbDecodeTarget() OVERRIDE { return false; }
-#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif // SB_HAS(GRAPHICS)
#if 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
#endif // SB_HAS(GRAPHICS)
scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes,
diff --git a/src/cobalt/render_tree/rounded_corners.cc b/src/cobalt/render_tree/rounded_corners.cc
index b0a3331..7eacb19 100644
--- a/src/cobalt/render_tree/rounded_corners.cc
+++ b/src/cobalt/render_tree/rounded_corners.cc
@@ -17,6 +17,17 @@
namespace cobalt {
namespace render_tree {
+RoundedCorners RoundedCorners::Scale(float sx, float sy) const {
+ return RoundedCorners(RoundedCorner(top_left.horizontal * sx,
+ top_left.vertical * sy),
+ RoundedCorner(top_right.horizontal * sx,
+ top_right.vertical * sy),
+ RoundedCorner(bottom_right.horizontal * sx,
+ bottom_right.vertical * sy),
+ RoundedCorner(bottom_left.horizontal * sx,
+ bottom_left.vertical * sy));
+}
+
RoundedCorners RoundedCorners::Normalize(const math::RectF& rect) const {
float scale = 1.0f;
float size;
@@ -48,14 +59,7 @@
scale = std::min(rect.height() / size, scale);
}
- return RoundedCorners(RoundedCorner(top_left.horizontal * scale,
- top_left.vertical * scale),
- RoundedCorner(top_right.horizontal * scale,
- top_right.vertical * scale),
- RoundedCorner(bottom_right.horizontal * scale,
- bottom_right.vertical * scale),
- RoundedCorner(bottom_left.horizontal * scale,
- bottom_left.vertical * scale));
+ return Scale(scale, scale);
}
bool RoundedCorners::IsNormalized(const math::RectF& rect) const {
diff --git a/src/cobalt/render_tree/rounded_corners.h b/src/cobalt/render_tree/rounded_corners.h
index cf1d03e..7646f65 100644
--- a/src/cobalt/render_tree/rounded_corners.h
+++ b/src/cobalt/render_tree/rounded_corners.h
@@ -91,6 +91,8 @@
return Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
}
+ RoundedCorners Scale(float sx, float sy) const;
+
// Ensure the rounded corners' radii do not exceed the length of the
// corresponding edge of the given rect.
RoundedCorners Normalize(const math::RectF& rect) const;
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_texture_masked_texture_domain.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_texture_masked_texture_domain.glsl
new file mode 100644
index 0000000..168df28
--- /dev/null
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_texture_masked_texture_domain.glsl
@@ -0,0 +1,33 @@
+#version 100
+precision mediump float;
+uniform sampler2D uSampler0_Stage0;
+uniform sampler2D uSampler0_Stage1;
+uniform vec4 uTexDom_Stage1;
+varying vec4 vColor;
+varying vec2 vMatrixCoord_Stage0;
+varying vec2 vMatrixCoord_Stage1;
+
+void main()
+{
+ vec4 output_Stage0;
+ {
+ // Stage 0: Texture
+ output_Stage0 =
+ (vColor * texture2D(uSampler0_Stage0, vMatrixCoord_Stage0).aaaa);
+ }
+ vec4 output_Stage1;
+ {
+ // Stage 1: TextureDomain
+ {
+ bvec4 outside;
+ outside.xy = lessThan(vMatrixCoord_Stage1, uTexDom_Stage1.xy);
+ outside.zw = greaterThan(vMatrixCoord_Stage1, uTexDom_Stage1.zw);
+ output_Stage1 =
+ any(outside) ?
+ vec4(0.0, 0.0, 0.0, 0.0) :
+ (output_Stage0 * texture2D(uSampler0_Stage1,
+ vMatrixCoord_Stage1));
+ }
+ }
+ gl_FragColor = output_Stage1;
+}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi b/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi
index 1997d06..d38588c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi
+++ b/src/cobalt/renderer/glimp_shaders/glsl/shaders.gypi
@@ -78,6 +78,7 @@
'fragment_skia_texture_domain.glsl',
'fragment_skia_texture_domain_masked_texture_domain.glsl',
'fragment_skia_texture_masked_texture.glsl',
+ 'fragment_skia_texture_masked_texture_domain.glsl',
'fragment_skia_yuv.glsl',
'fragment_textured_vbo_rgba.glsl',
'fragment_textured_vbo_uyvy_1plane.glsl',
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
index 1da3c9e..e66f6e5 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -41,9 +41,7 @@
: 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) {
@@ -83,25 +81,10 @@
return make_scoped_refptr(new SinglePlaneImage(blitter_source_data.Pass()));
}
-#if SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
scoped_refptr<render_tree::Image>
ResourceProvider::CreateImageFromSbDecodeTarget(SbDecodeTarget decode_target) {
-#if SB_API_VERSION < 4
- SbDecodeTargetFormat format = SbDecodeTargetGetFormat(decode_target);
- if (format == kSbDecodeTargetFormat1PlaneRGBA) {
- SbBlitterSurface surface =
- SbDecodeTargetGetPlane(decode_target, kSbDecodeTargetPlaneRGBA);
- DCHECK(SbBlitterIsSurfaceValid(surface));
- bool is_opaque = SbDecodeTargetIsOpaque(decode_target);
-
- // Now that we have the surface it contained, we are free to delete
- // |decode_target|.
- SbDecodeTargetDestroy(decode_target);
- return make_scoped_refptr(
- new SinglePlaneImage(surface, is_opaque, base::Closure()));
- }
-#else // SB_API_VERSION < 4
SbDecodeTargetInfo info;
SbMemorySet(&info, 0, sizeof(info));
CHECK(SbDecodeTargetGetInfo(decode_target, &info));
@@ -121,19 +104,14 @@
plane.surface, info.is_opaque,
base::Bind(&SbDecodeTargetRelease, decode_target)));
}
-#endif // SB_API_VERSION < 4
NOTREACHED()
<< "Only format kSbDecodeTargetFormat1PlaneRGBA is currently supported.";
-#if SB_API_VERSION < 4
- SbDecodeTargetDestroy(decode_target);
-#else // SB_API_VERSION < 4
SbDecodeTargetRelease(decode_target);
-#endif // SB_API_VERSION < 4
return NULL;
}
-#endif // SB_API_VERSION >= 3 && SB_HAS(GRAPHICS)
+#endif // SB_HAS(GRAPHICS)
scoped_ptr<render_tree::RawImageMemory>
ResourceProvider::AllocateRawImageMemory(size_t size_in_bytes,
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
index 3f01c64..92315fe 100644
--- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -43,21 +43,15 @@
void Finish() OVERRIDE {}
-#if SB_API_VERSION >= 3
scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
SbDecodeTarget decode_target) OVERRIDE;
bool SupportsSbDecodeTarget() OVERRIDE { return true; }
-#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;
@@ -123,10 +117,8 @@
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/egl/draw_object.cc b/src/cobalt/renderer/rasterizer/egl/draw_object.cc
index c2084cf..24ec5d0 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object.cc
@@ -17,6 +17,7 @@
#include <algorithm>
#include <limits>
+#include "cobalt/math/transform_2d.h"
#include "cobalt/renderer/backend/egl/utils.h"
namespace cobalt {
@@ -24,6 +25,34 @@
namespace rasterizer {
namespace egl {
+namespace {
+// To accommodate large radius values for rounded corners while using mediump
+// floats in the fragment shader, scale 1 / radius.xy by kRCornerGradientScale.
+// The fragment shader will take this into account.
+const float kRCornerGradientScale = 16.0f;
+
+// Get the midpoint of the given rounded rect. A normalized rounded rect
+// should have at least one point which the corners do not cross.
+math::PointF GetRRectCenter(const math::RectF& rect,
+ const render_tree::RoundedCorners& corners) {
+ return math::PointF(
+ 0.5f * (rect.x() + std::max(corners.top_left.horizontal,
+ corners.bottom_left.horizontal) +
+ rect.right() - std::max(corners.top_right.horizontal,
+ corners.bottom_right.horizontal)),
+ 0.5f * (rect.y() + std::max(corners.top_left.vertical,
+ corners.top_right.vertical) +
+ rect.bottom() - std::max(corners.bottom_left.vertical,
+ corners.bottom_right.vertical)));
+}
+
+math::PointF ClampToBounds(const math::RectF& bounds, float x, float y) {
+ return math::PointF(
+ std::min(std::max(bounds.x(), x), bounds.right()),
+ std::min(std::max(bounds.y(), y), bounds.bottom()));
+}
+} // namespace
+
DrawObject::BaseState::BaseState()
: transform(math::Matrix3F::Identity()),
scissor(0, 0,
@@ -38,9 +67,35 @@
rounded_scissor_corners(other.rounded_scissor_corners),
opacity(other.opacity) {}
+DrawObject::RCorner::RCorner(const float (&position)[2], const RCorner& init)
+ : x((position[0] - init.x) * init.rx),
+ y((position[1] - init.y) * init.ry),
+ rx(init.rx * kRCornerGradientScale),
+ ry(init.ry * kRCornerGradientScale) {}
+
DrawObject::DrawObject(const BaseState& base_state)
: base_state_(base_state) {}
+math::Vector2dF DrawObject::GetScale() const {
+ float m00 = base_state_.transform(0, 0);
+ float m01 = base_state_.transform(0, 1);
+ float m10 = base_state_.transform(1, 0);
+ float m11 = base_state_.transform(1, 1);
+ return math::Vector2dF(std::sqrt(m00 * m00 + m10 * m10),
+ std::sqrt(m01 * m01 + m11 * m11));
+}
+
+math::Vector2dF DrawObject::RemoveScaleFromTransform() {
+ // Avoid division by zero.
+ const float kEpsilon = 0.00001f;
+
+ math::Vector2dF scale = GetScale();
+ base_state_.transform = base_state_.transform *
+ math::ScaleMatrix(1.0f / std::max(scale.x(), kEpsilon),
+ 1.0f / std::max(scale.y(), kEpsilon));
+ return scale;
+}
+
// static
uint32_t DrawObject::GetGLRGBA(float r, float g, float b, float a) {
// Ensure color bytes represent RGBA, regardless of endianness.
@@ -56,74 +111,130 @@
}
// static
-void DrawObject::SetRRectUniforms(GLint rect_uniform, GLint corners_uniform,
- const math::RectF& rect, const render_tree::RoundedCorners& corners,
- float inset) {
- math::RectF inset_rect(rect);
- inset_rect.Inset(inset, inset);
- render_tree::RoundedCorners inset_corners =
- corners.Inset(inset, inset, inset, inset);
+void DrawObject::GetRRectAttributes(const math::RectF& bounds,
+ math::RectF rect, render_tree::RoundedCorners corners,
+ RRectAttributes (&out_attributes)[4]) {
+ GetRCornerValues(&rect, &corners, out_attributes);
- // Tweak corners that are square-ish so they have values that play
- // nicely with the shader. Interpolating x^2 / a^2 + y^2 / b^2 does not
- // work well when |a| or |b| are very small.
- if (inset_corners.top_left.horizontal <= 0.5f ||
- inset_corners.top_left.vertical <= 0.5f) {
- inset_corners.top_left.horizontal = 0.0f;
- inset_corners.top_left.vertical = 0.0f;
- }
- if (inset_corners.top_right.horizontal <= 0.5f ||
- inset_corners.top_right.vertical <= 0.5f) {
- inset_corners.top_right.horizontal = 0.0f;
- inset_corners.top_right.vertical = 0.0f;
- }
- if (inset_corners.bottom_left.horizontal <= 0.5f ||
- inset_corners.bottom_left.vertical <= 0.5f) {
- inset_corners.bottom_left.horizontal = 0.0f;
- inset_corners.bottom_left.vertical = 0.0f;
- }
- if (inset_corners.bottom_right.horizontal <= 0.5f ||
- inset_corners.bottom_right.vertical <= 0.5f) {
- inset_corners.bottom_right.horizontal = 0.0f;
- inset_corners.bottom_right.vertical = 0.0f;
- }
+ // Calculate the bounds for each patch. Four patches will be used to cover
+ // the entire bounded area.
+ math::PointF center = GetRRectCenter(rect, corners);
+ center = ClampToBounds(bounds, center.x(), center.y());
+ out_attributes[0].bounds.SetRect(bounds.x(), bounds.y(),
+ center.x() - bounds.x(), center.y() - bounds.y());
+ out_attributes[1].bounds.SetRect(center.x(), bounds.y(),
+ bounds.right() - center.x(), center.y() - bounds.y());
+ out_attributes[2].bounds.SetRect(bounds.x(), center.y(),
+ center.x() - bounds.x(), bounds.bottom() - center.y());
+ out_attributes[3].bounds.SetRect(center.x(), center.y(),
+ bounds.right() - center.x(), bounds.bottom() - center.y());
+}
+
+// static
+void DrawObject::GetRRectAttributes(const math::RectF& bounds,
+ math::RectF rect, render_tree::RoundedCorners corners,
+ RRectAttributes (&out_attributes)[8]) {
+ GetRCornerValues(&rect, &corners, out_attributes);
+ out_attributes[4].rcorner = out_attributes[0].rcorner;
+ out_attributes[5].rcorner = out_attributes[1].rcorner;
+ out_attributes[6].rcorner = out_attributes[2].rcorner;
+ out_attributes[7].rcorner = out_attributes[3].rcorner;
+
+ // Given an ellipse with radii A and B, the largest inscribed rectangle has
+ // dimensions sqrt(2) * A and sqrt(2) * B. To accommodate the antialiased
+ // edge, inset the inscribed rect by a pixel on each side.
+ const float kInsetScale = 0.2929f; // 1 - sqrt(2) / 2
+
+ // Calculate the bounds for each patch. Eight patches will be used to exclude
+ // the inscribed rect:
+ // +---+-----+-----+---+
+ // | | 4 | 5 | |
+ // | 0 +-----+-----+ 1 |
+ // | | | |
+ // +---+ C +---+ C = center point
+ // | | | |
+ // | 2 +-----+-----+ 3 |
+ // | | 6 | 7 | |
+ // +---+-----+-----+---+
+ math::PointF center = GetRRectCenter(rect, corners);
+ center = ClampToBounds(bounds, center.x(), center.y());
+ math::PointF inset0 = ClampToBounds(bounds,
+ rect.x() + kInsetScale * corners.top_left.horizontal + 1.0f,
+ rect.y() + kInsetScale * corners.top_left.vertical + 1.0f);
+ math::PointF inset1 = ClampToBounds(bounds,
+ rect.right() - kInsetScale * corners.top_right.horizontal - 1.0f,
+ rect.y() + kInsetScale * corners.top_right.vertical + 1.0f);
+ math::PointF inset2 = ClampToBounds(bounds,
+ rect.x() + kInsetScale * corners.bottom_left.horizontal + 1.0f,
+ rect.bottom() - kInsetScale * corners.bottom_left.vertical - 1.0f);
+ math::PointF inset3 = ClampToBounds(bounds,
+ rect.right() - kInsetScale * corners.bottom_right.horizontal - 1.0f,
+ rect.bottom() - kInsetScale * corners.bottom_right.vertical - 1.0f);
+
+ out_attributes[0].bounds.SetRect(bounds.x(), bounds.y(),
+ inset0.x() - bounds.x(), center.y() - bounds.y());
+ out_attributes[1].bounds.SetRect(inset1.x(), bounds.y(),
+ bounds.right() - inset1.x(), center.y() - bounds.y());
+ out_attributes[2].bounds.SetRect(bounds.x(), center.y(),
+ inset2.x() - bounds.x(), bounds.bottom() - center.y());
+ out_attributes[3].bounds.SetRect(inset3.x(), center.y(),
+ bounds.right() - inset3.x(), bounds.bottom() - center.y());
+ out_attributes[4].bounds.SetRect(inset0.x(), bounds.y(),
+ center.x() - inset0.x(), inset0.y() - bounds.y());
+ out_attributes[5].bounds.SetRect(center.x(), bounds.y(),
+ inset1.x() - center.x(), inset1.y() - bounds.y());
+ out_attributes[6].bounds.SetRect(inset2.x(), inset2.y(),
+ center.x() - inset2.x(), bounds.bottom() - inset2.y());
+ out_attributes[7].bounds.SetRect(center.x(), inset3.y(),
+ inset3.x() - center.x(), bounds.bottom() - inset3.y());
+}
+
+// static
+void DrawObject::GetRCornerValues(math::RectF* rect,
+ render_tree::RoundedCorners* corners, RRectAttributes out_rcorners[4]) {
// Ensure corner sizes are non-zero to allow generic handling of square and
- // rounded corners.
- const float kMinCornerSize = 0.01f;
- inset_rect.Outset(kMinCornerSize, kMinCornerSize);
- inset_corners = inset_corners.Inset(-kMinCornerSize, -kMinCornerSize,
- -kMinCornerSize, -kMinCornerSize);
- inset_corners = inset_corners.Normalize(inset_rect);
+ // rounded corners. Corner radii must be at least 1 pixel for antialiasing
+ // to work well.
+ const float kMinCornerSize = 1.0f;
- // The rect data is a vec4 representing (min.xy, max.xy).
- float rect_data[4] = {
- inset_rect.x(), inset_rect.y(), inset_rect.right(), inset_rect.bottom(),
- };
- GL_CALL(glUniform4fv(rect_uniform, 1, rect_data));
+ // First inset to make room for the minimum corner size. Then outset to
+ // enforce the minimum corner size. Be careful not to inset more than the
+ // rect size, otherwise the outset rect will be off-centered.
+ rect->Inset(std::min(rect->width() * 0.5f, kMinCornerSize),
+ std::min(rect->height() * 0.5f, kMinCornerSize));
+ *corners = corners->Inset(kMinCornerSize, kMinCornerSize, kMinCornerSize,
+ kMinCornerSize);
+ *corners = corners->Normalize(*rect);
+ rect->Outset(kMinCornerSize, kMinCornerSize);
+ *corners = corners->Inset(-kMinCornerSize, -kMinCornerSize, -kMinCornerSize,
+ -kMinCornerSize);
- // The corners data is a mat4 with each vector representing a corner
- // (ordered top left, top right, bottom left, bottom right). Each corner
- // vec4 represents (start.xy, radius.xy).
- float corners_data[16] = {
- inset_rect.x() + inset_corners.top_left.horizontal,
- inset_rect.y() + inset_corners.top_left.vertical,
- inset_corners.top_left.horizontal,
- inset_corners.top_left.vertical,
- inset_rect.right() - inset_corners.top_right.horizontal,
- inset_rect.y() + inset_corners.top_right.vertical,
- inset_corners.top_right.horizontal,
- inset_corners.top_right.vertical,
- inset_rect.x() + inset_corners.bottom_left.horizontal,
- inset_rect.bottom() - inset_corners.bottom_left.vertical,
- inset_corners.bottom_left.horizontal,
- inset_corners.bottom_left.vertical,
- inset_rect.right() - inset_corners.bottom_right.horizontal,
- inset_rect.bottom() - inset_corners.bottom_right.vertical,
- inset_corners.bottom_right.horizontal,
- inset_corners.bottom_right.vertical,
- };
- GL_CALL(glUniformMatrix4fv(corners_uniform, 1, false, corners_data));
+ // |rcorner| describes (start.xy, 1 / radius.xy) for the relevant corner.
+ // The sign of the radius component is used to facilitate the calculation:
+ // vec2 scaled_offset = (position - corner.xy) * corner.zw
+ // Such that |scaled_offset| is in the first quadrant when the pixel is
+ // in the given rounded corner.
+ COMPILE_ASSERT(sizeof(RCorner) == sizeof(float) * 4, struct_should_be_vec4);
+ out_rcorners[0].rcorner.x = rect->x() + corners->top_left.horizontal;
+ out_rcorners[0].rcorner.y = rect->y() + corners->top_left.vertical;
+ out_rcorners[0].rcorner.rx = -1.0f / corners->top_left.horizontal;
+ out_rcorners[0].rcorner.ry = -1.0f / corners->top_left.vertical;
+
+ out_rcorners[1].rcorner.x = rect->right() - corners->top_right.horizontal;
+ out_rcorners[1].rcorner.y = rect->y() + corners->top_right.vertical;
+ out_rcorners[1].rcorner.rx = 1.0f / corners->top_right.horizontal;
+ out_rcorners[1].rcorner.ry = -1.0f / corners->top_right.vertical;
+
+ out_rcorners[2].rcorner.x = rect->x() + corners->bottom_left.horizontal;
+ out_rcorners[2].rcorner.y = rect->bottom() - corners->bottom_left.vertical;
+ out_rcorners[2].rcorner.rx = -1.0f / corners->bottom_left.horizontal;
+ out_rcorners[2].rcorner.ry = 1.0f / corners->bottom_left.vertical;
+
+ out_rcorners[3].rcorner.x = rect->right() - corners->bottom_right.horizontal;
+ out_rcorners[3].rcorner.y = rect->bottom() - corners->bottom_right.vertical;
+ out_rcorners[3].rcorner.rx = 1.0f / corners->bottom_right.horizontal;
+ out_rcorners[3].rcorner.ry = 1.0f / corners->bottom_right.vertical;
}
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_object.h b/src/cobalt/renderer/rasterizer/egl/draw_object.h
index 1e2fb12..19af454 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_object.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_object.h
@@ -90,9 +90,32 @@
virtual base::TypeId GetTypeId() const = 0;
protected:
+ // Structures describing vertex data for rendering rounded rectangles.
+ struct RCorner {
+ // Constructor to transform a RCorner value into a format which the
+ // shader function IsOutsideRCorner() expects. This expresses the current
+ // vertex position as a scaled offset relevant for the corner, and provides
+ // scalars to assist in calculating the antialiased edge. For more details,
+ // see function_is_outside_rcorner.inc.
+ RCorner(const float (&position)[2], const RCorner& init);
+ RCorner() {}
+ float x, y;
+ float rx, ry;
+ };
+ struct RRectAttributes {
+ math::RectF bounds; // The region in which to use the rcorner data.
+ RCorner rcorner;
+ };
+
DrawObject() {}
explicit DrawObject(const BaseState& base_state);
+ // Extract the scale vector from this object's transform.
+ math::Vector2dF GetScale() const;
+
+ // Remove scale from the transform, and return the scale vector.
+ math::Vector2dF RemoveScaleFromTransform();
+
// Utility function to get the render color for the blend modes that will
// be used. These modes expect alpha to be pre-multiplied.
static render_tree::ColorRGBA GetDrawColor(
@@ -108,14 +131,28 @@
return GetGLRGBA(color.r(), color.g(), color.b(), color.a());
}
- // Set shader uniforms for a rounded rect. Specify a non-zero inset if
- // the rect will be used with anti-aliasing (e.g. 0.5 inset for a 1-pixel
- // anti-aliasing border).
- static void SetRRectUniforms(GLint rect_uniform, GLint corners_uniform,
- const math::RectF& rect, const render_tree::RoundedCorners& corners,
- float inset);
+ // Get the vertex attributes to use to draw the given rounded rect. Each
+ // corner uses a different attribute. These RCorner values must be transformed
+ // before being passed to the shader. (See RCorner constructor.)
+ static void GetRRectAttributes(const math::RectF& bounds,
+ math::RectF rect, render_tree::RoundedCorners corners,
+ RRectAttributes (&out_attributes)[4]);
+
+ // Get the vertex attributes to draw the given rounded rect excluding the
+ // inscribed rect. These RCorner values must be transformed before being
+ // passed to the shader. (See RCorner constructor.)
+ static void GetRRectAttributes(const math::RectF& bounds,
+ math::RectF rect, render_tree::RoundedCorners corners,
+ RRectAttributes (&out_attributes)[8]);
BaseState base_state_;
+
+ private:
+ // Return the RCorner values for the given rounded rect, and the normalized
+ // rect and corner values used.
+ static void GetRCornerValues(math::RectF* rect,
+ render_tree::RoundedCorners* corners,
+ RRectAttributes out_rcorners[4]);
};
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_border.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_border.cc
index cdc1d88..829ef32 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_border.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_border.cc
@@ -121,26 +121,18 @@
const math::RectF& border_rect, const math::RectF& content_rect,
const render_tree::ColorRGBA& border_color,
const render_tree::ColorRGBA& content_color) {
- // Extract scale from the transform matrix. This can be reliably done if no
- // rotations are involved.
- const float kEpsilon = 0.0001f;
- if (std::abs(base_state_.transform(0, 1)) >= kEpsilon ||
- std::abs(base_state_.transform(1, 0)) >= kEpsilon) {
- return false;
- }
-
- // If the scale is 0 in either direction, then there's nothing to render.
- float scale_x = std::abs(base_state_.transform(0, 0));
- float scale_y = std::abs(base_state_.transform(1, 1));
- if (scale_x <= kEpsilon || scale_y <= kEpsilon) {
+ // If the scaled border rect is too small, then don't bother rendering.
+ math::Vector2dF scale = GetScale();
+ if (border_rect.width() * scale.x() < 1.0f ||
+ border_rect.height() * scale.y() < 1.0f) {
return true;
}
// Antialiased subpixel borders are not supported at this time. It can be
// done by attenuating the alpha, but this can get complicated if the borders
// are of different widths.
- float pixel_size_x = 1.0f / scale_x;
- float pixel_size_y = 1.0f / scale_y;
+ float pixel_size_x = 1.0f / scale.x();
+ float pixel_size_y = 1.0f / scale.y();
if (border.left.width < pixel_size_x || border.right.width < pixel_size_x ||
border.top.width < pixel_size_y || border.bottom.width < pixel_size_y) {
return false;
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
index 13401c9..fcabd2d 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.cc
@@ -80,128 +80,340 @@
}
} // namespace
+DrawRectShadowBlur::VertexAttributesSquare::VertexAttributesSquare(
+ float x, float y, float offset_scale) {
+ position[0] = x;
+ position[1] = y;
+ offset[0] = x * offset_scale;
+ offset[1] = y * offset_scale;
+}
+
+DrawRectShadowBlur::VertexAttributesRound::VertexAttributesRound(
+ float x, float y, const RCorner& init) {
+ position[0] = x;
+ position[1] = y;
+ rcorner_scissor = RCorner(position, init);
+}
+
DrawRectShadowBlur::DrawRectShadowBlur(GraphicsState* graphics_state,
const BaseState& base_state, const math::RectF& base_rect,
const OptionalRoundedCorners& base_corners, const math::RectF& spread_rect,
const OptionalRoundedCorners& spread_corners,
const render_tree::ColorRGBA& color, float blur_sigma, bool inset)
- : DrawRectShadowSpread(graphics_state, base_state),
+ : DrawObject(base_state),
spread_rect_(spread_rect),
spread_corners_(spread_corners),
blur_sigma_(blur_sigma),
- is_inset_(inset) {
- const float kBlurExtentInPixels = kBlurExtentInSigmas * blur_sigma;
+ is_inset_(inset),
+ vertex_buffer_(nullptr),
+ index_buffer_(nullptr) {
+ color_ = GetDrawColor(color) * base_state_.opacity;
- if (inset) {
- outer_rect_ = base_rect;
- outer_corners_ = base_corners;
- inner_rect_ = spread_rect;
- inner_rect_.Inset(kBlurExtentInPixels, kBlurExtentInPixels);
- if (inner_rect_.IsEmpty()) {
- inner_rect_.set_origin(spread_rect.CenterPoint());
- }
- if (spread_corners) {
- inner_corners_ = spread_corners->Inset(kBlurExtentInPixels,
- kBlurExtentInPixels, kBlurExtentInPixels, kBlurExtentInPixels);
- }
- } else {
- inner_rect_ = base_rect;
- inner_corners_ = base_corners;
- outer_rect_ = spread_rect;
- outer_rect_.Outset(kBlurExtentInPixels, kBlurExtentInPixels);
- if (spread_corners) {
- outer_corners_ = spread_corners->Inset(-kBlurExtentInPixels,
- -kBlurExtentInPixels, -kBlurExtentInPixels, -kBlurExtentInPixels);
- }
+ // Extract scale from the transform and move it into the vertex attributes
+ // so that the anti-aliased edges remain 1 pixel wide.
+ math::Vector2dF scale = RemoveScaleFromTransform();
+ math::RectF scaled_base_rect(base_rect);
+ scaled_base_rect.Scale(scale.x(), scale.y());
+ OptionalRoundedCorners scaled_base_corners(base_corners);
+ if (scaled_base_corners) {
+ scaled_base_corners = scaled_base_corners->Scale(scale.x(), scale.y());
+ scaled_base_corners = scaled_base_corners->Normalize(scaled_base_rect);
+ }
+ spread_rect_.Scale(scale.x(), scale.y());
+ if (spread_corners_) {
+ spread_corners_ = spread_corners_->Scale(scale.x(), scale.y());
+ spread_corners_ = spread_corners_->Normalize(spread_rect_);
}
- if (base_corners || spread_corners) {
- // If rounded rects are specified, then both the base and spread rects
- // must have rounded corners.
- DCHECK(inner_corners_);
- DCHECK(outer_corners_);
- DCHECK(spread_corners_);
- } else {
- // Non-rounded rects specify vertex offset in terms of sigma from the
- // center of the spread rect.
- offset_scale_ = kBlurDistance / kBlurExtentInPixels;
- offset_center_ = spread_rect_.CenterPoint();
- }
+ // The blur algorithms used by the shaders do not produce good results with
+ // separate x and y blur sigmas. Select a single blur sigma to approximate
+ // the desired blur.
+ blur_sigma_ *= std::sqrt(scale.x() * scale.y());
- color_ = GetGLRGBA(GetDrawColor(color) * base_state_.opacity);
+ SetGeometry(graphics_state, scaled_base_rect, scaled_base_corners);
+}
+
+void DrawRectShadowBlur::ExecuteUpdateVertexBuffer(
+ GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) {
+ if (attributes_square_.size() > 0) {
+ vertex_buffer_ = graphics_state->AllocateVertexData(
+ attributes_square_.size() * sizeof(attributes_square_[0]));
+ SbMemoryCopy(vertex_buffer_, &attributes_square_[0],
+ attributes_square_.size() * sizeof(attributes_square_[0]));
+ } else if (attributes_round_.size() > 0) {
+ vertex_buffer_ = graphics_state->AllocateVertexData(
+ attributes_round_.size() * sizeof(attributes_round_[0]));
+ SbMemoryCopy(vertex_buffer_, &attributes_round_[0],
+ attributes_round_.size() * sizeof(attributes_round_[0]));
+ index_buffer_ = graphics_state->AllocateVertexIndices(indices_.size());
+ SbMemoryCopy(index_buffer_, &indices_[0],
+ indices_.size() * sizeof(indices_[0]));
+ }
}
void DrawRectShadowBlur::ExecuteRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
+ if (vertex_buffer_ == nullptr) {
+ return;
+ }
+
// Draw the blurred shadow.
- if (inner_corners_) {
- ShaderProgram<CommonVertexShader,
+ if (spread_corners_) {
+ ShaderProgram<ShaderVertexOffsetRcorner,
ShaderFragmentColorBlurRrects>* program;
program_manager->GetProgram(&program);
graphics_state->UseProgram(program->GetHandle());
- SetupShader(program->GetVertexShader(), graphics_state);
-
+ SetupVertexShader(graphics_state, program->GetVertexShader());
+ SetFragmentUniforms(program->GetFragmentShader().u_color(),
+ program->GetFragmentShader().u_scale_add());
float sigma_scale = kBlurDistance / (kBlurExtentInSigmas * blur_sigma_);
GL_CALL(glUniform2f(program->GetFragmentShader().u_sigma_scale(),
sigma_scale, sigma_scale));
-
// Pre-calculate the scale values to calculate the normalized gaussian.
GL_CALL(glUniform2f(program->GetFragmentShader().u_gaussian_scale(),
-1.0f / (2.0f * blur_sigma_ * blur_sigma_),
1.0f / (kSqrt2 * kSqrtPi * blur_sigma_)));
- if (is_inset_) {
- // Set the outer rect to be an inclusive scissor, and invert the shadow.
- SetRRectUniforms(program->GetFragmentShader().u_scissor_rect(),
- program->GetFragmentShader().u_scissor_corners(),
- outer_rect_, *outer_corners_, 0.5f);
- GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
- -1.0f, 1.0f));
- } else {
- // Set the inner rect to be an exclusive scissor.
- SetRRectUniforms(program->GetFragmentShader().u_scissor_rect(),
- program->GetFragmentShader().u_scissor_corners(),
- inner_rect_, *inner_corners_, 0.5f);
- GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
- 1.0f, 0.0f));
- }
SetBlurRRectUniforms(program->GetFragmentShader(),
spread_rect_, *spread_corners_, blur_sigma_);
+ GL_CALL(glDrawElements(GL_TRIANGLES, indices_.size(), GL_UNSIGNED_SHORT,
+ graphics_state->GetVertexIndexPointer(index_buffer_)));
} else {
- ShaderProgram<CommonVertexShader,
+ ShaderProgram<ShaderVertexOffset,
ShaderFragmentColorBlur>* program;
program_manager->GetProgram(&program);
graphics_state->UseProgram(program->GetHandle());
- SetupShader(program->GetVertexShader(), graphics_state);
- if (is_inset_) {
- // Invert the shadow.
- GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
- -1.0f, 1.0f));
- } else {
- // Keep the normal (outset) shadow.
- GL_CALL(glUniform2f(program->GetFragmentShader().u_scale_add(),
- 1.0f, 0.0f));
- }
+ SetupVertexShader(graphics_state, program->GetVertexShader());
+ SetFragmentUniforms(program->GetFragmentShader().u_color(),
+ program->GetFragmentShader().u_scale_add());
GL_CALL(glUniform4f(program->GetFragmentShader().u_blur_rect(),
- (spread_rect_.x() - offset_center_.x()) * offset_scale_,
- (spread_rect_.y() - offset_center_.y()) * offset_scale_,
- (spread_rect_.right() - offset_center_.x()) * offset_scale_,
- (spread_rect_.bottom() - offset_center_.y()) * offset_scale_));
+ spread_rect_.x(), spread_rect_.y(),
+ spread_rect_.right(), spread_rect_.bottom()));
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_square_.size()));
}
-
- GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count_));
}
base::TypeId DrawRectShadowBlur::GetTypeId() const {
- if (inner_corners_) {
- return ShaderProgram<CommonVertexShader,
+ if (spread_corners_) {
+ return ShaderProgram<ShaderVertexOffsetRcorner,
ShaderFragmentColorBlurRrects>::GetTypeId();
} else {
- return ShaderProgram<CommonVertexShader,
+ return ShaderProgram<ShaderVertexOffset,
ShaderFragmentColorBlur>::GetTypeId();
}
}
+void DrawRectShadowBlur::SetupVertexShader(GraphicsState* graphics_state,
+ const ShaderVertexOffset& shader) {
+ graphics_state->UpdateClipAdjustment(shader.u_clip_adjustment());
+ graphics_state->UpdateTransformMatrix(shader.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(
+ shader.a_position(), 2, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributesSquare), vertex_buffer_ +
+ offsetof(VertexAttributesSquare, position));
+ graphics_state->VertexAttribPointer(
+ shader.a_offset(), 2, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributesSquare), vertex_buffer_ +
+ offsetof(VertexAttributesSquare, offset));
+ graphics_state->VertexAttribFinish();
+}
+
+void DrawRectShadowBlur::SetupVertexShader(GraphicsState* graphics_state,
+ const ShaderVertexOffsetRcorner& shader) {
+ graphics_state->UpdateClipAdjustment(shader.u_clip_adjustment());
+ graphics_state->UpdateTransformMatrix(shader.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(
+ shader.a_position(), 2, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributesRound), vertex_buffer_ +
+ offsetof(VertexAttributesRound, position));
+ graphics_state->VertexAttribPointer(
+ shader.a_rcorner(), 4, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributesRound), vertex_buffer_ +
+ offsetof(VertexAttributesRound, rcorner_scissor));
+ graphics_state->VertexAttribFinish();
+}
+
+void DrawRectShadowBlur::SetFragmentUniforms(
+ GLint color_uniform, GLint scale_add_uniform) {
+ GL_CALL(glUniform4f(color_uniform,
+ color_.r(), color_.g(), color_.b(), color_.a()));
+ if (is_inset_) {
+ // Invert the shadow.
+ GL_CALL(glUniform2f(scale_add_uniform, -1.0f, 1.0f));
+ } else {
+ // Keep the normal (outset) shadow.
+ GL_CALL(glUniform2f(scale_add_uniform, 1.0f, 0.0f));
+ }
+}
+
+void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
+ const math::RectF& base_rect, const OptionalRoundedCorners& base_corners) {
+ const float kBlurExtentInPixels = kBlurExtentInSigmas * blur_sigma_;
+
+ if (base_corners || spread_corners_) {
+ // If rounded rects are specified, then both the base and spread rects
+ // must have rounded corners.
+ DCHECK(base_corners);
+ DCHECK(spread_corners_);
+
+ if (is_inset_) {
+ // Extend the outer rect to include the antialiased edge.
+ math::RectF outer_rect(base_rect);
+ outer_rect.Outset(1.0f, 1.0f);
+ RRectAttributes rrect_outer[4];
+ GetRRectAttributes(outer_rect, base_rect, *base_corners, rrect_outer);
+ // Inset the spread rect by the blur extent. Use that as the inner bounds.
+ RRectAttributes rrect_inner[8];
+ math::RectF inner_rect(spread_rect_);
+ inner_rect.Inset(kBlurExtentInPixels, kBlurExtentInPixels);
+ if (!inner_rect.IsEmpty()) {
+ // Get the inner bounds excluding the inscribed rect.
+ render_tree::RoundedCorners inner_corners = spread_corners_->Inset(
+ kBlurExtentInPixels, kBlurExtentInPixels, kBlurExtentInPixels,
+ kBlurExtentInPixels);
+ inner_corners = inner_corners.Normalize(inner_rect);
+ GetRRectAttributes(outer_rect, inner_rect, inner_corners, rrect_inner);
+ } else {
+ // The blur covers everything inside the outer rect.
+ rrect_inner[0].bounds = outer_rect;
+ }
+ SetGeometry(graphics_state, rrect_outer, rrect_inner);
+ } else {
+ // Extend the outer rect to include the blur.
+ math::RectF outer_rect(spread_rect_);
+ outer_rect.Outset(kBlurExtentInPixels, kBlurExtentInPixels);
+ // Exclude the inscribed rect of the base rounded rect.
+ RRectAttributes rrect[8];
+ GetRRectAttributes(outer_rect, base_rect, *base_corners, rrect);
+ SetGeometry(graphics_state, rrect);
+ }
+ } else {
+ // Handle box shadow with square corners.
+ if (is_inset_) {
+ math::RectF inner_rect(spread_rect_);
+ inner_rect.Inset(kBlurExtentInPixels, kBlurExtentInPixels);
+ SetGeometry(graphics_state, inner_rect, base_rect);
+ } else {
+ math::RectF outer_rect(spread_rect_);
+ outer_rect.Outset(kBlurExtentInPixels, kBlurExtentInPixels);
+ SetGeometry(graphics_state, base_rect, outer_rect);
+ }
+ }
+}
+
+void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
+ const math::RectF& inner_rect, const math::RectF& outer_rect) {
+ // Express offset in terms of blur sigma for the shader.
+ float offset_scale = kBlurDistance / (kBlurExtentInSigmas * blur_sigma_);
+
+ // The spread rect should also be expressed in terms of sigma.
+ spread_rect_.Scale(offset_scale, offset_scale);
+
+ // The box shadow is a triangle strip covering the area between outer rect
+ // and inner rect.
+ if (inner_rect.IsEmpty()) {
+ attributes_square_.reserve(4);
+ attributes_square_.emplace_back(
+ outer_rect.x(), outer_rect.y(), offset_scale);
+ attributes_square_.emplace_back(
+ outer_rect.right(), outer_rect.y(), offset_scale);
+ attributes_square_.emplace_back(
+ outer_rect.x(), outer_rect.bottom(), offset_scale);
+ attributes_square_.emplace_back(
+ outer_rect.right(), outer_rect.bottom(), offset_scale);
+ } else {
+ math::RectF inside_rect(inner_rect);
+ inside_rect.Intersect(outer_rect);
+ attributes_square_.reserve(10);
+ attributes_square_.emplace_back(
+ outer_rect.x(), outer_rect.y(), offset_scale);
+ attributes_square_.emplace_back(
+ inside_rect.x(), inside_rect.y(), offset_scale);
+ attributes_square_.emplace_back(
+ outer_rect.right(), outer_rect.y(), offset_scale);
+ attributes_square_.emplace_back(
+ inside_rect.right(), inside_rect.y(), offset_scale);
+ attributes_square_.emplace_back(
+ outer_rect.right(), outer_rect.bottom(), offset_scale);
+ attributes_square_.emplace_back(
+ inside_rect.right(), inside_rect.bottom(), offset_scale);
+ attributes_square_.emplace_back(
+ outer_rect.x(), outer_rect.bottom(), offset_scale);
+ attributes_square_.emplace_back(
+ inside_rect.x(), inside_rect.bottom(), offset_scale);
+ attributes_square_.emplace_back(
+ outer_rect.x(), outer_rect.y(), offset_scale);
+ attributes_square_.emplace_back(
+ inside_rect.x(), inside_rect.y(), offset_scale);
+ }
+
+ graphics_state->ReserveVertexData(
+ attributes_square_.size() * sizeof(attributes_square_[0]));
+}
+
+void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
+ const RRectAttributes (&rrect)[8]) {
+ // The shadowed area is already split into quads.
+ for (int i = 0; i < arraysize(rrect); ++i) {
+ uint16_t vert = static_cast<uint16_t>(attributes_round_.size());
+ const math::RectF& bounds = rrect[i].bounds;
+ const RCorner& rcorner = rrect[i].rcorner;
+ attributes_round_.emplace_back(bounds.x(), bounds.y(), rcorner);
+ attributes_round_.emplace_back(bounds.right(), bounds.y(), rcorner);
+ attributes_round_.emplace_back(bounds.x(), bounds.bottom(), rcorner);
+ attributes_round_.emplace_back(bounds.right(), bounds.bottom(), rcorner);
+ indices_.emplace_back(vert);
+ indices_.emplace_back(vert + 1);
+ indices_.emplace_back(vert + 2);
+ indices_.emplace_back(vert + 1);
+ indices_.emplace_back(vert + 2);
+ indices_.emplace_back(vert + 3);
+ }
+
+ graphics_state->ReserveVertexData(
+ attributes_round_.size() * sizeof(attributes_round_[0]));
+ graphics_state->ReserveVertexIndices(indices_.size());
+}
+
+void DrawRectShadowBlur::SetGeometry(GraphicsState* graphics_state,
+ const RRectAttributes (&rrect_outer)[4],
+ const RRectAttributes (&rrect_inner)[8]) {
+ // Draw the area between the inner rect and outer rect using the outer rect's
+ // rounded corners. The inner quads already exclude the inscribed rectangle.
+ for (int i = 0; i < arraysize(rrect_inner); ++i) {
+ for (int o = 0; o < arraysize(rrect_outer); ++o) {
+ math::RectF rect = math::IntersectRects(
+ rrect_inner[i].bounds, rrect_outer[o].bounds);
+ if (!rect.IsEmpty()) {
+ // Use two triangles to draw the intersection.
+ const RCorner& rcorner = rrect_outer[o].rcorner;
+ uint16_t vert = static_cast<uint16_t>(attributes_round_.size());
+ attributes_round_.emplace_back(rect.x(), rect.y(), rcorner);
+ attributes_round_.emplace_back(rect.right(), rect.y(), rcorner);
+ attributes_round_.emplace_back(rect.x(), rect.bottom(), rcorner);
+ attributes_round_.emplace_back(rect.right(), rect.bottom(), rcorner);
+ indices_.emplace_back(vert);
+ indices_.emplace_back(vert + 1);
+ indices_.emplace_back(vert + 2);
+ indices_.emplace_back(vert + 1);
+ indices_.emplace_back(vert + 2);
+ indices_.emplace_back(vert + 3);
+ }
+ }
+ }
+
+ graphics_state->ReserveVertexData(
+ attributes_round_.size() * sizeof(attributes_round_[0]));
+ graphics_state->ReserveVertexIndices(indices_.size());
+}
+
} // namespace egl
} // namespace rasterizer
} // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
index 8d36722..107af85 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_blur.h
@@ -15,7 +15,12 @@
#ifndef COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_BLUR_H_
#define COBALT_RENDERER_RASTERIZER_EGL_DRAW_RECT_SHADOW_BLUR_H_
-#include "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_object.h"
+#include "egl/generated_shader_impl.h"
namespace cobalt {
namespace renderer {
@@ -40,12 +45,7 @@
// Handles drawing a box shadow with blur. This uses a gaussian kernel to fade
// the "blur" region.
-//
-// 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 DrawRectShadowSpread {
+class DrawRectShadowBlur : public DrawObject {
public:
// Draw a blurred box shadow.
// The box shadow exists in the area between |base_rect| and |spread_rect|
@@ -59,16 +59,55 @@
const render_tree::ColorRGBA& color,
float blur_sigma, bool inset);
+ void ExecuteUpdateVertexBuffer(GraphicsState* graphics_state,
+ ShaderProgramManager* program_manager) OVERRIDE;
void ExecuteRasterize(GraphicsState* graphics_state,
ShaderProgramManager* program_manager) OVERRIDE;
base::TypeId GetTypeId() const OVERRIDE;
private:
+ struct VertexAttributesSquare {
+ VertexAttributesSquare(float x, float y, float offset_scale);
+ float position[2];
+ float offset[2];
+ };
+
+ struct VertexAttributesRound {
+ VertexAttributesRound(float x, float y, const RCorner& init);
+ float position[2];
+ RCorner rcorner_scissor;
+ };
+
+ void SetupVertexShader(GraphicsState* graphics_state,
+ const ShaderVertexOffset& shader);
+ void SetupVertexShader(GraphicsState* graphics_state,
+ const ShaderVertexOffsetRcorner& shader);
+ void SetFragmentUniforms(GLint color_uniform, GLint scale_add_uniform);
+
+ void SetGeometry(GraphicsState* graphics_state,
+ const math::RectF& base_rect,
+ const OptionalRoundedCorners& base_corners);
+ void SetGeometry(GraphicsState* graphics_state,
+ const math::RectF& inner_rect,
+ const math::RectF& outer_rect);
+ void SetGeometry(GraphicsState* graphics_state,
+ const RRectAttributes (&rrect)[8]);
+ void SetGeometry(GraphicsState* graphics_state,
+ const RRectAttributes (&rrect_outer)[4],
+ const RRectAttributes (&rrect_inner)[8]);
+
math::RectF spread_rect_;
OptionalRoundedCorners spread_corners_;
-
+ render_tree::ColorRGBA color_;
float blur_sigma_;
bool is_inset_;
+
+ std::vector<VertexAttributesSquare> attributes_square_;
+ std::vector<VertexAttributesRound> attributes_round_;
+ std::vector<uint16_t> indices_;
+
+ uint8_t* vertex_buffer_;
+ uint16_t* index_buffer_;
};
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
index 9dba0d3..faa7286 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.cc
@@ -26,8 +26,19 @@
namespace rasterizer {
namespace egl {
-namespace {
-const int kVertexCount = 10;
+DrawRectShadowSpread::VertexAttributesSquare::VertexAttributesSquare(
+ float x, float y, uint32_t in_color) {
+ position[0] = x;
+ position[1] = y;
+ color = in_color;
+}
+
+DrawRectShadowSpread::VertexAttributesRound::VertexAttributesRound(
+ float x, float y, const RCorner& inner, const RCorner& outer) {
+ position[0] = x;
+ position[1] = y;
+ rcorner_inner = RCorner(position, inner);
+ rcorner_outer = RCorner(position, outer);
}
DrawRectShadowSpread::DrawRectShadowSpread(GraphicsState* graphics_state,
@@ -36,121 +47,94 @@
const OptionalRoundedCorners& outer_corners,
const render_tree::ColorRGBA& color)
: DrawObject(base_state),
- inner_rect_(inner_rect),
- outer_rect_(outer_rect),
- inner_corners_(inner_corners),
- outer_corners_(outer_corners),
- offset_scale_(1.0f),
vertex_buffer_(nullptr),
- vertex_count_(0) {
- color_ = GetGLRGBA(GetDrawColor(color) * base_state_.opacity);
- if (inner_corners_ || outer_corners_) {
+ index_buffer_(nullptr) {
+ color_ = GetDrawColor(color) * base_state_.opacity;
+
+ // Extract scale from the transform and move it into the vertex attributes
+ // so that the anti-aliased edges remain 1 pixel wide.
+ math::Vector2dF scale = RemoveScaleFromTransform();
+ math::RectF inside_rect(inner_rect);
+ math::RectF outside_rect(outer_rect);
+ inside_rect.Scale(scale.x(), scale.y());
+ outside_rect.Scale(scale.x(), scale.y());
+
+ if (inner_corners || outer_corners) {
// If using rounded corners, then both inner and outer rects must have
// rounded corner definitions.
- DCHECK(inner_corners_);
- DCHECK(outer_corners_);
+ DCHECK(inner_corners);
+ DCHECK(outer_corners);
+ render_tree::RoundedCorners inside_corners =
+ inner_corners->Scale(scale.x(), scale.y());
+ render_tree::RoundedCorners outside_corners =
+ outer_corners->Scale(scale.x(), scale.y());
+ SetGeometry(graphics_state,
+ inside_rect, inside_corners,
+ outside_rect, outside_corners);
+ } else {
+ SetGeometry(graphics_state, inside_rect, outside_rect);
}
- graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
-}
-
-DrawRectShadowSpread::DrawRectShadowSpread(GraphicsState* graphics_state,
- const BaseState& base_state)
- : DrawObject(base_state),
- offset_scale_(1.0f),
- vertex_buffer_(nullptr),
- vertex_count_(0) {
- graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
}
void DrawRectShadowSpread::ExecuteUpdateVertexBuffer(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- // Draw the box shadow's spread. This is a triangle strip covering the area
- // between outer rect and inner rect.
- math::RectF inside_rect(inner_rect_);
- math::RectF outside_rect(outer_rect_);
- VertexAttributes attributes[kVertexCount];
-
- if (inner_corners_) {
- // Inset the inside rect to include the rounded corners.
- inside_rect.Inset(
- std::max(inner_corners_->bottom_left.horizontal,
- inner_corners_->top_left.horizontal),
- std::max(inner_corners_->top_left.vertical,
- inner_corners_->top_right.vertical),
- std::max(inner_corners_->top_right.horizontal,
- inner_corners_->bottom_right.horizontal),
- std::max(inner_corners_->bottom_right.vertical,
- inner_corners_->bottom_left.vertical));
-
- // Add a 1 pixel border to the outer rect for anti-aliasing.
- outside_rect.Outset(1.0f, 1.0f);
+ if (attributes_square_.size() > 0) {
+ vertex_buffer_ = graphics_state->AllocateVertexData(
+ attributes_square_.size() * sizeof(attributes_square_[0]));
+ SbMemoryCopy(vertex_buffer_, &attributes_square_[0],
+ attributes_square_.size() * sizeof(attributes_square_[0]));
+ } else if (attributes_round_.size() > 0) {
+ vertex_buffer_ = graphics_state->AllocateVertexData(
+ attributes_round_.size() * sizeof(attributes_round_[0]));
+ SbMemoryCopy(vertex_buffer_, &attributes_round_[0],
+ attributes_round_.size() * sizeof(attributes_round_[0]));
+ index_buffer_ = graphics_state->AllocateVertexIndices(indices_.size());
+ SbMemoryCopy(index_buffer_, &indices_[0],
+ indices_.size() * sizeof(indices_[0]));
}
-
- // Only pixels inside the outer rect should be touched.
- if (inside_rect.IsEmpty()) {
- vertex_count_ = 4;
- SetVertex(&attributes[0], outside_rect.x(), outside_rect.y());
- SetVertex(&attributes[1], outside_rect.right(), outside_rect.y());
- SetVertex(&attributes[2], outside_rect.x(), outside_rect.bottom());
- SetVertex(&attributes[3], outside_rect.right(), outside_rect.bottom());
- } else {
- inside_rect.Intersect(outside_rect);
- vertex_count_ = 10;
- SetVertex(&attributes[0], outside_rect.x(), outside_rect.y());
- SetVertex(&attributes[1], inside_rect.x(), inside_rect.y());
- SetVertex(&attributes[2], outside_rect.right(), outside_rect.y());
- SetVertex(&attributes[3], inside_rect.right(), inside_rect.y());
- SetVertex(&attributes[4], outside_rect.right(), outside_rect.bottom());
- SetVertex(&attributes[5], inside_rect.right(), inside_rect.bottom());
- SetVertex(&attributes[6], outside_rect.x(), outside_rect.bottom());
- SetVertex(&attributes[7], inside_rect.x(), inside_rect.bottom());
- SetVertex(&attributes[8], outside_rect.x(), outside_rect.y());
- SetVertex(&attributes[9], inside_rect.x(), inside_rect.y());
- }
-
- vertex_buffer_ = graphics_state->AllocateVertexData(
- vertex_count_ * sizeof(VertexAttributes));
- SbMemoryCopy(vertex_buffer_, attributes,
- vertex_count_ * sizeof(VertexAttributes));
}
void DrawRectShadowSpread::ExecuteRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- if (inner_corners_) {
- ShaderProgram<CommonVertexShader,
- ShaderFragmentColorBetweenRrects>* program;
- program_manager->GetProgram(&program);
- graphics_state->UseProgram(program->GetHandle());
- SetupShader(program->GetVertexShader(), graphics_state);
-
- SetRRectUniforms(program->GetFragmentShader().u_inner_rect(),
- program->GetFragmentShader().u_inner_corners(),
- inner_rect_, *inner_corners_, 0.5f);
- SetRRectUniforms(program->GetFragmentShader().u_outer_rect(),
- program->GetFragmentShader().u_outer_corners(),
- outer_rect_, *outer_corners_, 0.5f);
- } else {
- ShaderProgram<CommonVertexShader, ShaderFragmentColorInclude>* program;
- program_manager->GetProgram(&program);
- graphics_state->UseProgram(program->GetHandle());
- SetupShader(program->GetVertexShader(), graphics_state);
-
- float include[4] = {
- outer_rect_.x(),
- outer_rect_.y(),
- outer_rect_.right(),
- outer_rect_.bottom()
- };
- GL_CALL(glUniform4fv(program->GetFragmentShader().u_include(), 1, include));
+ if (vertex_buffer_ == nullptr) {
+ return;
}
- GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count_));
+ // Draw the box shadow.
+ if (attributes_square_.size() > 0) {
+ ShaderProgram<ShaderVertexColor,
+ ShaderFragmentColor>* program;
+ program_manager->GetProgram(&program);
+ graphics_state->UseProgram(program->GetHandle());
+ SetupVertexShader(graphics_state, program->GetVertexShader());
+ GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, attributes_square_.size()));
+ } else {
+ ShaderProgram<ShaderVertexRcorner2,
+ ShaderFragmentRcorner2Color>* program;
+ program_manager->GetProgram(&program);
+ graphics_state->UseProgram(program->GetHandle());
+ SetupVertexShader(graphics_state, program->GetVertexShader());
+ GL_CALL(glUniform4f(program->GetFragmentShader().u_color(),
+ color_.r(), color_.g(), color_.b(), color_.a()));
+ GL_CALL(glDrawElements(GL_TRIANGLES, indices_.size(), GL_UNSIGNED_SHORT,
+ graphics_state->GetVertexIndexPointer(index_buffer_)));
+ }
}
-void DrawRectShadowSpread::SetupShader(const CommonVertexShader& shader,
- GraphicsState* graphics_state) {
+base::TypeId DrawRectShadowSpread::GetTypeId() const {
+ if (attributes_square_.size() > 0) {
+ return ShaderProgram<ShaderVertexColor,
+ ShaderFragmentColor>::GetTypeId();
+ } else {
+ return ShaderProgram<ShaderVertexRcorner2,
+ ShaderFragmentRcorner2Color>::GetTypeId();
+ }
+}
+
+void DrawRectShadowSpread::SetupVertexShader(GraphicsState* graphics_state,
+ const ShaderVertexColor& shader) {
graphics_state->UpdateClipAdjustment(shader.u_clip_adjustment());
graphics_state->UpdateTransformMatrix(shader.u_view_matrix(),
base_state_.transform);
@@ -158,36 +142,134 @@
base_state_.scissor.width(), base_state_.scissor.height());
graphics_state->VertexAttribPointer(
shader.a_position(), 2, GL_FLOAT, GL_FALSE,
- sizeof(VertexAttributes), vertex_buffer_ +
- offsetof(VertexAttributes, position));
+ sizeof(VertexAttributesSquare), vertex_buffer_ +
+ offsetof(VertexAttributesSquare, position));
graphics_state->VertexAttribPointer(
shader.a_color(), 4, GL_UNSIGNED_BYTE, GL_TRUE,
- sizeof(VertexAttributes), vertex_buffer_ +
- offsetof(VertexAttributes, color));
- graphics_state->VertexAttribPointer(
- shader.a_offset(), 2, GL_FLOAT, GL_FALSE,
- sizeof(VertexAttributes), vertex_buffer_ +
- offsetof(VertexAttributes, offset));
+ sizeof(VertexAttributesSquare), vertex_buffer_ +
+ offsetof(VertexAttributesSquare, color));
graphics_state->VertexAttribFinish();
}
-base::TypeId DrawRectShadowSpread::GetTypeId() const {
- if (inner_corners_) {
- return ShaderProgram<CommonVertexShader,
- ShaderFragmentColorBetweenRrects>::GetTypeId();
- } else {
- return ShaderProgram<CommonVertexShader,
- ShaderFragmentColorInclude>::GetTypeId();
- }
+void DrawRectShadowSpread::SetupVertexShader(GraphicsState* graphics_state,
+ const ShaderVertexRcorner2& shader) {
+ graphics_state->UpdateClipAdjustment(shader.u_clip_adjustment());
+ graphics_state->UpdateTransformMatrix(shader.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(
+ shader.a_position(), 2, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributesRound), vertex_buffer_ +
+ offsetof(VertexAttributesRound, position));
+ graphics_state->VertexAttribPointer(
+ shader.a_rcorner_inner(), 4, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributesRound), vertex_buffer_ +
+ offsetof(VertexAttributesRound, rcorner_inner));
+ graphics_state->VertexAttribPointer(
+ shader.a_rcorner_outer(), 4, GL_FLOAT, GL_FALSE,
+ sizeof(VertexAttributesRound), vertex_buffer_ +
+ offsetof(VertexAttributesRound, rcorner_outer));
+ graphics_state->VertexAttribFinish();
}
-void DrawRectShadowSpread::SetVertex(VertexAttributes* vertex,
- float x, float y) {
- vertex->position[0] = x;
- vertex->position[1] = y;
- vertex->offset[0] = (x - offset_center_.x()) * offset_scale_;
- vertex->offset[1] = (y - offset_center_.y()) * offset_scale_;
- vertex->color = color_;
+void DrawRectShadowSpread::SetGeometry(GraphicsState* graphics_state,
+ const math::RectF& inner_rect, const math::RectF& outer_rect) {
+ // Draw the box shadow's spread. This is a triangle strip covering the area
+ // between outer rect and inner rect.
+ uint32_t color = GetGLRGBA(color_);
+
+ if (inner_rect.IsEmpty()) {
+ attributes_square_.reserve(4);
+ attributes_square_.emplace_back(
+ outer_rect.x(), outer_rect.y(), color);
+ attributes_square_.emplace_back(
+ outer_rect.right(), outer_rect.y(), color);
+ attributes_square_.emplace_back(
+ outer_rect.x(), outer_rect.bottom(), color);
+ attributes_square_.emplace_back(
+ outer_rect.right(), outer_rect.bottom(), color);
+ } else {
+ math::RectF inside_rect(inner_rect);
+ inside_rect.Intersect(outer_rect);
+ attributes_square_.reserve(10);
+ attributes_square_.emplace_back(
+ outer_rect.x(), outer_rect.y(), color);
+ attributes_square_.emplace_back(
+ inside_rect.x(), inside_rect.y(), color);
+ attributes_square_.emplace_back(
+ outer_rect.right(), outer_rect.y(), color);
+ attributes_square_.emplace_back(
+ inside_rect.right(), inside_rect.y(), color);
+ attributes_square_.emplace_back(
+ outer_rect.right(), outer_rect.bottom(), color);
+ attributes_square_.emplace_back(
+ inside_rect.right(), inside_rect.bottom(), color);
+ attributes_square_.emplace_back(
+ outer_rect.x(), outer_rect.bottom(), color);
+ attributes_square_.emplace_back(
+ inside_rect.x(), inside_rect.bottom(), color);
+ attributes_square_.emplace_back(
+ outer_rect.x(), outer_rect.y(), color);
+ attributes_square_.emplace_back(
+ inside_rect.x(), inside_rect.y(), color);
+ }
+
+ graphics_state->ReserveVertexData(
+ attributes_square_.size() * sizeof(attributes_square_[0]));
+}
+
+void DrawRectShadowSpread::SetGeometry(
+ GraphicsState* graphics_state,
+ const math::RectF& inner_rect,
+ const render_tree::RoundedCorners& inner_corners,
+ const math::RectF& outer_rect,
+ const render_tree::RoundedCorners& outer_corners) {
+ // Draw the area between the inner rounded rect and outer rounded rect. Add
+ // a 1-pixel border to include antialiasing.
+ math::RectF bounds(outer_rect);
+ bounds.Outset(1.0f, 1.0f);
+
+ // Get the render quads for the inner rounded rect excluding its inscribed
+ // rectangle.
+ RRectAttributes rrect_inner[8];
+ GetRRectAttributes(bounds, inner_rect, inner_corners, rrect_inner);
+
+ // Get the render quads for the outer rounded rect.
+ RRectAttributes rrect_outer[4];
+ GetRRectAttributes(bounds, outer_rect, outer_corners, rrect_outer);
+
+ // Add geometry to draw the area between the inner rrect and outer rrect.
+ for (int i = 0; i < arraysize(rrect_inner); ++i) {
+ for (int o = 0; o < arraysize(rrect_outer); ++o) {
+ math::RectF intersection = math::IntersectRects(
+ rrect_inner[i].bounds, rrect_outer[o].bounds);
+ if (!intersection.IsEmpty()) {
+ // Use two triangles to draw the intersection.
+ const RCorner& inner = rrect_inner[i].rcorner;
+ const RCorner& outer = rrect_outer[o].rcorner;
+ uint16_t vert = static_cast<uint16_t>(attributes_round_.size());
+ attributes_round_.emplace_back(
+ intersection.x(), intersection.y(), inner, outer);
+ attributes_round_.emplace_back(
+ intersection.right(), intersection.y(), inner, outer);
+ attributes_round_.emplace_back(
+ intersection.x(), intersection.bottom(), inner, outer);
+ attributes_round_.emplace_back(
+ intersection.right(), intersection.bottom(), inner, outer);
+ indices_.emplace_back(vert);
+ indices_.emplace_back(vert + 1);
+ indices_.emplace_back(vert + 2);
+ indices_.emplace_back(vert + 1);
+ indices_.emplace_back(vert + 2);
+ indices_.emplace_back(vert + 3);
+ }
+ }
+ }
+
+ graphics_state->ReserveVertexData(
+ attributes_round_.size() * sizeof(attributes_round_[0]));
+ graphics_state->ReserveVertexIndices(indices_.size());
}
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
index 1e4d590..6df79ca 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rect_shadow_spread.h
@@ -15,6 +15,8 @@
#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_object.h"
@@ -58,31 +60,43 @@
ShaderProgramManager* program_manager) OVERRIDE;
base::TypeId GetTypeId() const OVERRIDE;
- protected:
- typedef ShaderVertexColorOffset CommonVertexShader;
-
- struct VertexAttributes {
+ private:
+ struct VertexAttributesSquare {
+ VertexAttributesSquare(float x, float y, uint32_t color);
float position[2];
- float offset[2];
uint32_t color;
};
- DrawRectShadowSpread(GraphicsState* graphics_state,
- const BaseState& base_state);
- void SetupShader(const CommonVertexShader& shader,
- GraphicsState* graphics_state);
- void SetVertex(VertexAttributes* vertex, float x, float y);
+ struct VertexAttributesRound {
+ VertexAttributesRound(float x, float y,
+ const RCorner& inner, const RCorner& outer);
+ float position[2];
+ RCorner rcorner_inner;
+ RCorner rcorner_outer;
+ };
- math::RectF inner_rect_;
- math::RectF outer_rect_;
- OptionalRoundedCorners inner_corners_;
- OptionalRoundedCorners outer_corners_;
- math::PointF offset_center_;
- float offset_scale_;
- uint32_t color_;
+ void SetupVertexShader(GraphicsState* graphics_state,
+ const ShaderVertexColor& shader);
+ void SetupVertexShader(GraphicsState* graphics_state,
+ const ShaderVertexRcorner2& shader);
+
+ void SetGeometry(GraphicsState* graphics_state,
+ const math::RectF& inner_rect,
+ const math::RectF& outer_rect);
+ void SetGeometry(GraphicsState* graphics_state,
+ const math::RectF& inner_rect,
+ const render_tree::RoundedCorners& inner_corners,
+ const math::RectF& outer_rect,
+ const render_tree::RoundedCorners& outer_corners);
+
+ render_tree::ColorRGBA color_;
+
+ std::vector<VertexAttributesSquare> attributes_square_;
+ std::vector<VertexAttributesRound> attributes_round_;
+ std::vector<uint16_t> indices_;
uint8_t* vertex_buffer_;
- int vertex_count_;
+ uint16_t* index_buffer_;
};
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc
index f12e7ed..2443bbe 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.cc
@@ -26,21 +26,7 @@
namespace egl {
namespace {
-const int kVertexCount = 4;
-
-struct VertexAttributes {
- float position[2];
- float offset[2];
- uint32_t color;
-};
-
-void SetVertex(VertexAttributes* vertex, float x, float y, uint32_t color) {
- vertex->position[0] = x;
- vertex->position[1] = y;
- vertex->offset[0] = x;
- vertex->offset[1] = y;
- vertex->color = color;
-}
+const int kVertexCount = 4 * 6;
} // namespace
DrawRRectColor::DrawRRectColor(GraphicsState* graphics_state,
@@ -51,8 +37,14 @@
rect_(rect),
corners_(corners),
vertex_buffer_(NULL) {
- color_ = GetGLRGBA(GetDrawColor(color) * base_state_.opacity);
+ color_ = GetDrawColor(color) * base_state_.opacity;
graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
+
+ // Extract scale from the transform and move it into the vertex attributes
+ // so that the anti-aliased edges remain 1 pixel wide.
+ math::Vector2dF scale = RemoveScaleFromTransform();
+ rect_.Scale(scale.x(), scale.y());
+ corners_ = corners_.Scale(scale.x(), scale.y());
}
void DrawRRectColor::ExecuteUpdateVertexBuffer(
@@ -63,10 +55,31 @@
VertexAttributes attributes[kVertexCount];
math::RectF outer_rect(rect_);
outer_rect.Outset(1.0f, 1.0f);
- SetVertex(&attributes[0], outer_rect.x(), outer_rect.y(), color_);
- SetVertex(&attributes[1], outer_rect.right(), outer_rect.y(), color_);
- SetVertex(&attributes[2], outer_rect.right(), outer_rect.bottom(), color_);
- SetVertex(&attributes[3], outer_rect.x(), outer_rect.bottom(), color_);
+
+ RRectAttributes rrect[4];
+ GetRRectAttributes(outer_rect, rect_, corners_, rrect);
+ for (int r = 0, v = 0; r < arraysize(rrect); ++r) {
+ attributes[v ].position[0] = rrect[r].bounds.x();
+ attributes[v ].position[1] = rrect[r].bounds.y();
+ attributes[v ].rcorner =
+ RCorner(attributes[v ].position, rrect[r].rcorner);
+ attributes[v+1].position[0] = rrect[r].bounds.right();
+ attributes[v+1].position[1] = rrect[r].bounds.y();
+ attributes[v+1].rcorner =
+ RCorner(attributes[v+1].position, rrect[r].rcorner);
+ attributes[v+2].position[0] = rrect[r].bounds.x();
+ attributes[v+2].position[1] = rrect[r].bounds.bottom();
+ attributes[v+2].rcorner =
+ RCorner(attributes[v+2].position, rrect[r].rcorner);
+ attributes[v+3].position[0] = rrect[r].bounds.right();
+ attributes[v+3].position[1] = rrect[r].bounds.bottom();
+ attributes[v+3].rcorner =
+ RCorner(attributes[v+3].position, rrect[r].rcorner);
+ attributes[v+4] = attributes[v+1];
+ attributes[v+5] = attributes[v+2];
+ v += 6;
+ }
+
vertex_buffer_ = graphics_state->AllocateVertexData(sizeof(attributes));
SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
}
@@ -74,8 +87,8 @@
void DrawRRectColor::ExecuteRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- ShaderProgram<ShaderVertexColorOffset,
- ShaderFragmentColorRrect>* program;
+ ShaderProgram<ShaderVertexRcorner,
+ ShaderFragmentRcornerColor>* program;
program_manager->GetProgram(&program);
graphics_state->UseProgram(program->GetHandle());
graphics_state->UpdateClipAdjustment(
@@ -90,23 +103,19 @@
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, position));
graphics_state->VertexAttribPointer(
- program->GetVertexShader().a_color(), 4, GL_UNSIGNED_BYTE, GL_TRUE,
+ program->GetVertexShader().a_rcorner(), 4, GL_FLOAT, GL_FALSE,
sizeof(VertexAttributes), vertex_buffer_ +
- offsetof(VertexAttributes, color));
- graphics_state->VertexAttribPointer(
- program->GetVertexShader().a_offset(), 2, GL_FLOAT, GL_FALSE,
- sizeof(VertexAttributes), vertex_buffer_ +
- offsetof(VertexAttributes, offset));
+ offsetof(VertexAttributes, rcorner));
graphics_state->VertexAttribFinish();
- SetRRectUniforms(program->GetFragmentShader().u_rect(),
- program->GetFragmentShader().u_corners(),
- rect_, corners_, 0.5f);
- GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, kVertexCount));
+
+ GL_CALL(glUniform4f(program->GetFragmentShader().u_color(),
+ color_.r(), color_.g(), color_.b(), color_.a()));
+ GL_CALL(glDrawArrays(GL_TRIANGLES, 0, kVertexCount));
}
base::TypeId DrawRRectColor::GetTypeId() const {
- return ShaderProgram<ShaderVertexColorOffset,
- ShaderFragmentColorRrect>::GetTypeId();
+ return ShaderProgram<ShaderVertexRcorner,
+ ShaderFragmentRcornerColor>::GetTypeId();
}
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.h b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.h
index c5cd055..de71e9e 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color.h
@@ -41,9 +41,14 @@
base::TypeId GetTypeId() const OVERRIDE;
private:
+ struct VertexAttributes {
+ float position[2];
+ RCorner rcorner;
+ };
+
math::RectF rect_;
render_tree::RoundedCorners corners_;
- uint32_t color_;
+ render_tree::ColorRGBA color_;
uint8_t* vertex_buffer_;
};
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.cc b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.cc
index 7ec422b..dbcbec1 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.cc
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.cc
@@ -27,22 +27,7 @@
namespace egl {
namespace {
-const int kVertexCount = 4;
-
-struct VertexAttributes {
- float position[2];
- float offset[2];
- float texcoord[2];
-};
-
-void SetVertex(VertexAttributes* vertex, float x, float y, float u, float v) {
- vertex->position[0] = x;
- vertex->position[1] = y;
- vertex->offset[0] = x;
- vertex->offset[1] = y;
- vertex->texcoord[0] = u;
- vertex->texcoord[1] = v;
-}
+const int kVertexCount = 4 * 6;
} // namespace
DrawRRectColorTexture::DrawRRectColorTexture(GraphicsState* graphics_state,
@@ -60,25 +45,58 @@
DCHECK(base_state_.rounded_scissor_corners);
color_ = GetDrawColor(color) * base_state_.opacity;
graphics_state->ReserveVertexData(kVertexCount * sizeof(VertexAttributes));
+
+ // Extract scale from the transform and move it into the vertex attributes
+ // so that the anti-aliased edges remain 1 pixel wide.
+ math::Vector2dF scale = RemoveScaleFromTransform();
+ rect_.Scale(scale.x(), scale.y());
+ base_state_.rounded_scissor_rect.Scale(scale.x(), scale.y());
+ base_state_.rounded_scissor_corners =
+ base_state_.rounded_scissor_corners->Scale(scale.x(), scale.y());
}
void DrawRRectColorTexture::ExecuteUpdateVertexBuffer(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
+ const float kWidthScale = 1.0f / rect_.width();
+ const float kHeightScale = 1.0f / rect_.height();
+
VertexAttributes attributes[kVertexCount];
- SetVertex(&attributes[0], rect_.x(), rect_.y(),
- texcoord_transform_(0, 2), texcoord_transform_(1, 2)); // uv = (0,0)
- SetVertex(&attributes[1], rect_.right(), rect_.y(),
- texcoord_transform_(0, 0) + texcoord_transform_(0, 2), // uv = (1,0)
- texcoord_transform_(1, 0) + texcoord_transform_(1, 2));
- SetVertex(&attributes[2], rect_.right(), rect_.bottom(),
- texcoord_transform_(0, 0) + texcoord_transform_(0, 1) + // uv = (1,1)
- texcoord_transform_(0, 2),
- texcoord_transform_(1, 0) + texcoord_transform_(1, 1) +
- texcoord_transform_(1, 2));
- SetVertex(&attributes[3], rect_.x(), rect_.bottom(),
- texcoord_transform_(0, 1) + texcoord_transform_(0, 2), // uv = (0,1)
- texcoord_transform_(1, 1) + texcoord_transform_(1, 2));
+ RRectAttributes rrect[4];
+ GetRRectAttributes(rect_, base_state_.rounded_scissor_rect,
+ *base_state_.rounded_scissor_corners, rrect);
+ for (int r = 0, v = 0; r < arraysize(rrect); ++r) {
+ attributes[v ].position[0] = rrect[r].bounds.x();
+ attributes[v ].position[1] = rrect[r].bounds.y();
+ attributes[v ].rcorner =
+ RCorner(attributes[v ].position, rrect[r].rcorner);
+ attributes[v+1].position[0] = rrect[r].bounds.right();
+ attributes[v+1].position[1] = rrect[r].bounds.y();
+ attributes[v+1].rcorner =
+ RCorner(attributes[v+1].position, rrect[r].rcorner);
+ attributes[v+2].position[0] = rrect[r].bounds.x();
+ attributes[v+2].position[1] = rrect[r].bounds.bottom();
+ attributes[v+2].rcorner =
+ RCorner(attributes[v+2].position, rrect[r].rcorner);
+ attributes[v+3].position[0] = rrect[r].bounds.right();
+ attributes[v+3].position[1] = rrect[r].bounds.bottom();
+ attributes[v+3].rcorner =
+ RCorner(attributes[v+3].position, rrect[r].rcorner);
+
+ for (int t = v; t < v + 4; ++t) {
+ math::PointF texcoord(
+ (attributes[t].position[0] - rect_.x()) * kWidthScale,
+ (attributes[t].position[1] - rect_.y()) * kHeightScale);
+ texcoord = texcoord_transform_ * texcoord;
+ attributes[t].texcoord[0] = texcoord.x();
+ attributes[t].texcoord[1] = texcoord.y();
+ }
+
+ attributes[v+4] = attributes[v+1];
+ attributes[v+5] = attributes[v+2];
+ v += 6;
+ }
+
vertex_buffer_ = graphics_state->AllocateVertexData(sizeof(attributes));
SbMemoryCopy(vertex_buffer_, attributes, sizeof(attributes));
@@ -119,8 +137,8 @@
void DrawRRectColorTexture::ExecuteRasterize(
GraphicsState* graphics_state,
ShaderProgramManager* program_manager) {
- ShaderProgram<ShaderVertexOffsetTexcoord,
- ShaderFragmentTexcoordColorRrect>* program;
+ ShaderProgram<ShaderVertexRcornerTexcoord,
+ ShaderFragmentRcornerTexcoordColor>* program;
program_manager->GetProgram(&program);
graphics_state->UseProgram(program->GetHandle());
graphics_state->UpdateClipAdjustment(
@@ -135,19 +153,15 @@
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, position));
graphics_state->VertexAttribPointer(
- program->GetVertexShader().a_offset(), 2, GL_FLOAT, GL_FALSE,
+ program->GetVertexShader().a_rcorner(), 4, GL_FLOAT, GL_FALSE,
sizeof(VertexAttributes), vertex_buffer_ +
- offsetof(VertexAttributes, offset));
+ offsetof(VertexAttributes, rcorner));
graphics_state->VertexAttribPointer(
program->GetVertexShader().a_texcoord(), 2, GL_FLOAT, GL_FALSE,
sizeof(VertexAttributes), vertex_buffer_ +
offsetof(VertexAttributes, texcoord));
graphics_state->VertexAttribFinish();
- SetRRectUniforms(program->GetFragmentShader().u_rect(),
- program->GetFragmentShader().u_corners(),
- base_state_.rounded_scissor_rect,
- *base_state_.rounded_scissor_corners, 0.5f);
GL_CALL(glUniform4f(program->GetFragmentShader().u_color(),
color_.r(), color_.g(), color_.b(), color_.a()));
GL_CALL(glUniform4fv(program->GetFragmentShader().u_texcoord_clamp(), 1,
@@ -157,7 +171,7 @@
graphics_state->ActiveBindTexture(
program->GetFragmentShader().u_texture_texunit(),
texture_->GetTarget(), texture_->gl_handle(), GL_REPEAT);
- GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, kVertexCount));
+ GL_CALL(glDrawArrays(GL_TRIANGLES, 0, kVertexCount));
graphics_state->ActiveBindTexture(
program->GetFragmentShader().u_texture_texunit(),
texture_->GetTarget(), texture_->gl_handle(), GL_CLAMP_TO_EDGE);
@@ -165,13 +179,13 @@
graphics_state->ActiveBindTexture(
program->GetFragmentShader().u_texture_texunit(),
texture_->GetTarget(), texture_->gl_handle());
- GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, kVertexCount));
+ GL_CALL(glDrawArrays(GL_TRIANGLES, 0, kVertexCount));
}
}
base::TypeId DrawRRectColorTexture::GetTypeId() const {
- return ShaderProgram<ShaderVertexOffsetTexcoord,
- ShaderFragmentTexcoordColorRrect>::GetTypeId();
+ return ShaderProgram<ShaderVertexRcornerTexcoord,
+ ShaderFragmentRcornerTexcoordColor>::GetTypeId();
}
} // namespace egl
diff --git a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.h b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.h
index c65347e..98a5c84 100644
--- a/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.h
+++ b/src/cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.h
@@ -44,6 +44,12 @@
base::TypeId GetTypeId() const OVERRIDE;
private:
+ struct VertexAttributes {
+ float position[2];
+ float texcoord[2];
+ RCorner rcorner;
+ };
+
math::Matrix3F texcoord_transform_;
math::RectF rect_;
render_tree::ColorRGBA color_;
diff --git a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
index 0418992..74aba4b 100644
--- a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
@@ -17,6 +17,7 @@
#include <GLES2/gl2.h>
#include "cobalt/renderer/backend/egl/utils.h"
+#include "egl/generated_shader_impl.h"
namespace cobalt {
namespace renderer {
@@ -24,6 +25,13 @@
namespace egl {
ShaderProgramManager::ShaderProgramManager() {
+ // These are shaders that get instantiated during video playback when the
+ // users starts interacting with the transport controls. They are preloaded
+ // to prevent UI-hiccups.
+ // These shaders are generated from egl/generated_shader_impl.h
+ Preload<ShaderVertexOffsetRcorner, ShaderFragmentColorBlurRrects>();
+ Preload<ShaderVertexColorOffset, ShaderFragmentColorInclude>();
+ Preload<ShaderVertexRcornerTexcoord, ShaderFragmentRcornerTexcoordColor>();
}
ShaderProgramManager::~ShaderProgramManager() {
diff --git a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.h b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.h
index f9c396e..7184e57 100644
--- a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.h
+++ b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.h
@@ -44,6 +44,9 @@
ShaderProgramBase* FindProgram(base::TypeId program_type_id);
void AddProgram(base::TypeId program_type_id, ShaderProgramBase* program);
+ template <typename VertextShaderT, typename FragmentShaderT>
+ void Preload();
+
typedef base::linked_hash_map<base::TypeId, ShaderProgramBase*> ProgramMap;
ProgramMap program_map_;
};
@@ -59,6 +62,12 @@
*out_program = base::polymorphic_downcast<ShaderProgramType*>(program);
}
+template <typename VertextShaderT, typename FragmentShaderT>
+inline void ShaderProgramManager::Preload() {
+ ShaderProgram<VertextShaderT, FragmentShaderT>* program;
+ GetProgram(&program);
+}
+
} // namespace egl
} // namespace rasterizer
} // namespace renderer
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_between_rrects.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_between_rrects.glsl
deleted file mode 100644
index f2897ad..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_between_rrects.glsl
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-precision mediump float;
-
-// A rounded rect is represented by a vec4 specifying (min.xy, max.xy), and a
-// matrix of corners. Each vector in the matrix represents a corner (order:
-// top left, top right, bottom left, bottom right). Each corner vec4 represents
-// (start.xy, radius.xy).
-uniform vec4 u_inner_rect;
-uniform mat4 u_inner_corners;
-uniform vec4 u_outer_rect;
-uniform mat4 u_outer_corners;
-
-varying vec2 v_offset;
-varying vec4 v_color;
-
-#include "function_is_outside_rrect.inc"
-
-void main() {
- float inner_scale = IsOutsideRRect(v_offset, u_inner_rect, u_inner_corners);
- float outer_scale = IsOutsideRRect(v_offset, u_outer_rect, u_outer_corners);
- gl_FragColor = v_color * (inner_scale * (1.0 - outer_scale));
-}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
index 50ebd6a..4f15571 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl
@@ -13,11 +13,12 @@
// limitations under the License.
precision mediump float;
+
+uniform vec4 u_color;
uniform vec4 u_blur_rect;
uniform vec2 u_scale_add;
varying vec2 v_offset;
-varying vec4 v_color;
#include "function_gaussian_integral.inc"
@@ -27,5 +28,5 @@
float integral = GaussianIntegral(u_blur_rect.xz - v_offset.xx) *
GaussianIntegral(u_blur_rect.yw - v_offset.yy);
float blur_scale = integral * u_scale_add.x + u_scale_add.y;
- gl_FragColor = v_color * blur_scale;
+ gl_FragColor = u_color * blur_scale;
}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
index ee8c4ae..efc8747 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl
@@ -14,13 +14,6 @@
precision mediump float;
-// A rounded rect is represented by a vec4 specifying (min.xy, max.xy)
-// and a matrix of corners. Each vector in the matrix represents a corner
-// (order: top left, top right, bottom left, bottom right). Each corner vec4
-// represents (start.xy, radius.xy).
-uniform vec4 u_scissor_rect;
-uniform mat4 u_scissor_corners;
-
// The rounded spread rect is represented in a way to optimize calculation of
// the extents. Each element of a vec4 represents a corner's value -- order
// is top left, top right, bottom left, bottom right. Extents for each corner
@@ -49,14 +42,16 @@
// inset shadow with scissor rect behaving as an inclusive scissor.
uniform vec2 u_scale_add;
+uniform vec4 u_color;
+
// Blur calculations happen in terms in sigma distances. Use sigma_scale to
// translate pixel distances into sigma distances.
uniform vec2 u_sigma_scale;
varying vec2 v_offset;
-varying vec4 v_color;
+varying vec4 v_rcorner;
-#include "function_is_outside_rrect.inc"
+#include "function_is_outside_rcorner.inc"
#include "function_gaussian_integral.inc"
vec2 GetXExtents(float y) {
@@ -126,8 +121,7 @@
void main() {
float scissor_scale =
- IsOutsideRRect(v_offset, u_scissor_rect, u_scissor_corners) *
- u_scale_add.x + u_scale_add.y;
+ IsOutsideRCorner(v_rcorner) * u_scale_add.x + u_scale_add.y;
float blur_scale = GetBlur(v_offset) * u_scale_add.x + u_scale_add.y;
- gl_FragColor = v_color * (blur_scale * scissor_scale);
+ gl_FragColor = u_color * (blur_scale * scissor_scale);
}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_rrect.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_rrect.glsl
deleted file mode 100644
index 26549ed..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_color_rrect.glsl
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-precision mediump float;
-
-// A rounded rect is represented by a vec4 specifying (min.xy, max.xy), and a
-// matrix of corners. Each vector in the matrix represents a corner (order:
-// top left, top right, bottom left, bottom right). Each corner vec4 represents
-// (start.xy, radius.xy).
-uniform vec4 u_rect;
-uniform mat4 u_corners;
-
-varying vec2 v_offset;
-varying vec4 v_color;
-
-#include "function_is_outside_rrect.inc"
-
-void main() {
- float scale = IsOutsideRRect(v_offset, u_rect, u_corners);
- gl_FragColor = v_color * (1.0 - scale);
-}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner2_color.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner2_color.glsl
new file mode 100644
index 0000000..c7a8601
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner2_color.glsl
@@ -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.
+precision mediump float;
+
+uniform vec4 u_color;
+varying vec4 v_rcorner_inner;
+varying vec4 v_rcorner_outer;
+
+#include "function_is_outside_rcorner.inc"
+
+void main() {
+ float inner_scale = IsOutsideRCorner(v_rcorner_inner);
+ float outer_scale = 1.0 - IsOutsideRCorner(v_rcorner_outer);
+ gl_FragColor = u_color * (inner_scale * outer_scale);
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_color.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_color.glsl
new file mode 100644
index 0000000..02c2425
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_color.glsl
@@ -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.
+
+precision mediump float;
+
+uniform vec4 u_color;
+varying vec4 v_rcorner;
+
+#include "function_is_outside_rcorner.inc"
+
+void main() {
+ float scale = IsOutsideRCorner(v_rcorner);
+ gl_FragColor = u_color * (1.0 - scale);
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_texcoord_color.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_texcoord_color.glsl
new file mode 100644
index 0000000..4676317
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_texcoord_color.glsl
@@ -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.
+
+precision mediump float;
+
+uniform vec4 u_color;
+uniform vec4 u_texcoord_clamp;
+uniform sampler2D u_texture;
+
+varying vec4 v_rcorner;
+varying vec2 v_texcoord;
+
+#include "function_is_outside_rcorner.inc"
+
+void main() {
+ float scale = IsOutsideRCorner(v_rcorner);
+ gl_FragColor = u_color * (1.0 - scale) * texture2D(u_texture,
+ clamp(v_texcoord, u_texcoord_clamp.xy, u_texcoord_clamp.zw));
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord_color_rrect.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord_color_rrect.glsl
deleted file mode 100644
index 34a25ad..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord_color_rrect.glsl
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-precision mediump float;
-
-uniform vec4 u_color;
-uniform vec4 u_texcoord_clamp;
-uniform sampler2D u_texture;
-
-// A rounded rect is represented by a vec4 specifying (min.xy, max.xy), and a
-// matrix of corners. Each vector in the matrix represents a corner (order:
-// top left, top right, bottom left, bottom right). Each corner vec4 represents
-// (start.xy, radius.xy).
-uniform vec4 u_rect;
-uniform mat4 u_corners;
-
-varying vec2 v_offset;
-varying vec2 v_texcoord;
-
-#include "function_is_outside_rrect.inc"
-
-void main() {
- float scale = IsOutsideRRect(v_offset, u_rect, u_corners);
- gl_FragColor = u_color * (1.0 - scale) * texture2D(u_texture,
- clamp(v_texcoord, u_texcoord_clamp.xy, u_texcoord_clamp.zw));
-}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rcorner.inc b/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rcorner.inc
new file mode 100644
index 0000000..5233ae9
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rcorner.inc
@@ -0,0 +1,37 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Return 0 if the given position is inside the rounded corner, or scale
+// towards 1 as it goes outside a 1-pixel anti-aliasing border.
+// |rcorner| is a vec4 representing (scaled.xy, 1 / radius.xy) with scaled.xy
+// representing the offset of the current position in terms of radius.xy
+// (i.e. offset.xy / radius.xy). The scaled.xy values can be negative if the
+// current position is outside the corner start.
+float IsOutsideRCorner(vec4 rcorner) {
+ // Estimate the distance to an implicit function using
+ // dist = f(x,y) / length(gradient(f(x,y)))
+ // For an ellipse, f(x,y) = x^2 / a^2 + y^2 / b^2 - 1.
+ vec2 scaled = max(rcorner.xy, 0.0);
+ float implicit = dot(scaled, scaled) - 1.0;
+
+ // NOTE: To accommodate large radius values using mediump floats, rcorner.zw
+ // was scaled by kRCornerGradientScale in the vertex attribute data.
+ // Multiply inv_gradient by kRCornerGradientScale to undo that scaling.
+ const float kRCornerGradientScale = 16.0;
+ vec2 gradient = 2.0 * scaled * rcorner.zw;
+ float inv_gradient = kRCornerGradientScale *
+ inversesqrt(max(dot(gradient, gradient), 0.0001));
+
+ return clamp(0.5 + implicit * inv_gradient, 0.0, 1.0);
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rrect.inc b/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rrect.inc
deleted file mode 100644
index e3e58b9..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rrect.inc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Return 0 if the given point is inside the rounded rect, or scale towards 1
-// as it goes outside a 1-pixel anti-aliasing border.
-// |rect| represents (min.xy, max.xy) of the encompassing rectangle.
-// |corners| is a matrix with each vec4 representing (start.xy, radius.xy) of
-// a corner. The order is top left, top right, bottom left, bottom right.
-float IsOutsideRRect(vec2 point, vec4 rect, mat4 corners) {
- vec4 select_corner = vec4(
- step(point.x, corners[0].x) * step(point.y, corners[0].y),
- step(corners[1].x, point.x) * step(point.y, corners[1].y),
- step(point.x, corners[2].x) * step(corners[2].y, point.y),
- step(corners[3].x, point.x) * step(corners[3].y, point.y));
- if (dot(select_corner, vec4(1.0)) > 0.5) {
- // Estimate the amount of anti-aliasing that should be used by comparing
- // x^2 / a^2 + y^2 / b^2 for the ellipse and ellipse + 1 pixel.
- vec4 corner = corners * select_corner;
- vec2 pixel_offset = point - corner.xy;
-
- if (abs(corner.z - corner.w) < 0.1) {
- // This is a square or round corner.
- return clamp(length(pixel_offset) - corner.z, 0.0, 1.0);
- }
-
- vec2 offset_min = pixel_offset / corner.zw;
- vec2 offset_max = pixel_offset / (corner.zw + vec2(1.0));
- float result_min = dot(offset_min, offset_min);
- float result_max = dot(offset_max, offset_max);
-
- // Return 1.0 if outside, or interpolate if in the border, or 0 if inside.
- return (result_max >= 1.0) ? 1.0 :
- max(result_min - 1.0, 0.0) / (result_min - result_max);
- }
-
- return clamp(rect.x - point.x, 0.0, 1.0) +
- clamp(point.x - rect.z, 0.0, 1.0) +
- clamp(rect.y - point.y, 0.0, 1.0) +
- clamp(point.y - rect.w, 0.0, 1.0);
-}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
index 9d00899..2ebc954 100644
--- a/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/shaders.gyp
@@ -20,22 +20,25 @@
'generate_class_script': '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/generate_shader_impl.py',
'shader_sources': [
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color.glsl',
- '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_between_rrects.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_blur_rrects.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_include.glsl',
- '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_rrect.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_color_texcoord.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_opacity_texcoord1d.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_color.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner2_color.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_rcorner_texcoord_color.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord.glsl',
- '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/fragment_texcoord_color_rrect.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/function_gaussian_integral.inc',
- '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rrect.inc',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/function_is_outside_rcorner.inc',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_offset.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_color_texcoord.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_offset.glsl',
- '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_texcoord.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner2.glsl',
+ '<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner_texcoord.glsl',
'<(DEPTH)/cobalt/renderer/rasterizer/egl/shaders/vertex_texcoord.glsl',
],
},
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl
new file mode 100644
index 0000000..3d9ff46
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_rcorner.glsl
@@ -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.
+
+uniform vec4 u_clip_adjustment;
+uniform mat3 u_view_matrix;
+attribute vec2 a_position;
+attribute vec4 a_rcorner;
+varying vec2 v_offset;
+varying vec4 v_rcorner;
+
+void main() {
+ vec3 pos2d = u_view_matrix * vec3(a_position, 1);
+ gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
+ u_clip_adjustment.zw, 0, pos2d.z);
+ v_offset = a_position;
+ v_rcorner = a_rcorner;
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_texcoord.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_texcoord.glsl
deleted file mode 100644
index df81d24..0000000
--- a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_offset_texcoord.glsl
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-uniform vec4 u_clip_adjustment;
-uniform mat3 u_view_matrix;
-attribute vec2 a_position;
-attribute vec2 a_offset;
-attribute vec2 a_texcoord;
-varying vec2 v_offset;
-varying vec2 v_texcoord;
-
-void main() {
- vec3 pos2d = u_view_matrix * vec3(a_position, 1);
- gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
- u_clip_adjustment.zw, 0, pos2d.z);
- v_offset = a_offset;
- v_texcoord = a_texcoord;
-}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner.glsl
new file mode 100644
index 0000000..a84d4f2
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner.glsl
@@ -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.
+
+uniform vec4 u_clip_adjustment;
+uniform mat3 u_view_matrix;
+attribute vec2 a_position;
+attribute vec4 a_rcorner;
+varying vec4 v_rcorner;
+
+void main() {
+ vec3 pos2d = u_view_matrix * vec3(a_position, 1);
+ gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
+ u_clip_adjustment.zw, 0, pos2d.z);
+ v_rcorner = a_rcorner;
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner2.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner2.glsl
new file mode 100644
index 0000000..fa4d155
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner2.glsl
@@ -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.
+uniform vec4 u_clip_adjustment;
+uniform mat3 u_view_matrix;
+attribute vec2 a_position;
+attribute vec4 a_rcorner_inner;
+attribute vec4 a_rcorner_outer;
+varying vec4 v_rcorner_inner;
+varying vec4 v_rcorner_outer;
+
+void main() {
+ vec3 pos2d = u_view_matrix * vec3(a_position, 1);
+ gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
+ u_clip_adjustment.zw, 0, pos2d.z);
+ v_rcorner_inner = a_rcorner_inner;
+ v_rcorner_outer = a_rcorner_outer;
+}
diff --git a/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner_texcoord.glsl b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner_texcoord.glsl
new file mode 100644
index 0000000..103d1ab
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/egl/shaders/vertex_rcorner_texcoord.glsl
@@ -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.
+
+uniform vec4 u_clip_adjustment;
+uniform mat3 u_view_matrix;
+attribute vec2 a_position;
+attribute vec4 a_rcorner;
+attribute vec2 a_texcoord;
+varying vec4 v_rcorner;
+varying vec2 v_texcoord;
+
+void main() {
+ vec3 pos2d = u_view_matrix * vec3(a_position, 1);
+ gl_Position = vec4(pos2d.xy * u_clip_adjustment.xy +
+ u_clip_adjustment.zw, 0, pos2d.z);
+ v_rcorner = a_rcorner;
+ v_texcoord = a_texcoord;
+}
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index a3f66eb..760bb4d 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -216,6 +216,44 @@
RotateMatrix(static_cast<float>(M_PI) / 6.0f)));
}
+TEST_F(PixelTest, ScaledThenRotatedRectWithDifferentRoundedCorners) {
+ RoundedCorner top_left(6, 15);
+ RoundedCorner top_right(0, 0);
+ RoundedCorner bottom_right(6, 25);
+ RoundedCorner bottom_left(2, 25);
+
+ scoped_ptr<RoundedCorners> rounded_corners(
+ new RoundedCorners(top_left, top_right, bottom_right, bottom_left));
+
+ TestTree(new MatrixTransformNode(
+ new RectNode(RectF(-7, -25, 14, 50),
+ scoped_ptr<Brush>(
+ new SolidColorBrush(ColorRGBA(1, 1, 1, 1))),
+ rounded_corners.Pass()),
+ TranslateMatrix(100.0f, 100.0f) *
+ RotateMatrix(static_cast<float>(M_PI) / 3.0f) *
+ ScaleMatrix(-10.0f, 2.0f)));
+}
+
+TEST_F(PixelTest, RotatedThenScaledRectWithDifferentRoundedCorners) {
+ RoundedCorner top_left(4, 7);
+ RoundedCorner top_right(0, 0);
+ RoundedCorner bottom_right(10, 2);
+ RoundedCorner bottom_left(5, 3);
+
+ scoped_ptr<RoundedCorners> rounded_corners(
+ new RoundedCorners(top_left, top_right, bottom_right, bottom_left));
+
+ TestTree(new MatrixTransformNode(
+ new RectNode(RectF(-10, -7, 20, 14),
+ scoped_ptr<Brush>(
+ new SolidColorBrush(ColorRGBA(1, 1, 1, 1))),
+ rounded_corners.Pass()),
+ TranslateMatrix(100.0f, 100.0f) *
+ ScaleMatrix(6.0f, 9.0f) *
+ RotateMatrix(static_cast<float>(M_PI) / 6.0f)));
+}
+
TEST_F(PixelTest, RedRectWithDifferentRoundedCornersOnTopLeftOfSurface) {
RoundedCorner top_left(10, 10);
RoundedCorner top_right(20, 20);
@@ -1952,6 +1990,19 @@
new ImageNode(image)));
}
+TEST_F(PixelTest, ScaledThenRotatedRoundedCornersViewportOverImage) {
+ scoped_refptr<Image> image =
+ CreateColoredCheckersImage(GetResourceProvider(), output_surface_size());
+
+ TestTree(new MatrixTransformNode(
+ new FilterNode(
+ ViewportFilter(RectF(25, 5, 150, 10), RoundedCorners(25, 2)),
+ new ImageNode(image)),
+ TranslateMatrix(-30, 130) *
+ RotateMatrix(static_cast<float>(M_PI / 3.0f)) *
+ ScaleMatrix(1.0f, 10.0f)));
+}
+
TEST_F(PixelTest, RoundedCornersViewportOverWrappingImage) {
scoped_refptr<Image> image =
CreateColoredCheckersImage(GetResourceProvider(), output_surface_size());
@@ -2829,6 +2880,14 @@
Shadow(Vector2dF(0.0f, 0.0f), 100.0f, ColorRGBA(0, 0, 0, 1))));
}
+TEST_F(PixelTest, ScaledBoxShadowWithSpreadAndBlurCentered) {
+ TestTree(new MatrixTransformNode(CreateShadowRectWithBackground(
+ output_surface_size(), ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
+ ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f), RectF(50, 8, 100, 4),
+ Shadow(Vector2dF(0.0f, 0.0f), 3.0f, ColorRGBA(0, 0, 0, 1)), false, 5.0f),
+ ScaleMatrix(1.0f, 10.0f)));
+}
+
TEST_F(PixelTest, TransparentBoxShadowBlurOnGreenBackgroundCentered) {
TestTree(CreateShadowRectWithBackground(
output_surface_size(), ColorRGBA(0.3f, 0.8f, 0.3f, 1.0f),
@@ -2916,6 +2975,15 @@
RoundedCorners(25, 25)));
}
+TEST_F(PixelTest, ScaledBoxShadowEllipseWithOutset5pxSpreadAndRoundedCorners) {
+ TestTree(new MatrixTransformNode(CreateShadowRectWithBackground(
+ output_surface_size(), ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
+ ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f), RectF(6, 25, 2, 100),
+ Shadow(Vector2dF(0.0f, 0.0f), 0.0f, ColorRGBA(0, 0, 0, 1)), false, 5.0f,
+ RoundedCorners(1, 50)),
+ ScaleMatrix(15.0f, 1.0f)));
+}
+
TEST_F(PixelTest, BoxShadowCircleWithInset25pxSpread1pxBlurAndRoundedCorners) {
TestTree(CreateShadowRectWithBackground(
output_surface_size(), ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
@@ -3034,6 +3102,16 @@
}
TEST_F(PixelTest,
+ ScaledBoxShadowEllipseWithOutset25pxSpread3pxBlurAndRoundedCorners) {
+ TestTree(new MatrixTransformNode(CreateShadowRectWithBackground(
+ output_surface_size(), ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
+ ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f), RectF(20, 5, 140, 10),
+ Shadow(Vector2dF(8.0f, 1.0f), 3.0f, ColorRGBA(0, 0, 0, 1)), false, 4.0f,
+ RoundedCorners(70, 5)),
+ ScaleMatrix(1.0f, 10.0f)));
+}
+
+TEST_F(PixelTest,
BoxShadowEllipseWithInset25pxSpread50pxBlurAndRoundedCorners) {
TestTree(CreateShadowRectWithBackground(
output_surface_size(), ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f),
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index 576969c..ce39cca 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -60,7 +60,7 @@
// on multiple threads simultaneously later.
SkSafeUnref(SkFontMgr::RefDefault());
-#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
decode_target_graphics_context_provider_.egl_display =
cobalt_context_->system_egl()->GetDisplay();
decode_target_graphics_context_provider_.egl_context =
@@ -68,7 +68,7 @@
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)
+#endif // SB_HAS(GRAPHICS)
}
HardwareResourceProvider::~HardwareResourceProvider() {
@@ -146,7 +146,7 @@
self_message_loop_));
}
-#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
namespace {
#if SB_API_VERSION < SB_DECODE_TARGET_PLANES_FOR_FORMAT
@@ -378,7 +378,7 @@
}
}
-#endif // SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#endif // SB_HAS(GRAPHICS)
scoped_ptr<RawImageMemory> HardwareResourceProvider::AllocateRawImageMemory(
size_t size_in_bytes, size_t alignment) {
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
index 33ed4d2..5ad0c4a 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
@@ -56,7 +56,6 @@
scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE;
#if SB_HAS(GRAPHICS)
-#if SB_API_VERSION >= 4
scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
SbDecodeTarget decode_target) OVERRIDE;
@@ -71,24 +70,6 @@
// Whether SbDecodeTargetIsSupported or not.
bool SupportsSbDecodeTarget() OVERRIDE { return true; }
-#elif SB_API_VERSION >= 3
-
- scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
- SbDecodeTarget decode_target) OVERRIDE {
- NOTREACHED()
- << "CreateImageFromSbDecodeTarget is not supported on EGL yet.";
- SbDecodeTargetDestroy(decode_target);
- return NULL;
- }
-
- // Return the associated SbDecodeTargetProvider with the ResourceProvider,
- // if it exists. Returns NULL if SbDecodeTarget is not supported.
- SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; }
-
- // Whether SbDecodeTargetIsSupported or not.
- bool SupportsSbDecodeTarget() OVERRIDE { return false; }
-
-#endif // SB_API_VERSION >= 4
#endif // SB_HAS(GRAPHICS)
scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory(
@@ -147,7 +128,7 @@
TextShaper text_shaper_;
int max_texture_size_;
-#if SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#if SB_HAS(GRAPHICS)
static void GraphicsContextRunner(
SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
SbDecodeTargetGlesContextRunnerTarget target_function,
@@ -155,7 +136,7 @@
SbDecodeTargetGraphicsContextProvider
decode_target_graphics_context_provider_;
-#endif // SB_API_VERSION >= 4 && SB_HAS(GRAPHICS)
+#endif // SB_HAS(GRAPHICS)
// We keep a handle to the message loop that this resource provider was
// created on. This message loop is used whenever we need to issue graphics
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 0f2159b..918a60e 100644
--- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -949,6 +949,11 @@
SkiaBrushVisitor brush_visitor(&paint, *draw_state);
brush->Accept(&brush_visitor);
+ if (!draw_state->render_target->getTotalMatrix().preservesAxisAlignment()) {
+ // Enable anti-aliasing if we're rendering a rotated or skewed box.
+ paint.setAntiAlias(true);
+ }
+
draw_state->render_target->drawRect(
SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()), paint);
}
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
index 67e8e2d..492d3ab 100644
--- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
+++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
@@ -47,7 +47,6 @@
scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE;
#if SB_HAS(GRAPHICS)
-#if SB_API_VERSION >= 4
scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget(
SbDecodeTarget decode_target) OVERRIDE {
NOTREACHED();
@@ -61,18 +60,6 @@
}
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 >= 4
#endif // SB_HAS(GRAPHICS)
scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory(
diff --git a/src/cobalt/renderer/rasterizer/testdata/RotatedThenScaledRectWithDifferentRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/RotatedThenScaledRectWithDifferentRoundedCorners-expected.png
new file mode 100644
index 0000000..b1725a0
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/RotatedThenScaledRectWithDifferentRoundedCorners-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset25pxSpread3pxBlurAndRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset25pxSpread3pxBlurAndRoundedCorners-expected.png
new file mode 100644
index 0000000..94a9b2f
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset25pxSpread3pxBlurAndRoundedCorners-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset5pxSpreadAndRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset5pxSpreadAndRoundedCorners-expected.png
new file mode 100644
index 0000000..26f22df
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowEllipseWithOutset5pxSpreadAndRoundedCorners-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowWithSpreadAndBlurCentered-expected.png b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowWithSpreadAndBlurCentered-expected.png
new file mode 100644
index 0000000..9c2b5b6
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ScaledBoxShadowWithSpreadAndBlurCentered-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRectWithDifferentRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRectWithDifferentRoundedCorners-expected.png
new file mode 100644
index 0000000..9b62917
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRectWithDifferentRoundedCorners-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRoundedCornersViewportOverImage-expected.png b/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRoundedCornersViewportOverImage-expected.png
new file mode 100644
index 0000000..55c2d7d
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/ScaledThenRotatedRoundedCornersViewportOverImage-expected.png
Binary files differ
diff --git a/src/cobalt/script/error_report.h b/src/cobalt/script/error_report.h
new file mode 100644
index 0000000..82c1b12
--- /dev/null
+++ b/src/cobalt/script/error_report.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_SCRIPT_ERROR_REPORT_H_
+#define COBALT_SCRIPT_ERROR_REPORT_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/script/value_handle.h"
+
+namespace cobalt {
+namespace script {
+
+struct ErrorReport {
+ public:
+ ErrorReport() : line_number(0), column_number(0), is_muted(false) {}
+
+ std::string message;
+ std::string filename;
+ uint32 line_number;
+ uint32 column_number;
+ scoped_ptr<script::ValueHandleHolder> error;
+ bool is_muted;
+};
+
+} // namespace script
+} // namespace cobalt
+
+#endif // COBALT_SCRIPT_ERROR_REPORT_H_
diff --git a/src/cobalt/script/fake_global_environment.h b/src/cobalt/script/fake_global_environment.h
index ca8c3fa..391c5e7 100644
--- a/src/cobalt/script/fake_global_environment.h
+++ b/src/cobalt/script/fake_global_environment.h
@@ -49,6 +49,8 @@
void EnableEval() OVERRIDE {}
void DisableJit() OVERRIDE {}
void SetReportEvalCallback(const base::Closure& /*report_eval*/) OVERRIDE {}
+ void SetReportErrorCallback(
+ const ReportErrorCallback& /*report_eval*/) OVERRIDE {}
void Bind(const std::string& /*identifier*/,
const scoped_refptr<Wrappable>& /*impl*/) OVERRIDE {}
ScriptValueFactory* script_value_factory() { return NULL; }
diff --git a/src/cobalt/script/global_environment.h b/src/cobalt/script/global_environment.h
index 794cc22..039f05d 100644
--- a/src/cobalt/script/global_environment.h
+++ b/src/cobalt/script/global_environment.h
@@ -19,6 +19,7 @@
#include "base/memory/ref_counted.h"
#include "base/optional.h"
+#include "cobalt/script/error_report.h"
#include "cobalt/script/opaque_handle.h"
#include "cobalt/script/script_value.h"
#include "cobalt/script/script_value_factory.h"
@@ -35,6 +36,9 @@
// Manages a handle to a JavaScript engine's global object.
class GlobalEnvironment : public base::RefCounted<GlobalEnvironment> {
public:
+ typedef base::Callback<bool(const ErrorReport& error_report)>
+ ReportErrorCallback;
+
// Create a new global object with bindings as defined for the definition of
// the GlobalInterface type. The IDL for this interface must have the
// PrimaryGlobal or Global extended attribute.
@@ -95,6 +99,10 @@
// constructor is used.
virtual void SetReportEvalCallback(const base::Closure& report_eval) = 0;
+ // Set a callback that will be fired whenever a JavaScript error occurs.
+ virtual void SetReportErrorCallback(
+ const ReportErrorCallback& report_error) = 0;
+
// Dynamically bind a cpp object to the javascript global object with the
// supplied identifier.
// This method is useful for testing and debug purposes, as well as for
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index c850506..2673778 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -339,6 +339,12 @@
report_eval_ = report_eval;
}
+void MozjsGlobalEnvironment::SetReportErrorCallback(
+ const ReportErrorCallback& report_error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ report_error_callback_ = report_error_callback;
+}
+
void MozjsGlobalEnvironment::Bind(const std::string& identifier,
const scoped_refptr<Wrappable>& impl) {
TRACK_MEMORY_SCOPE("Javascript");
@@ -456,19 +462,54 @@
void MozjsGlobalEnvironment::ReportError(const char* message,
JSErrorReport* report) {
- std::string error_message;
+ JS::RootedValue exception(context_);
+ ::JS_GetPendingException(context_, &exception);
+
+ // Note: we must do this before running any more code on context.
+ ::JS_ClearPendingException(context_);
+
+ // Populate the error report.
+ ErrorReport error_report;
if (report->errorNumber == JSMSG_CSP_BLOCKED_EVAL) {
- error_message = eval_disabled_message_.value_or(message);
+ error_report.message = eval_disabled_message_.value_or(message);
} else {
- error_message = message;
+ error_report.message = message;
+ }
+ error_report.filename =
+ report->filename ? report->filename : "<internal exception>";
+ error_report.line_number = report->lineno;
+ error_report.column_number = report->column;
+ // Let error object be the object that represents the error: in the case of
+ // an uncaught exception, that would be the object that was thrown; in the
+ // case of a JavaScript error that would be an Error object. If there is no
+ // corresponding object, then the null value must be used instead.
+ // https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors
+ if (exception.isObject()) {
+ error_report.error.reset(
+ new MozjsValueHandleHolder(exception, context_, wrapper_factory()));
+ }
+ error_report.is_muted = report->isMuted;
+
+ // If this isn't simply a warning, and the error wasn't caused by JS running
+ // out of memory (in which case the callback will fail as well), then run
+ // the callback. In the case that it returns that the script was handled,
+ // simply return; the error should only be reported to the user if it wasn't
+ // handled.
+ if (!JSREPORT_IS_WARNING(report->flags) &&
+ report->errorNumber != JSMSG_OUT_OF_MEMORY &&
+ !report_error_callback_.is_null() &&
+ report_error_callback_.Run(error_report)) {
+ return;
}
+ // If the error is not handled, then the error may be reported to the user.
+ // https://www.w3.org/TR/html5/webappapis.html#runtime-script-errors-in-documents
if (last_error_message_) {
- *last_error_message_ = error_message;
+ *last_error_message_ = error_report.message;
} else {
- const char* filename = report->filename ? report->filename : "(none)";
- LOG(ERROR) << "JS Error: " << filename << ":" << report->lineno << ":"
- << report->column << ": " << error_message;
+ LOG(ERROR) << "JS Error: " << error_report.filename << ":"
+ << error_report.line_number << ":" << error_report.column_number
+ << ": " << error_report.message;
}
}
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
index e34eabe..a7bccae 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -75,6 +75,9 @@
void SetReportEvalCallback(const base::Closure& report_eval) OVERRIDE;
+ void SetReportErrorCallback(
+ const ReportErrorCallback& report_error_callback) OVERRIDE;
+
void Bind(const std::string& identifier,
const scoped_refptr<Wrappable>& impl) OVERRIDE;
@@ -182,6 +185,7 @@
bool eval_enabled_;
base::optional<std::string> eval_disabled_message_;
base::Closure report_eval_;
+ ReportErrorCallback report_error_callback_;
friend class GlobalObjectProxy;
};
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.cc b/src/cobalt/script/mozjs/mozjs_global_environment.cc
index 71b593d..52af1fe 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.cc
@@ -344,6 +344,12 @@
report_eval_ = report_eval;
}
+void MozjsGlobalEnvironment::SetReportErrorCallback(
+ const ReportErrorCallback& report_error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ report_error_callback_ = report_error_callback;
+}
+
void MozjsGlobalEnvironment::Bind(const std::string& identifier,
const scoped_refptr<Wrappable>& impl) {
TRACK_MEMORY_SCOPE("Javascript");
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.h b/src/cobalt/script/mozjs/mozjs_global_environment.h
index 4914e18..e221a5d 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.h
@@ -74,6 +74,9 @@
void SetReportEvalCallback(const base::Closure& report_eval) OVERRIDE;
+ void SetReportErrorCallback(
+ const ReportErrorCallback& report_error_callback) OVERRIDE;
+
void Bind(const std::string& identifier,
const scoped_refptr<Wrappable>& impl) OVERRIDE;
@@ -181,6 +184,7 @@
bool eval_enabled_;
base::optional<std::string> eval_disabled_message_;
base::Closure report_eval_;
+ ReportErrorCallback report_error_callback_;
friend class GlobalObjectProxy;
};
diff --git a/src/cobalt/script/script.gyp b/src/cobalt/script/script.gyp
index a7e9192..34a7c4e 100644
--- a/src/cobalt/script/script.gyp
+++ b/src/cobalt/script/script.gyp
@@ -21,6 +21,7 @@
'type': 'static_library',
'sources': [
'call_frame.h',
+ 'error_report.h',
'exception_message.cc',
'exception_message.h',
'execution_state.cc',
diff --git a/src/cobalt/speech/google_speech_service.cc b/src/cobalt/speech/google_speech_service.cc
index 23dedfd..6fb7a34 100644
--- a/src/cobalt/speech/google_speech_service.cc
+++ b/src/cobalt/speech/google_speech_service.cc
@@ -290,14 +290,12 @@
const char* speech_api_key = "";
#if defined(OS_STARBOARD)
-#if SB_API_VERSION >= 2
const int kSpeechApiKeyLength = 100;
char buffer[kSpeechApiKeyLength] = {0};
bool result = SbSystemGetProperty(kSbSystemPropertySpeechApiKey, buffer,
SB_ARRAY_SIZE_INT(buffer));
SB_DCHECK(result);
speech_api_key = result ? buffer : "";
-#endif // SB_API_VERSION >= 2
#endif // defined(OS_STARBOARD)
up_url = AppendQueryParameter(up_url, "key", speech_api_key);
diff --git a/src/cobalt/speech/speech_configuration.h b/src/cobalt/speech/speech_configuration.h
index 8b9e0ba..c23ee5e 100644
--- a/src/cobalt/speech/speech_configuration.h
+++ b/src/cobalt/speech/speech_configuration.h
@@ -16,15 +16,14 @@
#define COBALT_SPEECH_SPEECH_CONFIGURATION_H_
#include "build/build_config.h"
-
-#if defined(OS_STARBOARD)
#include "starboard/configuration.h"
-#if SB_HAS(MICROPHONE) && SB_API_VERSION >= 2
+
+#if SB_HAS(MICROPHONE)
#define SB_USE_SB_MICROPHONE 1
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
+#endif // SB_HAS(MICROPHONE)
+
#if SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
#define SB_USE_SB_SPEECH_RECOGNIZER 1
#endif // SB_HAS(SPEECH_RECOGNIZER) && SB_API_VERSION >= 5
-#endif // defined(OS_STARBOARD)
#endif // COBALT_SPEECH_SPEECH_CONFIGURATION_H_
diff --git a/src/cobalt/speech/speech_synthesis.cc b/src/cobalt/speech/speech_synthesis.cc
index 5e3e1bb..fb34eaa 100644
--- a/src/cobalt/speech/speech_synthesis.cc
+++ b/src/cobalt/speech/speech_synthesis.cc
@@ -100,12 +100,6 @@
return;
}
-#if SB_API_VERSION < 4
- // DEPRECATED IN API VERSION 4
- std::string language =
- utterance->lang().empty() ? navigator_->language() : utterance->lang();
- SbSpeechSynthesisSetLanguage(language.c_str());
-#endif
SB_DLOG(INFO) << "Speaking: \"" << utterance->text() << "\" "
<< utterance->lang();
SbSpeechSynthesisSpeak(utterance->text().c_str());
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index e9daa78..88582da 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 "12"
+#define COBALT_VERSION "13"
#endif // COBALT_VERSION_H_
diff --git a/src/cobalt/webdriver/server.cc b/src/cobalt/webdriver/server.cc
index 321c8c4..146b674 100644
--- a/src/cobalt/webdriver/server.cc
+++ b/src/cobalt/webdriver/server.cc
@@ -21,6 +21,8 @@
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/string_util.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
#include "net/base/tcp_listen_socket.h"
#include "net/server/http_server_request_info.h"
@@ -177,11 +179,21 @@
WebDriverServer::WebDriverServer(int port, const std::string& listen_ip,
const HandleRequestCallback& callback)
- : handle_request_callback_(callback) {
+ : handle_request_callback_(callback),
+ server_address_("WebDriver.Server",
+ "Address to communicate with WebDriver.") {
// Create http server
factory_.reset(new net::TCPListenSocketFactory(listen_ip, port));
server_ = new net::HttpServer(*factory_, this);
- LOG(INFO) << "Starting WebDriver server on port " << port;
+ GURL address;
+ int result = GetLocalAddress(&address);
+ if (result == net::OK) {
+ LOG(INFO) << "Starting WebDriver server on port " << port;
+ server_address_ = address.spec();
+ } else {
+ LOG(WARNING) << "Could not start WebDriver server";
+ server_address_ = "<NOT RUNNING>";
+ }
}
void WebDriverServer::OnHttpRequest(int connection_id,
@@ -217,5 +229,14 @@
parameters.Pass(), response_handler.Pass());
}
+int WebDriverServer::GetLocalAddress(GURL* out) const {
+ net::IPEndPoint ip_addr;
+ int result = server_->GetLocalAddress(&ip_addr);
+ if (result == net::OK) {
+ *out = GURL("http://" + ip_addr.ToString());
+ }
+ return result;
+}
+
} // namespace webdriver
} // namespace cobalt
diff --git a/src/cobalt/webdriver/server.h b/src/cobalt/webdriver/server.h
index 71a66fa..03ba2b4 100644
--- a/src/cobalt/webdriver/server.h
+++ b/src/cobalt/webdriver/server.h
@@ -22,7 +22,9 @@
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread.h"
#include "base/values.h"
+#include "cobalt/base/c_val.h"
#include "cobalt/webdriver/protocol/server_status.h"
+#include "googleurl/src/gurl.h"
#include "net/base/stream_listen_socket.h"
#include "net/server/http_server.h"
@@ -92,10 +94,13 @@
void OnClose(int) OVERRIDE {} // NOLINT(readability/casting)
private:
+ int GetLocalAddress(GURL* out) const;
+
base::ThreadChecker thread_checker_;
HandleRequestCallback handle_request_callback_;
scoped_ptr<net::StreamListenSocketFactory> factory_;
scoped_refptr<net::HttpServer> server_;
+ base::CVal<std::string> server_address_;
};
} // namespace webdriver
diff --git a/src/media/audio/android/audio_manager_android.cc b/src/media/audio/android/audio_manager_android.cc
deleted file mode 100644
index 0e4d6ba..0000000
--- a/src/media/audio/android/audio_manager_android.cc
+++ /dev/null
@@ -1,62 +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 "media/audio/android/audio_manager_android.h"
-
-#include "base/logging.h"
-#include "media/audio/android/opensles_input.h"
-#include "media/audio/android/opensles_output.h"
-#include "media/audio/audio_manager.h"
-#include "media/audio/fake_audio_input_stream.h"
-
-namespace media {
-
-// Maximum number of output streams that can be open simultaneously.
-static const int kMaxOutputStreams = 10;
-
-AudioManager* CreateAudioManager() {
- return new AudioManagerAndroid();
-}
-
-AudioManagerAndroid::AudioManagerAndroid() {
- SetMaxOutputStreamsAllowed(kMaxOutputStreams);
-}
-
-AudioManagerAndroid::~AudioManagerAndroid() {
- Shutdown();
-}
-
-bool AudioManagerAndroid::HasAudioOutputDevices() {
- return true;
-}
-
-bool AudioManagerAndroid::HasAudioInputDevices() {
- return false;
-}
-
-AudioOutputStream* AudioManagerAndroid::MakeLinearOutputStream(
- const AudioParameters& params) {
- DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
- return new OpenSLESOutputStream(this, params);
-}
-
-AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream(
- const AudioParameters& params) {
- DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
- return new OpenSLESOutputStream(this, params);
-}
-
-AudioInputStream* AudioManagerAndroid::MakeLinearInputStream(
- const AudioParameters& params, const std::string& device_id) {
- DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
- return new OpenSLESInputStream(this, params);
-}
-
-AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream(
- const AudioParameters& params, const std::string& device_id) {
- DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
- return new OpenSLESInputStream(this, params);
-}
-
-} // namespace media
diff --git a/src/media/audio/android/audio_manager_android.h b/src/media/audio/android/audio_manager_android.h
deleted file mode 100644
index 8f14808..0000000
--- a/src/media/audio/android/audio_manager_android.h
+++ /dev/null
@@ -1,40 +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 MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
-#define MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
-
-#include "media/audio/audio_manager_base.h"
-
-namespace media {
-
-// Android implemention of AudioManager.
-class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
- public:
- AudioManagerAndroid();
-
- // Implementation of AudioManager.
- virtual bool HasAudioOutputDevices() OVERRIDE;
- virtual bool HasAudioInputDevices() OVERRIDE;
-
- // Implementation of AudioManagerBase.
- virtual AudioOutputStream* MakeLinearOutputStream(
- const AudioParameters& params) OVERRIDE;
- virtual AudioOutputStream* MakeLowLatencyOutputStream(
- const AudioParameters& params) OVERRIDE;
- virtual AudioInputStream* MakeLinearInputStream(
- const AudioParameters& params, const std::string& device_id) OVERRIDE;
- virtual AudioInputStream* MakeLowLatencyInputStream(
- const AudioParameters& params, const std::string& device_id) OVERRIDE;
-
- protected:
- virtual ~AudioManagerAndroid();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AudioManagerAndroid);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
diff --git a/src/media/audio/android/opensles_input.cc b/src/media/audio/android/opensles_input.cc
deleted file mode 100644
index 0df5bc1..0000000
--- a/src/media/audio/android/opensles_input.cc
+++ /dev/null
@@ -1,301 +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 "media/audio/android/opensles_input.h"
-
-#include "base/logging.h"
-#include "media/audio/android/audio_manager_android.h"
-
-namespace media {
-
-OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager,
- const AudioParameters& params)
- : audio_manager_(audio_manager),
- callback_(NULL),
- recorder_(NULL),
- simple_buffer_queue_(NULL),
- active_queue_(0),
- buffer_size_bytes_(0),
- started_(false) {
- format_.formatType = SL_DATAFORMAT_PCM;
- format_.numChannels = static_cast<SLuint32>(params.channels());
- // Provides sampling rate in milliHertz to OpenSLES.
- format_.samplesPerSec = static_cast<SLuint32>(params.sample_rate() * 1000);
- format_.bitsPerSample = params.bits_per_sample();
- format_.containerSize = params.bits_per_sample();
- format_.endianness = SL_BYTEORDER_LITTLEENDIAN;
- if (format_.numChannels == 1)
- format_.channelMask = SL_SPEAKER_FRONT_CENTER;
- else if (format_.numChannels == 2)
- format_.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
- else
- NOTREACHED() << "Unsupported number of channels: " << format_.numChannels;
-
- buffer_size_bytes_ = params.GetBytesPerBuffer();
-
- memset(&audio_data_, 0, sizeof(audio_data_));
-}
-
-OpenSLESInputStream::~OpenSLESInputStream() {
- DCHECK(!recorder_object_.Get());
- DCHECK(!engine_object_.Get());
- DCHECK(!recorder_);
- DCHECK(!simple_buffer_queue_);
- DCHECK(!audio_data_[0]);
-}
-
-bool OpenSLESInputStream::Open() {
- if (engine_object_.Get())
- return false;
-
- if (!CreateRecorder())
- return false;
-
- SetupAudioBuffer();
-
- return true;
-}
-
-void OpenSLESInputStream::Start(AudioInputCallback* callback) {
- DCHECK(callback);
- DCHECK(recorder_);
- DCHECK(simple_buffer_queue_);
- if (started_)
- return;
-
- // Enable the flags before streaming.
- callback_ = callback;
- active_queue_ = 0;
- started_ = true;
-
- SLresult err = SL_RESULT_UNKNOWN_ERROR;
- // Enqueues |kNumOfQueuesInBuffer| zero buffers to get the ball rolling.
- for (int i = 0; i < kNumOfQueuesInBuffer - 1; ++i) {
- err = (*simple_buffer_queue_)->Enqueue(
- simple_buffer_queue_,
- audio_data_[i],
- buffer_size_bytes_);
- if (SL_RESULT_SUCCESS != err) {
- HandleError(err);
- return;
- }
- }
-
- // Start the recording by setting the state to |SL_RECORDSTATE_RECORDING|.
- err = (*recorder_)->SetRecordState(recorder_, SL_RECORDSTATE_RECORDING);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- HandleError(err);
-}
-
-void OpenSLESInputStream::Stop() {
- if (!started_)
- return;
-
- // Stop recording by setting the record state to |SL_RECORDSTATE_STOPPED|.
- SLresult err = (*recorder_)->SetRecordState(recorder_,
- SL_RECORDSTATE_STOPPED);
- if (SL_RESULT_SUCCESS != err) {
- DLOG(WARNING) << "SetRecordState() failed to set the state to stop";
- }
-
- // Clear the buffer queue to get rid of old data when resuming recording.
- err = (*simple_buffer_queue_)->Clear(simple_buffer_queue_);
- if (SL_RESULT_SUCCESS != err) {
- DLOG(WARNING) << "Clear() failed to clear the buffer queue";
- }
-
- started_ = false;
-}
-
-void OpenSLESInputStream::Close() {
- // Stop the stream if it is still recording.
- Stop();
-
- // Explicitly free the player objects and invalidate their associated
- // interfaces. They have to be done in the correct order.
- recorder_object_.Reset();
- engine_object_.Reset();
- simple_buffer_queue_ = NULL;
- recorder_ = NULL;
-
- ReleaseAudioBuffer();
-
- audio_manager_->ReleaseInputStream(this);
-}
-
-double OpenSLESInputStream::GetMaxVolume() {
- NOTIMPLEMENTED();
- return 0.0;
-}
-
-void OpenSLESInputStream::SetVolume(double volume) {
- NOTIMPLEMENTED();
-}
-
-double OpenSLESInputStream::GetVolume() {
- NOTIMPLEMENTED();
- return 0.0;
-}
-
-void OpenSLESInputStream::SetAutomaticGainControl(bool enabled) {
- NOTIMPLEMENTED();
-}
-
-bool OpenSLESInputStream::GetAutomaticGainControl() {
- NOTIMPLEMENTED();
- return false;
-}
-
-bool OpenSLESInputStream::CreateRecorder() {
- // Initializes the engine object with specific option. After working with the
- // object, we need to free the object and its resources.
- SLEngineOption option[] = {
- { SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE) }
- };
- SLresult err = slCreateEngine(engine_object_.Receive(),
- 1,
- option,
- 0,
- NULL,
- NULL);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Realize the SL engine object in synchronous mode.
- err = engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Get the SL engine interface which is implicit.
- SLEngineItf engine;
- err = engine_object_->GetInterface(
- engine_object_.Get(), SL_IID_ENGINE, &engine);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Audio source configuration.
- SLDataLocator_IODevice mic_locator = {
- SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
- SL_DEFAULTDEVICEID_AUDIOINPUT, NULL
- };
- SLDataSource audio_source = { &mic_locator, NULL };
-
- // Audio sink configuration.
- SLDataLocator_AndroidSimpleBufferQueue buffer_queue = {
- SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // Locator type.
- static_cast<SLuint32>(kNumOfQueuesInBuffer) // Number of buffers.
- };
- SLDataSink audio_sink = { &buffer_queue, &format_ };
-
- // Create an audio recorder.
- const SLuint32 number_of_interfaces = 1;
- const SLInterfaceID interface_id[number_of_interfaces] = {
- SL_IID_ANDROIDSIMPLEBUFFERQUEUE
- };
- const SLboolean interface_required[number_of_interfaces] = {
- SL_BOOLEAN_TRUE
- };
- err = (*engine)->CreateAudioRecorder(engine,
- recorder_object_.Receive(),
- &audio_source,
- &audio_sink,
- number_of_interfaces,
- interface_id,
- interface_required);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err) {
- DLOG(ERROR) << "CreateAudioRecorder failed with error code " << err;
- return false;
- }
-
- // Realize the recorder object in synchronous mode.
- err = recorder_object_->Realize(recorder_object_.Get(), SL_BOOLEAN_FALSE);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err) {
- DLOG(ERROR) << "Recprder Realize() failed with error code " << err;
- return false;
- }
-
- // Get an implicit recorder interface.
- err = recorder_object_->GetInterface(recorder_object_.Get(),
- SL_IID_RECORD,
- &recorder_);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Get the simple buffer queue interface.
- err = recorder_object_->GetInterface(recorder_object_.Get(),
- SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
- &simple_buffer_queue_);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Register the input callback for the simple buffer queue.
- // This callback will be called when receiving new data from the device.
- err = (*simple_buffer_queue_)->RegisterCallback(simple_buffer_queue_,
- SimpleBufferQueueCallback,
- this);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
-
- return (SL_RESULT_SUCCESS == err);
-}
-
-void OpenSLESInputStream::SimpleBufferQueueCallback(
- SLAndroidSimpleBufferQueueItf buffer_queue, void* instance) {
- OpenSLESInputStream* stream =
- reinterpret_cast<OpenSLESInputStream*>(instance);
- stream->ReadBufferQueue();
-}
-
-void OpenSLESInputStream::ReadBufferQueue() {
- if (!started_)
- return;
-
- // Get the enqueued buffer from the soundcard.
- SLresult err = (*simple_buffer_queue_)->Enqueue(
- simple_buffer_queue_,
- audio_data_[active_queue_],
- buffer_size_bytes_);
- if (SL_RESULT_SUCCESS != err)
- HandleError(err);
-
- // TODO(xians): Get an accurate delay estimation.
- callback_->OnData(this,
- audio_data_[active_queue_],
- buffer_size_bytes_,
- buffer_size_bytes_,
- 0.0);
-
- active_queue_ = (active_queue_ + 1) % kNumOfQueuesInBuffer;
-}
-
-void OpenSLESInputStream::SetupAudioBuffer() {
- DCHECK(!audio_data_[0]);
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
- audio_data_[i] = new uint8[buffer_size_bytes_];
- }
-}
-
-void OpenSLESInputStream::ReleaseAudioBuffer() {
- if (audio_data_[0]) {
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
- delete [] audio_data_[i];
- audio_data_[i] = NULL;
- }
- }
-}
-
-void OpenSLESInputStream::HandleError(SLresult error) {
- DLOG(FATAL) << "OpenSLES error " << error;
- if (callback_)
- callback_->OnError(this, error);
-}
-
-} // namespace media
diff --git a/src/media/audio/android/opensles_input.h b/src/media/audio/android/opensles_input.h
deleted file mode 100644
index 0d18f9b..0000000
--- a/src/media/audio/android/opensles_input.h
+++ /dev/null
@@ -1,86 +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 MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
-#define MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
-
-#include "base/compiler_specific.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_parameters.h"
-#include "media/audio/android/opensles_util.h"
-#include <SLES/OpenSLES_Android.h>
-
-namespace media {
-
-class AudioManagerAndroid;
-
-// Implements PCM audio input support for Android using the OpenSLES API.
-class OpenSLESInputStream : public AudioInputStream {
- public:
- static const int kNumOfQueuesInBuffer = 2;
-
- OpenSLESInputStream(AudioManagerAndroid* manager,
- const AudioParameters& params);
-
- virtual ~OpenSLESInputStream();
-
- // Implementation of AudioInputStream.
- virtual bool Open() OVERRIDE;
- virtual void Start(AudioInputCallback* callback) OVERRIDE;
- virtual void Stop() OVERRIDE;
- virtual void Close() OVERRIDE;
- virtual double GetMaxVolume() OVERRIDE;
- virtual void SetVolume(double volume) OVERRIDE;
- virtual double GetVolume() OVERRIDE;
- virtual void SetAutomaticGainControl(bool enabled) OVERRIDE;
- virtual bool GetAutomaticGainControl() OVERRIDE;
-
- private:
- bool CreateRecorder();
-
- static void SimpleBufferQueueCallback(
- SLAndroidSimpleBufferQueueItf buffer_queue, void* instance);
-
- void ReadBufferQueue();
-
- // Called in Open();
- void SetupAudioBuffer();
-
- // Called in Close();
- void ReleaseAudioBuffer();
-
- // If OpenSLES reports an error this function handles it and passes it to
- // the attached AudioInputCallback::OnError().
- void HandleError(SLresult error);
-
- AudioManagerAndroid* audio_manager_;
-
- AudioInputCallback* callback_;
-
- // Shared engine interfaces for the app.
- media::ScopedSLObjectItf recorder_object_;
- media::ScopedSLObjectItf engine_object_;
-
- SLRecordItf recorder_;
-
- // Buffer queue recorder interface.
- SLAndroidSimpleBufferQueueItf simple_buffer_queue_;
-
- SLDataFormat_PCM format_;
-
- // Audio buffers that are allocated in the constructor based on
- // info from audio parameters.
- uint8* audio_data_[kNumOfQueuesInBuffer];
-
- int active_queue_;
- int buffer_size_bytes_;
-
- bool started_;
-
- DISALLOW_COPY_AND_ASSIGN(OpenSLESInputStream);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
diff --git a/src/media/audio/android/opensles_output.cc b/src/media/audio/android/opensles_output.cc
deleted file mode 100644
index 26ae25b..0000000
--- a/src/media/audio/android/opensles_output.cc
+++ /dev/null
@@ -1,311 +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 "media/audio/android/opensles_output.h"
-
-#include "base/logging.h"
-#include "media/audio/audio_util.h"
-#include "media/audio/android/audio_manager_android.h"
-
-namespace media {
-
-OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
- const AudioParameters& params)
- : audio_manager_(manager),
- callback_(NULL),
- player_(NULL),
- simple_buffer_queue_(NULL),
- active_queue_(0),
- buffer_size_bytes_(0),
- started_(false),
- volume_(1.0) {
- format_.formatType = SL_DATAFORMAT_PCM;
- format_.numChannels = static_cast<SLuint32>(params.channels());
- // Provides sampling rate in milliHertz to OpenSLES.
- format_.samplesPerSec = static_cast<SLuint32>(params.sample_rate() * 1000);
- format_.bitsPerSample = params.bits_per_sample();
- format_.containerSize = params.bits_per_sample();
- format_.endianness = SL_BYTEORDER_LITTLEENDIAN;
- if (format_.numChannels == 1)
- format_.channelMask = SL_SPEAKER_FRONT_CENTER;
- else if (format_.numChannels == 2)
- format_.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
- else
- NOTREACHED() << "Unsupported number of channels: " << format_.numChannels;
-
- buffer_size_bytes_ = params.GetBytesPerBuffer();
- audio_bus_ = AudioBus::Create(params);
-
- memset(&audio_data_, 0, sizeof(audio_data_));
-}
-
-OpenSLESOutputStream::~OpenSLESOutputStream() {
- DCHECK(!engine_object_.Get());
- DCHECK(!player_object_.Get());
- DCHECK(!output_mixer_.Get());
- DCHECK(!player_);
- DCHECK(!simple_buffer_queue_);
- DCHECK(!audio_data_[0]);
-}
-
-bool OpenSLESOutputStream::Open() {
- if (engine_object_.Get())
- return false;
-
- if (!CreatePlayer())
- return false;
-
- SetupAudioBuffer();
-
- return true;
-}
-
-void OpenSLESOutputStream::Start(AudioSourceCallback* callback) {
- DCHECK(callback);
- DCHECK(player_);
- DCHECK(simple_buffer_queue_);
- if (started_)
- return;
-
- // Enable the flags before streaming.
- callback_ = callback;
- active_queue_ = 0;
- started_ = true;
-
- // Avoid start-up glitches by filling up one buffer queue before starting
- // the stream.
- FillBufferQueue();
-
- // Start streaming data by setting the play state to |SL_PLAYSTATE_PLAYING|.
- SLresult err = (*player_)->SetPlayState(player_, SL_PLAYSTATE_PLAYING);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err) {
- DLOG(WARNING) << "SetPlayState() failed to start playing";
- }
-}
-
-void OpenSLESOutputStream::Stop() {
- if (!started_)
- return;
-
- started_ = false;
- // Stop playing by setting the play state to |SL_PLAYSTATE_STOPPED|.
- SLresult err = (*player_)->SetPlayState(player_, SL_PLAYSTATE_STOPPED);
- if (SL_RESULT_SUCCESS != err) {
- DLOG(WARNING) << "SetPlayState() failed to set the state to stop";
- }
-
- // Clear the buffer queue so that the old data won't be played when
- // resuming playing.
- err = (*simple_buffer_queue_)->Clear(simple_buffer_queue_);
- if (SL_RESULT_SUCCESS != err) {
- DLOG(WARNING) << "Clear() failed to clear the buffer queue";
- }
-}
-
-void OpenSLESOutputStream::Close() {
- // Stop the stream if it is still playing.
- Stop();
-
- // Explicitly free the player objects and invalidate their associated
- // interfaces. They have to be done in the correct order.
- output_mixer_.Reset();
- player_object_.Reset();
- engine_object_.Reset();
- simple_buffer_queue_ = NULL;
- player_ = NULL;
-
- ReleaseAudioBuffer();
-
- audio_manager_->ReleaseOutputStream(this);
-}
-
-void OpenSLESOutputStream::SetVolume(double volume) {
- float volume_float = static_cast<float>(volume);
- if (volume_float < 0.0f || volume_float > 1.0f) {
- return;
- }
- volume_ = volume_float;
-}
-
-void OpenSLESOutputStream::GetVolume(double* volume) {
- *volume = static_cast<double>(volume_);
-}
-
-bool OpenSLESOutputStream::CreatePlayer() {
- // Initializes the engine object with specific option. After working with the
- // object, we need to free the object and its resources.
- SLEngineOption option[] = {
- { SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE) }
- };
- SLresult err = slCreateEngine(engine_object_.Receive(), 1, option, 0,
- NULL, NULL);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Realize the SL engine object in synchronous mode.
- err = engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Get the SL engine interface which is implicit.
- SLEngineItf engine;
- err = engine_object_->GetInterface(engine_object_.Get(),
- SL_IID_ENGINE,
- &engine);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Create ouput mixer object to be used by the player.
- // TODO(xians): Do we need the environmental reverb auxiliary effect?
- err = (*engine)->CreateOutputMix(engine,
- output_mixer_.Receive(),
- 0,
- NULL,
- NULL);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Realizing the output mix object in synchronous mode.
- err = output_mixer_->Realize(output_mixer_.Get(), SL_BOOLEAN_FALSE);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Audio source configuration.
- SLDataLocator_AndroidSimpleBufferQueue simple_buffer_queue = {
- SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
- static_cast<SLuint32>(kNumOfQueuesInBuffer)
- };
- SLDataSource audio_source = { &simple_buffer_queue, &format_ };
-
- // Audio sink configuration.
- SLDataLocator_OutputMix locator_output_mix = {
- SL_DATALOCATOR_OUTPUTMIX, output_mixer_.Get()
- };
- SLDataSink audio_sink = { &locator_output_mix, NULL };
-
- // Create an audio player.
- const SLuint32 number_of_interfaces = 1;
- const SLInterfaceID interface_id[number_of_interfaces] = {
- SL_IID_BUFFERQUEUE
- };
- const SLboolean interface_required[number_of_interfaces] = {
- SL_BOOLEAN_TRUE
- };
- err = (*engine)->CreateAudioPlayer(engine,
- player_object_.Receive(),
- &audio_source,
- &audio_sink,
- number_of_interfaces,
- interface_id,
- interface_required);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err) {
- DLOG(ERROR) << "CreateAudioPlayer() failed with error code " << err;
- return false;
- }
-
- // Realize the player object in synchronous mode.
- err = player_object_->Realize(player_object_.Get(), SL_BOOLEAN_FALSE);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err) {
- DLOG(ERROR) << "Player Realize() failed with error code " << err;
- return false;
- }
-
- // Get an implicit player interface.
- err = player_object_->GetInterface(
- player_object_.Get(), SL_IID_PLAY, &player_);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Get the simple buffer queue interface.
- err = player_object_->GetInterface(player_object_.Get(),
- SL_IID_BUFFERQUEUE,
- &simple_buffer_queue_);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
- if (SL_RESULT_SUCCESS != err)
- return false;
-
- // Register the input callback for the simple buffer queue.
- // This callback will be called when the soundcard needs data.
- err = (*simple_buffer_queue_)->RegisterCallback(simple_buffer_queue_,
- SimpleBufferQueueCallback,
- this);
- DCHECK_EQ(SL_RESULT_SUCCESS, err);
-
- return (SL_RESULT_SUCCESS == err);
-}
-
-void OpenSLESOutputStream::SimpleBufferQueueCallback(
- SLAndroidSimpleBufferQueueItf buffer_queue, void* instance) {
- OpenSLESOutputStream* stream =
- reinterpret_cast<OpenSLESOutputStream*>(instance);
- stream->FillBufferQueue();
-}
-
-void OpenSLESOutputStream::FillBufferQueue() {
- if (!started_)
- return;
-
- // Read data from the registered client source.
- // TODO(xians): Get an accurate delay estimation.
- uint32 hardware_delay = buffer_size_bytes_;
- int frames_filled = callback_->OnMoreData(
- audio_bus_.get(), AudioBuffersState(0, hardware_delay));
- int num_filled_bytes =
- frames_filled * audio_bus_->channels() * format_.bitsPerSample / 8;
- DCHECK_LE(static_cast<size_t>(num_filled_bytes), buffer_size_bytes_);
- // Note: If this ever changes to output raw float the data must be clipped and
- // sanitized since it may come from an untrusted source such as NaCl.
- audio_bus_->ToInterleaved(
- frames_filled, format_.bitsPerSample / 8, audio_data_[active_queue_]);
-
- // Perform in-place, software-volume adjustments.
- media::AdjustVolume(audio_data_[active_queue_],
- num_filled_bytes,
- format_.numChannels,
- format_.bitsPerSample / 8,
- volume_);
-
- // Enqueue the buffer for playback.
- SLresult err = (*simple_buffer_queue_)->Enqueue(
- simple_buffer_queue_,
- audio_data_[active_queue_],
- num_filled_bytes);
- if (SL_RESULT_SUCCESS != err)
- HandleError(err);
-
- active_queue_ = (active_queue_ + 1) % kNumOfQueuesInBuffer;
-}
-
-void OpenSLESOutputStream::SetupAudioBuffer() {
- DCHECK(!audio_data_[0]);
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
- audio_data_[i] = new uint8[buffer_size_bytes_];
- }
-}
-
-void OpenSLESOutputStream::ReleaseAudioBuffer() {
- if (audio_data_[0]) {
- for (int i = 0; i < kNumOfQueuesInBuffer; ++i) {
- delete [] audio_data_[i];
- audio_data_[i] = NULL;
- }
- }
-}
-
-void OpenSLESOutputStream::HandleError(SLresult error) {
- DLOG(FATAL) << "OpenSLES error " << error;
- if (callback_)
- callback_->OnError(this, error);
-}
-
-} // namespace media
diff --git a/src/media/audio/android/opensles_output.h b/src/media/audio/android/opensles_output.h
deleted file mode 100644
index 9ecfb6c..0000000
--- a/src/media/audio/android/opensles_output.h
+++ /dev/null
@@ -1,91 +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 MEDIA_AUDIO_ANDROID_OPENSLES_OUTPUT_H_
-#define MEDIA_AUDIO_ANDROID_OPENSLES_OUTPUT_H_
-
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "media/audio/android/opensles_util.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_parameters.h"
-#include <SLES/OpenSLES_Android.h>
-
-namespace media {
-
-class AudioManagerAndroid;
-
-// Implements PCM audio output support for Android using the OpenSLES API.
-class OpenSLESOutputStream : public AudioOutputStream {
- public:
- static const int kNumOfQueuesInBuffer = 2;
-
- OpenSLESOutputStream(AudioManagerAndroid* manager,
- const AudioParameters& params);
-
- virtual ~OpenSLESOutputStream();
-
- // Implementation of AudioOutputStream.
- virtual bool Open() OVERRIDE;
- virtual void Close() OVERRIDE;
- virtual void Start(AudioSourceCallback* callback) OVERRIDE;
- virtual void Stop() OVERRIDE;
- virtual void SetVolume(double volume) OVERRIDE;
- virtual void GetVolume(double* volume) OVERRIDE;
-
- private:
- bool CreatePlayer();
-
- static void SimpleBufferQueueCallback(
- SLAndroidSimpleBufferQueueItf buffer_queue, void* instance);
-
- void FillBufferQueue();
-
- // Called in Open();
- void SetupAudioBuffer();
-
- // Called in Close();
- void ReleaseAudioBuffer();
-
- // If OpenSLES reports an error this function handles it and passes it to
- // the attached AudioOutputCallback::OnError().
- void HandleError(SLresult error);
-
- AudioManagerAndroid* audio_manager_;
-
- AudioSourceCallback* callback_;
-
- // Shared engine interfaces for the app.
- media::ScopedSLObjectItf engine_object_;
- media::ScopedSLObjectItf player_object_;
- media::ScopedSLObjectItf output_mixer_;
-
- SLPlayItf player_;
-
- // Buffer queue recorder interface.
- SLAndroidSimpleBufferQueueItf simple_buffer_queue_;
-
- SLDataFormat_PCM format_;
-
- // Audio buffer arrays that are allocated in the constructor.
- uint8* audio_data_[kNumOfQueuesInBuffer];
-
- int active_queue_;
- size_t buffer_size_bytes_;
-
- bool started_;
-
- // Volume level from 0 to 1.
- float volume_;
-
- // Container for retrieving data from AudioSourceCallback::OnMoreData().
- scoped_ptr<AudioBus> audio_bus_;
-
- DISALLOW_COPY_AND_ASSIGN(OpenSLESOutputStream);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
diff --git a/src/media/audio/android/opensles_util.h b/src/media/audio/android/opensles_util.h
deleted file mode 100644
index 4a028e2..0000000
--- a/src/media/audio/android/opensles_util.h
+++ /dev/null
@@ -1,44 +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 MEDIA_AUDIO_ANDROID_OPENSLES_UTIL_H_
-#define MEDIA_AUDIO_ANDROID_OPENSLES_UTIL_H_
-
-#include "base/logging.h"
-#include <SLES/OpenSLES.h>
-
-namespace media {
-
-template <typename SLType, typename SLDerefType>
-class ScopedSLObject {
- public:
- ScopedSLObject() : obj_(NULL) {}
-
- ~ScopedSLObject() { Reset(); }
-
- SLType* Receive() {
- DCHECK(!obj_);
- return &obj_;
- }
-
- SLDerefType operator->() { return *obj_; }
-
- SLType Get() const { return obj_; }
-
- void Reset() {
- if (obj_) {
- (*obj_)->Destroy(obj_);
- obj_ = NULL;
- }
- }
-
- private:
- SLType obj_;
-};
-
-typedef ScopedSLObject<SLObjectItf, const SLObjectItf_*> ScopedSLObjectItf;
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_ANDROID_OPENSLES_UTIL_H_
diff --git a/src/media/audio/async_socket_io_handler.h b/src/media/audio/async_socket_io_handler.h
deleted file mode 100644
index d17e3d3..0000000
--- a/src/media/audio/async_socket_io_handler.h
+++ /dev/null
@@ -1,113 +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 MEDIA_AUDIO_ASYNC_SOCKET_IO_HANDLER_H_
-#define MEDIA_AUDIO_ASYNC_SOCKET_IO_HANDLER_H_
-
-#include "base/message_loop.h"
-#include "base/sync_socket.h"
-#include "base/threading/non_thread_safe.h"
-#include "media/base/media_export.h"
-
-namespace media {
-
-// The message loop callback interface is different based on platforms.
-#if defined(OS_WIN)
-typedef MessageLoopForIO::IOHandler MessageLoopIOHandler;
-#elif defined(OS_POSIX)
-typedef MessageLoopForIO::Watcher MessageLoopIOHandler;
-#endif
-
-// Extends the CancelableSyncSocket class to allow reading from a socket
-// asynchronously on a TYPE_IO message loop thread. This makes it easy to share
-// a thread that uses a message loop (e.g. for IPC and other things) and not
-// require a separate thread to read from the socket.
-//
-// Example usage (also see the unit tests):
-//
-// class SocketReader {
-// public:
-// SocketReader(base::CancelableSyncSocket* socket)
-// : socket_(socket), buffer_() {
-// io_handler.Initialize(socket_->handle(),
-// base::Bind(&SocketReader::OnDataAvailable,
-// base::Unretained(this));
-// }
-//
-// void AsyncRead() {
-// CHECK(io_handler.Read(&buffer_[0], sizeof(buffer_)));
-// }
-//
-// private:
-// void OnDataAvailable(int bytes_read) {
-// if (ProcessData(&buffer_[0], bytes_read)) {
-// // Issue another read.
-// CHECK(io_handler.Read(&buffer_[0], sizeof(buffer_)));
-// }
-// }
-//
-// media::AsyncSocketIoHandler io_handler;
-// base::CancelableSyncSocket* socket_;
-// char buffer_[kBufferSize];
-// };
-//
-class MEDIA_EXPORT AsyncSocketIoHandler
- : public NON_EXPORTED_BASE(base::NonThreadSafe),
- public NON_EXPORTED_BASE(MessageLoopIOHandler) {
- public:
- AsyncSocketIoHandler();
- virtual ~AsyncSocketIoHandler();
-
- // Type definition for the callback. The parameter tells how many
- // bytes were read and is 0 if an error occurred.
- typedef base::Callback<void(int)> ReadCompleteCallback;
-
- // Initializes the AsyncSocketIoHandler by hooking it up to the current
- // thread's message loop (must be TYPE_IO), to do async reads from the socket
- // on the current thread. The |callback| will be invoked whenever a Read()
- // has completed.
- bool Initialize(base::SyncSocket::Handle socket,
- const ReadCompleteCallback& callback);
-
- // Attempts to read from the socket. The return value will be |false|
- // if an error occurred and |true| if data was read or a pending read
- // was issued. Regardless of async or sync operation, the
- // ReadCompleteCallback (see above) will be called when data is available.
- bool Read(char* buffer, int buffer_len);
-
- private:
-#if defined(OS_WIN)
- // Implementation of IOHandler on Windows.
- virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
- DWORD bytes_transfered,
- DWORD error) OVERRIDE;
-#elif defined(OS_POSIX)
- // Implementation of MessageLoopForIO::Watcher.
- virtual void OnFileCanWriteWithoutBlocking(int socket) OVERRIDE {}
- virtual void OnFileCanReadWithoutBlocking(int socket) OVERRIDE;
-
- void EnsureWatchingSocket();
-#endif
-
- base::SyncSocket::Handle socket_;
-#if defined(OS_WIN)
- MessageLoopForIO::IOContext* context_;
- bool is_pending_;
-#elif defined(OS_POSIX)
- MessageLoopForIO::FileDescriptorWatcher socket_watcher_;
- // |pending_buffer_| and |pending_buffer_len_| are valid only between
- // Read() and OnFileCanReadWithoutBlocking().
- char* pending_buffer_;
- int pending_buffer_len_;
- // |true| iff the message loop is watching the socket for IO events.
- bool is_watching_;
-#endif
- ReadCompleteCallback read_complete_;
-
- DISALLOW_COPY_AND_ASSIGN(AsyncSocketIoHandler);
-};
-
-} // namespace media.
-
-#endif // MEDIA_AUDIO_ASYNC_SOCKET_IO_HANDLER_H_
diff --git a/src/media/audio/async_socket_io_handler_posix.cc b/src/media/audio/async_socket_io_handler_posix.cc
deleted file mode 100644
index eeec7c1..0000000
--- a/src/media/audio/async_socket_io_handler_posix.cc
+++ /dev/null
@@ -1,97 +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 "media/audio/async_socket_io_handler.h"
-
-#include <fcntl.h>
-
-#include "base/posix/eintr_wrapper.h"
-
-namespace media {
-
-AsyncSocketIoHandler::AsyncSocketIoHandler()
- : socket_(base::SyncSocket::kInvalidHandle),
- pending_buffer_(NULL),
- pending_buffer_len_(0),
- is_watching_(false) {
-}
-
-AsyncSocketIoHandler::~AsyncSocketIoHandler() {
- DCHECK(CalledOnValidThread());
-}
-
-void AsyncSocketIoHandler::OnFileCanReadWithoutBlocking(int socket) {
- DCHECK(CalledOnValidThread());
- DCHECK_EQ(socket, socket_);
- DCHECK(!read_complete_.is_null());
-
- if (pending_buffer_) {
- int bytes_read = HANDLE_EINTR(read(socket_, pending_buffer_,
- pending_buffer_len_));
- DCHECK_GT(bytes_read, 0);
- pending_buffer_ = NULL;
- pending_buffer_len_ = 0;
- read_complete_.Run(bytes_read > 0 ? bytes_read : 0);
- } else {
- // We're getting notifications that we can read from the socket while
- // we're not waiting for data. In order to not starve the message loop,
- // let's stop watching the fd and restart the watch when Read() is called.
- is_watching_ = false;
- socket_watcher_.StopWatchingFileDescriptor();
- }
-}
-
-bool AsyncSocketIoHandler::Read(char* buffer, int buffer_len) {
- DCHECK(CalledOnValidThread());
- DCHECK(!read_complete_.is_null());
- DCHECK(!pending_buffer_);
-
- EnsureWatchingSocket();
-
- int bytes_read = HANDLE_EINTR(read(socket_, buffer, buffer_len));
- if (bytes_read < 0) {
- if (errno == EAGAIN) {
- pending_buffer_ = buffer;
- pending_buffer_len_ = buffer_len;
- } else {
- NOTREACHED() << "read(): " << errno;
- return false;
- }
- } else {
- read_complete_.Run(bytes_read);
- }
- return true;
-}
-
-bool AsyncSocketIoHandler::Initialize(base::SyncSocket::Handle socket,
- const ReadCompleteCallback& callback) {
- DCHECK_EQ(socket_, base::SyncSocket::kInvalidHandle);
-
- DetachFromThread();
-
- socket_ = socket;
- read_complete_ = callback;
-
- // SyncSocket is blocking by default, so let's convert it to non-blocking.
- int value = fcntl(socket, F_GETFL);
- if (!(value & O_NONBLOCK)) {
- // Set the socket to be non-blocking so we can do async reads.
- if (fcntl(socket, F_SETFL, O_NONBLOCK) == -1) {
- NOTREACHED();
- return false;
- }
- }
-
- return true;
-}
-
-void AsyncSocketIoHandler::EnsureWatchingSocket() {
- DCHECK(CalledOnValidThread());
- if (!is_watching_ && socket_ != base::SyncSocket::kInvalidHandle) {
- is_watching_ = MessageLoopForIO::current()->WatchFileDescriptor(
- socket_, true, MessageLoopForIO::WATCH_READ, &socket_watcher_, this);
- }
-}
-
-} // namespace media.
diff --git a/src/media/audio/async_socket_io_handler_unittest.cc b/src/media/audio/async_socket_io_handler_unittest.cc
deleted file mode 100644
index c7fa47b..0000000
--- a/src/media/audio/async_socket_io_handler_unittest.cc
+++ /dev/null
@@ -1,142 +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 "media/audio/async_socket_io_handler.h"
-
-#include "base/bind.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-const char kAsyncSocketIoTestString[] = "Hello, AsyncSocketIoHandler";
-const size_t kAsyncSocketIoTestStringLength =
- arraysize(kAsyncSocketIoTestString);
-
-class TestSocketReader {
- public:
- // Set |number_of_reads_before_quit| to >0 when you expect a specific number
- // of Read operations to complete. Once that number is reached, the current
- // message loop will be Quit(). Set |number_of_reads_before_quit| to -1 if
- // callbacks should not be counted.
- TestSocketReader(base::CancelableSyncSocket* socket,
- int number_of_reads_before_quit,
- bool issue_reads_from_callback)
- : socket_(socket), buffer_(),
- number_of_reads_before_quit_(number_of_reads_before_quit),
- callbacks_received_(0),
- issue_reads_from_callback_(issue_reads_from_callback) {
- io_handler.Initialize(socket_->handle(),
- base::Bind(&TestSocketReader::OnRead,
- base::Unretained(this)));
- }
- ~TestSocketReader() {}
-
- bool IssueRead() {
- return io_handler.Read(&buffer_[0], sizeof(buffer_));
- }
-
- const char* buffer() const { return &buffer_[0]; }
-
- int callbacks_received() const { return callbacks_received_; }
-
- private:
- void OnRead(int bytes_read) {
- EXPECT_GT(bytes_read, 0);
- ++callbacks_received_;
- if (number_of_reads_before_quit_ == callbacks_received_) {
- MessageLoop::current()->Quit();
- } else if (issue_reads_from_callback_) {
- IssueRead();
- }
- }
-
- media::AsyncSocketIoHandler io_handler;
- base::CancelableSyncSocket* socket_; // Ownership lies outside the class.
- char buffer_[kAsyncSocketIoTestStringLength];
- int number_of_reads_before_quit_;
- int callbacks_received_;
- bool issue_reads_from_callback_;
-};
-
-// Workaround to be able to use a base::Closure for sending data.
-// Send() returns int but a closure must return void.
-void SendData(base::CancelableSyncSocket* socket,
- const void* buffer,
- size_t length) {
- socket->Send(buffer, length);
-}
-
-} // end namespace.
-
-// Tests doing a pending read from a socket and use an IO handler to get
-// notified of data.
-TEST(AsyncSocketIoHandlerTest, AsynchronousReadWithMessageLoop) {
- MessageLoopForIO loop;
-
- base::CancelableSyncSocket pair[2];
- ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1]));
-
- TestSocketReader reader(&pair[0], 1, false);
- EXPECT_TRUE(reader.IssueRead());
-
- pair[1].Send(kAsyncSocketIoTestString, kAsyncSocketIoTestStringLength);
- MessageLoop::current()->Run();
- EXPECT_EQ(strcmp(reader.buffer(), kAsyncSocketIoTestString), 0);
- EXPECT_EQ(1, reader.callbacks_received());
-}
-
-// Tests doing a read from a socket when we know that there is data in the
-// socket. Here we want to make sure that any async 'can read' notifications
-// won't trip us off and that the synchronous case works as well.
-TEST(AsyncSocketIoHandlerTest, SynchronousReadWithMessageLoop) {
- MessageLoopForIO loop;
-
- base::CancelableSyncSocket pair[2];
- ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1]));
-
- TestSocketReader reader(&pair[0], -1, false);
-
- pair[1].Send(kAsyncSocketIoTestString, kAsyncSocketIoTestStringLength);
- MessageLoop::current()->PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(),
- base::TimeDelta::FromMilliseconds(100));
- MessageLoop::current()->Run();
-
- EXPECT_TRUE(reader.IssueRead());
- EXPECT_EQ(strcmp(reader.buffer(), kAsyncSocketIoTestString), 0);
- // We've now verified that the read happened synchronously, but it's not
- // guaranteed that the callback has been issued since the callback will be
- // called asynchronously even though the read may have been done.
- // So we call RunUntilIdle() to allow any event notifications or APC's on
- // Windows, to execute before checking the count of how many callbacks we've
- // received.
- MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(1, reader.callbacks_received());
-}
-
-// Calls Read() from within a callback to test that simple read "loops" work.
-TEST(AsyncSocketIoHandlerTest, ReadFromCallback) {
- MessageLoopForIO loop;
-
- base::CancelableSyncSocket pair[2];
- ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&pair[0], &pair[1]));
-
- const int kReadOperationCount = 10;
- TestSocketReader reader(&pair[0], kReadOperationCount, true);
- EXPECT_TRUE(reader.IssueRead());
-
- // Issue sends on an interval to satisfy the Read() requirements.
- int64 milliseconds = 0;
- for (int i = 0; i < kReadOperationCount; ++i) {
- MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&SendData, &pair[1], kAsyncSocketIoTestString,
- kAsyncSocketIoTestStringLength),
- base::TimeDelta::FromMilliseconds(milliseconds));
- milliseconds += 10;
- }
-
- MessageLoop::current()->PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(),
- base::TimeDelta::FromMilliseconds(100 + milliseconds));
-
- MessageLoop::current()->Run();
- EXPECT_EQ(kReadOperationCount, reader.callbacks_received());
-}
diff --git a/src/media/audio/async_socket_io_handler_win.cc b/src/media/audio/async_socket_io_handler_win.cc
deleted file mode 100644
index f83f405..0000000
--- a/src/media/audio/async_socket_io_handler_win.cc
+++ /dev/null
@@ -1,76 +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 "media/audio/async_socket_io_handler.h"
-
-namespace media {
-
-AsyncSocketIoHandler::AsyncSocketIoHandler()
- : socket_(base::SyncSocket::kInvalidHandle),
- context_(NULL),
- is_pending_(false) {}
-
-AsyncSocketIoHandler::~AsyncSocketIoHandler() {
- // We need to be deleted on the correct thread to avoid racing with the
- // message loop thread.
- DCHECK(CalledOnValidThread());
-
- if (context_) {
- if (is_pending_) {
- // Make the context be deleted by the message pump when done.
- context_->handler = NULL;
- } else {
- delete context_;
- }
- }
-}
-
-// Implementation of IOHandler on Windows.
-void AsyncSocketIoHandler::OnIOCompleted(MessageLoopForIO::IOContext* context,
- DWORD bytes_transfered,
- DWORD error) {
- DCHECK(CalledOnValidThread());
- DCHECK_EQ(context_, context);
- DCHECK(!read_complete_.is_null());
- is_pending_ = false;
- read_complete_.Run(error == ERROR_SUCCESS ? bytes_transfered : 0);
-}
-
-bool AsyncSocketIoHandler::Read(char* buffer, int buffer_len) {
- DCHECK(CalledOnValidThread());
- DCHECK(!read_complete_.is_null());
- DCHECK(!is_pending_);
- DCHECK_NE(socket_, base::SyncSocket::kInvalidHandle);
-
- DWORD bytes_read = 0;
- BOOL ok = ::ReadFile(socket_, buffer, buffer_len, &bytes_read,
- &context_->overlapped);
- // The completion port will be signaled regardless of completing the read
- // straight away or asynchronously (ERROR_IO_PENDING). OnIOCompleted() will
- // be called regardless and we don't need to explicitly run the callback
- // in the case where ok is FALSE and GLE==ERROR_IO_PENDING.
- is_pending_ = !ok && (GetLastError() == ERROR_IO_PENDING);
- return ok || is_pending_;
-}
-
-bool AsyncSocketIoHandler::Initialize(base::SyncSocket::Handle socket,
- const ReadCompleteCallback& callback) {
- DCHECK(!context_);
- DCHECK_EQ(socket_, base::SyncSocket::kInvalidHandle);
-
- DetachFromThread();
-
- socket_ = socket;
- read_complete_ = callback;
-
- MessageLoopForIO::current()->RegisterIOHandler(socket, this);
-
- context_ = new MessageLoopForIO::IOContext();
- context_->handler = this;
- memset(&context_->overlapped, 0, sizeof(context_->overlapped));
-
- return true;
-}
-
-} // namespace media.
diff --git a/src/media/audio/audio_buffers_state.cc b/src/media/audio/audio_buffers_state.cc
deleted file mode 100644
index 6c4f950..0000000
--- a/src/media/audio/audio_buffers_state.cc
+++ /dev/null
@@ -1,20 +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 "media/audio/audio_buffers_state.h"
-
-namespace media {
-
-AudioBuffersState::AudioBuffersState()
- : pending_bytes(0),
- hardware_delay_bytes(0) {
-}
-
-AudioBuffersState::AudioBuffersState(int pending_bytes,
- int hardware_delay_bytes)
- : pending_bytes(pending_bytes),
- hardware_delay_bytes(hardware_delay_bytes) {
-}
-
-} // namespace media
diff --git a/src/media/audio/audio_buffers_state.h b/src/media/audio/audio_buffers_state.h
deleted file mode 100644
index 79244ae..0000000
--- a/src/media/audio/audio_buffers_state.h
+++ /dev/null
@@ -1,32 +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 MEDIA_AUDIO_AUDIO_BUFFERS_STATE_H_
-#define MEDIA_AUDIO_AUDIO_BUFFERS_STATE_H_
-
-#include "media/base/media_export.h"
-
-namespace media {
-
-// AudioBuffersState struct stores current state of audio buffers.
-// It is used for audio synchronization.
-struct MEDIA_EXPORT AudioBuffersState {
- AudioBuffersState();
- AudioBuffersState(int pending_bytes, int hardware_delay_bytes);
-
- int total_bytes() {
- return pending_bytes + hardware_delay_bytes;
- }
-
- // Number of bytes we currently have in our software buffer.
- int pending_bytes;
-
- // Number of bytes that have been written to the device, but haven't
- // been played yet.
- int hardware_delay_bytes;
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_BUFFERS_STATE_H_
diff --git a/src/media/audio/audio_device_name.cc b/src/media/audio/audio_device_name.cc
deleted file mode 100644
index 02bb03f..0000000
--- a/src/media/audio/audio_device_name.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2011 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 "media/audio/audio_device_name.h"
-
-namespace media {
-
-AudioDeviceName::AudioDeviceName() {}
-
-AudioDeviceName::AudioDeviceName(const std::string& device_name,
- const std::string& unique_id)
- : device_name(device_name),
- unique_id(unique_id) {
-}
-
-} // namespace media
-
diff --git a/src/media/audio/audio_device_name.h b/src/media/audio/audio_device_name.h
deleted file mode 100644
index aa3cca0..0000000
--- a/src/media/audio/audio_device_name.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2011 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_AUDIO_AUDIO_DEVICE_NAME_H_
-#define MEDIA_AUDIO_AUDIO_DEVICE_NAME_H_
-
-#include <list>
-#include <string>
-#include "media/base/media_export.h"
-
-namespace media {
-
-struct MEDIA_EXPORT AudioDeviceName {
- AudioDeviceName();
- AudioDeviceName(const std::string& device_name,
- const std::string& unique_id);
-
- std::string device_name; // Friendly name of the device.
- std::string unique_id; // Unique identifier for the device.
-};
-
-typedef std::list<AudioDeviceName> AudioDeviceNames;
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_DEVICE_NAME_H_
diff --git a/src/media/audio/audio_device_thread.cc b/src/media/audio/audio_device_thread.cc
deleted file mode 100644
index 51d5ecd..0000000
--- a/src/media/audio/audio_device_thread.cc
+++ /dev/null
@@ -1,201 +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 "media/audio/audio_device_thread.h"
-
-#include <algorithm>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/aligned_memory.h"
-#include "base/message_loop.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread_restrictions.h"
-#include "media/audio/audio_util.h"
-#include "media/base/audio_bus.h"
-
-using base::PlatformThread;
-
-namespace media {
-
-// The actual worker thread implementation. It's very bare bones and much
-// simpler than SimpleThread (no synchronization in Start, etc) and supports
-// joining the thread handle asynchronously via a provided message loop even
-// after the Thread object itself has been deleted.
-class AudioDeviceThread::Thread
- : public PlatformThread::Delegate,
- public base::RefCountedThreadSafe<AudioDeviceThread::Thread> {
- public:
- Thread(AudioDeviceThread::Callback* callback,
- base::SyncSocket::Handle socket,
- const char* thread_name);
-
- void Start();
-
- // Stops the thread. If |loop_for_join| is non-NULL, the function posts
- // a task to join (close) the thread handle later instead of waiting for
- // the thread. If loop_for_join is NULL, then the function waits
- // synchronously for the thread to terminate.
- void Stop(MessageLoop* loop_for_join);
-
- private:
- friend class base::RefCountedThreadSafe<AudioDeviceThread::Thread>;
- virtual ~Thread();
-
- // Overrides from PlatformThread::Delegate.
- virtual void ThreadMain() OVERRIDE;
-
- // Runs the loop that reads from the socket.
- void Run();
-
- private:
- base::PlatformThreadHandle thread_;
- AudioDeviceThread::Callback* callback_;
- base::CancelableSyncSocket socket_;
- base::Lock callback_lock_;
- const char* thread_name_;
-
- DISALLOW_COPY_AND_ASSIGN(Thread);
-};
-
-// AudioDeviceThread implementation
-
-AudioDeviceThread::AudioDeviceThread() {
-}
-
-AudioDeviceThread::~AudioDeviceThread() {
- DCHECK(!thread_);
-}
-
-void AudioDeviceThread::Start(AudioDeviceThread::Callback* callback,
- base::SyncSocket::Handle socket,
- const char* thread_name) {
- base::AutoLock auto_lock(thread_lock_);
- CHECK(thread_ == NULL);
- thread_ = new AudioDeviceThread::Thread(callback, socket, thread_name);
- thread_->Start();
-}
-
-void AudioDeviceThread::Stop(MessageLoop* loop_for_join) {
- base::AutoLock auto_lock(thread_lock_);
- if (thread_) {
- thread_->Stop(loop_for_join);
- thread_ = NULL;
- }
-}
-
-bool AudioDeviceThread::IsStopped() {
- base::AutoLock auto_lock(thread_lock_);
- return thread_ == NULL;
-}
-
-// AudioDeviceThread::Thread implementation
-AudioDeviceThread::Thread::Thread(AudioDeviceThread::Callback* callback,
- base::SyncSocket::Handle socket,
- const char* thread_name)
- : thread_(base::kNullThreadHandle),
- callback_(callback),
- socket_(socket),
- thread_name_(thread_name) {
-}
-
-AudioDeviceThread::Thread::~Thread() {
- DCHECK_EQ(thread_, base::kNullThreadHandle) << "Stop wasn't called";
-}
-
-void AudioDeviceThread::Thread::Start() {
- base::AutoLock auto_lock(callback_lock_);
- DCHECK_EQ(thread_, base::kNullThreadHandle);
- // This reference will be released when the thread exists.
- AddRef();
-
- PlatformThread::CreateWithPriority(0, this, &thread_,
- base::kThreadPriority_RealtimeAudio);
- CHECK(thread_ != base::kNullThreadHandle);
-}
-
-void AudioDeviceThread::Thread::Stop(MessageLoop* loop_for_join) {
- socket_.Shutdown();
-
- base::PlatformThreadHandle thread = base::kNullThreadHandle;
-
- { // NOLINT
- base::AutoLock auto_lock(callback_lock_);
- callback_ = NULL;
- std::swap(thread, thread_);
- }
-
- if (thread != base::kNullThreadHandle) {
- if (loop_for_join) {
- loop_for_join->PostTask(FROM_HERE,
- base::Bind(&base::PlatformThread::Join, thread));
- } else {
- base::PlatformThread::Join(thread);
- }
- }
-}
-
-void AudioDeviceThread::Thread::ThreadMain() {
- PlatformThread::SetName(thread_name_);
-
- // Singleton access is safe from this thread as long as callback is non-NULL.
- // The callback is the only point where the thread calls out to 'unknown' code
- // that might touch singletons and the lifetime of the callback is controlled
- // by another thread on which singleton access is OK as well.
- base::ThreadRestrictions::SetSingletonAllowed(true);
-
- { // NOLINT
- base::AutoLock auto_lock(callback_lock_);
- if (callback_)
- callback_->InitializeOnAudioThread();
- }
-
- Run();
-
- // Release the reference for the thread. Note that after this, the Thread
- // instance will most likely be deleted.
- Release();
-}
-
-void AudioDeviceThread::Thread::Run() {
- while (true) {
- int pending_data = 0;
- size_t bytes_read = socket_.Receive(&pending_data, sizeof(pending_data));
- if (bytes_read != sizeof(pending_data)) {
- DCHECK_EQ(bytes_read, 0U);
- break;
- }
-
- base::AutoLock auto_lock(callback_lock_);
- if (callback_)
- callback_->Process(pending_data);
- }
-}
-
-// AudioDeviceThread::Callback implementation
-
-AudioDeviceThread::Callback::Callback(
- const AudioParameters& audio_parameters,
- int input_channels,
- base::SharedMemoryHandle memory, int memory_length)
- : audio_parameters_(audio_parameters),
- input_channels_(input_channels),
- samples_per_ms_(audio_parameters.sample_rate() / 1000),
- bytes_per_ms_(audio_parameters.channels() *
- (audio_parameters_.bits_per_sample() / 8) *
- samples_per_ms_),
- shared_memory_(memory, false),
- memory_length_(memory_length) {
- CHECK_NE(bytes_per_ms_, 0); // Catch division by zero early.
- CHECK_NE(samples_per_ms_, 0);
-}
-
-AudioDeviceThread::Callback::~Callback() {}
-
-void AudioDeviceThread::Callback::InitializeOnAudioThread() {
- MapSharedMemory();
- DCHECK(shared_memory_.memory() != NULL);
-}
-
-} // namespace media.
diff --git a/src/media/audio/audio_device_thread.h b/src/media/audio/audio_device_thread.h
deleted file mode 100644
index 44dbc3a..0000000
--- a/src/media/audio/audio_device_thread.h
+++ /dev/null
@@ -1,111 +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 MEDIA_AUDIO_AUDIO_DEVICE_THREAD_H_
-#define MEDIA_AUDIO_AUDIO_DEVICE_THREAD_H_
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/shared_memory.h"
-#include "base/sync_socket.h"
-#include "base/synchronization/lock.h"
-#include "media/base/media_export.h"
-#include "media/audio/audio_parameters.h"
-#include "media/audio/shared_memory_util.h"
-
-class MessageLoop;
-
-namespace media {
-class AudioBus;
-
-// Data transfer between browser and render process uses a combination
-// of sync sockets and shared memory. To read from the socket and render
-// data, we use a worker thread, a.k.a. the AudioDeviceThread, which reads
-// data from the browser via the socket and fills the shared memory from the
-// audio thread via the AudioDeviceThread::Callback interface/class.
-// For more details see the documentation in audio_device.h.
-//
-// TODO(tommi): Multiple audio input/output device instances should be able to
-// share the same thread instead of spinning one per instance.
-class MEDIA_EXPORT AudioDeviceThread {
- public:
- // This is the callback interface/base class that Audio[Output|Input]Device
- // implements to render input/output data. The callbacks run on the
- // thread owned by AudioDeviceThread.
- class Callback {
- public:
- Callback(const AudioParameters& audio_parameters,
- int input_channels,
- base::SharedMemoryHandle memory,
- int memory_length);
- virtual ~Callback();
-
- // One time initialization for the callback object on the audio thread.
- void InitializeOnAudioThread();
-
- // Derived implementations must call shared_memory_.Map appropriately
- // before Process can be called.
- virtual void MapSharedMemory() = 0;
-
- // Called whenever we receive notifications about pending data.
- virtual void Process(int pending_data) = 0;
-
- protected:
- // Protected so that derived classes can access directly.
- // The variables are 'const' since values are calculated/set in the
- // constructor and must never change.
- const AudioParameters audio_parameters_;
- const int input_channels_;
- const int samples_per_ms_;
- const int bytes_per_ms_;
-
- base::SharedMemory shared_memory_;
- const int memory_length_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Callback);
- };
-
- AudioDeviceThread();
- ~AudioDeviceThread();
-
- // Starts the audio thread. The thread must not already be running.
- void Start(AudioDeviceThread::Callback* callback,
- base::SyncSocket::Handle socket,
- const char* thread_name);
-
- // This tells the audio thread to stop and clean up the data.
- // The method can stop the thread synchronously or asynchronously.
- // In the latter case, the thread will still be running after Stop()
- // returns, but the callback pointer is cleared so no further callbacks will
- // be made (IOW after Stop() returns, it is safe to delete the callback).
- // The |loop_for_join| parameter is required for asynchronous operation
- // in order to join the worker thread and close the thread handle later via a
- // posted task.
- // If set to NULL, function will wait for the thread to exit before returning.
- void Stop(MessageLoop* loop_for_join);
-
- // Returns true if the thread is stopped or stopping.
- bool IsStopped();
-
- private:
- // Our own private SimpleThread override. We implement this in a
- // private class so that we get the following benefits:
- // 1) AudioDeviceThread doesn't expose SimpleThread methods.
- // I.e. the caller can't call Start()/Stop() - which would be bad.
- // 2) We override ThreadMain to add additional on-thread initialization
- // while still synchronized with SimpleThread::Start() to provide
- // reliable initialization.
- class Thread;
-
- base::Lock thread_lock_;
- scoped_refptr<AudioDeviceThread::Thread> thread_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioDeviceThread);
-};
-
-} // namespace media.
-
-#endif // MEDIA_AUDIO_AUDIO_DEVICE_THREAD_H_
diff --git a/src/media/audio/audio_input_controller.cc b/src/media/audio/audio_input_controller.cc
deleted file mode 100644
index 99bb420..0000000
--- a/src/media/audio/audio_input_controller.cc
+++ /dev/null
@@ -1,319 +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 "media/audio/audio_input_controller.h"
-
-#include "base/bind.h"
-#include "base/threading/thread_restrictions.h"
-#include "media/base/limits.h"
-
-namespace {
-const int kMaxInputChannels = 2;
-const int kTimerResetIntervalSeconds = 1;
-#if defined(OS_IOS)
-// The first callback on iOS is received after the current background
-// audio has faded away.
-const int kTimerInitialIntervalSeconds = 4;
-#else
-const int kTimerInitialIntervalSeconds = 1;
-#endif // defined(OS_IOS)
-}
-
-namespace media {
-
-// static
-AudioInputController::Factory* AudioInputController::factory_ = NULL;
-
-AudioInputController::AudioInputController(EventHandler* handler,
- SyncWriter* sync_writer)
- : creator_loop_(base::MessageLoopProxy::current()),
- handler_(handler),
- stream_(NULL),
- data_is_active_(false),
- state_(kEmpty),
- sync_writer_(sync_writer),
- max_volume_(0.0) {
- DCHECK(creator_loop_);
-}
-
-AudioInputController::~AudioInputController() {
- DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_);
-}
-
-// static
-scoped_refptr<AudioInputController> AudioInputController::Create(
- AudioManager* audio_manager,
- EventHandler* event_handler,
- const AudioParameters& params) {
- DCHECK(audio_manager);
-
- if (!params.IsValid() || (params.channels() > kMaxInputChannels))
- return NULL;
-
- if (factory_)
- return factory_->Create(audio_manager, event_handler, params);
-
- scoped_refptr<AudioInputController> controller(new AudioInputController(
- event_handler, NULL));
-
- controller->message_loop_ = audio_manager->GetMessageLoop();
-
- // Create and open a new audio input stream from the existing
- // audio-device thread. Use the default audio-input device.
- std::string device_id = AudioManagerBase::kDefaultDeviceId;
- if (!controller->message_loop_->PostTask(FROM_HERE,
- base::Bind(&AudioInputController::DoCreate, controller,
- base::Unretained(audio_manager), params, device_id))) {
- controller = NULL;
- }
-
- return controller;
-}
-
-// static
-scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency(
- AudioManager* audio_manager,
- EventHandler* event_handler,
- const AudioParameters& params,
- const std::string& device_id,
- SyncWriter* sync_writer) {
- DCHECK(audio_manager);
- DCHECK(sync_writer);
-
- if (!params.IsValid() || (params.channels() > kMaxInputChannels))
- return NULL;
-
- // Create the AudioInputController object and ensure that it runs on
- // the audio-manager thread.
- scoped_refptr<AudioInputController> controller(new AudioInputController(
- event_handler, sync_writer));
- controller->message_loop_ = audio_manager->GetMessageLoop();
-
- // Create and open a new audio input stream from the existing
- // audio-device thread. Use the provided audio-input device.
- if (!controller->message_loop_->PostTask(FROM_HERE,
- base::Bind(&AudioInputController::DoCreate, controller,
- base::Unretained(audio_manager), params, device_id))) {
- controller = NULL;
- }
-
- return controller;
-}
-
-void AudioInputController::Record() {
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioInputController::DoRecord, this));
-}
-
-void AudioInputController::Close(const base::Closure& closed_task) {
- DCHECK(!closed_task.is_null());
- DCHECK(creator_loop_->BelongsToCurrentThread());
-
- message_loop_->PostTaskAndReply(
- FROM_HERE, base::Bind(&AudioInputController::DoClose, this), closed_task);
-}
-
-void AudioInputController::SetVolume(double volume) {
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioInputController::DoSetVolume, this, volume));
-}
-
-void AudioInputController::SetAutomaticGainControl(bool enabled) {
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioInputController::DoSetAutomaticGainControl, this, enabled));
-}
-
-void AudioInputController::DoCreate(AudioManager* audio_manager,
- const AudioParameters& params,
- const std::string& device_id) {
- DCHECK(message_loop_->BelongsToCurrentThread());
-
- stream_ = audio_manager->MakeAudioInputStream(params, device_id);
-
- if (!stream_) {
- // TODO(satish): Define error types.
- handler_->OnError(this, 0);
- return;
- }
-
- if (stream_ && !stream_->Open()) {
- stream_->Close();
- stream_ = NULL;
- // TODO(satish): Define error types.
- handler_->OnError(this, 0);
- return;
- }
-
- DCHECK(!no_data_timer_.get());
- // Create the data timer which will call DoCheckForNoData(). The timer
- // is started in DoRecord() and restarted in each DoCheckForNoData() callback.
- no_data_timer_.reset(new base::Timer(
- FROM_HERE, base::TimeDelta::FromSeconds(kTimerInitialIntervalSeconds),
- base::Bind(&AudioInputController::DoCheckForNoData,
- base::Unretained(this)), false));
- state_ = kCreated;
- handler_->OnCreated(this);
-}
-
-void AudioInputController::DoRecord() {
- DCHECK(message_loop_->BelongsToCurrentThread());
-
- if (state_ != kCreated)
- return;
-
- {
- base::AutoLock auto_lock(lock_);
- state_ = kRecording;
- }
-
- // Start the data timer. Once |kTimerResetIntervalSeconds| have passed,
- // a callback to DoCheckForNoData() is made.
- no_data_timer_->Reset();
-
- stream_->Start(this);
- handler_->OnRecording(this);
-}
-
-void AudioInputController::DoClose() {
- DCHECK(message_loop_->BelongsToCurrentThread());
-
- // Delete the timer on the same thread that created it.
- no_data_timer_.reset();
-
- if (state_ != kClosed) {
- DoStopCloseAndClearStream(NULL);
- SetDataIsActive(false);
-
- if (LowLatencyMode()) {
- sync_writer_->Close();
- }
-
- state_ = kClosed;
- }
-}
-
-void AudioInputController::DoReportError(int code) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- handler_->OnError(this, code);
-}
-
-void AudioInputController::DoSetVolume(double volume) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK_GE(volume, 0);
- DCHECK_LE(volume, 1.0);
-
- if (state_ != kCreated && state_ != kRecording)
- return;
-
- // Only ask for the maximum volume at first call and use cached value
- // for remaining function calls.
- if (!max_volume_) {
- max_volume_ = stream_->GetMaxVolume();
- }
-
- if (max_volume_ == 0.0) {
- DLOG(WARNING) << "Failed to access input volume control";
- return;
- }
-
- // Set the stream volume and scale to a range matched to the platform.
- stream_->SetVolume(max_volume_ * volume);
-}
-
-void AudioInputController::DoSetAutomaticGainControl(bool enabled) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK_NE(state_, kRecording);
-
- // Ensure that the AGC state only can be modified before streaming starts.
- if (state_ != kCreated || state_ == kRecording)
- return;
-
- stream_->SetAutomaticGainControl(enabled);
-}
-
-void AudioInputController::DoCheckForNoData() {
- DCHECK(message_loop_->BelongsToCurrentThread());
-
- if (!GetDataIsActive()) {
- // The data-is-active marker will be false only if it has been more than
- // one second since a data packet was recorded. This can happen if a
- // capture device has been removed or disabled.
- handler_->OnError(this, 0);
- return;
- }
-
- // Mark data as non-active. The flag will be re-enabled in OnData() each
- // time a data packet is received. Hence, under normal conditions, the
- // flag will only be disabled during a very short period.
- SetDataIsActive(false);
-
- // Restart the timer to ensure that we check the flag again in
- // |kTimerResetIntervalSeconds|.
- no_data_timer_->Start(
- FROM_HERE, base::TimeDelta::FromSeconds(kTimerResetIntervalSeconds),
- base::Bind(&AudioInputController::DoCheckForNoData,
- base::Unretained(this)));
-}
-
-void AudioInputController::OnData(AudioInputStream* stream, const uint8* data,
- uint32 size, uint32 hardware_delay_bytes,
- double volume) {
- {
- base::AutoLock auto_lock(lock_);
- if (state_ != kRecording)
- return;
- }
-
- // Mark data as active to ensure that the periodic calls to
- // DoCheckForNoData() does not report an error to the event handler.
- SetDataIsActive(true);
-
- // Use SyncSocket if we are in a low-latency mode.
- if (LowLatencyMode()) {
- sync_writer_->Write(data, size, volume);
- sync_writer_->UpdateRecordedBytes(hardware_delay_bytes);
- return;
- }
-
- handler_->OnData(this, data, size);
-}
-
-void AudioInputController::OnClose(AudioInputStream* stream) {
- DVLOG(1) << "AudioInputController::OnClose()";
- // TODO(satish): Sometimes the device driver closes the input stream without
- // us asking for it (may be if the device was unplugged?). Check how to handle
- // such cases here.
-}
-
-void AudioInputController::OnError(AudioInputStream* stream, int code) {
- // Handle error on the audio-manager thread.
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioInputController::DoReportError, this, code));
-}
-
-void AudioInputController::DoStopCloseAndClearStream(
- base::WaitableEvent *done) {
- DCHECK(message_loop_->BelongsToCurrentThread());
-
- // Allow calling unconditionally and bail if we don't have a stream to close.
- if (stream_ != NULL) {
- stream_->Stop();
- stream_->Close();
- stream_ = NULL;
- }
-
- // Should be last in the method, do not touch "this" from here on.
- if (done != NULL)
- done->Signal();
-}
-
-void AudioInputController::SetDataIsActive(bool enabled) {
- base::subtle::Release_Store(&data_is_active_, enabled);
-}
-
-bool AudioInputController::GetDataIsActive() {
- return (base::subtle::Acquire_Load(&data_is_active_) != false);
-}
-
-} // namespace media
diff --git a/src/media/audio/audio_input_controller.h b/src/media/audio/audio_input_controller.h
deleted file mode 100644
index 77e3e87..0000000
--- a/src/media/audio/audio_input_controller.h
+++ /dev/null
@@ -1,260 +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 MEDIA_AUDIO_AUDIO_INPUT_CONTROLLER_H_
-#define MEDIA_AUDIO_AUDIO_INPUT_CONTROLLER_H_
-
-#include <string>
-#include "base/atomicops.h"
-#include "base/callback.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 "base/threading/thread.h"
-#include "base/timer.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_manager_base.h"
-
-// An AudioInputController controls an AudioInputStream and records data
-// from this input stream. The two main methods are Record() and Close() and
-// they are both executed on the audio thread which is injected by the two
-// alternative factory methods, Create() or CreateLowLatency().
-//
-// All public methods of AudioInputController are non-blocking.
-//
-// Here is a state diagram for the AudioInputController:
-//
-// .--> [ Closed / Error ] <--.
-// | |
-// | |
-// [ Created ] ----------> [ Recording ]
-// ^
-// |
-// *[ Empty ]
-//
-// * Initial state
-//
-// State sequences (assuming low-latency):
-//
-// [Creating Thread] [Audio Thread]
-//
-// User AudioInputController EventHandler
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// CrateLowLatency() ==> DoCreate()
-// AudioManager::MakeAudioInputStream()
-// AudioInputStream::Open()
-// .- - - - - - - - - - - - -> OnError()
-// create the data timer
-// .-------------------------> OnCreated()
-// kCreated
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// Record() ==> DoRecord()
-// AudioInputStream::Start()
-// .-------------------------> OnRecording()
-// start the data timer
-// kRecording
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// Close() ==> DoClose()
-// delete the data timer
-// state_ = kClosed
-// AudioInputStream::Stop()
-// AudioInputStream::Close()
-// SyncWriter::Close()
-// Closure::Run() <-----------------.
-// (closure-task)
-//
-// The audio thread itself is owned by the AudioManager that the
-// AudioInputController holds a reference to. When performing tasks on the
-// audio thread, the controller must not add or release references to the
-// AudioManager or itself (since it in turn holds a reference to the manager).
-//
-namespace media {
-
-class MEDIA_EXPORT AudioInputController
- : public base::RefCountedThreadSafe<AudioInputController>,
- public AudioInputStream::AudioInputCallback {
- public:
- // An event handler that receives events from the AudioInputController. The
- // following methods are all called on the audio thread.
- class MEDIA_EXPORT EventHandler {
- public:
- virtual void OnCreated(AudioInputController* controller) = 0;
- virtual void OnRecording(AudioInputController* controller) = 0;
- virtual void OnError(AudioInputController* controller, int error_code) = 0;
- virtual void OnData(AudioInputController* controller, const uint8* data,
- uint32 size) = 0;
-
- protected:
- virtual ~EventHandler() {}
- };
-
- // A synchronous writer interface used by AudioInputController for
- // synchronous writing.
- class SyncWriter {
- public:
- virtual ~SyncWriter() {}
-
- // Notify the synchronous writer about the number of bytes in the
- // soundcard which has been recorded.
- virtual void UpdateRecordedBytes(uint32 bytes) = 0;
-
- // Write certain amount of data from |data|. This method returns
- // number of written bytes.
- virtual uint32 Write(const void* data, uint32 size, double volume) = 0;
-
- // Close this synchronous writer.
- virtual void Close() = 0;
- };
-
- // AudioInputController::Create() can use the currently registered Factory
- // to create the AudioInputController. Factory is intended for testing only.
- class Factory {
- public:
- virtual AudioInputController* Create(AudioManager* audio_manager,
- EventHandler* event_handler,
- AudioParameters params) = 0;
- protected:
- virtual ~Factory() {}
- };
-
- // Factory method for creating an AudioInputController.
- // The audio device will be created on the audio thread, and when that is
- // done, the event handler will receive an OnCreated() call from that same
- // thread.
- static scoped_refptr<AudioInputController> Create(
- AudioManager* audio_manager,
- EventHandler* event_handler,
- const AudioParameters& params);
-
- // Sets the factory used by the static method Create(). AudioInputController
- // does not take ownership of |factory|. A value of NULL results in an
- // AudioInputController being created directly.
- static void set_factory_for_testing(Factory* factory) { factory_ = factory; }
- AudioInputStream* stream_for_testing() { return stream_; }
-
- // Factory method for creating an AudioInputController for low-latency mode.
- // The audio device will be created on the audio thread, and when that is
- // done, the event handler will receive an OnCreated() call from that same
- // thread.
- static scoped_refptr<AudioInputController> CreateLowLatency(
- AudioManager* audio_manager,
- EventHandler* event_handler,
- const AudioParameters& params,
- const std::string& device_id,
- // External synchronous writer for audio controller.
- SyncWriter* sync_writer);
-
- // Starts recording using the created audio input stream.
- // This method is called on the creator thread.
- virtual void Record();
-
- // Closes the audio input stream. The state is changed and the resources
- // are freed on the audio thread. |closed_task| is then executed on the thread
- // that called Close().
- // Callbacks (EventHandler and SyncWriter) must exist until |closed_task|
- // is called.
- // It is safe to call this method more than once. Calls after the first one
- // will have no effect.
- // This method trampolines to the audio thread.
- virtual void Close(const base::Closure& closed_task);
-
- // Sets the capture volume of the input stream. The value 0.0 corresponds
- // to muted and 1.0 to maximum volume.
- virtual void SetVolume(double volume);
-
- // Sets the Automatic Gain Control (AGC) state of the input stream.
- // Changing the AGC state is not supported while recording is active.
- virtual void SetAutomaticGainControl(bool enabled);
-
- // AudioInputCallback implementation. Threading details depends on the
- // device-specific implementation.
- virtual void OnData(AudioInputStream* stream, const uint8* src, uint32 size,
- uint32 hardware_delay_bytes, double volume) OVERRIDE;
- virtual void OnClose(AudioInputStream* stream) OVERRIDE;
- virtual void OnError(AudioInputStream* stream, int code) OVERRIDE;
-
- bool LowLatencyMode() const { return sync_writer_ != NULL; }
-
- protected:
- friend class base::RefCountedThreadSafe<AudioInputController>;
-
- // Internal state of the source.
- enum State {
- kEmpty,
- kCreated,
- kRecording,
- kClosed,
- kError
- };
-
- AudioInputController(EventHandler* handler, SyncWriter* sync_writer);
- virtual ~AudioInputController();
-
- // Methods called on the audio thread (owned by the AudioManager).
- void DoCreate(AudioManager* audio_manager, const AudioParameters& params,
- const std::string& device_id);
- void DoRecord();
- void DoClose();
- void DoReportError(int code);
- void DoSetVolume(double volume);
- void DoSetAutomaticGainControl(bool enabled);
-
- // Method which ensures that OnError() is triggered when data recording
- // times out. Called on the audio thread.
- void DoCheckForNoData();
-
- // Helper method that stops, closes, and NULL:s |*stream_|.
- // Signals event when done if the event is not NULL.
- void DoStopCloseAndClearStream(base::WaitableEvent* done);
-
- void SetDataIsActive(bool enabled);
- bool GetDataIsActive();
-
- // Gives access to the message loop of the creating thread.
- scoped_refptr<base::MessageLoopProxy> creator_loop_;
-
- // The message loop of audio-manager thread that this object runs on.
- scoped_refptr<base::MessageLoopProxy> message_loop_;
-
- // Contains the AudioInputController::EventHandler which receives state
- // notifications from this class.
- EventHandler* handler_;
-
- // Pointer to the audio input stream object.
- AudioInputStream* stream_;
-
- // |no_data_timer_| is used to call OnError() when we stop receiving
- // OnData() calls without an OnClose() call. This can occur
- // when an audio input device is unplugged whilst recording on Windows.
- // See http://crbug.com/79936 for details.
- // This member is only touched by the audio thread.
- scoped_ptr<base::Timer> no_data_timer_;
-
- // This flag is used to signal that we are receiving OnData() calls, i.e,
- // that data is active. It can be touched by the audio thread and by the
- // low-level audio thread which calls OnData(). E.g. on Windows, the
- // low-level audio thread is called wasapi_capture_thread.
- base::subtle::Atomic32 data_is_active_;
-
- // |state_| is written on the audio thread and is read on the hardware audio
- // thread. These operations need to be locked. But lock is not required for
- // reading on the audio input controller thread.
- State state_;
-
- base::Lock lock_;
-
- // SyncWriter is used only in low-latency mode for synchronous writing.
- SyncWriter* sync_writer_;
-
- static Factory* factory_;
-
- double max_volume_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioInputController);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_INPUT_CONTROLLER_H_
diff --git a/src/media/audio/audio_input_controller_unittest.cc b/src/media/audio/audio_input_controller_unittest.cc
deleted file mode 100644
index 0a2a39b..0000000
--- a/src/media/audio/audio_input_controller_unittest.cc
+++ /dev/null
@@ -1,229 +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 "base/basictypes.h"
-#include "base/bind.h"
-#include "base/message_loop.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/test/test_timeouts.h"
-#include "media/audio/audio_input_controller.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::AtLeast;
-using ::testing::Exactly;
-using ::testing::InvokeWithoutArgs;
-using ::testing::NotNull;
-
-namespace media {
-
-static const int kSampleRate = AudioParameters::kAudioCDSampleRate;
-static const int kBitsPerSample = 16;
-static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
-static const int kSamplesPerPacket = kSampleRate / 10;
-
-// Posts MessageLoop::QuitClosure() on specified message loop.
-ACTION_P(QuitMessageLoop, loop_or_proxy) {
- loop_or_proxy->PostTask(FROM_HERE, MessageLoop::QuitClosure());
-}
-
-// Posts MessageLoop::QuitClosure() on specified message loop after a certain
-// number of calls given by |limit|.
-ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop_or_proxy) {
- if (++*count >= limit) {
- loop_or_proxy->PostTask(FROM_HERE, MessageLoop::QuitClosure());
- }
-}
-
-// Closes AudioOutputController synchronously.
-static void CloseAudioController(AudioInputController* controller) {
- controller->Close(MessageLoop::QuitClosure());
- MessageLoop::current()->Run();
-}
-
-class MockAudioInputControllerEventHandler
- : public AudioInputController::EventHandler {
- public:
- MockAudioInputControllerEventHandler() {}
-
- MOCK_METHOD1(OnCreated, void(AudioInputController* controller));
- MOCK_METHOD1(OnRecording, void(AudioInputController* controller));
- MOCK_METHOD2(OnError, void(AudioInputController* controller, int error_code));
- MOCK_METHOD3(OnData, void(AudioInputController* controller,
- const uint8* data, uint32 size));
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockAudioInputControllerEventHandler);
-};
-
-// Test fixture.
-class AudioInputControllerTest : public testing::Test {
- public:
- AudioInputControllerTest() {}
- virtual ~AudioInputControllerTest() {}
-
- protected:
- MessageLoop message_loop_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AudioInputControllerTest);
-};
-
-// Test AudioInputController for create and close without recording audio.
-TEST_F(AudioInputControllerTest, CreateAndClose) {
- MockAudioInputControllerEventHandler event_handler;
-
- // OnCreated() will be posted once.
- EXPECT_CALL(event_handler, OnCreated(NotNull()))
- .WillOnce(QuitMessageLoop(&message_loop_));
-
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket);
- scoped_refptr<AudioInputController> controller =
- AudioInputController::Create(audio_manager.get(), &event_handler, params);
- ASSERT_TRUE(controller.get());
-
- // Wait for OnCreated() to fire.
- message_loop_.Run();
-
- // Close the AudioInputController synchronously.
- CloseAudioController(controller);
-}
-
-// Test a normal call sequence of create, record and close.
-TEST_F(AudioInputControllerTest, RecordAndClose) {
- MockAudioInputControllerEventHandler event_handler;
- int count = 0;
-
- // OnCreated() will be called once.
- EXPECT_CALL(event_handler, OnCreated(NotNull()))
- .Times(Exactly(1));
-
- // OnRecording() will be called only once.
- EXPECT_CALL(event_handler, OnRecording(NotNull()))
- .Times(Exactly(1));
-
- // OnData() shall be called ten times.
- EXPECT_CALL(event_handler, OnData(NotNull(), NotNull(), _))
- .Times(AtLeast(10))
- .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10,
- message_loop_.message_loop_proxy()));
-
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket);
-
- // Creating the AudioInputController should render an OnCreated() call.
- scoped_refptr<AudioInputController> controller =
- AudioInputController::Create(audio_manager.get(), &event_handler, params);
- ASSERT_TRUE(controller.get());
-
- // Start recording and trigger one OnRecording() call.
- controller->Record();
-
- // Record and wait until ten OnData() callbacks are received.
- message_loop_.Run();
-
- // Close the AudioInputController synchronously.
- CloseAudioController(controller);
-}
-
-// Test that the AudioInputController reports an error when the input stream
-// stops without an OnClose() callback. This can happen when the underlying
-// audio layer stops feeding data as a result of a removed microphone device.
-TEST_F(AudioInputControllerTest, RecordAndError) {
- MockAudioInputControllerEventHandler event_handler;
- int count = 0;
-
- // OnCreated() will be called once.
- EXPECT_CALL(event_handler, OnCreated(NotNull()))
- .Times(Exactly(1));
-
- // OnRecording() will be called only once.
- EXPECT_CALL(event_handler, OnRecording(NotNull()))
- .Times(Exactly(1));
-
- // OnData() shall be called ten times.
- EXPECT_CALL(event_handler, OnData(NotNull(), NotNull(), _))
- .Times(AtLeast(10))
- .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10,
- message_loop_.message_loop_proxy()));
-
- // OnError() will be called after the data stream stops while the
- // controller is in a recording state.
- EXPECT_CALL(event_handler, OnError(NotNull(), 0))
- .Times(Exactly(1))
- .WillOnce(QuitMessageLoop(&message_loop_));
-
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket);
-
- // Creating the AudioInputController should render an OnCreated() call.
- scoped_refptr<AudioInputController> controller =
- AudioInputController::Create(audio_manager.get(), &event_handler, params);
- ASSERT_TRUE(controller.get());
-
- // Start recording and trigger one OnRecording() call.
- controller->Record();
-
- // Record and wait until ten OnData() callbacks are received.
- message_loop_.Run();
-
- // Stop the stream and verify that OnError() is posted.
- AudioInputStream* stream = controller->stream_for_testing();
- stream->Stop();
- message_loop_.Run();
-
- // Close the AudioInputController synchronously.
- CloseAudioController(controller);
-}
-
-// Test that AudioInputController rejects insanely large packet sizes.
-TEST_F(AudioInputControllerTest, SamplesPerPacketTooLarge) {
- // Create an audio device with a very large packet size.
- MockAudioInputControllerEventHandler event_handler;
-
- // OnCreated() shall not be called in this test.
- EXPECT_CALL(event_handler, OnCreated(NotNull()))
- .Times(Exactly(0));
-
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket * 1000);
- scoped_refptr<AudioInputController> controller =
- AudioInputController::Create(audio_manager.get(), &event_handler, params);
- ASSERT_FALSE(controller);
-}
-
-// Test calling AudioInputController::Close multiple times.
-TEST_F(AudioInputControllerTest, CloseTwice) {
- MockAudioInputControllerEventHandler event_handler;
-
- // OnRecording() will be called only once.
- EXPECT_CALL(event_handler, OnCreated(NotNull()));
-
- // OnRecording() will be called only once.
- EXPECT_CALL(event_handler, OnRecording(NotNull()))
- .Times(Exactly(1));
-
- scoped_ptr<AudioManager> audio_manager(AudioManager::Create());
- AudioParameters params(AudioParameters::AUDIO_FAKE, kChannelLayout,
- kSampleRate, kBitsPerSample, kSamplesPerPacket);
- scoped_refptr<AudioInputController> controller =
- AudioInputController::Create(audio_manager.get(), &event_handler, params);
- ASSERT_TRUE(controller.get());
-
- controller->Record();
-
- controller->Close(MessageLoop::QuitClosure());
- MessageLoop::current()->Run();
-
- controller->Close(MessageLoop::QuitClosure());
- MessageLoop::current()->Run();
-}
-
-} // namespace media
diff --git a/src/media/audio/audio_input_device.cc b/src/media/audio/audio_input_device.cc
deleted file mode 100644
index 9edf6db..0000000
--- a/src/media/audio/audio_input_device.cc
+++ /dev/null
@@ -1,344 +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 "media/audio/audio_input_device.h"
-
-#include "base/bind.h"
-#include "base/message_loop.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/time.h"
-#include "media/audio/audio_manager_base.h"
-#include "media/base/audio_bus.h"
-
-namespace media {
-
-// Takes care of invoking the capture callback on the audio thread.
-// An instance of this class is created for each capture stream in
-// OnLowLatencyCreated().
-class AudioInputDevice::AudioThreadCallback
- : public AudioDeviceThread::Callback {
- public:
- AudioThreadCallback(const AudioParameters& audio_parameters,
- base::SharedMemoryHandle memory,
- int memory_length,
- CaptureCallback* capture_callback);
- virtual ~AudioThreadCallback();
-
- virtual void MapSharedMemory() OVERRIDE;
-
- // Called whenever we receive notifications about pending data.
- virtual void Process(int pending_data) OVERRIDE;
-
- private:
- CaptureCallback* capture_callback_;
- scoped_ptr<AudioBus> audio_bus_;
- DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback);
-};
-
-AudioInputDevice::AudioInputDevice(
- AudioInputIPC* ipc,
- const scoped_refptr<base::MessageLoopProxy>& io_loop)
- : ScopedLoopObserver(io_loop),
- callback_(NULL),
- event_handler_(NULL),
- ipc_(ipc),
- stream_id_(0),
- session_id_(0),
- pending_device_ready_(false),
- agc_is_enabled_(false) {
- CHECK(ipc_);
-}
-
-void AudioInputDevice::Initialize(const AudioParameters& params,
- CaptureCallback* callback,
- CaptureEventHandler* event_handler) {
- DCHECK(!callback_);
- DCHECK(!event_handler_);
- audio_parameters_ = params;
- callback_ = callback;
- event_handler_ = event_handler;
-}
-
-void AudioInputDevice::SetDevice(int session_id) {
- DVLOG(1) << "SetDevice (session_id=" << session_id << ")";
- message_loop()->PostTask(FROM_HERE,
- base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id));
-}
-
-void AudioInputDevice::Start() {
- DVLOG(1) << "Start()";
- message_loop()->PostTask(FROM_HERE,
- base::Bind(&AudioInputDevice::InitializeOnIOThread, this));
-}
-
-void AudioInputDevice::Stop() {
- DVLOG(1) << "Stop()";
-
- {
- base::AutoLock auto_lock(audio_thread_lock_);
- audio_thread_.Stop(MessageLoop::current());
- }
-
- message_loop()->PostTask(FROM_HERE,
- base::Bind(&AudioInputDevice::ShutDownOnIOThread, this));
-}
-
-void AudioInputDevice::SetVolume(double volume) {
- if (volume < 0 || volume > 1.0) {
- DLOG(ERROR) << "Invalid volume value specified";
- return;
- }
-
- message_loop()->PostTask(FROM_HERE,
- base::Bind(&AudioInputDevice::SetVolumeOnIOThread, this, volume));
-}
-
-void AudioInputDevice::SetAutomaticGainControl(bool enabled) {
- DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")";
- message_loop()->PostTask(FROM_HERE,
- base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread,
- this, enabled));
-}
-
-void AudioInputDevice::OnStreamCreated(
- base::SharedMemoryHandle handle,
- base::SyncSocket::Handle socket_handle,
- int length) {
- DCHECK(message_loop()->BelongsToCurrentThread());
-#if defined(OS_WIN)
- DCHECK(handle);
- DCHECK(socket_handle);
-#elif defined(__LB_SHELL__) || defined(COBALT)
- DCHECK(handle.get());
-#else
- DCHECK_GE(handle.fd, 0);
- DCHECK_GE(socket_handle, 0);
-#endif
- DCHECK(length);
- DVLOG(1) << "OnStreamCreated (stream_id=" << stream_id_ << ")";
-
- // We should only get this callback if stream_id_ is valid. If it is not,
- // the IPC layer should have closed the shared memory and socket handles
- // for us and not invoked the callback. The basic assertion is that when
- // stream_id_ is 0 the AudioInputDevice instance is not registered as a
- // delegate and hence it should not receive callbacks.
- DCHECK(stream_id_);
-
- base::AutoLock auto_lock(audio_thread_lock_);
-
- DCHECK(audio_thread_.IsStopped());
- audio_callback_.reset(
- new AudioInputDevice::AudioThreadCallback(audio_parameters_, handle,
- length, callback_));
- audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice");
-
- MessageLoop::current()->PostTask(FROM_HERE,
- base::Bind(&AudioInputDevice::StartOnIOThread, this));
-}
-
-void AudioInputDevice::OnVolume(double volume) {
- NOTIMPLEMENTED();
-}
-
-void AudioInputDevice::OnStateChanged(
- AudioInputIPCDelegate::State state) {
- DCHECK(message_loop()->BelongsToCurrentThread());
-
- // Do nothing if the stream has been closed.
- if (!stream_id_)
- return;
-
- switch (state) {
- case AudioInputIPCDelegate::kStopped:
- // TODO(xians): Should we just call ShutDownOnIOThread here instead?
- ipc_->RemoveDelegate(stream_id_);
-
- audio_thread_.Stop(MessageLoop::current());
- audio_callback_.reset();
-
- if (event_handler_)
- event_handler_->OnDeviceStopped();
-
- stream_id_ = 0;
- pending_device_ready_ = false;
- break;
- case AudioInputIPCDelegate::kRecording:
- NOTIMPLEMENTED();
- break;
- case AudioInputIPCDelegate::kError:
- DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)";
- // Don't dereference the callback object if the audio thread
- // is stopped or stopping. That could mean that the callback
- // object has been deleted.
- // TODO(tommi): Add an explicit contract for clearing the callback
- // object. Possibly require calling Initialize again or provide
- // a callback object via Start() and clear it in Stop().
- if (!audio_thread_.IsStopped())
- callback_->OnCaptureError();
- break;
- default:
- NOTREACHED();
- break;
- }
-}
-
-void AudioInputDevice::OnDeviceReady(const std::string& device_id) {
- DCHECK(message_loop()->BelongsToCurrentThread());
- DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")";
-
- // Takes care of the case when Stop() is called before OnDeviceReady().
- if (!pending_device_ready_)
- return;
-
- // If AudioInputDeviceManager returns an empty string, it means no device
- // is ready for start.
- if (device_id.empty()) {
- ipc_->RemoveDelegate(stream_id_);
- stream_id_ = 0;
- } else {
- ipc_->CreateStream(stream_id_, audio_parameters_, device_id,
- agc_is_enabled_);
- }
-
- pending_device_ready_ = false;
- // Notify the client that the device has been started.
- if (event_handler_)
- event_handler_->OnDeviceStarted(device_id);
-}
-
-void AudioInputDevice::OnIPCClosed() {
- ipc_ = NULL;
-}
-
-AudioInputDevice::~AudioInputDevice() {
- // TODO(henrika): The current design requires that the user calls
- // Stop before deleting this class.
- CHECK_EQ(0, stream_id_);
-}
-
-void AudioInputDevice::InitializeOnIOThread() {
- DCHECK(message_loop()->BelongsToCurrentThread());
- // Make sure we don't call Start() more than once.
- DCHECK_EQ(0, stream_id_);
- if (stream_id_)
- return;
-
- stream_id_ = ipc_->AddDelegate(this);
- // If |session_id_| is not specified, it will directly create the stream;
- // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser
- // and create the stream when getting a OnDeviceReady() callback.
- if (!session_id_) {
- ipc_->CreateStream(stream_id_, audio_parameters_,
- AudioManagerBase::kDefaultDeviceId, agc_is_enabled_);
- } else {
- ipc_->StartDevice(stream_id_, session_id_);
- pending_device_ready_ = true;
- }
-}
-
-void AudioInputDevice::SetSessionIdOnIOThread(int session_id) {
- DCHECK(message_loop()->BelongsToCurrentThread());
- session_id_ = session_id;
-}
-
-void AudioInputDevice::StartOnIOThread() {
- DCHECK(message_loop()->BelongsToCurrentThread());
- if (stream_id_)
- ipc_->RecordStream(stream_id_);
-}
-
-void AudioInputDevice::ShutDownOnIOThread() {
- DCHECK(message_loop()->BelongsToCurrentThread());
- // NOTE: |completion| may be NULL.
- // Make sure we don't call shutdown more than once.
- if (stream_id_) {
- if (ipc_) {
- ipc_->CloseStream(stream_id_);
- ipc_->RemoveDelegate(stream_id_);
- }
-
- stream_id_ = 0;
- session_id_ = 0;
- pending_device_ready_ = false;
- agc_is_enabled_ = false;
- }
-
- // We can run into an issue where ShutDownOnIOThread is called right after
- // OnStreamCreated is called in cases where Start/Stop are called before we
- // get the OnStreamCreated callback. To handle that corner case, we call
- // Stop(). In most cases, the thread will already be stopped.
- // Another situation is when the IO thread goes away before Stop() is called
- // in which case, we cannot use the message loop to close the thread handle
- // and can't not rely on the main thread existing either.
- base::ThreadRestrictions::ScopedAllowIO allow_io;
- audio_thread_.Stop(NULL);
- audio_callback_.reset();
-}
-
-void AudioInputDevice::SetVolumeOnIOThread(double volume) {
- DCHECK(message_loop()->BelongsToCurrentThread());
- if (stream_id_)
- ipc_->SetVolume(stream_id_, volume);
-}
-
-void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) {
- DCHECK(message_loop()->BelongsToCurrentThread());
- DCHECK_EQ(0, stream_id_) <<
- "The AGC state can not be modified while capturing is active.";
- if (stream_id_)
- return;
-
- // We simply store the new AGC setting here. This value will be used when
- // a new stream is initialized and by GetAutomaticGainControl().
- agc_is_enabled_ = enabled;
-}
-
-void AudioInputDevice::WillDestroyCurrentMessageLoop() {
- LOG(ERROR) << "IO loop going away before the input device has been stopped";
- ShutDownOnIOThread();
-}
-
-// AudioInputDevice::AudioThreadCallback
-AudioInputDevice::AudioThreadCallback::AudioThreadCallback(
- const AudioParameters& audio_parameters,
- base::SharedMemoryHandle memory,
- int memory_length,
- CaptureCallback* capture_callback)
- : AudioDeviceThread::Callback(audio_parameters, 0, memory, memory_length),
- capture_callback_(capture_callback) {
- audio_bus_ = AudioBus::Create(audio_parameters_);
-}
-
-AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() {
-}
-
-void AudioInputDevice::AudioThreadCallback::MapSharedMemory() {
- shared_memory_.Map(memory_length_);
-}
-
-void AudioInputDevice::AudioThreadCallback::Process(int pending_data) {
- // The shared memory represents parameters, size of the data buffer and the
- // actual data buffer containing audio data. Map the memory into this
- // structure and parse out parameters and the data area.
- AudioInputBuffer* buffer =
- reinterpret_cast<AudioInputBuffer*>(shared_memory_.memory());
- DCHECK_EQ(buffer->params.size,
- memory_length_ - sizeof(AudioInputBufferParameters));
- double volume = buffer->params.volume;
-
- int audio_delay_milliseconds = pending_data / bytes_per_ms_;
- int16* memory = reinterpret_cast<int16*>(&buffer->audio[0]);
- const int bytes_per_sample = sizeof(memory[0]);
-
- // Deinterleave each channel and convert to 32-bit floating-point
- // with nominal range -1.0 -> +1.0.
- audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample);
-
- // Deliver captured data to the client in floating point format
- // and update the audio-delay measurement.
- capture_callback_->Capture(audio_bus_.get(),
- audio_delay_milliseconds, volume);
-}
-
-} // namespace media
diff --git a/src/media/audio/audio_input_device.h b/src/media/audio/audio_input_device.h
deleted file mode 100644
index edefdf1..0000000
--- a/src/media/audio/audio_input_device.h
+++ /dev/null
@@ -1,170 +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.
-
-// Low-latency audio capturing class utilizing audio input stream provided
-// by a server (browser) process by use of an IPC interface.
-//
-// Relationship of classes:
-//
-// AudioInputController AudioInputDevice
-// ^ ^
-// | |
-// v IPC v
-// AudioInputRendererHost <---------> AudioInputIPCDelegate
-// ^ (impl in AudioInputMessageFilter)
-// |
-// v
-// AudioInputDeviceManager
-//
-// Transportation of audio samples from the browser to the render process
-// is done by using shared memory in combination with a SyncSocket.
-// The AudioInputDevice user registers an AudioInputDevice::CaptureCallback by
-// calling Initialize(). The callback will be called with recorded audio from
-// the underlying audio layers.
-// The session ID is used by the AudioInputRendererHost to start the device
-// referenced by this ID.
-//
-// State sequences:
-//
-// Sequence where session_id has not been set using SetDevice():
-// ('<-' signifies callbacks, -> signifies calls made by AudioInputDevice)
-// Start -> InitializeOnIOThread -> CreateStream ->
-// <- OnStreamCreated <-
-// -> StartOnIOThread -> PlayStream ->
-//
-// Sequence where session_id has been set using SetDevice():
-// Start -> InitializeOnIOThread -> StartDevice ->
-// <- OnDeviceReady <-
-// -> CreateStream ->
-// <- OnStreamCreated <-
-// -> StartOnIOThread -> PlayStream ->
-//
-// AudioInputDevice::Capture => low latency audio transport on audio thread =>
-// |
-// Stop --> ShutDownOnIOThread ------> CloseStream -> Close
-//
-// This class depends on two threads to function:
-//
-// 1. An IO thread.
-// This thread is used to asynchronously process Start/Stop etc operations
-// that are available via the public interface. The public methods are
-// asynchronous and simply post a task to the IO thread to actually perform
-// the work.
-// 2. Audio transport thread.
-// Responsible for calling the CaptureCallback and feed audio samples from
-// the server side audio layer using a socket and shared memory.
-//
-// Implementation notes:
-// - The user must call Stop() before deleting the class instance.
-
-#ifndef MEDIA_AUDIO_AUDIO_INPUT_DEVICE_H_
-#define MEDIA_AUDIO_AUDIO_INPUT_DEVICE_H_
-
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/shared_memory.h"
-#include "media/audio/audio_device_thread.h"
-#include "media/audio/audio_input_ipc.h"
-#include "media/audio/audio_parameters.h"
-#include "media/audio/scoped_loop_observer.h"
-#include "media/base/audio_capturer_source.h"
-#include "media/base/media_export.h"
-
-namespace media {
-
-// TODO(henrika): This class is based on the AudioOutputDevice class and it has
-// many components in common. Investigate potential for re-factoring.
-// TODO(henrika): Add support for event handling (e.g. OnStateChanged,
-// OnCaptureStopped etc.) and ensure that we can deliver these notifications
-// to any clients using this class.
-class MEDIA_EXPORT AudioInputDevice
- : NON_EXPORTED_BASE(public AudioCapturerSource),
- NON_EXPORTED_BASE(public AudioInputIPCDelegate),
- NON_EXPORTED_BASE(public ScopedLoopObserver) {
- public:
- AudioInputDevice(AudioInputIPC* ipc,
- const scoped_refptr<base::MessageLoopProxy>& io_loop);
-
- // AudioCapturerSource implementation.
- virtual void Initialize(const AudioParameters& params,
- CaptureCallback* callback,
- CaptureEventHandler* event_handler) OVERRIDE;
- virtual void Start() OVERRIDE;
- virtual void Stop() OVERRIDE;
- virtual void SetVolume(double volume) OVERRIDE;
- virtual void SetDevice(int session_id) OVERRIDE;
- virtual void SetAutomaticGainControl(bool enabled) OVERRIDE;
-
- protected:
- // Methods called on IO thread ----------------------------------------------
- // AudioInputIPCDelegate implementation.
- virtual void OnStreamCreated(base::SharedMemoryHandle handle,
- base::SyncSocket::Handle socket_handle,
- int length) OVERRIDE;
- virtual void OnVolume(double volume) OVERRIDE;
- virtual void OnStateChanged(
- AudioInputIPCDelegate::State state) OVERRIDE;
- virtual void OnDeviceReady(const std::string& device_id) OVERRIDE;
- virtual void OnIPCClosed() OVERRIDE;
-
- friend class base::RefCountedThreadSafe<AudioInputDevice>;
- virtual ~AudioInputDevice();
-
- private:
- // Methods called on IO thread ----------------------------------------------
- // The following methods are tasks posted on the IO thread that needs to
- // be executed on that thread. They interact with AudioInputMessageFilter and
- // sends IPC messages on that thread.
- void InitializeOnIOThread();
- void SetSessionIdOnIOThread(int session_id);
- void StartOnIOThread();
- void ShutDownOnIOThread();
- void SetVolumeOnIOThread(double volume);
- void SetAutomaticGainControlOnIOThread(bool enabled);
-
- // MessageLoop::DestructionObserver implementation for the IO loop.
- // If the IO loop dies before we do, we shut down the audio thread from here.
- virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
-
- AudioParameters audio_parameters_;
-
- CaptureCallback* callback_;
- CaptureEventHandler* event_handler_;
-
- AudioInputIPC* ipc_;
-
- // Our stream ID on the message filter. Only modified on the IO thread.
- int stream_id_;
-
- // The media session ID used to identify which input device to be started.
- // Only modified on the IO thread.
- int session_id_;
-
- // State variable used to indicate it is waiting for a OnDeviceReady()
- // callback. Only modified on the IO thread.
- bool pending_device_ready_;
-
- // Stores the Automatic Gain Control state. Default is false.
- // Only modified on the IO thread.
- bool agc_is_enabled_;
-
- // Our audio thread callback class. See source file for details.
- class AudioThreadCallback;
-
- // In order to avoid a race between OnStreamCreated and Stop(), we use this
- // guard to control stopping and starting the audio thread.
- base::Lock audio_thread_lock_;
- AudioDeviceThread audio_thread_;
- scoped_ptr<AudioInputDevice::AudioThreadCallback> audio_callback_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(AudioInputDevice);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_INPUT_DEVICE_H_
diff --git a/src/media/audio/audio_input_device_unittest.cc b/src/media/audio/audio_input_device_unittest.cc
deleted file mode 100644
index dc211a4..0000000
--- a/src/media/audio/audio_input_device_unittest.cc
+++ /dev/null
@@ -1,199 +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 "base/environment.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "media/audio/audio_manager.h"
-#include "media/audio/audio_manager_base.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#include "media/audio/win/audio_manager_win.h"
-#include "media/audio/win/wavein_input_win.h"
-#endif
-
-namespace media {
-
-// Test fixture which allows us to override the default enumeration API on
-// Windows.
-class AudioInputDeviceTest
- : public ::testing::Test {
- protected:
- AudioInputDeviceTest()
- : audio_manager_(AudioManager::Create())
-#if defined(OS_WIN)
- , com_init_(base::win::ScopedCOMInitializer::kMTA)
-#endif
- {
- }
-
-#if defined(OS_WIN)
- bool SetMMDeviceEnumeration() {
- AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
- // Windows Wave is used as default if Windows XP was detected =>
- // return false since MMDevice is not supported on XP.
- if (amw->enumeration_type() == AudioManagerWin::kWaveEnumeration)
- return false;
-
- amw->SetEnumerationType(AudioManagerWin::kMMDeviceEnumeration);
- return true;
- }
-
- void SetWaveEnumeration() {
- AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
- amw->SetEnumerationType(AudioManagerWin::kWaveEnumeration);
- }
-
- std::string GetDeviceIdFromPCMWaveInAudioInputStream(
- const std::string& device_id) {
- AudioManagerWin* amw = static_cast<AudioManagerWin*>(audio_manager_.get());
- AudioParameters parameters(
- AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
- AudioParameters::kAudioCDSampleRate, 16,
- 1024);
- scoped_ptr<PCMWaveInAudioInputStream> stream(
- static_cast<PCMWaveInAudioInputStream*>(
- amw->CreatePCMWaveInAudioInputStream(parameters, device_id)));
- return stream.get() ? stream->device_id_ : std::string();
- }
-#endif
-
- // Helper method which verifies that the device list starts with a valid
- // default record followed by non-default device names.
- static void CheckDeviceNames(const AudioDeviceNames& device_names) {
- if (!device_names.empty()) {
- AudioDeviceNames::const_iterator it = device_names.begin();
-
- // The first device in the list should always be the default device.
- EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceName),
- it->device_name);
- EXPECT_EQ(std::string(AudioManagerBase::kDefaultDeviceId), it->unique_id);
- ++it;
-
- // Other devices should have non-empty name and id and should not contain
- // default name or id.
- while (it != device_names.end()) {
- EXPECT_FALSE(it->device_name.empty());
- EXPECT_FALSE(it->unique_id.empty());
- EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceName),
- it->device_name);
- EXPECT_NE(std::string(AudioManagerBase::kDefaultDeviceId),
- it->unique_id);
- ++it;
- }
- } else {
- // Log a warning so we can see the status on the build bots. No need to
- // break the test though since this does successfully test the code and
- // some failure cases.
- LOG(WARNING) << "No input devices detected";
- }
- }
-
- bool CanRunAudioTest() {
- return audio_manager_->HasAudioInputDevices();
- }
-
- scoped_ptr<AudioManager> audio_manager_;
-
-#if defined(OS_WIN)
- // The MMDevice API requires COM to be initialized on the current thread.
- base::win::ScopedCOMInitializer com_init_;
-#endif
-};
-
-// Test that devices can be enumerated.
-TEST_F(AudioInputDeviceTest, EnumerateDevices) {
- if (!CanRunAudioTest())
- return;
-
- AudioDeviceNames device_names;
- audio_manager_->GetAudioInputDeviceNames(&device_names);
- CheckDeviceNames(device_names);
-}
-
-// Run additional tests for Windows since enumeration can be done using
-// two different APIs. MMDevice is default for Vista and higher and Wave
-// is default for XP and lower.
-#if defined(OS_WIN)
-
-// Override default enumeration API and force usage of Windows MMDevice.
-// This test will only run on Windows Vista and higher.
-TEST_F(AudioInputDeviceTest, EnumerateDevicesWinMMDevice) {
- if (!CanRunAudioTest())
- return;
-
- AudioDeviceNames device_names;
- if (!SetMMDeviceEnumeration()) {
- // Usage of MMDevice will fail on XP and lower.
- LOG(WARNING) << "MM device enumeration is not supported.";
- return;
- }
- audio_manager_->GetAudioInputDeviceNames(&device_names);
- CheckDeviceNames(device_names);
-}
-
-// Override default enumeration API and force usage of Windows Wave.
-// This test will run on Windows XP, Windows Vista and Windows 7.
-TEST_F(AudioInputDeviceTest, EnumerateDevicesWinWave) {
- if (!CanRunAudioTest())
- return;
-
- AudioDeviceNames device_names;
- SetWaveEnumeration();
- audio_manager_->GetAudioInputDeviceNames(&device_names);
- CheckDeviceNames(device_names);
-}
-
-TEST_F(AudioInputDeviceTest, WinXPDeviceIdUnchanged) {
- if (!CanRunAudioTest())
- return;
-
- AudioDeviceNames xp_device_names;
- SetWaveEnumeration();
- audio_manager_->GetAudioInputDeviceNames(&xp_device_names);
- CheckDeviceNames(xp_device_names);
-
- // Device ID should remain unchanged, including the default device ID.
- for (AudioDeviceNames::iterator i = xp_device_names.begin();
- i != xp_device_names.end(); ++i) {
- EXPECT_EQ(i->unique_id,
- GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id));
- }
-}
-
-TEST_F(AudioInputDeviceTest, ConvertToWinXPDeviceId) {
- if (!CanRunAudioTest())
- return;
-
- if (!SetMMDeviceEnumeration()) {
- // Usage of MMDevice will fail on XP and lower.
- LOG(WARNING) << "MM device enumeration is not supported.";
- return;
- }
-
- AudioDeviceNames device_names;
- audio_manager_->GetAudioInputDeviceNames(&device_names);
- CheckDeviceNames(device_names);
-
- for (AudioDeviceNames::iterator i = device_names.begin();
- i != device_names.end(); ++i) {
- std::string converted_id =
- GetDeviceIdFromPCMWaveInAudioInputStream(i->unique_id);
- if (i == device_names.begin()) {
- // The first in the list is the default device ID, which should not be
- // changed when passed to PCMWaveInAudioInputStream.
- EXPECT_EQ(i->unique_id, converted_id);
- } else {
- // MMDevice-style device IDs should be converted to WaveIn-style device
- // IDs.
- EXPECT_NE(i->unique_id, converted_id);
- }
- }
-}
-
-#endif
-
-} // namespace media
diff --git a/src/media/audio/audio_input_ipc.cc b/src/media/audio/audio_input_ipc.cc
deleted file mode 100644
index 69253b0..0000000
--- a/src/media/audio/audio_input_ipc.cc
+++ /dev/null
@@ -1,13 +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 "media/audio/audio_input_ipc.h"
-
-namespace media {
-
-AudioInputIPCDelegate::~AudioInputIPCDelegate() {}
-
-AudioInputIPC::~AudioInputIPC() {}
-
-} // namespace media
diff --git a/src/media/audio/audio_input_ipc.h b/src/media/audio/audio_input_ipc.h
deleted file mode 100644
index eb4e72d..0000000
--- a/src/media/audio/audio_input_ipc.h
+++ /dev/null
@@ -1,104 +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 MEDIA_AUDIO_AUDIO_INPUT_IPC_H_
-#define MEDIA_AUDIO_AUDIO_INPUT_IPC_H_
-
-#include "base/shared_memory.h"
-#include "base/sync_socket.h"
-#include "media/audio/audio_parameters.h"
-#include "media/base/media_export.h"
-
-namespace media {
-
-// Contains IPC notifications for the state of the server side
-// (AudioInputController) audio state changes and when an AudioInputController
-// has been created. Implemented by AudioInputDevice.
-class MEDIA_EXPORT AudioInputIPCDelegate {
- public:
- // Valid states for the input stream.
- enum State {
- kRecording,
- kStopped,
- kError
- };
-
- // Called when an AudioInputController has been created.
- // The shared memory |handle| points to a memory section that's used to
- // transfer data between the AudioInputDevice and AudioInputController
- // objects. The implementation of OnStreamCreated takes ownership.
- // The |socket_handle| is used by the AudioInputController to signal
- // notifications that more data is available and can optionally provide
- // parameter changes back. The AudioInputDevice must read from this socket
- // and process the shared memory whenever data is read from the socket.
- virtual void OnStreamCreated(base::SharedMemoryHandle handle,
- base::SyncSocket::Handle socket_handle,
- int length) = 0;
-
- // Called when state of an audio stream has changed.
- virtual void OnStateChanged(State state) = 0;
-
- // Called when the input stream volume has changed.
- virtual void OnVolume(double volume) = 0;
-
- // Called when a device has been started on the server side.
- // If the device could not be started, |device_id| will be empty.
- virtual void OnDeviceReady(const std::string& device_id) = 0;
-
- // Called when the AudioInputIPC object is going away and/or when the
- // IPC channel has been closed and no more IPC requests can be made.
- // Implementations must clear any references to the AudioInputIPC object
- // at this point.
- virtual void OnIPCClosed() = 0;
-
- protected:
- virtual ~AudioInputIPCDelegate();
-};
-
-// Provides IPC functionality for an AudioInputDevice. The implementation
-// should asynchronously deliver the messages to an AudioInputController object
-// (or create one in the case of CreateStream()), that may live in a separate
-// process.
-class MEDIA_EXPORT AudioInputIPC {
- public:
- // Registers an AudioInputIPCDelegate and returns a |stream_id| that
- // must be used with all other IPC functions in this interface.
- virtual int AddDelegate(AudioInputIPCDelegate* delegate) = 0;
-
- // Unregisters a delegate that was previously registered via a call to
- // AddDelegate(). The audio stream should be in a closed state prior to
- // calling this function.
- virtual void RemoveDelegate(int stream_id) = 0;
-
- // Sends a request to create an AudioInputController object in the peer
- // process, identify it by |stream_id| and configure it to use the specified
- // audio |params|. Once the stream has been created, the implementation must
- // generate a notification to the AudioInputIPCDelegate and call
- // OnStreamCreated().
- virtual void CreateStream(int stream_id, const AudioParameters& params,
- const std::string& device_id, bool automatic_gain_control) = 0;
-
- // Starts the device on the server side. Once the device has started,
- // or failed to start, a callback to
- // AudioInputIPCDelegate::OnDeviceReady() must be made.
- virtual void StartDevice(int stream_id, int session_id) = 0;
-
- // Corresponds to a call to AudioInputController::Record() on the server side.
- virtual void RecordStream(int stream_id) = 0;
-
- // Sets the volume of the audio stream.
- virtual void SetVolume(int stream_id, double volume) = 0;
-
- // Closes the audio stream and deletes the matching AudioInputController
- // instance. Prior to deleting the AudioInputController object, a call to
- // AudioInputController::Close must be made.
- virtual void CloseStream(int stream_id) = 0;
-
- protected:
- virtual ~AudioInputIPC();
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_INPUT_IPC_H_
diff --git a/src/media/audio/audio_input_stream_impl.cc b/src/media/audio/audio_input_stream_impl.cc
deleted file mode 100644
index f68317c..0000000
--- a/src/media/audio/audio_input_stream_impl.cc
+++ /dev/null
@@ -1,71 +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 "base/logging.h"
-#include "media/audio/audio_input_stream_impl.h"
-
-namespace media {
-
-static const int kMinIntervalBetweenVolumeUpdatesMs = 1000;
-
-AudioInputStreamImpl::AudioInputStreamImpl()
- : agc_is_enabled_(false),
- max_volume_(0.0),
- normalized_volume_(0.0) {
-}
-
-AudioInputStreamImpl::~AudioInputStreamImpl() {}
-
-void AudioInputStreamImpl::SetAutomaticGainControl(bool enabled) {
- agc_is_enabled_ = enabled;
-}
-
-bool AudioInputStreamImpl::GetAutomaticGainControl() {
- return agc_is_enabled_;
-}
-
-void AudioInputStreamImpl::UpdateAgcVolume() {
- base::AutoLock lock(lock_);
-
- // We take new volume samples once every second when the AGC is enabled.
- // To ensure that a new setting has an immediate effect, the new volume
- // setting is cached here. It will ensure that the next OnData() callback
- // will contain a new valid volume level. If this approach was not taken,
- // we could report invalid volume levels to the client for a time period
- // of up to one second.
- if (agc_is_enabled_) {
- GetNormalizedVolume();
- }
-}
-
-void AudioInputStreamImpl::QueryAgcVolume(double* normalized_volume) {
- base::AutoLock lock(lock_);
-
- // Only modify the |volume| output reference if AGC is enabled and if
- // more than one second has passed since the volume was updated the last time.
- if (agc_is_enabled_) {
- base::Time now = base::Time::Now();
- if ((now - last_volume_update_time_).InMilliseconds() >
- kMinIntervalBetweenVolumeUpdatesMs) {
- GetNormalizedVolume();
- last_volume_update_time_ = now;
- }
- *normalized_volume = normalized_volume_;
- }
-}
-
-void AudioInputStreamImpl::GetNormalizedVolume() {
- if (max_volume_ == 0.0) {
- // Cach the maximum volume if this is the first time we ask for it.
- max_volume_ = GetMaxVolume();
- }
-
- if (max_volume_ != 0.0) {
- // Retrieve the current volume level by asking the audio hardware.
- // Range is normalized to [0.0,1.0] or [0.0, 1.5] on Linux.
- normalized_volume_ = GetVolume() / max_volume_;
- }
-}
-
-} // namespace media
diff --git a/src/media/audio/audio_input_stream_impl.h b/src/media/audio/audio_input_stream_impl.h
deleted file mode 100644
index 64980a9..0000000
--- a/src/media/audio/audio_input_stream_impl.h
+++ /dev/null
@@ -1,71 +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 MEDIA_AUDIO_AUDIO_INPUT_STREAM_IMPL_H_
-#define MEDIA_AUDIO_AUDIO_INPUT_STREAM_IMPL_H_
-
-#include "base/compiler_specific.h"
-#include "base/synchronization/lock.h"
-#include "base/time.h"
-#include "media/audio/audio_io.h"
-
-namespace media {
-
-// AudioInputStreamImpl implements platform-independent parts of the
-// AudioInputStream interface. Each platform dependent implementation
-// should derive from this class.
-// TODO(henrika): we can probably break out more parts from our current
-// AudioInputStream implementation and move out to this class.
-class MEDIA_EXPORT AudioInputStreamImpl : public AudioInputStream {
- public:
- AudioInputStreamImpl();
- virtual ~AudioInputStreamImpl();
-
- // Sets the automatic gain control (AGC) to on or off. When AGC is enabled,
- // the microphone volume is queried periodically and the volume level is
- // provided in each AudioInputCallback::OnData() callback and fed to the
- // render-side AGC.
- virtual void SetAutomaticGainControl(bool enabled) OVERRIDE;
-
- // Gets the current automatic gain control state.
- virtual bool GetAutomaticGainControl() OVERRIDE;
-
- protected:
- // Stores a new volume level by asking the audio hardware.
- // This method only has an effect if AGC is enabled.
- void UpdateAgcVolume();
-
- // Gets the latest stored volume level if AGC is enabled and if
- // more than one second has passed since the volume was updated the last time.
- void QueryAgcVolume(double* normalized_volume);
-
- private:
- // Takes a volume sample and stores it in |normalized_volume_|.
- void GetNormalizedVolume();
-
- // True when automatic gain control is enabled, false otherwise.
- // Guarded by |lock_|.
- bool agc_is_enabled_;
-
- // Stores the maximum volume which is used for normalization to a volume
- // range of [0.0, 1.0].
- double max_volume_;
-
- // Contains last result of internal call to GetVolume(). We save resources
- // but not querying the capture volume for each callback. Guarded by |lock_|.
- // The range is normalized to [0.0, 1.0].
- double normalized_volume_;
-
- // Protects |agc_is_enabled_| and |volume_| .
- base::Lock lock_;
-
- // Keeps track of the last time the microphone volume level was queried.
- base::Time last_volume_update_time_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioInputStreamImpl);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_INPUT_STREAM_IMPL_H_
diff --git a/src/media/audio/audio_input_unittest.cc b/src/media/audio/audio_input_unittest.cc
deleted file mode 100644
index 5a02323..0000000
--- a/src/media/audio/audio_input_unittest.cc
+++ /dev/null
@@ -1,164 +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 "base/basictypes.h"
-#include "base/environment.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop.h"
-#include "base/threading/platform_thread.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_manager_base.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace media {
-
-static const int kSamplingRate = 8000;
-static const int kSamplesPerPacket = kSamplingRate / 20;
-
-// This class allows to find out if the callbacks are occurring as
-// expected and if any error has been reported.
-class TestInputCallback : public AudioInputStream::AudioInputCallback {
- public:
- explicit TestInputCallback(int max_data_bytes)
- : callback_count_(0),
- had_error_(0),
- max_data_bytes_(max_data_bytes) {
- }
- virtual void OnData(AudioInputStream* stream, const uint8* data,
- uint32 size, uint32 hardware_delay_bytes, double volume) {
- ++callback_count_;
- // Read the first byte to make sure memory is good.
- if (size) {
- ASSERT_LE(static_cast<int>(size), max_data_bytes_);
- int value = data[0];
- EXPECT_GE(value, 0);
- }
- }
- virtual void OnClose(AudioInputStream* stream) {}
- virtual void OnError(AudioInputStream* stream, int code) {
- ++had_error_;
- }
- // Returns how many times OnData() has been called.
- int callback_count() const {
- return callback_count_;
- }
- // Returns how many times the OnError callback was called.
- int had_error() const {
- return had_error_;
- }
-
- private:
- int callback_count_;
- int had_error_;
- int max_data_bytes_;
-};
-
-static bool CanRunAudioTests(AudioManager* audio_man) {
- bool has_input = audio_man->HasAudioInputDevices();
-
- if (!has_input)
- LOG(WARNING) << "No input devices detected";
-
- return has_input;
-}
-
-static AudioInputStream* CreateTestAudioInputStream(AudioManager* audio_man) {
- AudioInputStream* ais = audio_man->MakeAudioInputStream(
- AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
- kSamplingRate, 16, kSamplesPerPacket),
- AudioManagerBase::kDefaultDeviceId);
- EXPECT_TRUE(NULL != ais);
- return ais;
-}
-
-// Test that AudioInputStream rejects out of range parameters.
-TEST(AudioInputTest, SanityOnMakeParams) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
- if (!CanRunAudioTests(audio_man.get()))
- return;
-
- AudioParameters::Format fmt = AudioParameters::AUDIO_PCM_LINEAR;
- EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
- AudioParameters(fmt, CHANNEL_LAYOUT_7_1, 8000, 16,
- kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
- EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
- AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 1024 * 1024, 16,
- kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
- EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
- AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, 80,
- kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
- EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
- AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, 80,
- 1000 * kSamplesPerPacket),
- AudioManagerBase::kDefaultDeviceId));
- EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
- AudioParameters(fmt, CHANNEL_LAYOUT_UNSUPPORTED, 8000, 16,
- kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
- EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
- AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, -8000, 16,
- kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
- EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
- AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, -16,
- kSamplesPerPacket), AudioManagerBase::kDefaultDeviceId));
- EXPECT_TRUE(NULL == audio_man->MakeAudioInputStream(
- AudioParameters(fmt, CHANNEL_LAYOUT_STEREO, 8000, 16, -1024),
- AudioManagerBase::kDefaultDeviceId));
-}
-
-// Test create and close of an AudioInputStream without recording audio.
-TEST(AudioInputTest, CreateAndClose) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
- if (!CanRunAudioTests(audio_man.get()))
- return;
- AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
- ais->Close();
-}
-
-// Test create, open and close of an AudioInputStream without recording audio.
-TEST(AudioInputTest, OpenAndClose) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
- if (!CanRunAudioTests(audio_man.get()))
- return;
- AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
- EXPECT_TRUE(ais->Open());
- ais->Close();
-}
-
-// Test create, open, stop and close of an AudioInputStream without recording.
-TEST(AudioInputTest, OpenStopAndClose) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
- if (!CanRunAudioTests(audio_man.get()))
- return;
- AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
- EXPECT_TRUE(ais->Open());
- ais->Stop();
- ais->Close();
-}
-
-// Test a normal recording sequence using an AudioInputStream.
-TEST(AudioInputTest, Record) {
- scoped_ptr<AudioManager> audio_man(AudioManager::Create());
- if (!CanRunAudioTests(audio_man.get()))
- return;
- MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
- AudioInputStream* ais = CreateTestAudioInputStream(audio_man.get());
- EXPECT_TRUE(ais->Open());
-
- TestInputCallback test_callback(kSamplesPerPacket * 4);
- ais->Start(&test_callback);
- // Verify at least 500ms worth of audio was recorded, after giving sufficient
- // extra time.
- message_loop.PostDelayedTask(
- FROM_HERE,
- MessageLoop::QuitClosure(),
- base::TimeDelta::FromMilliseconds(690));
- message_loop.Run();
- EXPECT_GE(test_callback.callback_count(), 1);
- EXPECT_FALSE(test_callback.had_error());
-
- ais->Stop();
- ais->Close();
-}
-
-} // namespace media
diff --git a/src/media/audio/audio_input_volume_unittest.cc b/src/media/audio/audio_input_volume_unittest.cc
deleted file mode 100644
index 8f754cc..0000000
--- a/src/media/audio/audio_input_volume_unittest.cc
+++ /dev/null
@@ -1,171 +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 <cmath>
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_manager_base.h"
-#include "media/audio/audio_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#include "media/audio/win/core_audio_util_win.h"
-#endif
-
-namespace media {
-
-class AudioInputVolumeTest : public ::testing::Test {
- protected:
- AudioInputVolumeTest()
- : audio_manager_(AudioManager::Create())
-#if defined(OS_WIN)
- , com_init_(base::win::ScopedCOMInitializer::kMTA)
-#endif
- {
- }
-
- bool CanRunAudioTests() {
-#if defined(OS_WIN)
- // TODO(henrika): add support for volume control on Windows XP as well.
- // For now, we might as well signal false already here to avoid running
- // these tests on Windows XP.
- if (!CoreAudioUtil::IsSupported())
- return false;
-#endif
- if (!audio_manager_.get())
- return false;
-
- return audio_manager_->HasAudioInputDevices();
- }
-
- // Helper method which checks if the stream has volume support.
- bool HasDeviceVolumeControl(AudioInputStream* stream) {
- if (!stream)
- return false;
-
- return (stream->GetMaxVolume() != 0.0);
- }
-
- AudioInputStream* CreateAndOpenStream(const std::string& device_id) {
- AudioParameters::Format format = AudioParameters::AUDIO_PCM_LOW_LATENCY;
- ChannelLayout channel_layout =
- media::GetAudioInputHardwareChannelLayout(device_id);
- int bits_per_sample = 16;
- int sample_rate =
- static_cast<int>(media::GetAudioInputHardwareSampleRate(device_id));
- int samples_per_packet = 0;
-#if defined(OS_MACOSX)
- samples_per_packet = (sample_rate / 100);
-#elif defined(OS_LINUX) || defined(OS_OPENBSD)
- samples_per_packet = (sample_rate / 100);
-#elif defined(OS_WIN)
- if (sample_rate == 44100)
- samples_per_packet = 448;
- else
- samples_per_packet = (sample_rate / 100);
-#else
-#error Unsupported platform
-#endif
- AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
- AudioParameters(format, channel_layout, sample_rate, bits_per_sample,
- samples_per_packet),
- device_id);
- EXPECT_TRUE(NULL != ais);
-
-#if defined(OS_LINUX) || defined(OS_OPENBSD)
- // Some linux devices do not support our settings, we may fail to open
- // those devices.
- if (!ais->Open()) {
- // Default device should always be able to be opened.
- EXPECT_TRUE(AudioManagerBase::kDefaultDeviceId != device_id);
- ais->Close();
- ais = NULL;
- }
-#elif defined(OS_WIN) || defined(OS_MACOSX)
- EXPECT_TRUE(ais->Open());
-#endif
-
- return ais;
- }
-
- scoped_ptr<AudioManager> audio_manager_;
-
-#if defined(OS_WIN)
- base::win::ScopedCOMInitializer com_init_;
-#endif
-};
-
-TEST_F(AudioInputVolumeTest, InputVolumeTest) {
- if (!CanRunAudioTests())
- return;
-
- // Retrieve a list of all available input devices.
- AudioDeviceNames device_names;
- audio_manager_->GetAudioInputDeviceNames(&device_names);
- if (device_names.empty()) {
- LOG(WARNING) << "Could not find any available input device";
- return;
- }
-
- // Scan all available input devices and repeat the same test for all of them.
- for (AudioDeviceNames::const_iterator it = device_names.begin();
- it != device_names.end();
- ++it) {
- AudioInputStream* ais = CreateAndOpenStream(it->unique_id);
- if (!ais) {
- DLOG(WARNING) << "Failed to open stream for device " << it->unique_id;
- continue;
- }
-
- if (!HasDeviceVolumeControl(ais)) {
- DLOG(WARNING) << "Device: " << it->unique_id
- << ", does not have volume control.";
- ais->Close();
- continue;
- }
-
- double max_volume = ais->GetMaxVolume();
- EXPECT_GT(max_volume, 0.0);
-
- // Store the current input-device volume level.
- double original_volume = ais->GetVolume();
- EXPECT_GE(original_volume, 0.0);
-#if defined(OS_WIN) || defined(OS_MACOSX)
- // Note that |original_volume| can be higher than |max_volume| on Linux.
- EXPECT_LE(original_volume, max_volume);
-#endif
-
- // Set the volume to the maxiumum level..
- ais->SetVolume(max_volume);
- double current_volume = ais->GetVolume();
- EXPECT_EQ(max_volume, current_volume);
-
- // Set the volume to the mininum level (=0).
- double new_volume = 0.0;
- ais->SetVolume(new_volume);
- current_volume = ais->GetVolume();
- EXPECT_EQ(new_volume, current_volume);
-
- // Set the volume to the mid level (50% of max).
- // Verify that the absolute error is small enough.
- new_volume = max_volume / 2;
- ais->SetVolume(new_volume);
- current_volume = ais->GetVolume();
- EXPECT_LT(current_volume, max_volume);
- EXPECT_GT(current_volume, 0);
- EXPECT_NEAR(current_volume, new_volume, 0.25 * max_volume);
-
- // Restores the volume to the original value.
- ais->SetVolume(original_volume);
- current_volume = ais->GetVolume();
- EXPECT_EQ(original_volume, current_volume);
-
- ais->Close();
- }
-}
-
-} // namespace media
diff --git a/src/media/audio/audio_io.h b/src/media/audio/audio_io.h
deleted file mode 100644
index e7b9a36..0000000
--- a/src/media/audio/audio_io.h
+++ /dev/null
@@ -1,180 +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 MEDIA_AUDIO_AUDIO_IO_H_
-#define MEDIA_AUDIO_AUDIO_IO_H_
-
-#include "base/basictypes.h"
-#include "media/audio/audio_buffers_state.h"
-#include "media/base/audio_bus.h"
-
-// Low-level audio output support. To make sound there are 3 objects involved:
-// - AudioSource : produces audio samples on a pull model. Implements
-// the AudioSourceCallback interface.
-// - AudioOutputStream : uses the AudioSource to render audio on a given
-// channel, format and sample frequency configuration. Data from the
-// AudioSource is delivered in a 'pull' model.
-// - AudioManager : factory for the AudioOutputStream objects, manager
-// of the hardware resources and mixer control.
-//
-// The number and configuration of AudioOutputStream does not need to match the
-// physically available hardware resources. For example you can have:
-//
-// MonoPCMSource1 --> MonoPCMStream1 --> | | --> audio left channel
-// StereoPCMSource -> StereoPCMStream -> | mixer |
-// MonoPCMSource2 --> MonoPCMStream2 --> | | --> audio right channel
-//
-// This facility's objective is mix and render audio with low overhead using
-// the OS basic audio support, abstracting as much as possible the
-// idiosyncrasies of each platform. Non-goals:
-// - Positional, 3d audio
-// - Dependence on non-default libraries such as DirectX 9, 10, XAudio
-// - Digital signal processing or effects
-// - Extra features if a specific hardware is installed (EAX, X-fi)
-//
-// The primary client of this facility is audio coming from several tabs.
-// Specifically for this case we avoid supporting complex formats such as MP3
-// or WMA. Complex format decoding should be done by the renderers.
-
-
-// Models an audio stream that gets rendered to the audio hardware output.
-// Because we support more audio streams than physically available channels
-// a given AudioOutputStream might or might not talk directly to hardware.
-// An audio stream allocates several buffers for audio data and calls
-// AudioSourceCallback::OnMoreData() periodically to fill these buffers,
-// as the data is written to the audio device. Size of each packet is determined
-// by |samples_per_packet| specified in AudioParameters when the stream is
-// created.
-
-namespace media {
-
-class MEDIA_EXPORT AudioOutputStream {
- public:
- // Audio sources must implement AudioSourceCallback. This interface will be
- // called in a random thread which very likely is a high priority thread. Do
- // not rely on using this thread TLS or make calls that alter the thread
- // itself such as creating Windows or initializing COM.
- class MEDIA_EXPORT AudioSourceCallback {
- public:
- // Provide more data by fully filling |dest|. The source will return
- // the number of frames it filled. |buffers_state| contains current state
- // of the buffers, and can be used by the source to calculate delay.
- virtual int OnMoreData(AudioBus* dest,
- AudioBuffersState buffers_state) = 0;
-
- virtual int OnMoreIOData(AudioBus* source,
- AudioBus* dest,
- AudioBuffersState buffers_state) = 0;
-
- // There was an error while playing a buffer. Audio source cannot be
- // destroyed yet. No direct action needed by the AudioStream, but it is
- // a good place to stop accumulating sound data since is is likely that
- // playback will not continue. |code| is an error code that is platform
- // specific.
- virtual void OnError(AudioOutputStream* stream, int code) = 0;
-
- // Deprecated. DO NOT USE. Waits until data becomes available. Used only
- // by Windows' WaveOut clients which may be extremely laggy. Will yield the
- // current thread until the renderer client has written its audio data or
- // 1.5 seconds have elapsed.
- virtual void WaitTillDataReady() {}
-
- protected:
- virtual ~AudioSourceCallback() {}
- };
-
- virtual ~AudioOutputStream() {}
-
- // Open the stream. false is returned if the stream cannot be opened. Open()
- // must always be followed by a call to Close() even if Open() fails.
- virtual bool Open() = 0;
-
- // Starts playing audio and generating AudioSourceCallback::OnMoreData().
- // Since implementor of AudioOutputStream may have internal buffers, right
- // after calling this method initial buffers are fetched.
- //
- // The output stream does not take ownership of this callback.
- virtual void Start(AudioSourceCallback* callback) = 0;
-
- // Stops playing audio. Effect might not be instantaneous as the hardware
- // might have locked audio data that is processing.
- virtual void Stop() = 0;
-
- // Sets the relative volume, with range [0.0, 1.0] inclusive.
- virtual void SetVolume(double volume) = 0;
-
- // Gets the relative volume, with range [0.0, 1.0] inclusive.
- virtual void GetVolume(double* volume) = 0;
-
- // Close the stream. This also generates AudioSourceCallback::OnClose().
- // After calling this method, the object should not be used anymore.
- virtual void Close() = 0;
-};
-
-// Models an audio sink receiving recorded audio from the audio driver.
-class MEDIA_EXPORT AudioInputStream {
- public:
- class MEDIA_EXPORT AudioInputCallback {
- public:
- // Called by the audio recorder when a full packet of audio data is
- // available. This is called from a special audio thread and the
- // implementation should return as soon as possible.
- virtual void OnData(AudioInputStream* stream, const uint8* src,
- uint32 size, uint32 hardware_delay_bytes,
- double volume) = 0;
-
- // The stream is done with this callback, the last call received by this
- // audio sink.
- virtual void OnClose(AudioInputStream* stream) = 0;
-
- // There was an error while recording audio. The audio sink cannot be
- // destroyed yet. No direct action needed by the AudioInputStream, but it
- // is a good place to stop accumulating sound data since is is likely that
- // recording will not continue. |code| is an error code that is platform
- // specific.
- virtual void OnError(AudioInputStream* stream, int code) = 0;
-
- protected:
- virtual ~AudioInputCallback() {}
- };
-
- virtual ~AudioInputStream() {}
-
- // Open the stream and prepares it for recording. Call Start() to actually
- // begin recording.
- virtual bool Open() = 0;
-
- // Starts recording audio and generating AudioInputCallback::OnData().
- // The input stream does not take ownership of this callback.
- virtual void Start(AudioInputCallback* callback) = 0;
-
- // Stops recording audio. Effect might not be instantaneous as there could be
- // pending audio callbacks in the queue which will be issued first before
- // recording stops.
- virtual void Stop() = 0;
-
- // Close the stream. This also generates AudioInputCallback::OnClose(). This
- // should be the last call made on this object.
- virtual void Close() = 0;
-
- // Returns the maximum microphone analog volume or 0.0 if device does not
- // have volume control.
- virtual double GetMaxVolume() = 0;
-
- // Sets the microphone analog volume, with range [0, max_volume] inclusive.
- virtual void SetVolume(double volume) = 0;
-
- // Returns the microphone analog volume, with range [0, max_volume] inclusive.
- virtual double GetVolume() = 0;
-
- // Sets the Automatic Gain Control (AGC) state.
- virtual void SetAutomaticGainControl(bool enabled) = 0;
-
- // Returns the Automatic Gain Control (AGC) state.
- virtual bool GetAutomaticGainControl() = 0;
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_IO_H_
diff --git a/src/media/audio/audio_low_latency_input_output_unittest.cc b/src/media/audio/audio_low_latency_input_output_unittest.cc
deleted file mode 100644
index 463321a..0000000
--- a/src/media/audio/audio_low_latency_input_output_unittest.cc
+++ /dev/null
@@ -1,464 +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 "base/basictypes.h"
-#include "base/environment.h"
-#include "base/file_util.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop.h"
-#include "base/path_service.h"
-#include "base/synchronization/lock.h"
-#include "base/test/test_timeouts.h"
-#include "base/time.h"
-#include "build/build_config.h"
-#include "media/audio/audio_io.h"
-#include "media/audio/audio_manager_base.h"
-#include "media/audio/audio_util.h"
-#include "media/base/seekable_buffer.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_LINUX) || defined(OS_OPENBSD)
-#include "media/audio/linux/audio_manager_linux.h"
-#elif defined(OS_MACOSX)
-#include "media/audio/mac/audio_manager_mac.h"
-#elif defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#include "media/audio/win/audio_manager_win.h"
-#include "media/audio/win/core_audio_util_win.h"
-#elif defined(OS_ANDROID)
-#include "media/audio/android/audio_manager_android.h"
-#endif
-
-namespace media {
-
-#if defined(OS_LINUX) || defined(OS_OPENBSD)
-typedef AudioManagerLinux AudioManagerAnyPlatform;
-#elif defined(OS_MACOSX)
-typedef AudioManagerMac AudioManagerAnyPlatform;
-#elif defined(OS_WIN)
-typedef AudioManagerWin AudioManagerAnyPlatform;
-#elif defined(OS_ANDROID)
-typedef AudioManagerAndroid AudioManagerAnyPlatform;
-#endif
-
-// Limits the number of delay measurements we can store in an array and
-// then write to file at end of the WASAPIAudioInputOutputFullDuplex test.
-static const size_t kMaxDelayMeasurements = 1000;
-
-// Name of the output text file. The output file will be stored in the
-// directory containing media_unittests.exe.
-// Example: \src\build\Debug\audio_delay_values_ms.txt.
-// See comments for the WASAPIAudioInputOutputFullDuplex test for more details
-// about the file format.
-static const char* kDelayValuesFileName = "audio_delay_values_ms.txt";
-
-// Contains delay values which are reported during the full-duplex test.
-// Total delay = |buffer_delay_ms| + |input_delay_ms| + |output_delay_ms|.
-struct AudioDelayState {
- AudioDelayState()
- : delta_time_ms(0),
- buffer_delay_ms(0),
- input_delay_ms(0),
- output_delay_ms(0) {
- }
-
- // Time in milliseconds since last delay report. Typical value is ~10 [ms].
- int delta_time_ms;
-
- // Size of internal sync buffer. Typical value is ~0 [ms].
- int buffer_delay_ms;
-
- // Reported capture/input delay. Typical value is ~10 [ms].
- int input_delay_ms;
-
- // Reported render/output delay. Typical value is ~40 [ms].
- int output_delay_ms;
-};
-
-// This class mocks the platform specific audio manager and overrides
-// the GetMessageLoop() method to ensure that we can run our tests on
-// the main thread instead of the audio thread.
-class MockAudioManager : public AudioManagerAnyPlatform {
- public:
- MockAudioManager() {}
- virtual ~MockAudioManager() {}
-
- virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE {
- return MessageLoop::current()->message_loop_proxy();
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockAudioManager);
-};
-
-// Test fixture class.
-class AudioLowLatencyInputOutputTest : public testing::Test {
- protected:
- AudioLowLatencyInputOutputTest() {}
-
- virtual ~AudioLowLatencyInputOutputTest() {}
-
- AudioManager* audio_manager() { return &mock_audio_manager_; }
- MessageLoopForUI* message_loop() { return &message_loop_; }
-
- // Convenience method which ensures that we are not running on the build
- // bots and that at least one valid input and output device can be found.
- bool CanRunAudioTests() {
- bool input = audio_manager()->HasAudioInputDevices();
- bool output = audio_manager()->HasAudioOutputDevices();
- LOG_IF(WARNING, !input) << "No input device detected.";
- LOG_IF(WARNING, !output) << "No output device detected.";
- return input && output;
- }
-
- private:
- MessageLoopForUI message_loop_;
- MockAudioManager mock_audio_manager_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioLowLatencyInputOutputTest);
-};
-
-// This audio source/sink implementation should be used for manual tests
-// only since delay measurements are stored on an output text file.
-// All incoming/recorded audio packets are stored in an intermediate media
-// buffer which the renderer reads from when it needs audio for playout.
-// The total effect is that recorded audio is played out in loop back using
-// a sync buffer as temporary storage.
-class FullDuplexAudioSinkSource
- : public AudioInputStream::AudioInputCallback,
- public AudioOutputStream::AudioSourceCallback {
- public:
- FullDuplexAudioSinkSource(int sample_rate,
- int samples_per_packet,
- int channels)
- : sample_rate_(sample_rate),
- samples_per_packet_(samples_per_packet),
- channels_(channels),
- input_elements_to_write_(0),
- output_elements_to_write_(0),
- previous_write_time_(base::Time::Now()) {
- // Size in bytes of each audio frame (4 bytes for 16-bit stereo PCM).
- frame_size_ = (16 / 8) * channels_;
-
- // Start with the smallest possible buffer size. It will be increased
- // dynamically during the test if required.
- buffer_.reset(
- new media::SeekableBuffer(0, samples_per_packet_ * frame_size_));
-
- frames_to_ms_ = static_cast<double>(1000.0 / sample_rate_);
- delay_states_.reset(new AudioDelayState[kMaxDelayMeasurements]);
- }
-
- virtual ~FullDuplexAudioSinkSource() {
- // Get complete file path to output file in the directory containing
- // media_unittests.exe. Example: src/build/Debug/audio_delay_values_ms.txt.
- FilePath file_name;
- EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name));
- file_name = file_name.AppendASCII(kDelayValuesFileName);
-
- FILE* text_file = file_util::OpenFile(file_name, "wt");
- DLOG_IF(ERROR, !text_file) << "Failed to open log file.";
- LOG(INFO) << ">> Output file " << file_name.value() << " has been created.";
-
- // Write the array which contains time-stamps, buffer size and
- // audio delays values to a text file.
- size_t elements_written = 0;
- while (elements_written <
- std::min(input_elements_to_write_, output_elements_to_write_)) {
- const AudioDelayState state = delay_states_[elements_written];
- fprintf(text_file, "%d %d %d %d\n",
- state.delta_time_ms,
- state.buffer_delay_ms,
- state.input_delay_ms,
- state.output_delay_ms);
- ++elements_written;
- }
-
- file_util::CloseFile(text_file);
- }
-
- // AudioInputStream::AudioInputCallback.
- virtual void OnData(AudioInputStream* stream,
- const uint8* src, uint32 size,
- uint32 hardware_delay_bytes,
- double volume) OVERRIDE {
- base::AutoLock lock(lock_);
-
- // Update three components in the AudioDelayState for this recorded
- // audio packet.
- base::Time now_time = base::Time::Now();
- int diff = (now_time - previous_write_time_).InMilliseconds();
- previous_write_time_ = now_time;
- if (input_elements_to_write_ < kMaxDelayMeasurements) {
- delay_states_[input_elements_to_write_].delta_time_ms = diff;
- delay_states_[input_elements_to_write_].buffer_delay_ms =
- BytesToMilliseconds(buffer_->forward_bytes());
- delay_states_[input_elements_to_write_].input_delay_ms =
- BytesToMilliseconds(hardware_delay_bytes);
- ++input_elements_to_write_;
- }
-
- // Store the captured audio packet in a seekable media buffer.
- if (!buffer_->Append(src, size)) {
- // An attempt to write outside the buffer limits has been made.
- // Double the buffer capacity to ensure that we have a buffer large
- // enough to handle the current sample test scenario.
- buffer_->set_forward_capacity(2 * buffer_->forward_capacity());
- buffer_->Clear();
- }
- }
-
- virtual void OnClose(AudioInputStream* stream) OVERRIDE {}
- virtual void OnError(AudioInputStream* stream, int code) OVERRIDE {}
-
- // AudioOutputStream::AudioSourceCallback.
- virtual int OnMoreData(AudioBus* audio_bus,
- AudioBuffersState buffers_state) OVERRIDE {
- base::AutoLock lock(lock_);
-
- // Update one component in the AudioDelayState for the packet
- // which is about to be played out.
- if (output_elements_to_write_ < kMaxDelayMeasurements) {
- int output_delay_bytes = buffers_state.hardware_delay_bytes;
-#if defined(OS_WIN)
- // Special fix for Windows in combination with Wave where the
- // pending bytes field of the audio buffer state is used to
- // report the delay.
- if (!CoreAudioUtil::IsSupported()) {
- output_delay_bytes = buffers_state.pending_bytes;
- }
-#endif
- delay_states_[output_elements_to_write_].output_delay_ms =
- BytesToMilliseconds(output_delay_bytes);
- ++output_elements_to_write_;
- }
-
- int size;
- const uint8* source;
- // Read the data from the seekable media buffer which contains
- // captured data at the same size and sample rate as the output side.
- if (buffer_->GetCurrentChunk(&source, &size) && size > 0) {
- EXPECT_EQ(channels_, audio_bus->channels());
- size = std::min(audio_bus->frames() * frame_size_, size);
- EXPECT_EQ(static_cast<size_t>(size) % sizeof(*audio_bus->channel(0)), 0U);
- audio_bus->FromInterleaved(
- source, size / frame_size_, frame_size_ / channels_);
- buffer_->Seek(size);
- return size / frame_size_;
- }
-
- return 0;
- }
-
- virtual int OnMoreIOData(AudioBus* source,
- AudioBus* dest,
- AudioBuffersState buffers_state) OVERRIDE {
- NOTREACHED();
- return 0;
- }
-
- virtual void OnError(AudioOutputStream* stream, int code) OVERRIDE {}
- virtual void WaitTillDataReady() OVERRIDE {}
-
- protected:
- // Converts from bytes to milliseconds taking the sample rate and size
- // of an audio frame into account.
- int BytesToMilliseconds(uint32 delay_bytes) const {
- return static_cast<int>((delay_bytes / frame_size_) * frames_to_ms_ + 0.5);
- }
-
- private:
- base::Lock lock_;
- scoped_ptr<media::SeekableBuffer> buffer_;
- int sample_rate_;
- int samples_per_packet_;
- int channels_;
- int frame_size_;
- double frames_to_ms_;
- scoped_array<AudioDelayState> delay_states_;
- size_t input_elements_to_write_;
- size_t output_elements_to_write_;
- base::Time previous_write_time_;
-};
-
-class AudioInputStreamTraits {
- public:
- typedef AudioInputStream StreamType;
-
- static int HardwareSampleRate() {
- return static_cast<int>(media::GetAudioInputHardwareSampleRate(
- AudioManagerBase::kDefaultDeviceId));
- }
-
- // TODO(henrika): add support for GetAudioInputHardwareBufferSize in media.
- static int HardwareBufferSize() {
- return static_cast<int>(media::GetAudioHardwareBufferSize());
- }
-
- static StreamType* CreateStream(AudioManager* audio_manager,
- const AudioParameters& params) {
- return audio_manager->MakeAudioInputStream(params,
- AudioManagerBase::kDefaultDeviceId);
- }
-};
-
-class AudioOutputStreamTraits {
- public:
- typedef AudioOutputStream StreamType;
-
- static int HardwareSampleRate() {
- return static_cast<int>(media::GetAudioHardwareSampleRate());
- }
-
- static int HardwareBufferSize() {
- return static_cast<int>(media::GetAudioHardwareBufferSize());
- }
-
- static StreamType* CreateStream(AudioManager* audio_manager,
- const AudioParameters& params) {
- return audio_manager->MakeAudioOutputStream(params);
- }
-};
-
-// Traits template holding a trait of StreamType. It encapsulates
-// AudioInputStream and AudioOutputStream stream types.
-template <typename StreamTraits>
-class StreamWrapper {
- public:
- typedef typename StreamTraits::StreamType StreamType;
-
- explicit StreamWrapper(AudioManager* audio_manager)
- :
-#if defined(OS_WIN)
- com_init_(base::win::ScopedCOMInitializer::kMTA),
-#endif
- audio_manager_(audio_manager),
- format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
-#if defined(OS_ANDROID)
- channel_layout_(CHANNEL_LAYOUT_MONO),
-#else
- channel_layout_(CHANNEL_LAYOUT_STEREO),
-#endif
- bits_per_sample_(16) {
- // Use the preferred sample rate.
- sample_rate_ = StreamTraits::HardwareSampleRate();
-
- // Use the preferred buffer size. Note that the input side uses the same
- // size as the output side in this implementation.
- samples_per_packet_ = StreamTraits::HardwareBufferSize();
- }
-
- virtual ~StreamWrapper() {}
-
- // Creates an Audio[Input|Output]Stream stream object using default
- // parameters.
- StreamType* Create() {
- return CreateStream();
- }
-
- int channels() const {
- return ChannelLayoutToChannelCount(channel_layout_);
- }
- int bits_per_sample() const { return bits_per_sample_; }
- int sample_rate() const { return sample_rate_; }
- int samples_per_packet() const { return samples_per_packet_; }
-
- private:
- StreamType* CreateStream() {
- StreamType* stream = StreamTraits::CreateStream(audio_manager_,
- AudioParameters(format_, channel_layout_, sample_rate_,
- bits_per_sample_, samples_per_packet_));
- EXPECT_TRUE(stream);
- return stream;
- }
-
-#if defined(OS_WIN)
- base::win::ScopedCOMInitializer com_init_;
-#endif
-
- AudioManager* audio_manager_;
- AudioParameters::Format format_;
- ChannelLayout channel_layout_;
- int bits_per_sample_;
- int sample_rate_;
- int samples_per_packet_;
-};
-
-typedef StreamWrapper<AudioInputStreamTraits> AudioInputStreamWrapper;
-typedef StreamWrapper<AudioOutputStreamTraits> AudioOutputStreamWrapper;
-
-// This test is intended for manual tests and should only be enabled
-// when it is required to make a real-time test of audio in full duplex and
-// at the same time create a text file which contains measured delay values.
-// The file can later be analyzed off line using e.g. MATLAB.
-// MATLAB example:
-// D=load('audio_delay_values_ms.txt');
-// x=cumsum(D(:,1));
-// plot(x, D(:,2), x, D(:,3), x, D(:,4), x, D(:,2)+D(:,3)+D(:,4));
-// axis([0, max(x), 0, max(D(:,2)+D(:,3)+D(:,4))+10]);
-// legend('buffer delay','input delay','output delay','total delay');
-// xlabel('time [msec]')
-// ylabel('delay [msec]')
-// title('Full-duplex audio delay measurement');
-TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) {
- if (!CanRunAudioTests())
- return;
-
- AudioInputStreamWrapper aisw(audio_manager());
- AudioInputStream* ais = aisw.Create();
- EXPECT_TRUE(ais);
-
- AudioOutputStreamWrapper aosw(audio_manager());
- AudioOutputStream* aos = aosw.Create();
- EXPECT_TRUE(aos);
-
- // This test only supports identical parameters in both directions.
- // TODO(henrika): it is possible to cut delay here by using different
- // buffer sizes for input and output.
- if (aisw.sample_rate() != aosw.sample_rate() ||
- aisw.samples_per_packet() != aosw.samples_per_packet() ||
- aisw.channels()!= aosw.channels() ||
- aisw.bits_per_sample() != aosw.bits_per_sample()) {
- LOG(ERROR) << "This test requires symmetric input and output parameters. "
- "Ensure that sample rate and number of channels are identical in "
- "both directions";
- aos->Close();
- ais->Close();
- return;
- }
-
- EXPECT_TRUE(ais->Open());
- EXPECT_TRUE(aos->Open());
-
- FullDuplexAudioSinkSource full_duplex(
- aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels());
-
- LOG(INFO) << ">> You should now be able to hear yourself in loopback...";
- DLOG(INFO) << " sample_rate : " << aisw.sample_rate();
- DLOG(INFO) << " samples_per_packet: " << aisw.samples_per_packet();
- DLOG(INFO) << " channels : " << aisw.channels();
-
- ais->Start(&full_duplex);
- aos->Start(&full_duplex);
-
- // Wait for approximately 10 seconds. The user shall hear his own voice
- // in loop back during this time. At the same time, delay recordings are
- // performed and stored in the output text file.
- message_loop()->PostDelayedTask(FROM_HERE,
- MessageLoop::QuitClosure(), TestTimeouts::action_timeout());
- message_loop()->Run();
-
- aos->Stop();
- ais->Stop();
-
- // All Close() operations that run on the mocked audio thread,
- // should be synchronous and not post additional close tasks to
- // mocked the audio thread. Hence, there is no need to call
- // message_loop()->RunUntilIdle() after the Close() methods.
- aos->Close();
- ais->Close();
-}
-
-} // namespace media
diff --git a/src/media/audio/audio_manager.cc b/src/media/audio/audio_manager.cc
deleted file mode 100644
index 9372d08..0000000
--- a/src/media/audio/audio_manager.cc
+++ /dev/null
@@ -1,29 +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 "media/audio/audio_manager.h"
-
-#include "base/at_exit.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/logging.h"
-#include "base/message_loop.h"
-
-namespace media {
-
-// Forward declaration of the platform specific AudioManager factory function.
-AudioManager* CreateAudioManager();
-
-AudioManager::AudioManager() {
-}
-
-AudioManager::~AudioManager() {
-}
-
-// static
-AudioManager* AudioManager::Create() {
- return CreateAudioManager();
-}
-
-} // namespace media
diff --git a/src/media/audio/audio_manager.h b/src/media/audio/audio_manager.h
deleted file mode 100644
index ca4c468..0000000
--- a/src/media/audio/audio_manager.h
+++ /dev/null
@@ -1,136 +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 MEDIA_AUDIO_AUDIO_MANAGER_H_
-#define MEDIA_AUDIO_AUDIO_MANAGER_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/string16.h"
-#include "media/audio/audio_device_name.h"
-#include "media/audio/audio_parameters.h"
-
-class MessageLoop;
-
-namespace base {
-class MessageLoopProxy;
-}
-
-namespace media {
-
-class AudioInputStream;
-class AudioOutputStream;
-
-// Manages all audio resources. In particular it owns the AudioOutputStream
-// objects. Provides some convenience functions that avoid the need to provide
-// iterators over the existing streams.
-class MEDIA_EXPORT AudioManager {
- public:
- virtual ~AudioManager();
-
- // Use to construct the audio manager.
- // NOTE: There should only be one instance.
- static AudioManager* Create();
-
- // Returns true if the OS reports existence of audio devices. This does not
- // guarantee that the existing devices support all formats and sample rates.
- virtual bool HasAudioOutputDevices() = 0;
-
- // Returns true if the OS reports existence of audio recording devices. This
- // does not guarantee that the existing devices support all formats and
- // sample rates.
- virtual bool HasAudioInputDevices() = 0;
-
- // Returns a human readable string for the model/make of the active audio
- // input device for this computer.
- virtual string16 GetAudioInputDeviceModel() = 0;
-
- // Returns true if the platform specific audio input settings UI is known
- // and can be shown.
- virtual bool CanShowAudioInputSettings() = 0;
-
- // Opens the platform default audio input settings UI.
- // Note: This could invoke an external application/preferences pane, so
- // ideally must not be called from the UI thread or other time sensitive
- // threads to avoid blocking the rest of the application.
- virtual void ShowAudioInputSettings() = 0;
-
- // Appends a list of available input devices. It is not guaranteed that
- // all the devices in the list support all formats and sample rates for
- // recording.
- virtual void GetAudioInputDeviceNames(AudioDeviceNames* device_names) = 0;
-
- // Factory for all the supported stream formats. |params| defines parameters
- // of the audio stream to be created.
- //
- // |params.sample_per_packet| is the requested buffer allocation which the
- // audio source thinks it can usually fill without blocking. Internally two
- // or three buffers are created, one will be locked for playback and one will
- // be ready to be filled in the call to AudioSourceCallback::OnMoreData().
- //
- // Returns NULL if the combination of the parameters is not supported, or if
- // we have reached some other platform specific limit.
- //
- // |params.format| can be set to AUDIO_PCM_LOW_LATENCY and that has two
- // effects:
- // 1- Instead of triple buffered the audio will be double buffered.
- // 2- A low latency driver or alternative audio subsystem will be used when
- // available.
- //
- // Do not free the returned AudioOutputStream. It is owned by AudioManager.
- virtual AudioOutputStream* MakeAudioOutputStream(
- const AudioParameters& params) = 0;
-
- // Creates new audio output proxy. A proxy implements
- // AudioOutputStream interface, but unlike regular output stream
- // created with MakeAudioOutputStream() it opens device only when a
- // sound is actually playing.
- virtual AudioOutputStream* MakeAudioOutputStreamProxy(
- const AudioParameters& params) = 0;
-
- // Factory to create audio recording streams.
- // |channels| can be 1 or 2.
- // |sample_rate| is in hertz and can be any value supported by the platform.
- // |bits_per_sample| can be any value supported by the platform.
- // |samples_per_packet| is in hertz as well and can be 0 to |sample_rate|,
- // with 0 suggesting that the implementation use a default value for that
- // platform.
- // Returns NULL if the combination of the parameters is not supported, or if
- // we have reached some other platform specific limit.
- //
- // Do not free the returned AudioInputStream. It is owned by AudioManager.
- // When you are done with it, call |Stop()| and |Close()| to release it.
- virtual AudioInputStream* MakeAudioInputStream(
- const AudioParameters& params, const std::string& device_id) = 0;
-
- // Used to determine if something else is currently making use of audio input.
- virtual bool IsRecordingInProcess() = 0;
-
- // Returns message loop used for audio IO.
- virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() = 0;
-
- // Allows clients to listen for device state changes; e.g. preferred sample
- // rate or channel layout changes. The typical response to receiving this
- // callback is to recreate the stream.
- class AudioDeviceListener {
- public:
- virtual void OnDeviceChange() = 0;
- };
-
- virtual void AddOutputDeviceChangeListener(AudioDeviceListener* listener) = 0;
- virtual void RemoveOutputDeviceChangeListener(
- AudioDeviceListener* listener) = 0;
-
- protected:
- AudioManager();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AudioManager);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_MANAGER_H_
diff --git a/src/media/audio/audio_manager_base.cc b/src/media/audio/audio_manager_base.cc
deleted file mode 100644
index 6333039..0000000
--- a/src/media/audio/audio_manager_base.cc
+++ /dev/null
@@ -1,397 +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 "media/audio/audio_manager_base.h"
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/message_loop_proxy.h"
-#include "base/threading/thread.h"
-#include "media/audio/audio_output_dispatcher_impl.h"
-#include "media/audio/audio_output_proxy.h"
-#include "media/audio/audio_output_resampler.h"
-#include "media/audio/audio_util.h"
-#include "media/audio/fake_audio_input_stream.h"
-#include "media/audio/fake_audio_output_stream.h"
-#include "media/audio/virtual_audio_input_stream.h"
-#include "media/audio/virtual_audio_output_stream.h"
-#include "media/base/media_switches.h"
-
-// TODO(dalecurtis): Temporarily disabled while switching pipeline to use float,
-// http://crbug.com/114700
-#if defined(ENABLE_AUDIO_MIXER)
-#include "media/audio/audio_output_mixer.h"
-#endif
-
-namespace media {
-
-static const int kStreamCloseDelaySeconds = 5;
-
-// Default maximum number of output streams that can be open simultaneously
-// for all platforms.
-static const int kDefaultMaxOutputStreams = 16;
-
-// Default maximum number of input streams that can be open simultaneously
-// for all platforms.
-static const int kDefaultMaxInputStreams = 16;
-
-static const int kMaxInputChannels = 2;
-
-const char AudioManagerBase::kDefaultDeviceName[] = "Default";
-const char AudioManagerBase::kDefaultDeviceId[] = "default";
-
-AudioManagerBase::AudioManagerBase()
- : num_active_input_streams_(0),
- max_num_output_streams_(kDefaultMaxOutputStreams),
- max_num_input_streams_(kDefaultMaxInputStreams),
- num_output_streams_(0),
- num_input_streams_(0),
- audio_thread_(new base::Thread("AudioThread")),
- virtual_audio_input_stream_(NULL) {
-#if defined(OS_WIN)
- audio_thread_->init_com_with_mta(true);
-#endif
- CHECK(audio_thread_->Start());
- message_loop_ = audio_thread_->message_loop_proxy();
-}
-
-AudioManagerBase::~AudioManagerBase() {
- // The platform specific AudioManager implementation must have already
- // stopped the audio thread. Otherwise, we may destroy audio streams before
- // stopping the thread, resulting an unexpected behavior.
- // This way we make sure activities of the audio streams are all stopped
- // before we destroy them.
- CHECK(!audio_thread_.get());
- // All the output streams should have been deleted.
- DCHECK_EQ(0, num_output_streams_);
- // All the input streams should have been deleted.
- DCHECK_EQ(0, num_input_streams_);
-}
-
-string16 AudioManagerBase::GetAudioInputDeviceModel() {
- return string16();
-}
-
-scoped_refptr<base::MessageLoopProxy> AudioManagerBase::GetMessageLoop() {
- return message_loop_;
-}
-
-AudioOutputStream* AudioManagerBase::MakeAudioOutputStream(
- const AudioParameters& params) {
- if (!params.IsValid()) {
- DLOG(ERROR) << "Audio parameters are invalid";
- return NULL;
- }
-
- // Limit the number of audio streams opened. This is to prevent using
- // excessive resources for a large number of audio streams. More
- // importantly it prevents instability on certain systems.
- // See bug: http://crbug.com/30242.
- if (num_output_streams_ >= max_num_output_streams_) {
- DLOG(ERROR) << "Number of opened output audio streams "
- << num_output_streams_
- << " exceed the max allowed number "
- << max_num_output_streams_;
- return NULL;
- }
-
- // If there are no audio output devices we should use a FakeAudioOutputStream
- // to ensure video playback continues to work.
- bool audio_output_disabled =
- params.format() == AudioParameters::AUDIO_FAKE ||
- !HasAudioOutputDevices();
-
- AudioOutputStream* stream = NULL;
- if (virtual_audio_input_stream_) {
-#if defined(OS_IOS)
- // We do not currently support iOS. It does not link.
- NOTIMPLEMENTED();
- return NULL;
-#else
- stream = VirtualAudioOutputStream::MakeStream(this, params, message_loop_,
- virtual_audio_input_stream_);
-#endif
- } else if (audio_output_disabled) {
- stream = FakeAudioOutputStream::MakeFakeStream(this, params);
- } else if (params.format() == AudioParameters::AUDIO_PCM_LINEAR) {
- stream = MakeLinearOutputStream(params);
- } else if (params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
- stream = MakeLowLatencyOutputStream(params);
- }
-
- if (stream)
- ++num_output_streams_;
-
- return stream;
-}
-
-AudioInputStream* AudioManagerBase::MakeAudioInputStream(
- const AudioParameters& params, const std::string& device_id) {
- if (!params.IsValid() || (params.channels() > kMaxInputChannels) ||
- device_id.empty()) {
- DLOG(ERROR) << "Audio parameters are invalid for device " << device_id;
- return NULL;
- }
-
- if (num_input_streams_ >= max_num_input_streams_) {
- DLOG(ERROR) << "Number of opened input audio streams "
- << num_input_streams_
- << " exceed the max allowed number " << max_num_input_streams_;
- return NULL;
- }
-
- AudioInputStream* stream = NULL;
- if (params.format() == AudioParameters::AUDIO_VIRTUAL) {
-#if defined(OS_IOS)
- // We do not currently support iOS.
- NOTIMPLEMENTED();
- return NULL;
-#else
- // TODO(justinlin): Currently, audio mirroring will only work for the first
- // request. Subsequent requests will not get audio.
- if (!virtual_audio_input_stream_) {
- virtual_audio_input_stream_ =
- VirtualAudioInputStream::MakeStream(this, params, message_loop_);
- stream = virtual_audio_input_stream_;
- DVLOG(1) << "Virtual audio input stream created.";
-
- // Make all current output streams recreate themselves as
- // VirtualAudioOutputStreams that will attach to the above
- // VirtualAudioInputStream.
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioManagerBase::NotifyAllOutputDeviceChangeListeners,
- base::Unretained(this)));
- } else {
- stream = NULL;
- }
-#endif
- } else if (params.format() == AudioParameters::AUDIO_FAKE) {
- stream = FakeAudioInputStream::MakeFakeStream(this, params);
- } else if (params.format() == AudioParameters::AUDIO_PCM_LINEAR) {
- stream = MakeLinearInputStream(params, device_id);
- } else if (params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
- stream = MakeLowLatencyInputStream(params, device_id);
- }
-
- if (stream)
- ++num_input_streams_;
-
- return stream;
-}
-
-AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy(
- const AudioParameters& params) {
-#if defined(OS_IOS)
- // IOS implements audio input only.
- NOTIMPLEMENTED();
- return NULL;
-#else
- DCHECK(message_loop_->BelongsToCurrentThread());
-
- bool use_audio_output_resampler =
- !CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableAudioOutputResampler) &&
- params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY;
-
- // If we're not using AudioOutputResampler our output parameters are the same
- // as our input parameters.
- AudioParameters output_params = params;
- if (use_audio_output_resampler) {
- output_params = GetPreferredLowLatencyOutputStreamParameters(params);
-
- // Ensure we only pass on valid output parameters.
- if (!output_params.IsValid()) {
- // We've received invalid audio output parameters, so switch to a mock
- // output device based on the input parameters. This may happen if the OS
- // provided us junk values for the hardware configuration.
- LOG(ERROR) << "Invalid audio output parameters received; using fake "
- << "audio path. Channels: " << output_params.channels() << ", "
- << "Sample Rate: " << output_params.sample_rate() << ", "
- << "Bits Per Sample: " << output_params.bits_per_sample()
- << ", Frames Per Buffer: "
- << output_params.frames_per_buffer();
-
- // Tell the AudioManager to create a fake output device.
- output_params = AudioParameters(
- AudioParameters::AUDIO_FAKE, params.channel_layout(),
- params.sample_rate(), params.bits_per_sample(),
- params.frames_per_buffer());
- }
- }
-
- std::pair<AudioParameters, AudioParameters> dispatcher_key =
- std::make_pair(params, output_params);
- AudioOutputDispatchersMap::iterator it =
- output_dispatchers_.find(dispatcher_key);
- if (it != output_dispatchers_.end())
- return new AudioOutputProxy(it->second);
-
- base::TimeDelta close_delay =
- base::TimeDelta::FromSeconds(kStreamCloseDelaySeconds);
-
- if (use_audio_output_resampler &&
- output_params.format() != AudioParameters::AUDIO_FAKE) {
- scoped_refptr<AudioOutputDispatcher> dispatcher =
- new AudioOutputResampler(this, params, output_params, close_delay);
- output_dispatchers_[dispatcher_key] = dispatcher;
- return new AudioOutputProxy(dispatcher);
- }
-
-#if defined(ENABLE_AUDIO_MIXER)
- // TODO(dalecurtis): Browser side mixing has a couple issues that must be
- // fixed before it can be turned on by default: http://crbug.com/138098 and
- // http://crbug.com/140247
- if (cmd_line->HasSwitch(switches::kEnableAudioMixer)) {
- scoped_refptr<AudioOutputDispatcher> dispatcher =
- new AudioOutputMixer(this, params, close_delay);
- output_dispatchers_[dispatcher_key] = dispatcher;
- return new AudioOutputProxy(dispatcher);
- }
-#endif
-
- scoped_refptr<AudioOutputDispatcher> dispatcher =
- new AudioOutputDispatcherImpl(this, output_params, close_delay);
- output_dispatchers_[dispatcher_key] = dispatcher;
- return new AudioOutputProxy(dispatcher);
-#endif // defined(OS_IOS)
-}
-
-bool AudioManagerBase::CanShowAudioInputSettings() {
- return false;
-}
-
-void AudioManagerBase::ShowAudioInputSettings() {
-}
-
-void AudioManagerBase::GetAudioInputDeviceNames(
- media::AudioDeviceNames* device_names) {
-}
-
-void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) {
- DCHECK(stream);
- // TODO(xians) : Have a clearer destruction path for the AudioOutputStream.
- // For example, pass the ownership to AudioManager so it can delete the
- // streams.
- num_output_streams_--;
- delete stream;
-}
-
-void AudioManagerBase::ReleaseInputStream(AudioInputStream* stream) {
- DCHECK(stream);
- // TODO(xians) : Have a clearer destruction path for the AudioInputStream.
-
- if (virtual_audio_input_stream_ == stream) {
- DVLOG(1) << "Virtual audio input stream stopping.";
- virtual_audio_input_stream_->Stop();
- virtual_audio_input_stream_ = NULL;
-
- // Make all VirtualAudioOutputStreams unregister from the
- // VirtualAudioInputStream and recreate themselves as regular audio streams
- // to return sound to hardware.
- NotifyAllOutputDeviceChangeListeners();
- }
-
- num_input_streams_--;
- delete stream;
-}
-
-void AudioManagerBase::IncreaseActiveInputStreamCount() {
- base::AtomicRefCountInc(&num_active_input_streams_);
-}
-
-void AudioManagerBase::DecreaseActiveInputStreamCount() {
- DCHECK(IsRecordingInProcess());
- base::AtomicRefCountDec(&num_active_input_streams_);
-}
-
-bool AudioManagerBase::IsRecordingInProcess() {
- return !base::AtomicRefCountIsZero(&num_active_input_streams_);
-}
-
-void AudioManagerBase::Shutdown() {
- // To avoid running into deadlocks while we stop the thread, shut it down
- // via a local variable while not holding the audio thread lock.
- scoped_ptr<base::Thread> audio_thread;
- {
- base::AutoLock lock(audio_thread_lock_);
- audio_thread_.swap(audio_thread);
- }
-
- if (!audio_thread.get())
- return;
-
- CHECK_NE(MessageLoop::current(), audio_thread->message_loop());
-
- // We must use base::Unretained since Shutdown might have been called from
- // the destructor and we can't alter the refcount of the object at that point.
- audio_thread->message_loop()->PostTask(FROM_HERE, base::Bind(
- &AudioManagerBase::ShutdownOnAudioThread,
- base::Unretained(this)));
-
- // Stop() will wait for any posted messages to be processed first.
- audio_thread->Stop();
-}
-
-void AudioManagerBase::ShutdownOnAudioThread() {
-// IOS implements audio input only.
-#if defined(OS_IOS)
- return;
-#else
- // This should always be running on the audio thread, but since we've cleared
- // the audio_thread_ member pointer when we get here, we can't verify exactly
- // what thread we're running on. The method is not public though and only
- // called from one place, so we'll leave it at that.
- AudioOutputDispatchersMap::iterator it = output_dispatchers_.begin();
- for (; it != output_dispatchers_.end(); ++it) {
- scoped_refptr<AudioOutputDispatcher>& dispatcher = (*it).second;
- if (dispatcher) {
- dispatcher->Shutdown();
- // All AudioOutputProxies must have been freed before Shutdown is called.
- // If they still exist, things will go bad. They have direct pointers to
- // both physical audio stream objects that belong to the dispatcher as
- // well as the message loop of the audio thread that will soon go away.
- // So, better crash now than later.
- DCHECK(dispatcher->HasOneRef()) << "AudioOutputProxies are still alive";
- dispatcher = NULL;
- }
- }
-
- output_dispatchers_.clear();
-#endif // defined(OS_IOS)
-}
-
-AudioParameters AudioManagerBase::GetPreferredLowLatencyOutputStreamParameters(
- const AudioParameters& input_params) {
-#if defined(OS_IOS)
- // IOS implements audio input only.
- NOTIMPLEMENTED();
- return AudioParameters();
-#else
- // TODO(dalecurtis): This should include bits per channel and channel layout
- // eventually.
- return AudioParameters(
- AudioParameters::AUDIO_PCM_LOW_LATENCY, input_params.channel_layout(),
- GetAudioHardwareSampleRate(), 16, GetAudioHardwareBufferSize());
-#endif // defined(OS_IOS)
-}
-
-void AudioManagerBase::AddOutputDeviceChangeListener(
- AudioDeviceListener* listener) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- output_listeners_.AddObserver(listener);
-}
-
-void AudioManagerBase::RemoveOutputDeviceChangeListener(
- AudioDeviceListener* listener) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- output_listeners_.RemoveObserver(listener);
-}
-
-void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DVLOG(1) << "Firing OnDeviceChange() notifications.";
- FOR_EACH_OBSERVER(AudioDeviceListener, output_listeners_, OnDeviceChange());
-}
-
-} // namespace media
diff --git a/src/media/audio/audio_manager_base.h b/src/media/audio/audio_manager_base.h
deleted file mode 100644
index 83ad98b..0000000
--- a/src/media/audio/audio_manager_base.h
+++ /dev/null
@@ -1,173 +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 MEDIA_AUDIO_AUDIO_MANAGER_BASE_H_
-#define MEDIA_AUDIO_AUDIO_MANAGER_BASE_H_
-
-#include <map>
-#include <string>
-#include <utility>
-
-#include "base/atomic_ref_count.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/observer_list.h"
-#include "base/synchronization/lock.h"
-#include "media/audio/audio_manager.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#endif
-
-namespace base {
-class Thread;
-}
-
-namespace media {
-
-class AudioOutputDispatcher;
-class VirtualAudioInputStream;
-
-// AudioManagerBase provides AudioManager functions common for all platforms.
-class MEDIA_EXPORT AudioManagerBase : public AudioManager {
- public:
- // Name of the generic "default" device.
- static const char kDefaultDeviceName[];
- // Unique Id of the generic "default" device.
- static const char kDefaultDeviceId[];
-
- virtual ~AudioManagerBase();
-
- virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE;
-
- virtual string16 GetAudioInputDeviceModel() OVERRIDE;
-
- virtual bool CanShowAudioInputSettings() OVERRIDE;
- virtual void ShowAudioInputSettings() OVERRIDE;
-
- virtual void GetAudioInputDeviceNames(
- media::AudioDeviceNames* device_names) OVERRIDE;
-
- virtual AudioOutputStream* MakeAudioOutputStream(
- const AudioParameters& params) OVERRIDE;
-
- virtual AudioInputStream* MakeAudioInputStream(
- const AudioParameters& params, const std::string& device_id) OVERRIDE;
-
- virtual AudioOutputStream* MakeAudioOutputStreamProxy(
- const AudioParameters& params) OVERRIDE;
-
- virtual bool IsRecordingInProcess() OVERRIDE;
-
- // Called internally by the audio stream when it has been closed.
- virtual void ReleaseOutputStream(AudioOutputStream* stream);
- virtual void ReleaseInputStream(AudioInputStream* stream);
-
- void IncreaseActiveInputStreamCount();
- void DecreaseActiveInputStreamCount();
-
- // Creates the output stream for the |AUDIO_PCM_LINEAR| format. The legacy
- // name is also from |AUDIO_PCM_LINEAR|.
- virtual AudioOutputStream* MakeLinearOutputStream(
- const AudioParameters& params) = 0;
-
- // Creates the output stream for the |AUDIO_PCM_LOW_LATENCY| format.
- virtual AudioOutputStream* MakeLowLatencyOutputStream(
- const AudioParameters& params) = 0;
-
- // Creates the input stream for the |AUDIO_PCM_LINEAR| format. The legacy
- // name is also from |AUDIO_PCM_LINEAR|.
- virtual AudioInputStream* MakeLinearInputStream(
- const AudioParameters& params, const std::string& device_id) = 0;
-
- // Creates the input stream for the |AUDIO_PCM_LOW_LATENCY| format.
- virtual AudioInputStream* MakeLowLatencyInputStream(
- const AudioParameters& params, const std::string& device_id) = 0;
-
- // Returns the preferred hardware audio output parameters for opening output
- // streams in the |AUDIO_PCM_LOW_LATENCY| format.
- // TODO(dalecurtis): Retrieve the |channel_layout| value from hardware instead
- // of accepting the value.
- // TODO(dalecurtis): Each AudioManager should implement their own version, see
- // http://crbug.com/137326
- virtual AudioParameters GetPreferredLowLatencyOutputStreamParameters(
- const AudioParameters& input_params);
-
- // Listeners will be notified on the AudioManager::GetMessageLoop() loop.
- virtual void AddOutputDeviceChangeListener(
- AudioDeviceListener* listener) OVERRIDE;
- virtual void RemoveOutputDeviceChangeListener(
- AudioDeviceListener* listener) OVERRIDE;
-
- protected:
- AudioManagerBase();
-
- // TODO(dalecurtis): This must change to map both input and output parameters
- // to a single dispatcher, otherwise on a device state change we'll just get
- // the exact same invalid dispatcher.
- typedef std::map<std::pair<AudioParameters, AudioParameters>,
- scoped_refptr<AudioOutputDispatcher> >
- AudioOutputDispatchersMap;
-
- // Shuts down the audio thread and releases all the audio output dispatchers
- // on the audio thread. All audio streams should be freed before Shutdown()
- // is called. This must be called in the destructor of every AudioManagerBase
- // implementation.
- void Shutdown();
-
- void SetMaxOutputStreamsAllowed(int max) { max_num_output_streams_ = max; }
-
- // Called by each platform specific AudioManager to notify output state change
- // listeners that a state change has occurred. Must be called from the audio
- // thread.
- void NotifyAllOutputDeviceChangeListeners();
-
- // Map of cached AudioOutputDispatcher instances. Must only be touched
- // from the audio thread (no locking).
- AudioOutputDispatchersMap output_dispatchers_;
-
- private:
- // Called by Shutdown().
- void ShutdownOnAudioThread();
-
- // Counts the number of active input streams to find out if something else
- // is currently recording in Chrome.
- base::AtomicRefCount num_active_input_streams_;
-
- // Max number of open output streams, modified by
- // SetMaxOutputStreamsAllowed().
- int max_num_output_streams_;
-
- // Max number of open input streams.
- int max_num_input_streams_;
-
- // Number of currently open output streams.
- int num_output_streams_;
-
- // Number of currently open input streams.
- int num_input_streams_;
-
- // Track output state change listeners.
- ObserverList<AudioDeviceListener> output_listeners_;
-
- // Thread used to interact with audio streams created by this audio manager.
- scoped_ptr<base::Thread> audio_thread_;
- mutable base::Lock audio_thread_lock_;
-
- // The message loop of the audio thread this object runs on. Used for internal
- // tasks which run on the audio thread even after Shutdown() has been started
- // and GetMessageLoop() starts returning NULL.
- scoped_refptr<base::MessageLoopProxy> message_loop_;
-
- // Currently active VirtualAudioInputStream. When this is set, we will
- // create all audio output streams as virtual streams so as to redirect audio
- // data to this virtual input stream.
- VirtualAudioInputStream* virtual_audio_input_stream_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioManagerBase);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_AUDIO_MANAGER_BASE_H_
diff --git a/src/media/audio/audio_output_controller.cc b/src/media/audio/audio_output_controller.cc
deleted file mode 100644
index 50850c9..0000000
--- a/src/media/audio/audio_output_controller.cc
+++ /dev/null
@@ -1,400 +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 "media/audio/audio_output_controller.h"
-
-#include "base/bind.h"
-#include "base/debug/trace_event.h"
-#include "base/message_loop.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/time.h"
-#include "build/build_config.h"
-#include "media/audio/shared_memory_util.h"
-
-using base::Time;
-using base::TimeDelta;
-using base::WaitableEvent;
-
-namespace media {
-
-// Polling-related constants.
-const int AudioOutputController::kPollNumAttempts = 3;
-const int AudioOutputController::kPollPauseInMilliseconds = 3;
-
-AudioOutputController::AudioOutputController(AudioManager* audio_manager,
- EventHandler* handler,
- const AudioParameters& params,
- SyncReader* sync_reader)
- : audio_manager_(audio_manager),
- handler_(handler),
- stream_(NULL),
- volume_(1.0),
- state_(kEmpty),
- sync_reader_(sync_reader),
- message_loop_(audio_manager->GetMessageLoop()),
- number_polling_attempts_left_(0),
- params_(params),
- ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) {
-}
-
-AudioOutputController::~AudioOutputController() {
- DCHECK_EQ(kClosed, state_);
-
- if (message_loop_->BelongsToCurrentThread()) {
- DoStopCloseAndClearStream(NULL);
- } else {
- // http://crbug.com/120973
- base::ThreadRestrictions::ScopedAllowWait allow_wait;
- WaitableEvent completion(true /* manual reset */,
- false /* initial state */);
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&AudioOutputController::DoStopCloseAndClearStream,
- base::Unretained(this),
- &completion));
- completion.Wait();
- }
-}
-
-// static
-scoped_refptr<AudioOutputController> AudioOutputController::Create(
- AudioManager* audio_manager,
- EventHandler* event_handler,
- const AudioParameters& params,
- SyncReader* sync_reader) {
- DCHECK(audio_manager);
- DCHECK(sync_reader);
-
- if (!params.IsValid() || !audio_manager)
- return NULL;
-
- // Starts the audio controller thread.
- scoped_refptr<AudioOutputController> controller(new AudioOutputController(
- audio_manager, event_handler, params, sync_reader));
-
- controller->message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioOutputController::DoCreate, controller));
-
- return controller;
-}
-
-void AudioOutputController::Play() {
- DCHECK(message_loop_);
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioOutputController::DoPlay, this));
-}
-
-void AudioOutputController::Pause() {
- DCHECK(message_loop_);
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioOutputController::DoPause, this));
-}
-
-void AudioOutputController::Flush() {
- DCHECK(message_loop_);
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioOutputController::DoFlush, this));
-}
-
-void AudioOutputController::Close(const base::Closure& closed_task) {
- DCHECK(!closed_task.is_null());
- DCHECK(message_loop_);
- message_loop_->PostTaskAndReply(FROM_HERE, base::Bind(
- &AudioOutputController::DoClose, this), closed_task);
-}
-
-void AudioOutputController::SetVolume(double volume) {
- DCHECK(message_loop_);
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &AudioOutputController::DoSetVolume, this, volume));
-}
-
-void AudioOutputController::DoCreate() {
- DCHECK(message_loop_->BelongsToCurrentThread());
-
- // Close() can be called before DoCreate() is executed.
- if (state_ == kClosed)
- return;
- DCHECK(state_ == kEmpty || state_ == kRecreating) << state_;
-