Import Cobalt 22.lts.1.304707
diff --git a/src/.pre-commit-config.yaml b/src/.pre-commit-config.yaml
index 6240f63..7bc0f93 100644
--- a/src/.pre-commit-config.yaml
+++ b/src/.pre-commit-config.yaml
@@ -117,6 +117,7 @@
language: python
stages: [push]
always_run: true
+ pass_filenames: false
- id: run-py2-tests
name: Run Python 2 Tests
description: Run Python 2 unittests
diff --git a/src/CONTRIBUTING.md b/src/CONTRIBUTING.md
index 71f6b83..332d6ab 100644
--- a/src/CONTRIBUTING.md
+++ b/src/CONTRIBUTING.md
@@ -53,7 +53,7 @@
1. Run `git clang-format HEAD~` to apply default C++ formatting rules,
followed by `git commit -a --amend` to squash any formatting changes
into your commit.
- 1. Run `git cl upload` to upload the review to
+ 1. Run `git push origin HEAD:refs/for/master` to upload the review to
[Cobalt's Gerrit instance](https://cobalt-review.googlesource.com/).
1. Someone from the maintainers team will review the code, putting up comments
on any things that need to change for submission.
@@ -62,4 +62,3 @@
1. If you do not need to make any more changes, a maintainer will integrate
the change into our private repository, and it will get pushed out to the
public repository after some time.
-
diff --git a/src/cobalt/black_box_tests/testdata/freeze_timers.html b/src/cobalt/black_box_tests/testdata/freeze_timers.html
new file mode 100644
index 0000000..0ad6f4c
--- /dev/null
+++ b/src/cobalt/black_box_tests/testdata/freeze_timers.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+
+<head>
+ <title>Cobalt WindowTimers test with freeze state</title>
+ <script src='black_box_js_test_utils.js'></script>
+</head>
+
+<body>
+ <h1>
+ <span>ID element</span>
+ </h1>
+ <script>
+
+ // Delay for setInterval (ms)
+ var kSetIntervalDelay = 600;
+ // Delay for setTimeout (ms)
+ var kSetTimeoutDelay = 800;
+ // Delay for the long setTimeout (ms)
+ var kLongSetTimeoutDelay = 13000;
+
+ // Allowed time difference for callback expected execution time (ms)
+ // This value is quite high because in devel builds on slower devices there
+ // may occesionally be larger delays.
+ var kAllowedTimeDifference = 100;
+
+ // Last time when the app was resumed from frozen state.
+ var resumeTime = 0;
+ // Expected execution time for interval callback
+ var expectedIntervalTime = 0;
+ // Expected execution time for timeout callback
+ var expectedTimeoutTime = 0;
+ // Expected execution time for long timeout callback
+ var expectedLongTimeoutTime = 0;
+
+ let wasEverFrozen = false;
+ let frozen = false;
+ let eventWhileFrozen = false;
+
+ // Callback for registering entering freeze state.
+ function freezeCallback() {
+ console.log("document.onfreeze")
+ wasEverFrozen = true;
+ frozen = true;
+ }
+
+ // Callback for registering leaving freeze state.
+ function resumeCallback() {
+ resumeTime = performance.now();
+ console.log("document.onresume")
+ if (!frozen) {
+ console.log('ERROR: Resuming while not frozen');
+ notReached();
+ }
+ if (eventWhileFrozen) {
+ console.log('ERROR: Timer callback received while frozen');
+ notReached();
+ }
+ frozen = false;
+ }
+
+ function logState(prefix) {
+ console.log(prefix + ": State:" +
+ " document.visibilityState == " + document.visibilityState +
+ " document.hidden == " + document.hidden +
+ " document.hasFocus() == " + document.hasFocus());
+ }
+
+ function verifyNotFrozen() {
+ eventWhileFrozen |= frozen;
+ }
+
+ function CheckTiming(expectedTime) {
+ if (expectedTime != 0) {
+ // Callbacks are allowed to execute no more than kAllowedTimeDifference
+ // before or after the expected time, except when the app was frozen,
+ // in which case it should be executed immediately when resuming, while
+ // the document is still hidden.
+ if (wasEverFrozen && resumeTime > expectedTime && document.hidden) {
+ // The callback is too late, but that is because the document was
+ // frozen.
+ return;
+ }
+
+ if (Math.abs(performance.now() - expectedTime) > kAllowedTimeDifference) {
+ console.log('ERROR: Timer callback timing out of allowed range (' +
+ Math.round(Math.abs(performance.now() - expectedTime)) +
+ 'ms difference)');
+ notReached();
+ }
+ }
+ }
+
+ function DoTimeout() {
+ verifyNotFrozen();
+ CheckTiming(expectedTimeoutTime);
+ logState('Timeout');
+ expectedTimeoutTime = performance.now() + kSetTimeoutDelay;
+ setTimeout(DoTimeout, kSetTimeoutDelay);
+ }
+
+ function DoLongTimeout() {
+ verifyNotFrozen();
+ CheckTiming(expectedLongTimeoutTime);
+ logState('Long Timeout');
+ if (wasEverFrozen && !eventWhileFrozen) {
+ onEndTest();
+ }
+
+ expectedLongTimeoutTime = performance.now() + kLongSetTimeoutDelay;
+ setTimeout(DoLongTimeout, kLongSetTimeoutDelay);
+ }
+
+ function DoInterval() {performance.now()
+ var now = performance.now();
+ verifyNotFrozen();
+ CheckTiming(expectedIntervalTime);
+ logState('Interval');
+ expectedIntervalTime = now + kSetIntervalDelay;
+ }
+
+ document.addEventListener("freeze", freezeCallback);
+ document.addEventListener("resume", resumeCallback);
+
+ DoLongTimeout();
+ DoTimeout();
+ setInterval(DoInterval, kSetIntervalDelay);
+
+ setupFinished();
+ </script>
+</body>
diff --git a/src/cobalt/black_box_tests/testdata/override_ua_parameters.html b/src/cobalt/black_box_tests/testdata/override_ua_parameters.html
new file mode 100644
index 0000000..d005b11
--- /dev/null
+++ b/src/cobalt/black_box_tests/testdata/override_ua_parameters.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Cobalt override ua parameters test</title>
+ <script src='black_box_js_test_utils.js'></script>
+</head>
+
+<body>
+ <div>Full User-Agent Data</div>
+
+ <script>
+ // Enable User-Agent Client Hints API
+ h5vcc.settings.set("NavigatorUAData", 1);
+
+ navigator.userAgentData.getHighEntropyValues(
+ ["model", "uaFullVersion", "cobaltBuildNumber", "cobaltBuildConfiguration",
+ "jsEngineVersion", "rasterizer", "evergreenVersion",
+ "starboardVersion", "originalDesignManufacturer",
+ "deviceType", "chipset", "modelYear", "deviceBrand",
+ "connectionType", "aux"])
+ .then(
+ ua => {
+ assertEqual(ua.aux, "foo.bar.baz.qux/21.2.1.41.0");
+ assertEqual(ua.deviceBrand, "Cobalt");
+ assertEqual(ua.cobaltBuildConfiguration, "debug");
+ assertEqual(ua.chipset, "foobar0000");
+ assertEqual(ua.cobaltBuildNumber, "289852");
+ assertEqual(ua.connectionType, "Wireless");
+ assertEqual(ua.deviceType, "ATV");
+ assertEqual(ua.evergreenVersion, "");
+ assertEqual(ua.jsEngineVersion, "v8/7.7.299.8-jit");
+ assertEqual(ua.model, "QUUX");
+ assertEqual(ua.modelYear, "2018");
+ assertEqual(ua.originalDesignManufacturer, "Quuz");
+ assertEqual(ua.starboardVersion, "Starboard/12");
+ assertEqual(ua.rasterizer, "gles");
+ assertEqual(ua.uaFullVersion, "21.lts.2.289852-debug");
+
+ onEndTest();
+ setupFinished();
+ });
+ </script>
+</body>
+
+</html>
diff --git a/src/cobalt/black_box_tests/tests/freeze_timers.py b/src/cobalt/black_box_tests/tests/freeze_timers.py
new file mode 100644
index 0000000..538743c
--- /dev/null
+++ b/src/cobalt/black_box_tests/tests/freeze_timers.py
@@ -0,0 +1,44 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests WindowTimers and application freezing and resuming."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import _env # pylint: disable=unused-import
+
+import time
+
+from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
+
+
+class FreezeVisibilityTest(black_box_tests.BlackBoxTestCase):
+ """Verify correct timer suspend and resume during and after freeze event."""
+
+ def test_simple(self):
+
+ with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server:
+ url = server.GetURL(file_name='testdata/freeze_timers.html')
+
+ with self.CreateCobaltRunner(url=url) as runner:
+ runner.WaitForJSTestsSetup()
+ time.sleep(2.5)
+ runner.SendConceal()
+ time.sleep(2.5)
+ runner.SendFreeze()
+ time.sleep(3.333)
+ runner.SendFocus()
+ self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/override_ua_parameters.py b/src/cobalt/black_box_tests/tests/override_ua_parameters.py
new file mode 100644
index 0000000..4f5fd45
--- /dev/null
+++ b/src/cobalt/black_box_tests/tests/override_ua_parameters.py
@@ -0,0 +1,87 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Test overriding UA parameters when launching Cobalt."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import SimpleHTTPServer
+
+from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import MakeRequestHandlerClass
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
+
+# The base path of the requested assets is the parent directory.
+_SERVER_ROOT_PATH = os.path.join(os.path.dirname(__file__), os.pardir)
+
+
+class JavascriptRequestDetector(MakeRequestHandlerClass(_SERVER_ROOT_PATH)):
+ """Proxies everything to SimpleHTTPRequestHandler, except some paths."""
+
+ def do_GET(self): # pylint: disable=invalid-name
+ """Handles HTTP GET requests for resources."""
+ # Check if UA string in the request header reflects correct UA params
+ # overrides
+ ua_request_header = self.headers.get('user-agent', '')
+ expected_ua_request_header = 'Mozilla/5.0 (Corge grault-v7a; '\
+ 'Garply 7.1.2; Waldo OS 6.0) Cobalt/21.lts.2.289852-debug '\
+ '(unlike Gecko) v8/7.7.299.8-jit gles Starboard/12, '\
+ 'Quuz_ATV_foobar0000_2018/Unknown (Cobalt, QUUX, Wireless) '\
+ 'foo.bar.baz.qux/21.2.1.41.0'
+
+ if not ua_request_header == expected_ua_request_header:
+ raise ValueError('UA string in HTTP request header does not match with '\
+ 'UA params overrides specified in command line\n'\
+ 'UA string in HTTP request header:%s' % (ua_request_header))
+
+ return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
+
+
+class OverrideUAParametersTest(black_box_tests.BlackBoxTestCase):
+ """Test overriding UA parameters."""
+
+ def test_simple(self):
+ """Set UA parameters when launching Cobalt."""
+
+ with ThreadedWebServer(
+ JavascriptRequestDetector,
+ binding_address=self.GetBindingAddress()) as server:
+ with self.CreateCobaltRunner(
+ url=server.GetURL(file_name='testdata/override_ua_parameters.html'),
+ target_params=[
+ '--user_agent_client_hints='\
+ 'aux_field=foo.bar.baz.qux/21.2.1.41.0;'\
+ 'brand=Cobalt;'\
+ 'build_configuration=debug;'\
+ 'chipset_model_number=foobar0000;'\
+ 'cobalt_build_version_number=289852;'\
+ 'cobalt_version=21.lts.2;'\
+ 'connection_type=Wireless;'\
+ 'device_type=ATV;'\
+ 'evergreen_type=;'\
+ 'evergreen_version=;'\
+ 'javascript_engine_version=v8/7.7.299.8-jit;'\
+ 'firmware_version=;'\
+ 'model=QUUX;'\
+ 'model_year=2018;'\
+ 'original_design_manufacturer=Quuz;'\
+ 'os_name_and_version=Corge grault-v7a\\; Garply 7.1.2\\; '\
+ 'Waldo OS 6.0;'\
+ 'starboard_version=Starboard/12;'\
+ 'rasterizer_type=gles'
+ ]) as runner:
+ runner.WaitForJSTestsSetup()
+ self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 984540e..05e4364 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -591,9 +591,6 @@
ssize_t Application::available_memory_ = 0;
int64 Application::lifetime_in_ms_ = 0;
-Application::AppStatus Application::app_status_ =
- Application::kUninitializedAppStatus;
-
Application::Application(const base::Closure& quit_closure, bool should_preload,
SbTimeMonotonic timestamp)
: message_loop_(base::MessageLoop::current()),
@@ -860,8 +857,6 @@
timestamp);
UpdateUserAgent();
- app_status_ = (should_preload ? kConcealedAppStatus : kRunningAppStatus);
-
// Register event callbacks.
window_size_change_event_callback_ = base::Bind(
&Application::OnWindowSizeChangedEvent, base::Unretained(this));
@@ -1009,21 +1004,19 @@
base::DateTimeConfigurationChangedEvent::TypeId(),
on_date_time_configuration_changed_event_callback_);
#endif
-
- app_status_ = kShutDownAppStatus;
}
void Application::Start(SbTimeMonotonic timestamp) {
if (base::MessageLoop::current() != message_loop_) {
message_loop_->task_runner()->PostTask(
- FROM_HERE, base::Bind(&Application::Start, base::Unretained(this),
- timestamp));
+ FROM_HERE,
+ base::Bind(&Application::Start, base::Unretained(this), timestamp));
return;
}
OnApplicationEvent(kSbEventTypeStart, timestamp);
- browser_module_->SetApplicationStartOrPreloadTimestamp(
- false /*is_preload*/, timestamp);
+ browser_module_->SetApplicationStartOrPreloadTimestamp(false /*is_preload*/,
+ timestamp);
}
void Application::Quit() {
@@ -1034,7 +1027,6 @@
}
quit_closure_.Run();
- app_status_ = kQuitAppStatus;
}
void Application::HandleStarboardEvent(const SbEvent* starboard_event) {
@@ -1103,10 +1095,10 @@
case kSbEventTypeLink: {
#if SB_API_VERSION >= 13
DispatchDeepLink(static_cast<const char*>(starboard_event->data),
- starboard_event->timestamp);
-#else // SB_API_VERSION >= 13
+ starboard_event->timestamp);
+#else // SB_API_VERSION >= 13
DispatchDeepLink(static_cast<const char*>(starboard_event->data),
- SbTimeGetMonotonicNow());
+ SbTimeGetMonotonicNow());
#endif // SB_API_VERSION >= 13
break;
}
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index d902680..c08330c 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -203,8 +203,6 @@
static ssize_t available_memory_;
static int64 lifetime_in_ms_;
- static AppStatus app_status_;
-
CValStats c_val_stats_;
base::RepeatingTimer stats_update_timer_;
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index bb922b0..1f6e058 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -1471,7 +1471,6 @@
DCHECK(application_state_ == base::kApplicationStateBlurred);
application_state_ = base::kApplicationStateConcealed;
ConcealInternal(timestamp);
- OnMaybeFreeze();
}
void BrowserModule::Focus(SbTimeMonotonic timestamp) {
@@ -1824,7 +1823,7 @@
}
void BrowserModule::OnMaybeFreeze() {
- TRACE_EVENT0("cobalt::browser", "BrowserModule::MaybeFreeze()");
+ TRACE_EVENT0("cobalt::browser", "BrowserModule::OnMaybeFreeze()");
if (base::MessageLoop::current() != self_message_loop_) {
self_message_loop_->task_runner()->PostTask(
FROM_HERE,
@@ -1846,6 +1845,7 @@
web_module_ready_to_freeze &&
application_state_ == base::kApplicationStateConcealed) {
#if SB_API_VERSION >= 13
+ DLOG(INFO) << "System request to freeze the app.";
SbSystemRequestFreeze();
#endif // SB_API_VERSION >= 13
}
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 1411c37..439de72 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -175,6 +175,24 @@
"'network_operator'_'device_type'_'chipset_model_number'_'model_year'/"
"'firmware_version' ('brand', 'model', 'connection_type') 'aux_field'\".";
+const char kUserAgentClientHints[] = "user_agent_client_hints";
+const char kUserAgentClientHintsHelp[] =
+ "Specify custom user agent client hints for device simulations. "
+ "Configure user agent fields in a string delimited by semicolon ';'. "
+ "If semicolon is expected to be in value of any user agent field, "
+ "escape the semicolon by prefixing it with backslash '\\'. "
+ "Empty field value is allowed. "
+ "Example: "
+ "--user_agent_client_hints=\"device_type=GAME;"
+ "os_name_and_version=Linux armeabi-v7a\\; Android 7.1.2;evergreen_type=\" "
+ "The 18 supported UA fields for overriding are: aux_field, brand, "
+ "build_configuration, chipset_model_number, cobalt_build_version_number, "
+ "cobalt_build_version_number, connection_type, device_type, "
+ "evergreen_type,evergreen_version, firmware_version, "
+ "javascript_engine_version, model, model_year, "
+ "original_design_manufacturer, os_name_and_version, starboard_version, "
+ "rasterizer_type";
+
const char kUserAgentOsNameVersion[] = "user_agent_os_name_version";
const char kUserAgentOsNameVersionHelp[] =
"Specifies a custom 'os_name_and_version' user agent field with otherwise "
@@ -441,6 +459,7 @@
{kStubImageDecoder, kStubImageDecoderHelp},
{kSuspendFuzzer, kSuspendFuzzerHelp}, {kTimedTrace, kTimedTraceHelp},
{kUserAgent, kUserAgentHelp},
+ {kUserAgentClientHints, kUserAgentClientHintsHelp},
{kUserAgentOsNameVersion, kUserAgentOsNameVersionHelp},
{kUseTTS, kUseTTSHelp}, {kWebDriverListenIp, kWebDriverListenIpHelp},
{kWebDriverPort, kWebDriverPortHelp},
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 53fe622..0a95017 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -78,6 +78,8 @@
extern const char kSuspendFuzzerHelp[];
extern const char kTimedTrace[];
extern const char kTimedTraceHelp[];
+extern const char kUserAgentClientHints[];
+extern const char kUserAgentClienthintsHelp[];
extern const char kUserAgentOsNameVersion[];
extern const char kUserAgentOsNameVersionHelp[];
extern const char kUseTTS[];
diff --git a/src/cobalt/browser/user_agent_platform_info.cc b/src/cobalt/browser/user_agent_platform_info.cc
index 8c47fee..aab3d91 100644
--- a/src/cobalt/browser/user_agent_platform_info.cc
+++ b/src/cobalt/browser/user_agent_platform_info.cc
@@ -14,6 +14,7 @@
#include "cobalt/browser/user_agent_platform_info.h"
+#include <map>
#include <memory>
#include "base/command_line.h"
@@ -37,49 +38,145 @@
namespace cobalt {
namespace browser {
-namespace {
+void GetUserAgentInputMap(
+ const std::string& user_agent_input,
+ std::map<std::string, std::string>& user_agent_input_map) {
+ struct state {
+ std::string field{""};
+ std::string value{""};
+ bool field_value_delimiter_found{false};
-std::string CreateDeviceTypeString(SbSystemDeviceType device_type) {
- switch (device_type) {
- case kSbSystemDeviceTypeBlueRayDiskPlayer:
- return "BDP";
- case kSbSystemDeviceTypeGameConsole:
- return "GAME";
- case kSbSystemDeviceTypeOverTheTopBox:
- return "OTT";
- case kSbSystemDeviceTypeSetTopBox:
- return "STB";
- case kSbSystemDeviceTypeTV:
- return "TV";
- case kSbSystemDeviceTypeAndroidTV:
- return "ATV";
- case kSbSystemDeviceTypeDesktopPC:
- return "DESKTOP";
- case kSbSystemDeviceTypeUnknown:
- return "UNKNOWN";
- default:
- NOTREACHED();
- return "UNKNOWN";
+ void reset() {
+ field.clear();
+ value.clear();
+ field_value_delimiter_found = false;
+ }
+ } current_state;
+
+ char escape_char = '\\';
+ char override_delimit_char = ';';
+ char field_value_delimit_char = '=';
+ bool prev_is_escape_char = false;
+
+ for (auto cur_char : user_agent_input) {
+ if (cur_char == override_delimit_char) {
+ if (prev_is_escape_char) {
+ if (!current_state.value.empty() &&
+ current_state.value.back() == escape_char) { // escape delimiter
+ // found in value.
+
+ current_state.value.back() = override_delimit_char;
+ } else { // not a valid case for field, reset
+ current_state.reset();
+ }
+ } else {
+ if (current_state.field_value_delimiter_found) { // valid field value
+ // pair found.
+ user_agent_input_map[current_state.field] = current_state.value;
+ } // else, in current captured override, field_value_delimit_char
+ // is not found, invalid.
+ current_state.reset();
+ }
+ } else if (cur_char == field_value_delimit_char) {
+ if (current_state.field.empty()) { // field is not found when encounter
+ // field_value_delimit_char,
+ // invalid.
+ current_state.reset();
+ } else {
+ current_state.field_value_delimiter_found =
+ true; // a field is found, next char is expected to be value
+ }
+ } else {
+ if (current_state.field_value_delimiter_found) {
+ current_state.value.push_back(cur_char);
+ } else {
+ current_state.field.push_back(cur_char);
+ }
+ }
+ if (cur_char == escape_char) {
+ prev_is_escape_char = true;
+ } else {
+ prev_is_escape_char = false;
+ }
+ }
+ if (current_state.field_value_delimiter_found) {
+ user_agent_input_map[current_state.field] = current_state.value;
}
}
-const char kUnspecifiedConnectionTypeName[] = "UnspecifiedConnectionType";
+namespace {
+
+struct DeviceTypeName {
+ SbSystemDeviceType device_type;
+ char device_type_string[8];
+};
+
+const DeviceTypeName kDeviceTypeStrings[] = {
+ {kSbSystemDeviceTypeBlueRayDiskPlayer, "BDP"},
+ {kSbSystemDeviceTypeGameConsole, "GAME"},
+ {kSbSystemDeviceTypeOverTheTopBox, "OTT"},
+ {kSbSystemDeviceTypeSetTopBox, "STB"},
+ {kSbSystemDeviceTypeTV, "TV"},
+ {kSbSystemDeviceTypeAndroidTV, "ATV"},
+ {kSbSystemDeviceTypeDesktopPC, "DESKTOP"},
+ {kSbSystemDeviceTypeUnknown, "UNKNOWN"}};
+
+std::string CreateDeviceTypeString(SbSystemDeviceType device_type) {
+ for (auto& map : kDeviceTypeStrings) {
+ if (map.device_type == device_type) {
+ return std::string(map.device_type_string);
+ }
+ }
+ NOTREACHED();
+ return "UNKNOWN";
+}
+
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+SbSystemDeviceType GetDeviceType(std::string device_type_string) {
+ for (auto& map : kDeviceTypeStrings) {
+ if (!SbStringCompareNoCase(map.device_type_string,
+ device_type_string.c_str())) {
+ return map.device_type;
+ }
+ }
+ return kSbSystemDeviceTypeUnknown;
+}
+#endif
+
+struct ConnectionTypeName {
+ SbSystemConnectionType connection_type;
+ char connection_type_string[26];
+};
+
+const ConnectionTypeName kConnectionTypeStrings[] = {
+ {kSbSystemConnectionTypeWired, "Wired"},
+ {kSbSystemConnectionTypeWireless, "Wireless"},
+ {kSbSystemConnectionTypeUnknown, "UnspecifiedConnectionType"}};
std::string CreateConnectionTypeString(
const base::Optional<SbSystemConnectionType>& connection_type) {
if (connection_type) {
- switch (*connection_type) {
- case kSbSystemConnectionTypeWired:
- return "Wired";
- case kSbSystemConnectionTypeWireless:
- return "Wireless";
- case kSbSystemConnectionTypeUnknown:
- return kUnspecifiedConnectionTypeName;
+ for (auto& map : kConnectionTypeStrings) {
+ if (map.connection_type == connection_type) {
+ return std::string(map.connection_type_string);
+ }
}
}
- return kUnspecifiedConnectionTypeName;
+ return "UnspecifiedConnectionType";
}
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+SbSystemConnectionType GetConnectionType(std::string connection_type_string) {
+ for (auto& map : kConnectionTypeStrings) {
+ if (!SbStringCompareNoCase(map.connection_type_string,
+ connection_type_string.c_str())) {
+ return map.connection_type;
+ }
+ }
+ return kSbSystemConnectionTypeUnknown;
+}
+#endif
+
static bool isAsciiAlphaDigit(int c) {
return base::IsAsciiAlpha(c) || base::IsAsciiDigit(c);
}
@@ -142,7 +239,6 @@
return base::Optional<std::string>(clean);
}
-
// Function that will query Starboard and populate a UserAgentPlatformInfo
// object based on those results. This is de-coupled from
// CreateUserAgentString() so that the common logic in CreateUserAgentString()
@@ -269,8 +365,85 @@
// Connection type
info.set_connection_type(SbSystemGetConnectionType());
-}
+// Apply overrides from command line
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+ if (base::CommandLine::InitializedForCurrentProcess()) {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kUserAgentClientHints)) {
+ LOG(INFO) << "Entering UA overrides";
+ std::string user_agent_input =
+ command_line->GetSwitchValueASCII(switches::kUserAgentClientHints);
+
+ std::map<std::string, std::string> user_agent_input_map;
+ GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+ for (const auto& input : user_agent_input_map) {
+ LOG(INFO) << "Overriding " << input.first << " to " << input.second;
+
+ if (!input.first.compare("starboard_version")) {
+ info.set_starboard_version(input.second);
+ LOG(INFO) << "Set starboard version to " << input.second;
+ } else if (!input.first.compare("os_name_and_version")) {
+ info.set_os_name_and_version(input.second);
+ LOG(INFO) << "Set os name and version to " << input.second;
+ } else if (!input.first.compare("original_design_manufacturer")) {
+ info.set_original_design_manufacturer(input.second);
+ LOG(INFO) << "Set original design manufacturer to " << input.second;
+ } else if (!input.first.compare("device_type")) {
+ info.set_device_type(GetDeviceType(input.second));
+ LOG(INFO) << "Set device type to " << input.second;
+ } else if (!input.first.compare("chipset_model_number")) {
+ info.set_chipset_model_number(input.second);
+ LOG(INFO) << "Set chipset model to " << input.second;
+ } else if (!input.first.compare("model_year")) {
+ info.set_model_year(input.second);
+ LOG(INFO) << "Set model year to " << input.second;
+ } else if (!input.first.compare("firmware_version")) {
+ info.set_firmware_version(input.second);
+ LOG(INFO) << "Set firmware version to " << input.second;
+ } else if (!input.first.compare("brand")) {
+ info.set_brand(input.second);
+ LOG(INFO) << "Set brand to " << input.second;
+ } else if (!input.first.compare("model")) {
+ info.set_model(input.second);
+ LOG(INFO) << "Set model to " << input.second;
+ } else if (!input.first.compare("aux_field")) {
+ info.set_aux_field(input.second);
+ LOG(INFO) << "Set aux field to " << input.second;
+ } else if (!input.first.compare("connection_type")) {
+ info.set_connection_type(GetConnectionType(input.second));
+ LOG(INFO) << "Set connection type to " << input.second;
+ } else if (!input.first.compare("javascript_engine_version")) {
+ info.set_javascript_engine_version(input.second);
+ LOG(INFO) << "Set javascript engine version to " << input.second;
+ } else if (!input.first.compare("rasterizer_type")) {
+ info.set_rasterizer_type(input.second);
+ LOG(INFO) << "Set rasterizer type to " << input.second;
+ } else if (!input.first.compare("evergreen_type")) {
+ info.set_evergreen_type(input.second);
+ LOG(INFO) << "Set evergreen type to " << input.second;
+ } else if (!input.first.compare("evergreen_version")) {
+ info.set_evergreen_version(input.second);
+ LOG(INFO) << "Set evergreen version to " << input.second;
+ } else if (!input.first.compare("cobalt_version")) {
+ info.set_cobalt_version(input.second);
+ LOG(INFO) << "Set cobalt type to " << input.second;
+ } else if (!input.first.compare("cobalt_build_version_number")) {
+ info.set_cobalt_build_version_number(input.second);
+ LOG(INFO) << "Set cobalt build version to " << input.second;
+ } else if (!input.first.compare("build_configuration")) {
+ info.set_build_configuration(input.second);
+ LOG(INFO) << "Set build configuration to " << input.second;
+ } else {
+ LOG(WARNING) << "Unsupported user agent field: " << input.first;
+ }
+ }
+ }
+ }
+
+#endif
+}
} // namespace
UserAgentPlatformInfo::UserAgentPlatformInfo() {
diff --git a/src/cobalt/browser/user_agent_platform_info.h b/src/cobalt/browser/user_agent_platform_info.h
index db6c94d..7922671 100644
--- a/src/cobalt/browser/user_agent_platform_info.h
+++ b/src/cobalt/browser/user_agent_platform_info.h
@@ -15,6 +15,7 @@
#ifndef COBALT_BROWSER_USER_AGENT_PLATFORM_INFO_H_
#define COBALT_BROWSER_USER_AGENT_PLATFORM_INFO_H_
+#include <map>
#include <string>
#include "cobalt/dom/user_agent_platform_info.h"
@@ -22,6 +23,10 @@
namespace cobalt {
namespace browser {
+void GetUserAgentInputMap(
+ const std::string& user_agent_input,
+ std::map<std::string, std::string>& user_agent_input_map);
+
class UserAgentPlatformInfo : public dom::UserAgentPlatformInfo {
public:
UserAgentPlatformInfo();
diff --git a/src/cobalt/browser/user_agent_string_test.cc b/src/cobalt/browser/user_agent_string_test.cc
index d2537e7..bbd66a7 100644
--- a/src/cobalt/browser/user_agent_string_test.cc
+++ b/src/cobalt/browser/user_agent_string_test.cc
@@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/browser/user_agent_string.h"
+#include <map>
+#include "cobalt/browser/user_agent_platform_info.h"
+#include "cobalt/browser/user_agent_string.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
@@ -349,6 +351,87 @@
EXPECT_NE(std::string::npos, user_agent_string.find(" V8/6.5.254.28"));
}
+TEST(GetUserAgentInputMapTest, DelimitParamsBySemicolon) {
+ std::map<std::string, std::string> user_agent_input_map;
+ const std::string user_agent_input =
+ "model_year=2049;starboard_version=Starboard/"
+ "13;original_design_manufacturer=foo;device_type=GAME";
+ GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+ std::map<std::string, std::string> expected_user_agent_input_map{
+ {"device_type", "GAME"},
+ {"model_year", "2049"},
+ {"original_design_manufacturer", "foo"},
+ {"starboard_version", "Starboard/13"},
+ };
+ EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
+
+TEST(GetUserAgentInputMapTest, HandleSpecialChar) {
+ std::map<std::string, std::string> user_agent_input_map;
+ const std::string user_agent_input =
+ "aux_field=foo.bar.baz.qux/"
+ "21.2.1.41.0;invalid-field~name=quux(#quuz)";
+ GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+ std::map<std::string, std::string> expected_user_agent_input_map{
+ {"aux_field", "foo.bar.baz.qux/21.2.1.41.0"},
+ {"invalid-field~name", "quux(#quuz)"},
+ };
+ EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
+
+TEST(GetUserAgentInputMapTest, EscapeSemicolonInValue) {
+ std::map<std::string, std::string> user_agent_input_map;
+ const std::string user_agent_input =
+ "os_name_and_version=Foo bar-v7a\\; Baz 7.1.2\\; Qux OS "
+ "6.0;model=QUUX";
+ GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+ std::map<std::string, std::string> expected_user_agent_input_map{
+ {"model", "QUUX"},
+ {"os_name_and_version", "Foo bar-v7a; Baz 7.1.2; Qux OS 6.0"},
+ };
+ EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
+
+TEST(GetUserAgentInputMapTest, OmitEscapeSemicolonInField) {
+ std::map<std::string, std::string> user_agent_input_map;
+ const std::string user_agent_input = "foo//;bar=baz";
+
+ GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+ std::map<std::string, std::string> expected_user_agent_input_map{
+ {"bar", "baz"},
+ };
+ EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
+
+TEST(GetUserAgentInputMapTest, HandleEmptyFieldValue) {
+ std::map<std::string, std::string> user_agent_input_map;
+ const std::string user_agent_input =
+ "evergreen_type=;device_type=GAME;evergreen_version=";
+ GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+ std::map<std::string, std::string> expected_user_agent_input_map{
+ {"device_type", "GAME"},
+ {"evergreen_type", ""},
+ {"evergreen_version", ""},
+ };
+ EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
+
+TEST(GetUserAgentInputMapTest, FailSafeWithInvalidInput) {
+ std::map<std::string, std::string> user_agent_input_map;
+ const std::string user_agent_input =
+ ";model;aux_field=;=dummy;device_type=GAME;invalid_field";
+ GetUserAgentInputMap(user_agent_input, user_agent_input_map);
+
+ std::map<std::string, std::string> expected_user_agent_input_map{
+ {"aux_field", ""}, {"device_type", "GAME"},
+ };
+ EXPECT_TRUE(user_agent_input_map == expected_user_agent_input_map);
+}
} // namespace
} // namespace browser
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 606938d..ef84f3b 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -261,8 +261,8 @@
void DoSynchronousLayoutAndGetRenderTree(
scoped_refptr<render_tree::Node>* render_tree);
- void SetApplicationStartOrPreloadTimestamp(
- bool is_preload, SbTimeMonotonic timestamp);
+ void SetApplicationStartOrPreloadTimestamp(bool is_preload,
+ SbTimeMonotonic timestamp);
void SetDeepLinkTimestamp(SbTimeMonotonic timestamp);
private:
@@ -1031,8 +1031,8 @@
void WebModule::Impl::SetApplicationStartOrPreloadTimestamp(
bool is_preload, SbTimeMonotonic timestamp) {
DCHECK(window_);
- window_->performance()->SetApplicationStartOrPreloadTimestamp(
- is_preload, timestamp);
+ window_->performance()->SetApplicationStartOrPreloadTimestamp(is_preload,
+ timestamp);
}
void WebModule::Impl::SetDeepLinkTimestamp(SbTimeMonotonic timestamp) {
@@ -1171,12 +1171,12 @@
SetApplicationState(base::kApplicationStateBlurred, timestamp);
}
-void WebModule::Impl::Conceal(
- render_tree::ResourceProvider* resource_provider,
- SbTimeMonotonic timestamp) {
+void WebModule::Impl::Conceal(render_tree::ResourceProvider* resource_provider,
+ SbTimeMonotonic timestamp) {
TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Conceal()");
SetResourceProvider(resource_provider);
+ SetApplicationState(base::kApplicationStateConcealed, timestamp);
layout_manager_->Suspend();
// Purge the cached resources prior to the freeze. That may cancel pending
// loads, allowing the freeze to occur faster and preventing unnecessary
@@ -1201,21 +1201,25 @@
}
loader_factory_->UpdateResourceProvider(resource_provider_);
- SetApplicationState(base::kApplicationStateConcealed, timestamp);
+
+ if (window_->media_session()->media_session_client() != NULL) {
+ window_->media_session()
+ ->media_session_client()
+ ->PostDelayedTaskForMaybeFreezeCallback();
+ }
}
void WebModule::Impl::Freeze(SbTimeMonotonic timestamp) {
TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Freeze()");
+ SetApplicationState(base::kApplicationStateFrozen, timestamp);
// Clear out the loader factory's resource provider, possibly aborting any
// in-progress loads.
loader_factory_->Suspend();
- SetApplicationState(base::kApplicationStateFrozen, timestamp);
}
-void WebModule::Impl::Unfreeze(
- render_tree::ResourceProvider* resource_provider,
- SbTimeMonotonic timestamp) {
+void WebModule::Impl::Unfreeze(render_tree::ResourceProvider* resource_provider,
+ SbTimeMonotonic timestamp) {
TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Unfreeze()");
synchronous_loader_interrupt_.Reset();
DCHECK(resource_provider);
@@ -1224,9 +1228,8 @@
SetApplicationState(base::kApplicationStateConcealed, timestamp);
}
-void WebModule::Impl::Reveal(
- render_tree::ResourceProvider* resource_provider,
- SbTimeMonotonic timestamp) {
+void WebModule::Impl::Reveal(render_tree::ResourceProvider* resource_provider,
+ SbTimeMonotonic timestamp) {
TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Reveal()");
synchronous_loader_interrupt_.Reset();
DCHECK(resource_provider);
@@ -1693,9 +1696,8 @@
impl_->CancelSynchronousLoads();
- auto impl_blur =
- base::Bind(&WebModule::Impl::Blur,
- base::Unretained(impl_.get()), timestamp);
+ auto impl_blur = base::Bind(&WebModule::Impl::Blur,
+ base::Unretained(impl_.get()), timestamp);
#if defined(ENABLE_DEBUGGER)
// We normally need to block here so that the call doesn't return until the
@@ -1725,9 +1727,9 @@
// We must block here so that the call doesn't return until the web
// application has had a chance to process the whole event.
message_loop()->task_runner()->PostBlockingTask(
- FROM_HERE, base::Bind(&WebModule::Impl::Conceal,
- base::Unretained(impl_.get()),
- resource_provider, timestamp));
+ FROM_HERE,
+ base::Bind(&WebModule::Impl::Conceal, base::Unretained(impl_.get()),
+ resource_provider, timestamp));
}
void WebModule::Freeze(SbTimeMonotonic timestamp) {
@@ -1737,9 +1739,8 @@
// We must block here so that the call doesn't return until the web
// application has had a chance to process the whole event.
message_loop()->task_runner()->PostBlockingTask(
- FROM_HERE,
- base::Bind(&WebModule::Impl::Freeze,
- base::Unretained(impl_.get()), timestamp));
+ FROM_HERE, base::Bind(&WebModule::Impl::Freeze,
+ base::Unretained(impl_.get()), timestamp));
}
void WebModule::Unfreeze(render_tree::ResourceProvider* resource_provider,
@@ -1748,9 +1749,9 @@
DCHECK_NE(base::MessageLoop::current(), message_loop());
message_loop()->task_runner()->PostTask(
- FROM_HERE, base::Bind(&WebModule::Impl::Unfreeze,
- base::Unretained(impl_.get()),
- resource_provider, timestamp));
+ FROM_HERE,
+ base::Bind(&WebModule::Impl::Unfreeze, base::Unretained(impl_.get()),
+ resource_provider, timestamp));
}
void WebModule::Reveal(render_tree::ResourceProvider* resource_provider,
@@ -1759,9 +1760,9 @@
DCHECK_NE(base::MessageLoop::current(), message_loop());
message_loop()->task_runner()->PostTask(
- FROM_HERE, base::Bind(&WebModule::Impl::Reveal,
- base::Unretained(impl_.get()),
- resource_provider, timestamp));
+ FROM_HERE,
+ base::Bind(&WebModule::Impl::Reveal, base::Unretained(impl_.get()),
+ resource_provider, timestamp));
}
void WebModule::Focus(SbTimeMonotonic timestamp) {
@@ -1769,9 +1770,8 @@
DCHECK_NE(base::MessageLoop::current(), message_loop());
message_loop()->task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&WebModule::Impl::Focus,
- base::Unretained(impl_.get()), timestamp));
+ FROM_HERE, base::Bind(&WebModule::Impl::Focus,
+ base::Unretained(impl_.get()), timestamp));
}
void WebModule::ReduceMemory() {
@@ -1834,24 +1834,22 @@
DCHECK(impl_);
if (base::MessageLoop::current() != message_loop()) {
message_loop()->task_runner()->PostBlockingTask(
- FROM_HERE,
- base::Bind(&WebModule::Impl::SetApplicationStartOrPreloadTimestamp,
- base::Unretained(impl_.get()), is_preload, timestamp));
+ FROM_HERE,
+ base::Bind(&WebModule::Impl::SetApplicationStartOrPreloadTimestamp,
+ base::Unretained(impl_.get()), is_preload, timestamp));
} else {
impl_->SetApplicationStartOrPreloadTimestamp(is_preload, timestamp);
}
}
void WebModule::SetDeepLinkTimestamp(SbTimeMonotonic timestamp) {
- TRACE_EVENT0("cobalt::browser",
- "WebModule::SetDeepLinkTimestamp()");
+ TRACE_EVENT0("cobalt::browser", "WebModule::SetDeepLinkTimestamp()");
DCHECK(message_loop());
DCHECK(impl_);
if (base::MessageLoop::current() != message_loop()) {
message_loop()->task_runner()->PostBlockingTask(
- FROM_HERE,
- base::Bind(&WebModule::Impl::SetDeepLinkTimestamp,
- base::Unretained(impl_.get()), timestamp));
+ FROM_HERE, base::Bind(&WebModule::Impl::SetDeepLinkTimestamp,
+ base::Unretained(impl_.get()), timestamp));
} else {
impl_->SetDeepLinkTimestamp(timestamp);
}
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 6446170..ba0b6d4 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-304063
\ No newline at end of file
+304707
\ No newline at end of file
diff --git a/src/cobalt/demos/content/performance-api-demo/performance-lifecycle-timing-demo.html b/src/cobalt/demos/content/performance-api-demo/performance-lifecycle-timing-demo.html
new file mode 100644
index 0000000..72e472f
--- /dev/null
+++ b/src/cobalt/demos/content/performance-api-demo/performance-lifecycle-timing-demo.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Performance Lifecycle Timing Demo</title>
+ <style>
+ body {
+ background-color: rgb(255, 255, 255);
+ color: #0047ab;
+ font-size: 50px;
+ }
+ </style>
+</head>
+
+<body>
+ <script>
+ var lifecycleTiming = performance.getEntriesByType("lifecycle")[0];
+
+ function LogInfo(state, time) {
+ var message = state + ": " + time;
+ console.log(message);
+ document.getElementById('info').innerHTML += message + ".\n";
+ }
+
+ function handleVisibilityChange() {
+ if (document.visibilityState == "visible") {
+ ReportPerformanceLifecycleTimingInfo();
+ }
+ }
+
+ function ReportPerformanceLifecycleTimingInfo() {
+ if (lifecycleTiming.appBlur != 0) {
+ LogInfo("Blur time", lifecycleTiming.appBlur);
+ }
+ if (lifecycleTiming.appConceal != 0) {
+ LogInfo("Conceal time", lifecycleTiming.appConceal);
+ }
+ if (lifecycleTiming.appReveal != 0) {
+ LogInfo("Reveal time", lifecycleTiming.appReveal);
+ }
+ if (lifecycleTiming.appFocus != 0) {
+ LogInfo("Focus time", lifecycleTiming.appFocus);
+ }
+ if (lifecycleTiming.appFreeze != 0) {
+ LogInfo("Freeze time", lifecycleTiming.appFreeze);
+ }
+ if (lifecycleTiming.appUnfreeze != 0) {
+ LogInfo("Unfreeze time", lifecycleTiming.appUnfreeze);
+ }
+ }
+
+ document.addEventListener("visibilitychange", handleVisibilityChange);
+
+ window.onload = function () {
+ LogInfo("Performance Now", window.performance.now());
+ LogInfo("Start time", lifecycleTiming.appStart);
+ };
+
+ </script>
+ <div>
+ <span id='info' style='white-space: pre; background-color:#00FF00'></span>
+ </div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/cobalt/demos/content/performance-api-demo/performance-api-demo.html b/src/cobalt/demos/content/performance-api-demo/performance-resource-timing-demo.html
similarity index 100%
rename from src/cobalt/demos/content/performance-api-demo/performance-api-demo.html
rename to src/cobalt/demos/content/performance-api-demo/performance-resource-timing-demo.html
diff --git a/src/cobalt/doc/docker_build.md b/src/cobalt/doc/docker_build.md
index 7a04812..4ba59a9 100644
--- a/src/cobalt/doc/docker_build.md
+++ b/src/cobalt/doc/docker_build.md
@@ -21,7 +21,7 @@
where config is one of the four optimization levels, debug, devel, qa and gold,
and target is the build target passed to `ninja`
-See [Cobalt README](https://cobalt.googlesource.com/cobalt/+/master/src/README.md#build-types)
+See [Cobalt README](../../README.md#build-types)
for full details.
Builds will be available in your `${COBALT_SRC}/out` directory.
@@ -63,4 +63,4 @@
`docker-compose run linux-x64x11 /bin/bash`
-and try to build cobalt [with the usual `gyp / ninja` flow](https://cobalt.googlesource.com/cobalt/+/master/src/README.md#building-and-running-the-code).
+and try to build cobalt [with the usual `gyp / ninja` flow](../../README.md#building-and-running-the-code).
diff --git a/src/cobalt/doc/platform_services.md b/src/cobalt/doc/platform_services.md
index d2991b3..dadf6ae 100644
--- a/src/cobalt/doc/platform_services.md
+++ b/src/cobalt/doc/platform_services.md
@@ -53,7 +53,7 @@
The Platform Services extension is exposed to the web app via the following IDL:
-* [src/cobalt/h5vcc/h5vcc\_platform\_service.idl](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/cobalt/h5vcc/h5vcc_platform_service.idl)
+* [src/cobalt/h5vcc/h5vcc\_platform\_service.idl](../h5vcc/h5vcc_platform_service.idl)
The entrypoint for defined Platform Services extensions will be accessible in
the `H5vccPlatformService` object. Note that ArrayBuffers are chosen to
@@ -62,10 +62,10 @@
### Starboard Interface
-Implementing the Starboard layer of Platform Service extension support uses the
+Implementing the Starboard layer of Platform Service extension support uses the
following interface in parallel with the IDL interface:
-* [src/cobalt/extension/platform\_service.h](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/cobalt/extension/platform_service.h)
+* [src/cobalt/extension/platform\_service.h](../extension/platform_service.h)
`CobaltExtensionPlatformServiceApi` is the main interface for the Starboard
layer.
diff --git a/src/cobalt/dom/font_cache.cc b/src/cobalt/dom/font_cache.cc
index 93e64d6..ce58fe0 100644
--- a/src/cobalt/dom/font_cache.cc
+++ b/src/cobalt/dom/font_cache.cc
@@ -159,21 +159,34 @@
return cached_font_info.font;
}
+std::vector<FontFace*> FontCache::GetFacesForFamilyAndStyle(
+ const std::string& family, render_tree::FontStyle style) {
+ std::vector<FontFace*> faces;
+ FontFaceMap::iterator font_face_map_iterator = font_face_map_->find(family);
+ if (font_face_map_iterator != font_face_map_->end()) {
+ // Add all font-face entries that match the family.
+ std::vector<const FontFaceStyleSet::Entry*> entries =
+ font_face_map_iterator->second.GetEntriesThatMatchStyle(style);
+ for (auto entry : entries) {
+ FontFace* face = new FontFace();
+ face->entry = entry;
+ faces.push_back(face);
+ }
+ } else {
+ // This is a local font. One face can represent it.
+ FontFace* face = new FontFace();
+ faces.push_back(face);
+ }
+ return faces;
+}
+
scoped_refptr<render_tree::Font> FontCache::TryGetFont(
const std::string& family, render_tree::FontStyle style, float size,
- FontListFont::State* state) {
+ FontFace::State* state,
+ const FontFaceStyleSet::Entry* maybe_style_set_entry) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- FontFaceMap::iterator font_face_map_iterator = font_face_map_->find(family);
int64 request_time_start = base::TimeTicks::Now().ToInternalValue();
- if (font_face_map_iterator != font_face_map_->end()) {
- // Retrieve the font face style set entry that most closely matches the
- // desired style. Given that a font face was found for this family, it
- // should never be NULL.
- // https://www.w3.org/TR/css3-fonts/#font-prop-desc
- const FontFaceStyleSet::Entry* style_set_entry =
- font_face_map_iterator->second.MatchStyle(style);
- DCHECK(style_set_entry != NULL);
-
+ if (maybe_style_set_entry) {
// Walk the entry's sources:
// - If a remote source is encountered, always return the results of its
// attempted retrieval, regardless of its success.
@@ -183,8 +196,9 @@
// instead.
// https://www.w3.org/TR/css3-fonts/#src-desc
for (FontFaceSources::const_iterator source_iterator =
- style_set_entry->sources.begin();
- source_iterator != style_set_entry->sources.end(); ++source_iterator) {
+ maybe_style_set_entry->sources.begin();
+ source_iterator != maybe_style_set_entry->sources.end();
+ ++source_iterator) {
if (source_iterator->IsUrlSource()) {
auto font = TryGetRemoteFont(source_iterator->GetUrl(), size, state);
GlobalStats::GetInstance()->OnFontRequestComplete(request_time_start);
@@ -199,7 +213,7 @@
}
}
- *state = FontListFont::kUnavailableState;
+ *state = FontFace::kUnavailableState;
return NULL;
} else {
auto font = TryGetLocalFont(family, style, size, state);
@@ -335,7 +349,7 @@
}
scoped_refptr<render_tree::Font> FontCache::TryGetRemoteFont(
- const GURL& url, float size, FontListFont::State* state) {
+ const GURL& url, float size, FontFace::State* state) {
// Retrieve the font from the remote typeface cache, potentially triggering a
// load.
scoped_refptr<loader::font::CachedRemoteTypeface> cached_remote_typeface =
@@ -370,16 +384,16 @@
scoped_refptr<render_tree::Typeface> typeface =
cached_remote_typeface->TryGetResource();
if (typeface.get() != NULL) {
- *state = FontListFont::kLoadedState;
+ *state = FontFace::kLoadedState;
return GetFontFromTypefaceAndSize(typeface, size);
} else {
if (cached_remote_typeface->IsLoadingComplete()) {
- *state = FontListFont::kUnavailableState;
+ *state = FontFace::kUnavailableState;
} else if (requested_remote_typeface_iterator->second
->HasActiveRequestTimer()) {
- *state = FontListFont::kLoadingWithTimerActiveState;
+ *state = FontFace::kLoadingWithTimerActiveState;
} else {
- *state = FontListFont::kLoadingWithTimerExpiredState;
+ *state = FontFace::kLoadingWithTimerExpiredState;
}
return NULL;
}
@@ -387,7 +401,7 @@
scoped_refptr<render_tree::Font> FontCache::TryGetLocalFont(
const std::string& family, render_tree::FontStyle style, float size,
- FontListFont::State* state) {
+ FontFace::State* state) {
DCHECK(resource_provider());
DCHECK(resource_provider() != NULL);
// Only request the local font from the resource provider if the family is
@@ -398,10 +412,10 @@
// signifies using the default font.
if (!family.empty() &&
!resource_provider()->HasLocalFontFamily(family.c_str())) {
- *state = FontListFont::kUnavailableState;
+ *state = FontFace::kUnavailableState;
return NULL;
} else {
- *state = FontListFont::kLoadedState;
+ *state = FontFace::kLoadedState;
return GetFontFromTypefaceAndSize(
GetCachedLocalTypeface(
resource_provider()->GetLocalTypeface(family.c_str(), style)),
@@ -410,7 +424,7 @@
}
scoped_refptr<render_tree::Font> FontCache::TryGetLocalFontByFaceName(
- const std::string& font_face, float size, FontListFont::State* state) {
+ const std::string& font_face, float size, FontFace::State* state) {
do {
if (font_face.empty()) {
break;
@@ -424,11 +438,11 @@
const scoped_refptr<render_tree::Typeface>& typeface_cached(
GetCachedLocalTypeface(typeface));
- *state = FontListFont::kLoadedState;
+ *state = FontFace::kLoadedState;
return GetFontFromTypefaceAndSize(typeface_cached, size);
} while (false);
- *state = FontListFont::kUnavailableState;
+ *state = FontFace::kUnavailableState;
return NULL;
}
diff --git a/src/cobalt/dom/font_cache.h b/src/cobalt/dom/font_cache.h
index 44d10a9..932743c 100644
--- a/src/cobalt/dom/font_cache.h
+++ b/src/cobalt/dom/font_cache.h
@@ -201,13 +201,19 @@
const scoped_refptr<render_tree::Font>& GetFontFromTypefaceAndSize(
const scoped_refptr<render_tree::Typeface>& typeface, float size);
+
+ // Retrieves a list of the font faces that match the given family name and
+ // style. If no explicit font faces exist, creates a faux face for
+ // representing a local font.
+ std::vector<FontFace*> GetFacesForFamilyAndStyle(
+ const std::string& family, render_tree::FontStyle style);
+
// Attempts to retrieve a font. If the family maps to a font face, then this
// makes a request to |TryGetRemoteFont()|; otherwise, it makes a request
// to |TryGetLocalFont()|. This function may return NULL.
- scoped_refptr<render_tree::Font> TryGetFont(const std::string& family,
- render_tree::FontStyle style,
- float size,
- FontListFont::State* state);
+ scoped_refptr<render_tree::Font> TryGetFont(
+ const std::string& family, render_tree::FontStyle style, float size,
+ FontFace::State* state, const FontFaceStyleSet::Entry* maybe_entry);
// Returns the character fallback typeface map associated with the specified
// style. Each unique style has its own exclusive map. If it doesn't already
@@ -259,20 +265,20 @@
// font are registered with the remote typeface cache to be called when the
// load finishes.
scoped_refptr<render_tree::Font> TryGetRemoteFont(const GURL& url, float size,
- FontListFont::State* state);
+ FontFace::State* state);
// Returns NULL if the requested family is not empty and is not available in
// the resource provider. Otherwise, returns the best matching local font.
scoped_refptr<render_tree::Font> TryGetLocalFont(const std::string& family,
render_tree::FontStyle style,
float size,
- FontListFont::State* state);
+ FontFace::State* state);
// Lookup by a typeface (aka font_face), typeface is defined as font family +
// style (weight, width, and style).
// Returns NULL if the requested font face is not found.
scoped_refptr<render_tree::Font> TryGetLocalFontByFaceName(
- const std::string& font_face, float size, FontListFont::State* state);
+ const std::string& font_face, float size, FontFace::State* state);
// Called when a remote typeface either successfully loads or fails to load.
// In either case, the event can impact the fonts contained within the font
diff --git a/src/cobalt/dom/font_cache_test.cc b/src/cobalt/dom/font_cache_test.cc
index 49656eb..1c22311 100644
--- a/src/cobalt/dom/font_cache_test.cc
+++ b/src/cobalt/dom/font_cache_test.cc
@@ -100,6 +100,13 @@
const std::string postscript_font_name("DancingScript");
std::unique_ptr<FontCache::FontFaceMap> ffm =
CreateFontFaceMapHelper(family_name, postscript_font_name);
+
+ const FontFaceStyleSet::Entry* entry = nullptr;
+ FontCache::FontFaceMap::iterator ffm_iterator = ffm->find(family_name);
+ if (ffm_iterator != ffm->end()) {
+ entry = ffm_iterator->second.GetEntriesThatMatchStyle(kNormalUpright)[0];
+ }
+ EXPECT_TRUE(entry);
font_cache_->SetFontFaceMap(std::move(ffm));
EXPECT_CALL(loader_factory_, CreateTypefaceLoaderMock(_, _, _, _, _))
@@ -109,9 +116,9 @@
GetLocalTypefaceIfAvailableMock(postscript_font_name))
.WillOnce(Return(sample_typeface_));
- FontListFont::State state;
+ FontFace::State state;
scoped_refptr<render_tree::Font> f =
- font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state);
+ font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state, entry);
EXPECT_TRUE(f);
}
@@ -121,6 +128,13 @@
const std::string family_name("Dancing Script");
std::unique_ptr<FontCache::FontFaceMap> ffm =
CreateFontFaceMapHelper(family_name, invalid_postscript_font_name);
+
+ const FontFaceStyleSet::Entry* entry = nullptr;
+ FontCache::FontFaceMap::iterator ffm_iterator = ffm->find(family_name);
+ if (ffm_iterator != ffm->end()) {
+ entry = ffm_iterator->second.GetEntriesThatMatchStyle(kNormalUpright)[0];
+ }
+ EXPECT_TRUE(entry);
font_cache_->SetFontFaceMap(std::move(ffm));
EXPECT_CALL(mock_resource_provider_,
@@ -128,9 +142,9 @@
.Times(1);
EXPECT_CALL(loader_factory_, CreateTypefaceLoaderMock(_, _, _, _, _));
- FontListFont::State state;
+ FontFace::State state;
scoped_refptr<render_tree::Font> f =
- font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state);
+ font_cache_->TryGetFont(family_name, kNormalUpright, 12.0, &state, entry);
EXPECT_FALSE(f);
}
diff --git a/src/cobalt/dom/font_face.cc b/src/cobalt/dom/font_face.cc
index 5f6fdb5..6c44d75 100644
--- a/src/cobalt/dom/font_face.cc
+++ b/src/cobalt/dom/font_face.cc
@@ -39,25 +39,22 @@
}
}
-const FontFaceStyleSet::Entry* FontFaceStyleSet::MatchStyle(
+std::vector<const FontFaceStyleSet::Entry*>
+FontFaceStyleSet::GetEntriesThatMatchStyle(
const render_tree::FontStyle& pattern) const {
- return entries_.empty() ? NULL
- : &entries_[GetClosestStyleEntryIndex(pattern)];
-}
-
-size_t FontFaceStyleSet::GetClosestStyleEntryIndex(
- const render_tree::FontStyle& pattern) const {
- size_t closest_index = 0;
+ std::vector<const FontFaceStyleSet::Entry*> entries;
int max_score = std::numeric_limits<int>::min();
- for (size_t i = 0; i < entries_.size(); ++i) {
- int score = MatchScore(pattern, entries_[i].style);
- if (score > max_score) {
- closest_index = i;
- max_score = score;
+ for (const auto& entry : entries_) {
+ int score = MatchScore(pattern, entry.style);
+ if (score >= max_score) {
+ if (score > max_score) {
+ max_score = score;
+ entries.clear();
+ }
+ entries.push_back(&entry);
}
}
-
- return closest_index;
+ return entries;
}
int FontFaceStyleSet::MatchScore(
diff --git a/src/cobalt/dom/font_face.h b/src/cobalt/dom/font_face.h
index 1bfb40a..a5f80cc 100644
--- a/src/cobalt/dom/font_face.h
+++ b/src/cobalt/dom/font_face.h
@@ -66,7 +66,19 @@
return style.weight == other.style.weight &&
style.slant == other.style.slant && sources == other.sources;
}
+ struct UnicodeRange {
+ // Sorts ranges primarily based on start and secondarily based on end.
+ bool operator<(const UnicodeRange& range) const {
+ if (start == range.start) {
+ return end < range.end;
+ }
+ return start < range.start;
+ }
+ uint32 start;
+ uint32 end;
+ };
+ std::set<UnicodeRange> unicode_range;
render_tree::FontStyle style;
FontFaceSources sources;
};
@@ -77,10 +89,11 @@
// into the set. All pre-existing url entries within the set are retained.
void CollectUrlSources(std::set<GURL>* urls) const;
- // Returns the style set entry with the style most closely matching the
- // requested pattern. If the style set contains any entries, it is guaranteed
- // to not return NULL.
- const Entry* MatchStyle(const render_tree::FontStyle& pattern) const;
+
+ // Returns a list of entries with the style that most closesly matches the
+ // pattern.
+ std::vector<const Entry*> GetEntriesThatMatchStyle(
+ const render_tree::FontStyle& pattern) const;
bool operator==(const FontFaceStyleSet& other) const {
return entries_ == other.entries_;
@@ -89,11 +102,6 @@
private:
typedef std::vector<Entry> Entries;
- // Returns the index of the entry with a style most closely matching the
- // pattern (the lower the score, the more closely it matches). In the case of
- // a tie, the earliest encountered entry is given priority.
- size_t GetClosestStyleEntryIndex(const render_tree::FontStyle& pattern) const;
-
// Returns the match score between two patterns. The score logic matches that
// within SkFontStyleSet_Cobalt::match_score().
int MatchScore(const render_tree::FontStyle& pattern,
diff --git a/src/cobalt/dom/font_face_updater.cc b/src/cobalt/dom/font_face_updater.cc
index 2a486c6..940eaaa 100644
--- a/src/cobalt/dom/font_face_updater.cc
+++ b/src/cobalt/dom/font_face_updater.cc
@@ -29,6 +29,7 @@
#include "cobalt/cssom/property_value_visitor.h"
#include "cobalt/cssom/string_value.h"
#include "cobalt/cssom/style_sheet_list.h"
+#include "cobalt/cssom/unicode_range_value.h"
#include "cobalt/cssom/url_src_value.h"
#include "cobalt/cssom/url_value.h"
@@ -214,8 +215,10 @@
void FontFaceProvider::VisitUnicodeRange(
cssom::UnicodeRangeValue* unicode_range) {
- NOTIMPLEMENTED()
- << "FontFaceProvider::UnicodeRange support not implemented yet.";
+ FontFaceStyleSet::Entry::UnicodeRange range = {
+ static_cast<uint32>(unicode_range->start()),
+ static_cast<uint32>(unicode_range->end())};
+ style_set_entry_.unicode_range.insert(range);
}
// Check for a supported format. If no format hints are supplied, then the user
@@ -307,6 +310,9 @@
if (css_font_face_rule->data()->weight()) {
css_font_face_rule->data()->weight()->Accept(&font_face_provider);
}
+ if (css_font_face_rule->data()->unicode_range()) {
+ css_font_face_rule->data()->unicode_range()->Accept(&font_face_provider);
+ }
if (font_face_provider.IsFontFaceValid()) {
(*font_face_map_)[font_face_provider.font_family()].AddEntry(
diff --git a/src/cobalt/dom/font_list.cc b/src/cobalt/dom/font_list.cc
index ae4cbb4..45e8d5c 100644
--- a/src/cobalt/dom/font_list.cc
+++ b/src/cobalt/dom/font_list.cc
@@ -15,6 +15,7 @@
#include "cobalt/dom/font_list.h"
#include "base/i18n/char_iterator.h"
+#include "cobalt/base/unicode/character_values.h"
#include "cobalt/dom/font_cache.h"
namespace cobalt {
@@ -24,6 +25,19 @@
const base::char16 kHorizontalEllipsisValue = 0x2026;
+bool CharInRange(
+ const std::set<FontFaceStyleSet::Entry::UnicodeRange>& unicode_range,
+ uint32 utf32_character) {
+ if (unicode_range.empty()) return true;
+ for (const auto& range : unicode_range) {
+ if (range.start > utf32_character) break;
+ if ((range.start <= utf32_character) && (utf32_character <= range.end)) {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace
FontList::FontList(FontCache* font_cache, const FontListKey& font_list_key)
@@ -40,18 +54,34 @@
font_cache_->GetCharacterFallbackTypefaceMap(style_)) {
// Add all of the family names to the font list fonts.
for (size_t i = 0; i < font_list_key.family_names.size(); ++i) {
- fonts_.push_back(FontListFont(font_list_key.family_names[i]));
+ FontListFont font = FontListFont(font_list_key.family_names[i]);
+ font.faces =
+ font_cache_->GetFacesForFamilyAndStyle(font.family_name, style_);
+ fonts_.push_back(font);
}
// Add an empty font at the end in order to fall back to the default typeface.
- fonts_.push_back(FontListFont(""));
+ FontListFont default_font = FontListFont("");
+ FontFace* default_face = new FontFace();
+ default_font.faces = std::vector<FontFace*>{default_face};
+ fonts_.push_back(default_font);
+}
+
+FontList::~FontList() {
+ for (FontListFont font : fonts_) {
+ for (FontFace* face : font.faces) {
+ delete face;
+ }
+ }
}
void FontList::Reset() {
for (size_t i = 0; i < fonts_.size(); ++i) {
FontListFont& font_list_font = fonts_[i];
- font_list_font.set_state(FontListFont::kUnrequestedState);
- font_list_font.set_font(NULL);
+ for (auto face : font_list_font.faces) {
+ face->state = FontFace::kUnrequestedState;
+ face->font = NULL;
+ }
}
primary_font_ = NULL;
@@ -69,20 +99,22 @@
for (size_t i = 0; i < fonts_.size(); ++i) {
FontListFont& font_list_font = fonts_[i];
- if (font_list_font.state() == FontListFont::kLoadingWithTimerActiveState ||
- font_list_font.state() == FontListFont::kLoadingWithTimerExpiredState) {
- font_list_font.set_state(FontListFont::kUnrequestedState);
- // If a loaded font hasn't been found yet, then the cached values need to
- // be reset. It'll potentially change the primary font.
- if (!found_loaded_font) {
- primary_font_ = NULL;
- is_font_metrics_set_ = false;
- is_space_width_set_ = false;
- is_ellipsis_info_set_ = false;
- ellipsis_font_ = NULL;
+ for (auto face : font_list_font.faces) {
+ if (face->state == FontFace::kLoadingWithTimerActiveState ||
+ face->state == FontFace::kLoadingWithTimerExpiredState) {
+ face->state = FontFace::kUnrequestedState;
+ // If a loaded font hasn't been found yet, then the cached values need
+ // to be reset. It'll potentially change the primary font.
+ if (!found_loaded_font) {
+ primary_font_ = NULL;
+ is_font_metrics_set_ = false;
+ is_space_width_set_ = false;
+ is_ellipsis_info_set_ = false;
+ ellipsis_font_ = NULL;
+ }
+ } else if (face->state == FontFace::kLoadedState) {
+ found_loaded_font = true;
}
- } else if (font_list_font.state() == FontListFont::kLoadedState) {
- found_loaded_font = true;
}
}
}
@@ -97,8 +129,10 @@
// display text, simply leaving transparent text is considered
// non-conformant behavior."
// https://www.w3.org/TR/css3-fonts/#font-face-loading
- if (fonts_[i].state() == FontListFont::kLoadingWithTimerActiveState) {
- return false;
+ for (auto face : fonts_[i].faces) {
+ if (face->state == FontFace::kLoadingWithTimerActiveState) {
+ return false;
+ }
}
}
@@ -197,15 +231,20 @@
for (size_t i = 0; i < fonts_.size(); ++i) {
FontListFont& font_list_font = fonts_[i];
- if (font_list_font.state() == FontListFont::kUnrequestedState) {
- RequestFont(i);
- }
+ for (FontFace* face : font_list_font.faces) {
+ const FontFaceStyleSet::Entry* entry = face->entry;
+ if (entry && !CharInRange(entry->unicode_range, utf32_character)) {
+ continue;
+ }
+ if (face->state == FontFace::kUnrequestedState) {
+ RequestFont(i, face);
+ }
- if (font_list_font.state() == FontListFont::kLoadedState) {
- *glyph_index =
- font_list_font.font()->GetGlyphForCharacter(utf32_character);
- if (*glyph_index != render_tree::kInvalidGlyphIndex) {
- return font_list_font.font();
+ if (face->state == FontFace::kLoadedState) {
+ *glyph_index = face->font->GetGlyphForCharacter(utf32_character);
+ if (*glyph_index != render_tree::kInvalidGlyphIndex) {
+ return face->font;
+ }
}
}
}
@@ -241,18 +280,27 @@
// time to do it now.
if (!primary_font_) {
// Walk the list of fonts, requesting any encountered that are in an
- // unrequested state. The first font encountered that is loaded is
- // the primary font.
+ // unrequested state. The first font encountered that is loaded and whose
+ // unicode range includes the space character is the primary font.
+ // https://www.w3.org/TR/css-fonts-4/#first-available-font
for (size_t i = 0; i < fonts_.size(); ++i) {
FontListFont& font_list_font = fonts_[i];
- if (font_list_font.state() == FontListFont::kUnrequestedState) {
- RequestFont(i);
- }
+ for (FontFace* face : font_list_font.faces) {
+ const FontFaceStyleSet::Entry* entry = face->entry;
+ if (entry && !CharInRange(entry->unicode_range,
+ base::unicode::kSpaceCharacter)) {
+ continue;
+ }
+ if (face->state == FontFace::kUnrequestedState) {
+ RequestFont(i, face);
+ }
- if (font_list_font.state() == FontListFont::kLoadedState) {
- primary_font_ = font_list_font.font();
- break;
+ if (face->state == FontFace::kLoadedState) {
+ primary_font_ = face->font;
+ DCHECK(primary_font_);
+ return primary_font_;
+ }
}
}
}
@@ -261,40 +309,41 @@
return primary_font_;
}
-void FontList::RequestFont(size_t index) {
+void FontList::RequestFont(size_t index, FontFace* used_face) {
FontListFont& font_list_font = fonts_[index];
- FontListFont::State state;
+ FontFace::State state;
// Request the font from the font cache; the state of the font will be set
// during the call.
scoped_refptr<render_tree::Font> render_tree_font = font_cache_->TryGetFont(
- font_list_font.family_name(), style_, size_, &state);
+ font_list_font.family_name, style_, size_, &state, used_face->entry);
- if (state == FontListFont::kLoadedState) {
+ if (state == FontFace::kLoadedState) {
DCHECK(render_tree_font.get() != NULL);
// Walk all of the fonts in the list preceding the loaded font. If they have
- // the same typeface as the loaded font, then set the font list font as a
- // duplicate. There's no reason to have multiple fonts in the list with the
- // same typeface.
+ // the same typeface as the loaded font, then do not create a new face.
+ // There's no reason to have multiple fonts in the list with the same
+ // typeface.
render_tree::TypefaceId typeface_id = render_tree_font->GetTypefaceId();
for (size_t i = 0; i < index; ++i) {
FontListFont& check_font = fonts_[i];
- if (check_font.state() == FontListFont::kLoadedState &&
- check_font.font()->GetTypefaceId() == typeface_id) {
- font_list_font.set_state(FontListFont::kDuplicateState);
- break;
+ for (auto face : check_font.faces) {
+ if (face->state == FontFace::kLoadedState &&
+ face->font->GetTypefaceId() == typeface_id) {
+ used_face->state = FontFace::kDuplicateState;
+ }
}
}
// If this font wasn't a duplicate, then its time to initialize its font
// data. This font is now available to use.
- if (font_list_font.state() != FontListFont::kDuplicateState) {
- font_list_font.set_state(FontListFont::kLoadedState);
- font_list_font.set_font(render_tree_font);
+ if (used_face->state != FontFace::kDuplicateState) {
+ used_face->state = FontFace::kLoadedState;
+ used_face->font = render_tree_font;
}
} else {
- font_list_font.set_state(state);
+ used_face->state = state;
}
}
@@ -310,10 +359,10 @@
void FontList::GenerateSpaceWidth() {
if (!is_space_width_set_) {
- const scoped_refptr<render_tree::Font>& primary_font = GetPrimaryFont();
- render_tree::GlyphIndex space_glyph =
- primary_font->GetGlyphForCharacter(' ');
- space_width_ = primary_font->GetGlyphWidth(space_glyph);
+ render_tree::GlyphIndex space_glyph = render_tree::kInvalidGlyphIndex;
+ space_width_ =
+ GetCharacterFont(base::unicode::kSpaceCharacter, &space_glyph)
+ ->GetGlyphWidth(space_glyph);
if (space_width_ == 0) {
DLOG(WARNING) << "Font being used with space width of 0!";
}
diff --git a/src/cobalt/dom/font_list.h b/src/cobalt/dom/font_list.h
index 8c95c94..6676b1f 100644
--- a/src/cobalt/dom/font_list.h
+++ b/src/cobalt/dom/font_list.h
@@ -22,6 +22,7 @@
#include "base/containers/hash_tables.h"
#include "base/containers/small_map.h"
#include "base/memory/ref_counted.h"
+#include "cobalt/dom/font_face.h"
#include "cobalt/math/rect_f.h"
#include "cobalt/render_tree/font.h"
#include "cobalt/render_tree/font_provider.h"
@@ -33,12 +34,11 @@
class FontCache;
-// A specific font family within a font list. It has an internal state, which
-// lets the font list know whether or not the font has already been requested,
-// and if so, whether or not it was available. |font_| and |character_map_|
-// will only be non-NULL in the case where |state_| is set to |kLoadedState|.
-class FontListFont {
- public:
+// A font-face for a font-family. It has an internal state, which lets the font
+// list know whether or not the font has already been requested, and if so,
+// whether or not it was available. |font_| will only be non-NULL in the case
+// where |state_| is set to |kLoadedState|.
+struct FontFace {
enum State {
kUnrequestedState,
kLoadingWithTimerActiveState,
@@ -47,27 +47,29 @@
kUnavailableState,
kDuplicateState,
};
+ State state = kUnrequestedState;
+ const FontFaceStyleSet::Entry* entry = nullptr;
+ // The render_tree::Font obtained via the font cache using |family_name_| in
+ // font list font, along with |style_| and |size_| from the containing font
+ // list, and the unicode range needed for the requested character. It is only
+ // non-NULL in the case where |state_| is set to |kLoadedState|.
+ scoped_refptr<render_tree::Font> font;
+};
+
+// A specific font family within a font list. A family may have more than one
+// FontFace if the FontFaces have different unicode ranges specified.
+struct FontListFont {
explicit FontListFont(const std::string& family_name)
- : family_name_(family_name), state_(kUnrequestedState) {}
+ : family_name(family_name) {}
- const std::string& family_name() const { return family_name_; }
-
- State state() const { return state_; }
- void set_state(State state) { state_ = state; }
-
- const scoped_refptr<render_tree::Font>& font() const { return font_; }
- void set_font(const scoped_refptr<render_tree::Font>& font) { font_ = font; }
-
- private:
- std::string family_name_;
- State state_;
+ std::string family_name;
// The render_tree::Font obtained via the font cache using |family_name_| in
// font list font, along with |style_| and |size_| from the containing font
// list. It is only non-NULL in the case where |state_| is set to
// |kLoadedState|.
- scoped_refptr<render_tree::Font> font_;
+ std::vector<FontFace*> faces;
};
// The key used for maps with a |FontList| value. It is also used for
@@ -178,6 +180,8 @@
int32 utf32_character, render_tree::GlyphIndex* glyph_index) override;
private:
+ ~FontList() override;
+
const scoped_refptr<render_tree::Font>& GetFallbackCharacterFont(
int32 utf32_character, render_tree::GlyphIndex* glyph_index);
@@ -191,9 +195,10 @@
const scoped_refptr<render_tree::Font>& GetPrimaryFont();
// Request a font from the font cache and update its state depending on the
- // results of the request. If the font is successfully set, then both its
- // |font_| and |character_map_| are non-NULL after this call.
- void RequestFont(size_t index);
+ // results of the request. If the font is successfully set, then its |font_|
+ // is non-NULL after this call.
+ void RequestFont(size_t index, FontFace* face);
+
// Lazily generates the ellipsis font and ellipsis width. If it is already
// generated then it immediately returns.
diff --git a/src/cobalt/dom/html_style_element.cc b/src/cobalt/dom/html_style_element.cc
index a83be61..aa80cde 100644
--- a/src/cobalt/dom/html_style_element.cc
+++ b/src/cobalt/dom/html_style_element.cc
@@ -79,9 +79,8 @@
base::Optional<std::string> content = text_content();
const std::string& text = content.value_or(base::EmptyString());
- if (bypass_csp || text.empty() ||
- csp_delegate->AllowInline(CspDelegate::kStyle, inline_style_location_,
- text)) {
+ if (bypass_csp || csp_delegate->AllowInline(CspDelegate::kStyle,
+ inline_style_location_, text)) {
scoped_refptr<cssom::CSSStyleSheet> css_style_sheet =
document->html_element_context()->css_parser()->ParseStyleSheet(
text, inline_style_location_);
diff --git a/src/cobalt/dom/performance.cc b/src/cobalt/dom/performance.cc
index 8154cc1..390fc55 100644
--- a/src/cobalt/dom/performance.cc
+++ b/src/cobalt/dom/performance.cc
@@ -87,7 +87,10 @@
base::DefaultClock::GetInstance(), tick_clock_);
lifecycle_timing_ = base::MakeRefCounted<PerformanceLifecycleTiming>(
"lifecycle timing", time_origin());
+ // Queue lifecycle timing.
QueuePerformanceEntry(lifecycle_timing_);
+ // Add lifecycle timing to the performance entry buffer.
+ performance_entry_buffer_.push_back(lifecycle_timing_);
}
// static
diff --git a/src/cobalt/dom/performance_high_resolution_time.h b/src/cobalt/dom/performance_high_resolution_time.h
index 0684e41..4388b2e 100644
--- a/src/cobalt/dom/performance_high_resolution_time.h
+++ b/src/cobalt/dom/performance_high_resolution_time.h
@@ -43,19 +43,26 @@
// https://w3c.github.io/hr-time/#clock-resolution
inline DOMHighResTimeStamp ClampTimeStampMinimumResolution(
base::TimeTicks ticks,
- int64_t min_resolution_in_microseconds) {
- int64_t microseconds = ticks.ToInternalValue();
- return base::TimeDelta::FromMicroseconds(microseconds -
- (microseconds % min_resolution_in_microseconds)).InMillisecondsF();
+ int64_t min_resolution_in_microseconds) {
+ int64_t microseconds = ticks.since_origin().InMicroseconds();
+ return base::TimeDelta::FromMicroseconds(microseconds -
+ (microseconds % min_resolution_in_microseconds)).InMillisecondsF();
}
// Clamp customized minimum clock resolution in milliseconds.
// https://w3c.github.io/hr-time/#clock-resolution
inline DOMHighResTimeStamp ClampTimeStampMinimumResolution(base::TimeDelta delta,
int64_t min_resolution_in_microseconds) {
- int64_t microseconds = delta.InMicroseconds();
- return base::TimeDelta::FromMicroseconds(microseconds -
- (microseconds % min_resolution_in_microseconds)).InMillisecondsF();
+ int64_t microseconds = delta.InMicroseconds();
+ return base::TimeDelta::FromMicroseconds(microseconds -
+ (microseconds % min_resolution_in_microseconds)).InMillisecondsF();
+}
+
+inline DOMHighResTimeStamp
+ ClampTimeStampMinimumResolution(DOMHighResTimeStamp time_delta,
+ int64_t min_resolution_in_microseconds) {
+ return time_delta -
+ (static_cast<int64_t>(time_delta) % min_resolution_in_microseconds);
}
} // namespace dom
diff --git a/src/cobalt/dom/performance_lifecycle_timing.cc b/src/cobalt/dom/performance_lifecycle_timing.cc
index f09fee0..d81ebb0 100644
--- a/src/cobalt/dom/performance_lifecycle_timing.cc
+++ b/src/cobalt/dom/performance_lifecycle_timing.cc
@@ -44,11 +44,8 @@
const DOMHighResTimeStamp time_origin, SbTimeMonotonic monotonic_time) {
SbTimeMonotonic time_delta = SbTimeGetNow() - SbTimeGetMonotonicNow();
base::Time base_time = base::Time::FromSbTime(time_delta + monotonic_time);
- base::TimeTicks time_ticks =
- base::TimeTicks::FromInternalValue(static_cast<int64_t>(
- base_time.ToJsTime()));
- return ClampTimeStampMinimumResolution(time_ticks,
- Performance::kPerformanceTimerMinResolutionInMicroseconds) - time_origin;
+ return ClampTimeStampMinimumResolution(base_time.ToJsTime() - time_origin,
+ Performance::kPerformanceTimerMinResolutionInMicroseconds);
}
} // namespace
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 52fe0bb..34e0c86 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -164,8 +164,8 @@
captions, script_value_factory)),
ALLOW_THIS_IN_INITIALIZER_LIST(
relay_on_load_event_(new RelayLoadEvent(this))),
- ALLOW_THIS_IN_INITIALIZER_LIST(
- window_timers_(new WindowTimers(this, debugger_hooks()))),
+ ALLOW_THIS_IN_INITIALIZER_LIST(window_timers_(
+ new WindowTimers(this, debugger_hooks(), initial_application_state))),
ALLOW_THIS_IN_INITIALIZER_LIST(animation_frame_request_callback_list_(
new AnimationFrameRequestCallbackList(this, debugger_hooks()))),
crypto_(new Crypto()),
@@ -540,6 +540,7 @@
state);
if (timestamp == 0) return;
performance_->SetApplicationState(state, timestamp);
+ window_timers_->SetApplicationState(state);
}
bool Window::ReportScriptError(const script::ErrorReport& error_report) {
diff --git a/src/cobalt/dom/window_timers.cc b/src/cobalt/dom/window_timers.cc
index 283208c..3b5c079 100644
--- a/src/cobalt/dom/window_timers.cc
+++ b/src/cobalt/dom/window_timers.cc
@@ -19,15 +19,18 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
+#include "cobalt/base/application_state.h"
+#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/dom/global_stats.h"
#include "nb/memory_scope.h"
namespace cobalt {
namespace dom {
-int WindowTimers::SetTimeout(const TimerCallbackArg& handler, int timeout) {
- TRACK_MEMORY_SCOPE("DOM");
+int WindowTimers::TryAddNewTimer(Timer::TimerType type,
+ const TimerCallbackArg& handler, int timeout) {
int handle = GetFreeTimerHandle();
DCHECK(handle);
@@ -37,15 +40,16 @@
}
if (callbacks_active_) {
- auto* timer = new base::OneShotTimer();
- timer->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(timeout),
- base::Bind(&WindowTimers::RunTimerCallback,
- base::Unretained(this), handle));
- timers_[handle] = new TimerInfo(
- owner_, std::unique_ptr<base::internal::TimerBase>(timer), handler);
+ auto* timer = new Timer(type, owner_, handler, timeout, handle, this);
+ if (application_state_ != base::kApplicationStateFrozen) {
+ timer->StartOrResume();
+ }
+ timers_[handle] = timer;
debugger_hooks_.AsyncTaskScheduled(
- timers_[handle], "SetTimeout",
- base::DebuggerHooks::AsyncTaskFrequency::kOneshot);
+ timers_[handle], type == Timer::kOneShot ? "SetTimeout" : "SetInterval",
+ type == Timer::kOneShot
+ ? base::DebuggerHooks::AsyncTaskFrequency::kOneshot
+ : base::DebuggerHooks::AsyncTaskFrequency::kRecurring);
} else {
timers_[handle] = nullptr;
}
@@ -53,32 +57,16 @@
return handle;
}
+int WindowTimers::SetTimeout(const TimerCallbackArg& handler, int timeout) {
+ TRACK_MEMORY_SCOPE("DOM");
+ return TryAddNewTimer(Timer::kOneShot, handler, timeout);
+}
+
void WindowTimers::ClearTimeout(int handle) { timers_.erase(handle); }
int WindowTimers::SetInterval(const TimerCallbackArg& handler, int timeout) {
- int handle = GetFreeTimerHandle();
- DCHECK(handle);
-
- if (handle == 0) { // unable to get a free timer handle
- // avoid accidentally overwriting existing timers
- return 0;
- }
-
- if (callbacks_active_) {
- auto* timer(new base::RepeatingTimer());
- timer->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(timeout),
- base::Bind(&WindowTimers::RunTimerCallback,
- base::Unretained(this), handle));
- timers_[handle] = new TimerInfo(
- owner_, std::unique_ptr<base::internal::TimerBase>(timer), handler);
- debugger_hooks_.AsyncTaskScheduled(
- timers_[handle], "SetInterval",
- base::DebuggerHooks::AsyncTaskFrequency::kRecurring);
- } else {
- timers_[handle] = nullptr;
- }
-
- return handle;
+ TRACK_MEMORY_SCOPE("DOM");
+ return TryAddNewTimer(Timer::kRepeating, handler, timeout);
}
void WindowTimers::ClearInterval(int handle) {
@@ -138,7 +126,7 @@
{
// Keep a |TimerInfo| reference, so it won't be released when running the
// callback.
- scoped_refptr<TimerInfo> timer_info = timer->second;
+ scoped_refptr<Timer> timer_info = timer->second;
base::ScopedAsyncTask async_task(debugger_hooks_, timer_info);
timer_info->callback_reference().value().Run();
}
@@ -157,5 +145,92 @@
GlobalStats::GetInstance()->StopJavaScriptEvent();
}
+void WindowTimers::SetApplicationState(base::ApplicationState state) {
+ switch (state) {
+ case base::kApplicationStateFrozen:
+ DCHECK_EQ(application_state_, base::kApplicationStateConcealed);
+ for (auto timer : timers_) {
+ timer.second->Pause();
+ }
+ break;
+ case base::kApplicationStateConcealed:
+ if (application_state_ == base::kApplicationStateFrozen) {
+ for (auto timer : timers_) {
+ timer.second->StartOrResume();
+ }
+ }
+ break;
+ case base::kApplicationStateStopped:
+ case base::kApplicationStateBlurred:
+ case base::kApplicationStateStarted:
+ break;
+ }
+ application_state_ = state;
+}
+
+WindowTimers::Timer::Timer(TimerType type, script::Wrappable* const owner,
+ const TimerCallbackArg& callback, int timeout,
+ int handle, WindowTimers* window_timers)
+ : type_(type),
+ callback_(owner, callback),
+ timeout_(timeout),
+ handle_(handle),
+ window_timers_(window_timers) {}
+
+void WindowTimers::Timer::Pause() {
+ if (timer_) {
+ // The desired runtime is preserved here to determine whether the timer
+ // should fire immediately when resuming.
+ desired_run_time_ = timer_->desired_run_time();
+ timer_.reset();
+ }
+}
+
+void WindowTimers::Timer::StartOrResume() {
+ if (timer_ != nullptr) return;
+ switch (type_) {
+ case kOneShot:
+ if (desired_run_time_) {
+ // Adjust the new timeout for the time spent while paused.
+ auto now = base::TimeTicks::Now();
+ if (desired_run_time_ <= now) {
+ // The previous desired run time was in the past or is right now, so
+ // it should fire immediately.
+ timeout_ = 0;
+ } else {
+ // Set the timeout to keep the same desired run time. Note that since
+ // the timer uses the timeout that we request to set a new desired
+ // run time from the clock, the resumed timer will likely fire
+ // slightly later.
+ base::TimeDelta time_delta(desired_run_time_.value() - now);
+ timeout_ = static_cast<int>(time_delta.InMilliseconds());
+ }
+ }
+ timer_ = CreateAndStart<base::OneShotTimer>();
+ break;
+ case kRepeating:
+ timer_ = CreateAndStart<base::RepeatingTimer>();
+ if (timeout_ && desired_run_time_ &&
+ desired_run_time_ < base::TimeTicks::Now()) {
+ // The timer was paused and the desired run time is in the past.
+ // Call the callback once before continuing the repeating timer.
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&WindowTimers::RunTimerCallback,
+ base::Unretained(window_timers_), handle_));
+ }
+ break;
+ }
+}
+
+template <class TimerClass>
+std::unique_ptr<base::internal::TimerBase>
+WindowTimers::Timer::CreateAndStart() {
+ auto* timer = new TimerClass();
+ timer->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(timeout_),
+ base::Bind(&WindowTimers::RunTimerCallback,
+ base::Unretained(window_timers_), handle_));
+ return std::unique_ptr<base::internal::TimerBase>(timer);
+}
+
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/window_timers.h b/src/cobalt/dom/window_timers.h
index 5d4442b..42dcf56 100644
--- a/src/cobalt/dom/window_timers.h
+++ b/src/cobalt/dom/window_timers.h
@@ -16,10 +16,15 @@
#define COBALT_DOM_WINDOW_TIMERS_H_
#include <memory>
+#include <utility>
+#include <vector>
#include "base/containers/hash_tables.h"
#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "base/time/time.h"
#include "base/timer/timer.h"
+#include "cobalt/base/application_state.h"
#include "cobalt/base/debugger_hooks.h"
#include "cobalt/script/callback_function.h"
#include "cobalt/script/script_value.h"
@@ -33,10 +38,11 @@
typedef script::CallbackFunction<void()> TimerCallback;
typedef script::ScriptValue<TimerCallback> TimerCallbackArg;
explicit WindowTimers(script::Wrappable* const owner,
- const base::DebuggerHooks& debugger_hooks)
- : current_timer_index_(0),
- owner_(owner),
- debugger_hooks_(debugger_hooks) {}
+ const base::DebuggerHooks& debugger_hooks,
+ base::ApplicationState application_state)
+ : owner_(owner),
+ debugger_hooks_(debugger_hooks),
+ application_state_(application_state) {}
~WindowTimers() {}
int SetTimeout(const TimerCallbackArg& handler, int timeout);
@@ -55,25 +61,51 @@
// event queue without adding more on to the end of it.
void DisableCallbacks();
+ void SetApplicationState(base::ApplicationState state);
+
private:
- class TimerInfo : public base::RefCounted<TimerInfo> {
+ class Timer : public base::RefCounted<Timer> {
public:
- TimerInfo(script::Wrappable* const owner,
- std::unique_ptr<base::internal::TimerBase> timer,
- const TimerCallbackArg& callback)
- : timer_(std::move(timer)), callback_(owner, callback) {}
+ enum TimerType { kOneShot, kRepeating };
+
+ Timer(TimerType type, script::Wrappable* const owner,
+ const TimerCallbackArg& callback, int timeout, int handle,
+ WindowTimers* window_timers);
base::internal::TimerBase* timer() { return timer_.get(); }
TimerCallbackArg::Reference& callback_reference() { return callback_; }
+ // Pause this timer. The timer will not fire when paused.
+ void Pause();
+ // Start or Resume this timer. If the timer was paused and the desired run
+ // time is in the past, it will fire immediately.
+ void StartOrResume();
+
private:
- ~TimerInfo() {}
+ ~Timer() {}
+
+ // Create and start a timer of the specified TimerClass type.
+ template <class TimerClass>
+ std::unique_ptr<base::internal::TimerBase> CreateAndStart();
+
+ TimerType type_;
std::unique_ptr<base::internal::TimerBase> timer_;
TimerCallbackArg::Reference callback_;
+ int timeout_;
+ int handle_;
+ WindowTimers* window_timers_;
- friend class base::RefCounted<TimerInfo>;
+ // Store the desired run tim of a paused timer.
+ base::Optional<base::TimeTicks> desired_run_time_;
+
+ friend class base::RefCounted<Timer>;
};
- typedef base::hash_map<int, scoped_refptr<TimerInfo> > Timers;
+ typedef base::hash_map<int, scoped_refptr<Timer> > Timers;
+
+ // Try to add a new timer of the given type, return the handle or 0 when
+ // failed.
+ int TryAddNewTimer(Timer::TimerType type, const TimerCallbackArg& handler,
+ int timeout);
// Returns a positive integer timer handle that hasn't been assigned, or 0
// if none can be found.
@@ -84,7 +116,7 @@
void RunTimerCallback(int handle);
Timers timers_;
- int current_timer_index_;
+ int current_timer_index_ = 0;
script::Wrappable* const owner_;
const base::DebuggerHooks& debugger_hooks_;
@@ -92,6 +124,8 @@
// is fired as we are waiting for it to drain.
bool callbacks_active_ = true;
+ base::ApplicationState application_state_;
+
DISALLOW_COPY_AND_ASSIGN(WindowTimers);
};
diff --git a/src/cobalt/h5vcc/h5vcc_platform_service.cc b/src/cobalt/h5vcc/h5vcc_platform_service.cc
index f744176..2ee9d73 100644
--- a/src/cobalt/h5vcc/h5vcc_platform_service.cc
+++ b/src/cobalt/h5vcc/h5vcc_platform_service.cc
@@ -99,9 +99,21 @@
}
uint64_t output_length = 0;
bool invalid_state = 0;
+
+ // Make sure the data pointer is not null as the platform service may not
+ // handle that properly.
+ void* data_ptr = data->Data();
+ size_t data_length = data->ByteLength();
+ if (data_ptr == nullptr) {
+ // If the data length is 0, then it's okay to point to a static array.
+ DCHECK(data_length == 0);
+ static int null_data = 0;
+ data_ptr = &null_data;
+ data_length = 0;
+ }
+
void* output_data = platform_service_api_->Send(
- ext_service_, data->Data(), data->ByteLength(), &output_length,
- &invalid_state);
+ ext_service_, data_ptr, data_length, &output_length, &invalid_state);
if (invalid_state) {
dom::DOMException::Raise(dom::DOMException::kInvalidStateErr,
"Service unable to accept data currently.",
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 3254864..a42730d 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -567,6 +567,11 @@
return margin_top() + border_top_width();
}
+void Box::SetPaddingInsets(LayoutUnit left, LayoutUnit top, LayoutUnit right,
+ LayoutUnit bottom) {
+ padding_insets_.SetInsets(left, top, right, bottom);
+}
+
RectLayoutUnit Box::GetContentBoxFromMarginBox() const {
return RectLayoutUnit(GetContentBoxLeftEdgeOffsetFromMarginBox(),
GetContentBoxTopEdgeOffsetFromMarginBox(), width(),
@@ -982,20 +987,11 @@
}
}
-bool IsBorderStyleNoneOrHidden(
- const scoped_refptr<cssom::PropertyValue>& border_style) {
- if (border_style == cssom::KeywordValue::GetNone() ||
- border_style == cssom::KeywordValue::GetHidden()) {
- return true;
- }
- return false;
-}
-
render_tree::BorderStyle GetRenderTreeBorderStyle(
const scoped_refptr<cssom::PropertyValue>& border_style) {
render_tree::BorderStyle render_tree_border_style =
render_tree::kBorderStyleNone;
- if (!IsBorderStyleNoneOrHidden(border_style)) {
+ if (!Box::IsBorderStyleNoneOrHidden(border_style)) {
DCHECK_EQ(border_style, cssom::KeywordValue::GetSolid());
render_tree_border_style = render_tree::kBorderStyleSolid;
}
@@ -1003,25 +999,26 @@
return render_tree_border_style;
}
-Border CreateBorderFromStyle(
- const scoped_refptr<const cssom::CSSComputedStyleData>& style) {
+Border CreateBorderFromUsedStyle(
+ const scoped_refptr<const cssom::CSSComputedStyleData>& style,
+ InsetsLayoutUnit border_insets) {
render_tree::BorderSide left(
- GetUsedNonNegativeLength(style->border_left_width()).toFloat(),
+ border_insets.left().toFloat(),
GetRenderTreeBorderStyle(style->border_left_style()),
GetUsedColor(style->border_left_color()));
render_tree::BorderSide right(
- GetUsedNonNegativeLength(style->border_right_width()).toFloat(),
+ border_insets.right().toFloat(),
GetRenderTreeBorderStyle(style->border_right_style()),
GetUsedColor(style->border_right_color()));
render_tree::BorderSide top(
- GetUsedNonNegativeLength(style->border_top_width()).toFloat(),
+ border_insets.top().toFloat(),
GetRenderTreeBorderStyle(style->border_top_style()),
GetUsedColor(style->border_top_color()));
render_tree::BorderSide bottom(
- GetUsedNonNegativeLength(style->border_bottom_width()).toFloat(),
+ border_insets.bottom().toFloat(),
GetRenderTreeBorderStyle(style->border_bottom_style()),
GetUsedColor(style->border_bottom_color()));
@@ -1033,38 +1030,35 @@
const scoped_refptr<cssom::MutableCSSComputedStyleData>&
destination_style) {
// NOTE: Properties set by PopulateBaseStyleForBorderNode() should match the
- // properties used by SetupBorderNodeFromStyle().
+ // properties used by SetupBorderNodeFromUsedStyle(), except for the border
+ // width which is not used to determine the render tree border.
// Left
- destination_style->set_border_left_width(source_style->border_left_width());
destination_style->set_border_left_style(source_style->border_left_style());
destination_style->set_border_left_color(source_style->border_left_color());
// Right
- destination_style->set_border_right_width(source_style->border_right_width());
destination_style->set_border_right_style(source_style->border_right_style());
destination_style->set_border_right_color(source_style->border_right_color());
// Top
- destination_style->set_border_top_width(source_style->border_top_width());
destination_style->set_border_top_style(source_style->border_top_style());
destination_style->set_border_top_color(source_style->border_top_color());
// Bottom
- destination_style->set_border_bottom_width(
- source_style->border_bottom_width());
destination_style->set_border_bottom_style(
source_style->border_bottom_style());
destination_style->set_border_bottom_color(
source_style->border_bottom_color());
}
-void SetupBorderNodeFromStyle(
+void SetupBorderNodeFromUsedStyle(
const base::Optional<RoundedCorners>& rounded_corners,
+ const InsetsLayoutUnit border_insets,
const scoped_refptr<const cssom::CSSComputedStyleData>& style,
RectNode::Builder* rect_node_builder) {
- rect_node_builder->border =
- std::unique_ptr<Border>(new Border(CreateBorderFromStyle(style)));
+ rect_node_builder->border = std::unique_ptr<Border>(
+ new Border(CreateBorderFromUsedStyle(style, border_insets)));
if (rounded_corners) {
rect_node_builder->rounded_corners =
@@ -1358,7 +1352,7 @@
IsBorderStyleNoneOrHidden(computed_style()->border_top_style()) &&
IsBorderStyleNoneOrHidden(computed_style()->border_right_style()) &&
IsBorderStyleNoneOrHidden(computed_style()->border_bottom_style())) {
- border_insets_ = InsetsLayoutUnit();
+ ResetBorderInsets();
return;
}
@@ -1631,18 +1625,25 @@
math::RectF rect(GetClampedBorderBoxSize());
RectNode::Builder rect_node_builder(rect);
- SetupBorderNodeFromStyle(rounded_corners, computed_style(),
- &rect_node_builder);
+
+ // When an inline box is split, margins, borders, and padding
+ // have no visual effect where the split occurs. (or at any split, when there
+ // are several).
+ // https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+
+ SetupBorderNodeFromUsedStyle(rounded_corners, border_insets_,
+ computed_style(), &rect_node_builder);
scoped_refptr<RectNode> border_node(
new RectNode(std::move(rect_node_builder)));
border_node_builder->AddChild(border_node);
if (has_animated_border) {
- AddAnimations<RectNode>(
- base::Bind(&PopulateBaseStyleForBorderNode),
- base::Bind(&SetupBorderNodeFromStyle, rounded_corners),
- *css_computed_style_declaration(), border_node, animate_node_builder);
+ AddAnimations<RectNode>(base::Bind(&PopulateBaseStyleForBorderNode),
+ base::Bind(&SetupBorderNodeFromUsedStyle,
+ rounded_corners, border_insets_),
+ *css_computed_style_declaration(), border_node,
+ animate_node_builder);
}
}
@@ -2116,6 +2117,20 @@
}
}
+void Box::SetBorderInsets(LayoutUnit left, LayoutUnit top, LayoutUnit right,
+ LayoutUnit bottom) {
+ border_insets_.SetInsets(left, top, right, bottom);
+}
+
+bool Box::IsBorderStyleNoneOrHidden(
+ const scoped_refptr<cssom::PropertyValue>& border_style) {
+ if (border_style == cssom::KeywordValue::GetNone() ||
+ border_style == cssom::KeywordValue::GetHidden()) {
+ return true;
+ }
+ return false;
+}
+
bool Box::ApplyTransformActionToCoordinate(TransformAction action,
math::Vector2dF* coordinate) const {
std::vector<math::Vector2dF> coordinate_vector;
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index 06a2790..a36935a 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -403,6 +403,8 @@
bool transform_forms_root) const;
Vector2dLayoutUnit GetBorderBoxOffsetFromMarginBox() const;
+ void ResetBorderInsets() { border_insets_ = InsetsLayoutUnit(); }
+
// Padding box.
LayoutUnit padding_left() const { return padding_insets_.left(); }
LayoutUnit padding_top() const { return padding_insets_.top(); }
@@ -419,6 +421,11 @@
LayoutUnit GetPaddingBoxLeftEdgeOffsetFromMarginBox() const;
LayoutUnit GetPaddingBoxTopEdgeOffsetFromMarginBox() const;
+ // Set padding insets in InlineContainerBox UpdatePaddings to an empty
+ // LayoutUnit or the computed_style value using is_split_on_*_.
+ void SetPaddingInsets(LayoutUnit left, LayoutUnit top, LayoutUnit right,
+ LayoutUnit bottom);
+
// Content box.
LayoutUnit width() const { return content_size_.width(); }
LayoutUnit height() const { return content_size_.height(); }
@@ -729,6 +736,9 @@
base::Optional<LayoutUnit> collapsed_margin_bottom_;
base::Optional<LayoutUnit> collapsed_empty_margin_;
+ static bool IsBorderStyleNoneOrHidden(
+ const scoped_refptr<cssom::PropertyValue>& border_style);
+
protected:
UsedStyleProvider* used_style_provider() const {
return used_style_provider_;
@@ -807,6 +817,11 @@
const base::Optional<LayoutUnit>& possibly_overconstrained_margin_left,
const base::Optional<LayoutUnit>& possibly_overconstrained_margin_right);
+ // Set border insets in InlineContainerBox UpdateBorders to an empty
+ // LayoutUnit or the computed_style value using is_split_on_*_.
+ void SetBorderInsets(LayoutUnit left, LayoutUnit top, LayoutUnit right,
+ LayoutUnit bottom);
+
private:
struct CachedRenderTreeNodeInfo {
explicit CachedRenderTreeNodeInfo(const math::Vector2dF& offset)
@@ -817,9 +832,9 @@
};
// Updates used values of "border" properties.
- void UpdateBorders();
+ virtual void UpdateBorders();
// Updates used values of "padding" properties.
- void UpdatePaddings(const LayoutParams& layout_params);
+ virtual void UpdatePaddings(const LayoutParams& layout_params);
// Computes the normalized "outer" rounded corners (if there are any) from the
// border radii.
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc
index ad09c7d..6a09ed1 100644
--- a/src/cobalt/layout/box_generator.cc
+++ b/src/cobalt/layout/box_generator.cc
@@ -626,7 +626,7 @@
container_box_ = base::WrapRefCounted(new InlineContainerBox(
css_computed_style_declaration_, context_->used_style_provider,
- context_->layout_stat_tracker));
+ context_->layout_stat_tracker, (*paragraph_)->base_direction()));
break;
// Generate an inline-level block container box. The inside of
// an inline-block is formatted as a block box, and the element itself
diff --git a/src/cobalt/layout/inline_container_box.cc b/src/cobalt/layout/inline_container_box.cc
index e59dd7f..2a9bc8d 100644
--- a/src/cobalt/layout/inline_container_box.cc
+++ b/src/cobalt/layout/inline_container_box.cc
@@ -27,7 +27,7 @@
const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
css_computed_style_declaration,
UsedStyleProvider* used_style_provider,
- LayoutStatTracker* layout_stat_tracker)
+ LayoutStatTracker* layout_stat_tracker, BaseDirection base_direction)
: ContainerBox(css_computed_style_declaration, used_style_provider,
layout_stat_tracker),
should_collapse_leading_white_space_(false),
@@ -41,7 +41,10 @@
css_computed_style_declaration->data()->font_family(),
css_computed_style_declaration->data()->font_size(),
css_computed_style_declaration->data()->font_style(),
- css_computed_style_declaration->data()->font_weight())) {}
+ css_computed_style_declaration->data()->font_weight())),
+ is_split_on_left_(false),
+ is_split_on_right_(false),
+ base_direction_(base_direction) {}
InlineContainerBox::~InlineContainerBox() {}
@@ -55,7 +58,7 @@
// container box.
return false;
}
- // Fall through if out-of-flow.
+ // Fall through if out-of-flow.
case kInlineLevel:
// If the inline container box already contains a line break, then no
@@ -74,13 +77,19 @@
}
scoped_refptr<ContainerBox> InlineContainerBox::TrySplitAtEnd() {
- scoped_refptr<InlineContainerBox> box_after_split(
- new InlineContainerBox(css_computed_style_declaration(),
- used_style_provider(), layout_stat_tracker()));
- // When an inline box is split, margins, borders, and padding have no visual
- // effect where the split occurs.
- // https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
- // TODO: Implement the above comment.
+ scoped_refptr<InlineContainerBox> box_after_split(new InlineContainerBox(
+ css_computed_style_declaration(), used_style_provider(),
+ layout_stat_tracker(), base_direction_));
+ // Set the state of where the sibling boxes are split using
+ // base_direction_ to determine the correct way to split the boxes for
+ // dir : rtl or ltr.
+ if (base_direction_ == kLeftToRightBaseDirection) {
+ is_split_on_right_ = true;
+ box_after_split->SetIsSplitOnLeft(true);
+ } else {
+ is_split_on_left_ = true;
+ box_after_split->SetIsSplitOnRight(true);
+ }
return box_after_split;
}
@@ -93,6 +102,14 @@
return inline_top_margin_;
}
+void InlineContainerBox::SetIsSplitOnLeft(bool is_split_on_left) {
+ is_split_on_left_ = is_split_on_left;
+}
+
+void InlineContainerBox::SetIsSplitOnRight(bool is_split_on_right) {
+ is_split_on_right_ = is_split_on_right;
+}
+
void InlineContainerBox::UpdateContentSizeAndMargins(
const LayoutParams& layout_params) {
// Lay out child boxes as one line without width constraints and white space
@@ -127,16 +144,36 @@
// https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
set_width(line_box.shrink_to_fit_width());
- base::Optional<LayoutUnit> maybe_margin_left = GetUsedMarginLeftIfNotAuto(
- computed_style(), layout_params.containing_block_size);
- base::Optional<LayoutUnit> maybe_margin_right = GetUsedMarginRightIfNotAuto(
- computed_style(), layout_params.containing_block_size);
+ if (is_split_on_left_) {
+ // When an inline box is split, margins, borders, and padding
+ // have no visual effect where the split occurs. (or at any split, when
+ // there are several).
+ // https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+ set_margin_left(LayoutUnit());
+ } else {
+ // A computed value of "auto" for "margin-left" or "margin-right" becomes
+ // a used value of "0".
+ // https://www.w3.org/TR/CSS21/visudet.html#inline-width
+ base::Optional<LayoutUnit> maybe_margin_left = GetUsedMarginLeftIfNotAuto(
+ computed_style(), layout_params.containing_block_size);
+ set_margin_left(maybe_margin_left.value_or(LayoutUnit()));
+ }
- // A computed value of "auto" for "margin-left" or "margin-right" becomes
- // a used value of "0".
- // https://www.w3.org/TR/CSS21/visudet.html#inline-width
- set_margin_left(maybe_margin_left.value_or(LayoutUnit()));
- set_margin_right(maybe_margin_right.value_or(LayoutUnit()));
+ if (is_split_on_right_) {
+ // When an inline box is split, margins, borders, and padding
+ // have no visual effect where the split occurs. (or at any split, when
+ // there are several).
+ // https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+ set_margin_right(LayoutUnit());
+ } else {
+ // A computed value of "auto" for "margin-left" or "margin-right" becomes
+ // a used value of "0".
+ // https://www.w3.org/TR/CSS21/visudet.html#inline-width
+ base::Optional<LayoutUnit> maybe_margin_right =
+ GetUsedMarginRightIfNotAuto(computed_style(),
+ layout_params.containing_block_size);
+ set_margin_right(maybe_margin_right.value_or(LayoutUnit()));
+ }
}
// The "height" property does not apply. The height of the content area should
@@ -172,6 +209,44 @@
baseline_offset_from_margin_box_top_ = line_box.baseline_offset_from_top();
}
+void InlineContainerBox::UpdateBorders() {
+ if (IsBorderStyleNoneOrHidden(computed_style()->border_left_style()) &&
+ IsBorderStyleNoneOrHidden(computed_style()->border_top_style()) &&
+ IsBorderStyleNoneOrHidden(computed_style()->border_right_style()) &&
+ IsBorderStyleNoneOrHidden(computed_style()->border_bottom_style())) {
+ ResetBorderInsets();
+ return;
+ }
+ // When an inline box is split, margins, borders, and padding
+ // have no visual effect where the split occurs. (or at any split, when there
+ // are several).
+ // https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+ SetBorderInsets(
+ is_split_on_left_ ? LayoutUnit() : GetUsedBorderLeft(computed_style()),
+ GetUsedBorderTop(computed_style()),
+ is_split_on_right_ ? LayoutUnit() : GetUsedBorderRight(computed_style()),
+ GetUsedBorderBottom(computed_style()));
+}
+
+void InlineContainerBox::UpdatePaddings(const LayoutParams& layout_params) {
+ // When an inline box is split, margins, borders, and padding
+ // have no visual effect where the split occurs. (or at any split, when there
+ // are several).
+ // https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+ SetPaddingInsets(
+ is_split_on_left_
+ ? LayoutUnit()
+ : GetUsedPaddingLeft(computed_style(),
+ layout_params.containing_block_size),
+ GetUsedPaddingTop(computed_style(), layout_params.containing_block_size),
+ is_split_on_right_
+ ? LayoutUnit()
+ : GetUsedPaddingRight(computed_style(),
+ layout_params.containing_block_size),
+ GetUsedPaddingBottom(computed_style(),
+ layout_params.containing_block_size));
+}
+
WrapResult InlineContainerBox::TryWrapAt(
WrapAtPolicy wrap_at_policy, WrapOpportunityPolicy wrap_opportunity_policy,
bool is_line_existence_justified, LayoutUnit available_width,
@@ -632,14 +707,21 @@
void InlineContainerBox::SplitAtIterator(
Boxes::const_iterator child_split_iterator) {
- // TODO: When an inline box is split, margins, borders, and padding
- // have no visual effect where the split occurs.
- // https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
-
// Move the children after the split into a new box.
- scoped_refptr<InlineContainerBox> box_after_split(
- new InlineContainerBox(css_computed_style_declaration(),
- used_style_provider(), layout_stat_tracker()));
+ scoped_refptr<InlineContainerBox> box_after_split(new InlineContainerBox(
+ css_computed_style_declaration(), used_style_provider(),
+ layout_stat_tracker(), base_direction_));
+
+ // Set the state of where the sibling boxes are split using
+ // base_direction_ to determine the correct way to split the boxes for
+ // dir : rtl or ltr.
+ if (base_direction_ == kLeftToRightBaseDirection) {
+ is_split_on_right_ = true;
+ box_after_split->SetIsSplitOnLeft(true);
+ } else {
+ is_split_on_left_ = true;
+ box_after_split->SetIsSplitOnRight(true);
+ }
// Update the split sibling links.
box_after_split->split_sibling_ = split_sibling_;
diff --git a/src/cobalt/layout/inline_container_box.h b/src/cobalt/layout/inline_container_box.h
index 1c15c83..dbd3df7 100644
--- a/src/cobalt/layout/inline_container_box.h
+++ b/src/cobalt/layout/inline_container_box.h
@@ -39,7 +39,8 @@
InlineContainerBox(const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
css_computed_style_declaration,
UsedStyleProvider* used_style_provider,
- LayoutStatTracker* layout_stat_tracker);
+ LayoutStatTracker* layout_stat_tracker,
+ BaseDirection base_direction);
~InlineContainerBox() override;
// From |Box|.
@@ -47,6 +48,10 @@
void UpdateContentSizeAndMargins(const LayoutParams& layout_params) override;
+ void UpdateBorders() override;
+
+ void UpdatePaddings(const LayoutParams& layout_params) override;
+
WrapResult TryWrapAt(WrapAtPolicy wrap_at_policy,
WrapOpportunityPolicy wrap_opportunity_policy,
bool is_line_existence_justified,
@@ -76,6 +81,8 @@
LayoutUnit GetBaselineOffsetFromTopMarginEdge() const override;
LayoutUnit GetInlineLevelBoxHeight() const override;
LayoutUnit GetInlineLevelTopMargin() const override;
+ void SetIsSplitOnLeft(bool is_split_on_left);
+ void SetIsSplitOnRight(bool is_split_on_right);
// From |ContainerBox|.
bool TryAddChild(const scoped_refptr<Box>& child_box) override;
@@ -136,6 +143,11 @@
// A font used for text width and line height calculations.
const scoped_refptr<dom::FontList> used_font_;
+ bool is_split_on_left_;
+ bool is_split_on_right_;
+
+ BaseDirection base_direction_;
+
// A reference to the next inline container box in a linked list of inline
// container boxes produced from splits of the initial text box. This enables
// HTMLElement to retain access to all of its layout boxes after they are
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index cd89a85..8ab844f 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -722,7 +722,7 @@
// The specifications indicate that if positions are not specified for color
// stops, then they should be filled in automatically by evenly spacing them
-// between the two neighbooring color stops that DO have positions specified.
+// between the two neighboring color stops that DO have positions specified.
// This function implements this. It assumes that unspecified position values
// are indicated by a value of -1.0f.
void InterpolateUnspecifiedColorStopPositions(
@@ -1706,8 +1706,7 @@
// https://www.w3.org/TR/css-flexbox-1/#flex-basis-property
base::Optional<LayoutUnit> GetUsedFlexBasisIfNotContent(
const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style,
- bool main_direction_is_horizontal,
- LayoutUnit main_space,
+ bool main_direction_is_horizontal, LayoutUnit main_space,
bool* flex_basis_depends_on_available_space) {
// Percentage values of flex-basis are resolved against the flex item's
// containing block (i.e. its flex container).
@@ -1861,30 +1860,26 @@
LayoutUnit GetUsedBorderLeft(
const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style) {
- return LayoutUnit(base::polymorphic_downcast<const cssom::LengthValue*>(
- computed_style->border_left_width().get())
- ->value());
+ return LayoutUnit(
+ GetUsedNonNegativeLength(computed_style->border_left_width()));
}
LayoutUnit GetUsedBorderTop(
const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style) {
- return LayoutUnit(base::polymorphic_downcast<const cssom::LengthValue*>(
- computed_style->border_top_width().get())
- ->value());
+ return LayoutUnit(
+ GetUsedNonNegativeLength(computed_style->border_top_width()));
}
LayoutUnit GetUsedBorderRight(
const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style) {
- return LayoutUnit(base::polymorphic_downcast<const cssom::LengthValue*>(
- computed_style->border_right_width().get())
- ->value());
+ return LayoutUnit(
+ GetUsedNonNegativeLength(computed_style->border_right_width()));
}
LayoutUnit GetUsedBorderBottom(
const scoped_refptr<const cssom::CSSComputedStyleData>& computed_style) {
- return LayoutUnit(base::polymorphic_downcast<const cssom::LengthValue*>(
- computed_style->border_bottom_width().get())
- ->value());
+ return LayoutUnit(
+ GetUsedNonNegativeLength(computed_style->border_bottom_width()));
}
LayoutUnit GetUsedPaddingLeft(
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs-expected.png
new file mode 100644
index 0000000..7e58ccb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html
similarity index 96%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html
rename to src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html
index cd91b37..de17dbd 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html
@@ -12,6 +12,7 @@
body {
margin: 0px;
font-family: Roboto;
+ font-size: 16px;
}
span {
padding-right: 12px;
@@ -24,4 +25,3 @@
</div>
</body>
</html>
-
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2-expected.png
new file mode 100644
index 0000000..3bcf7d0
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2.html b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2.html
new file mode 100644
index 0000000..317def4
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<!--
+ | When an inline box is split, margins, borders, and padding have no visual
+ | effect where the split occurs (or at any split, when there are several).
+ | https://www.w3.org/TR/CSS21/visuren.html#inline-formatting
+ -->
+<html>
+ <head>
+ <style>
+ .containing-block {
+ width: 15rem;
+ display: inline-block;
+ }
+ .text-block {
+ color: white;
+ background-color: blue;
+ margin-left: 0.5rem;
+ font-family: Roboto;
+ font-size: 16px;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="containing-block">
+ <span class="text-block"> BADGE </span> <span class="text-block"> FOO BAR BAZ, 11:00 AM - 7:00 PM </span>
+ </div>
+ </body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-expected.png
new file mode 100644
index 0000000..f62eddb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html
similarity index 94%
rename from src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html
rename to src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html
index 78fdaaf..0f30f53 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/DISABLED-9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html
@@ -14,6 +14,8 @@
border: 5px solid green;
margin: 20px;
padding: 5px;
+ font-family: Roboto;
+ font-size: 16px;
}
</style>
</head>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
index 7f7ba6b..3659681 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
@@ -182,6 +182,9 @@
9-4-2-inline-boxes-should-split-at-br-elements
9-4-2-long-inline-boxes-should-be-split
9-4-2-margin-should-not-overflow-line-box
+9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs
+9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs
+9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2
9-4-2-multiple-inline-boxes-exceeding-width-of-line-box-should-be-split
9-4-2-nested-inline-boxes-should-split-at-br-elements
9-4-2-splitting-of-boxes-can-affect-containing-box-of-children
diff --git a/src/cobalt/layout_tests/testdata/css-text-3/5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity-expected.png b/src/cobalt/layout_tests/testdata/css-text-3/5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity-expected.png
index 42ad0ea..87075bf 100644
--- a/src/cobalt/layout_tests/testdata/css-text-3/5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity-expected.png
+++ b/src/cobalt/layout_tests/testdata/css-text-3/5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range-expected.png
new file mode 100644
index 0000000..2cd7bd0
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range.html
new file mode 100644
index 0000000..c257b7e
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<!--
+ | Unicode range test. Tests that font style takes precedence over a matching unicode-range.
+ | https://www.w3.org/TR/css3-fonts/#font-prop-desc
+ -->
+<html>
+<head>
+ <style>
+ @font-face {
+ font-family: 'TestFont';
+ src: local('Noto Serif');
+ unicode-range: U+4d-5a; /* =, M-Z */
+ font-weight: bold;
+ }
+ @font-face {
+ font-family: 'TestFont';
+ src: local('Roboto');
+ font-weight: normal;
+ }
+ @font-face {
+ font-family: 'TestFont2';
+ src: local('Dancing Script');
+ font-weight: bold;
+ }
+ body {
+ background-color: black;
+ font-size: 75px;
+ color: #fff;
+ font-weight: bold;
+ font-family: 'TestFont', 'TestFont2'; /*TestFont2 should be used. */
+ }
+ </style>
+</head>
+<body>
+ <div>Hello</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range-expected.png
new file mode 100644
index 0000000..c32526d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range.html b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range.html
new file mode 100644
index 0000000..3ff6b53
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/4-5-use-correct-font-with-unicode-range.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!--
+ | Unicode range test. Tests multiple unicode-range values and multiple
+ | font-families with the same style but different unicode ranges and sources.
+ | https://www.w3.org/TR/css3-fonts/#font-prop-desc
+ -->
+<html>
+<head>
+ <style>
+ @font-face {
+ font-family: 'TestFont';
+ src: local('Dancing Script');
+ unicode-range: U+3d, U+4d-5a; /* =, M-Z */
+ }
+ @font-face {
+ font-family: 'TestFont';
+ src: local('Noto Serif');
+ unicode-range: U+26; /* & */
+ }
+ body {
+ background-color: black;
+ font-size: 75px;
+ color: #fff;
+ font-family: 'TestFont', 'Roboto';
+ }
+ </style>
+</head>
+<body>
+ <div>Me & You = Us</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
index aac78cf..8c922d4 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/layout_tests.txt
@@ -4,6 +4,8 @@
4-3-use-first-available-local-font-face
4-3-use-next-font-family-if-font-face-sources-unavailable
4-4-use-correct-style-in-font-face-set
+4-5-use-correct-font-with-unicode-range
+4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range
5-2-use-correct-style-in-font-family
5-2-use-first-available-listed-font-family
5-2-use-numerical-font-weights-in-family-face-matching
diff --git a/src/cobalt/media/base/playback_statistics.cc b/src/cobalt/media/base/playback_statistics.cc
index a17c72a..b5001ec 100644
--- a/src/cobalt/media/base/playback_statistics.cc
+++ b/src/cobalt/media/base/playback_statistics.cc
@@ -17,6 +17,7 @@
#include <math.h>
#include <stdint.h>
+#include "base/strings/stringprintf.h"
#include "starboard/atomic.h"
#include "starboard/common/log.h"
#include "starboard/common/string.h"
@@ -77,6 +78,55 @@
} // namespace
+PlaybackStatistics::PlaybackStatistics(const std::string& pipeline_identifier)
+ : seek_time_(base::StringPrintf("Media.Pipeline.%s.SeekTime",
+ pipeline_identifier.c_str()),
+ base::TimeDelta(), "The seek-to time of the media pipeline."),
+ first_written_audio_timestamp_(
+ base::StringPrintf("Media.Pipeline.%s.FirstWrittenAudioTimestamp",
+ pipeline_identifier.c_str()),
+ base::TimeDelta(),
+ "The timestamp of the first written audio buffer in the media "
+ "pipeline."),
+ first_written_video_timestamp_(
+ base::StringPrintf("Media.Pipeline.%s.FirstWrittenVideoTimestamp",
+ pipeline_identifier.c_str()),
+ base::TimeDelta(),
+ "The timestamp of the first written audio buffer in the media "
+ "pipeline."),
+ last_written_audio_timestamp_(
+ base::StringPrintf("Media.Pipeline.%s.LastWrittenAudioTimestamp",
+ pipeline_identifier.c_str()),
+ base::TimeDelta(),
+ "The timestamp of the last written audio buffer in the media "
+ "pipeline."),
+ last_written_video_timestamp_(
+ base::StringPrintf("Media.Pipeline.%s.LastWrittenVideoTimestamp",
+ pipeline_identifier.c_str()),
+ base::TimeDelta(),
+ "The timestamp of the last written video buffer in the media "
+ "pipeline."),
+ video_width_(base::StringPrintf("Media.Pipeline.%s.VideoWidth",
+ pipeline_identifier.c_str()),
+ 0, "The frame width of the video."),
+ video_height_(base::StringPrintf("Media.Pipeline.%s.VideoHeight",
+ pipeline_identifier.c_str()),
+ 0, "The frame height of the video."),
+ is_audio_eos_written_(
+ base::StringPrintf("Media.Pipeline.%s.IsAudioEOSWritten",
+ pipeline_identifier.c_str()),
+ false, "Indicator of if the audio eos is written."),
+ is_video_eos_written_(
+ base::StringPrintf("Media.Pipeline.%s.IsVideoEOSWritten",
+ pipeline_identifier.c_str()),
+ false, "Indicator of if the video eos is written."),
+ pipeline_status_(base::StringPrintf("Media.Pipeline.%s.PipelineStatus",
+ pipeline_identifier.c_str()),
+ PIPELINE_OK, "The status of the media pipeline."),
+ error_message_(base::StringPrintf("Media.Pipeline.%s.ErrorMessage",
+ pipeline_identifier.c_str()),
+ "", "The error message of the media pipeline error.") {}
+
PlaybackStatistics::~PlaybackStatistics() {
SbAtomicNoBarrier_Increment(&s_active_instances, -1);
}
@@ -103,10 +153,11 @@
}
}
- const auto width =
- static_cast<SbAtomic32>(video_config.natural_size().width());
- const auto height =
- static_cast<SbAtomic32>(video_config.natural_size().height());
+ video_width_ = video_config.natural_size().width();
+ video_height_ = video_config.natural_size().height();
+
+ const auto width = static_cast<SbAtomic32>(video_width_);
+ const auto height = static_cast<SbAtomic32>(video_height_);
UpdateMinValue(width, &s_min_video_width);
UpdateMaxValue(width, &s_max_video_width);
@@ -124,22 +175,38 @@
void PlaybackStatistics::OnSeek(const base::TimeDelta& seek_time) {
seek_time_ = seek_time;
- first_written_audio_timestamp_.reset();
- first_written_video_timestamp_.reset();
+ is_first_audio_buffer_written_ = false;
+ is_first_video_buffer_written_ = false;
}
-void PlaybackStatistics::OnAudioAU(const base::TimeDelta& timestamp) {
- last_written_audio_timestamp_ = timestamp;
- if (!first_written_audio_timestamp_) {
- first_written_audio_timestamp_.emplace(timestamp);
+void PlaybackStatistics::OnAudioAU(const scoped_refptr<DecoderBuffer>& buffer) {
+ if (buffer->end_of_stream()) {
+ is_audio_eos_written_ = true;
+ return;
+ }
+ last_written_audio_timestamp_ = buffer->timestamp();
+ if (!is_first_audio_buffer_written_) {
+ is_first_audio_buffer_written_ = true;
+ first_written_audio_timestamp_ = buffer->timestamp();
}
}
-void PlaybackStatistics::OnVideoAU(const base::TimeDelta& timestamp) {
- last_written_video_timestamp_ = timestamp;
- if (!first_written_video_timestamp_) {
- first_written_video_timestamp_.emplace(timestamp);
+void PlaybackStatistics::OnVideoAU(const scoped_refptr<DecoderBuffer>& buffer) {
+ if (buffer->end_of_stream()) {
+ is_video_eos_written_ = true;
+ return;
}
+ last_written_video_timestamp_ = buffer->timestamp();
+ if (!is_first_video_buffer_written_) {
+ is_first_video_buffer_written_ = true;
+ first_written_video_timestamp_ = buffer->timestamp();
+ }
+}
+
+void PlaybackStatistics::OnError(PipelineStatus status,
+ const std::string& error_message) {
+ pipeline_status_ = status;
+ error_message_ = error_message;
}
std::string PlaybackStatistics::GetStatistics(
@@ -149,14 +216,12 @@
", active_players (max): %d (%d), av1: ~%" PRId64 ", h264: ~%" PRId64
", hevc: ~%" PRId64 ", vp9: ~%" PRId64
", min_width: %d, min_height: %d, max_width: %d, max_height: %d"
- ", last_working_codec: %s, seek_time: %" PRId64
+ ", last_working_codec: %s, seek_time: %s"
", first_audio_time: ~%" PRId64 ", first_video_time: ~%" PRId64
", last_audio_time: ~%" PRId64 ", last_video_time: ~%" PRId64,
GetCodecName(current_video_config.codec()).c_str(),
- (current_video_config.is_encrypted() ? "Y" : "N"),
- static_cast<int>(current_video_config.natural_size().width()),
- static_cast<int>(current_video_config.natural_size().height()),
- SbAtomicNoBarrier_Load(&s_active_instances),
+ (current_video_config.is_encrypted() ? "Y" : "N"), video_width_.value(),
+ video_height_.value(), SbAtomicNoBarrier_Load(&s_active_instances),
SbAtomicNoBarrier_Load(&s_max_active_instances),
RoundValues(SbAtomicNoBarrier_Load(&s_av1_played)),
RoundValues(SbAtomicNoBarrier_Load(&s_h264_played)),
@@ -169,18 +234,18 @@
GetCodecName(static_cast<VideoCodec>(
SbAtomicNoBarrier_Load(&s_last_working_codec)))
.c_str(),
- seek_time_.InMicroseconds(),
- first_written_audio_timestamp_.has_value()
- ? RoundValues(first_written_audio_timestamp_->InSeconds())
+ ValToString(seek_time_).c_str(),
+ is_first_audio_buffer_written_
+ ? RoundValues(first_written_audio_timestamp_.value().InSeconds())
: -1,
- first_written_video_timestamp_.has_value()
- ? RoundValues(first_written_video_timestamp_->InSeconds())
+ is_first_video_buffer_written_
+ ? RoundValues(first_written_video_timestamp_.value().InSeconds())
: -1,
- first_written_audio_timestamp_.has_value()
- ? RoundValues(last_written_audio_timestamp_.InSeconds())
+ is_first_audio_buffer_written_
+ ? RoundValues(last_written_audio_timestamp_.value().InSeconds())
: -1,
- first_written_video_timestamp_.has_value()
- ? RoundValues(last_written_video_timestamp_.InSeconds())
+ is_first_video_buffer_written_
+ ? RoundValues(last_written_video_timestamp_.value().InSeconds())
: -1);
}
diff --git a/src/cobalt/media/base/playback_statistics.h b/src/cobalt/media/base/playback_statistics.h
index 9cbb562..78808b3 100644
--- a/src/cobalt/media/base/playback_statistics.h
+++ b/src/cobalt/media/base/playback_statistics.h
@@ -19,6 +19,9 @@
#include "base/optional.h"
#include "base/time/time.h"
+#include "cobalt/base/c_val.h"
+#include "cobalt/media/base/decoder_buffer.h"
+#include "cobalt/media/base/pipeline_status.h"
#include "cobalt/media/base/video_decoder_config.h"
namespace cobalt {
@@ -26,24 +29,34 @@
class PlaybackStatistics {
public:
+ explicit PlaybackStatistics(const std::string& pipeline_identifier);
~PlaybackStatistics();
void UpdateVideoConfig(const VideoDecoderConfig& video_config);
void OnPresenting(const VideoDecoderConfig& video_config);
void OnSeek(const base::TimeDelta& seek_time);
- void OnAudioAU(const base::TimeDelta& timestamp);
- void OnVideoAU(const base::TimeDelta& timestamp);
+ void OnAudioAU(const scoped_refptr<DecoderBuffer>& buffer);
+ void OnVideoAU(const scoped_refptr<DecoderBuffer>& buffer);
+ void OnError(PipelineStatus status, const std::string& error_message);
std::string GetStatistics(
const VideoDecoderConfig& current_video_config) const;
private:
- base::TimeDelta seek_time_;
- base::Optional<base::TimeDelta> first_written_audio_timestamp_;
- base::Optional<base::TimeDelta> first_written_video_timestamp_;
- base::TimeDelta last_written_audio_timestamp_;
- base::TimeDelta last_written_video_timestamp_;
+ base::CVal<base::TimeDelta> seek_time_;
+ base::CVal<base::TimeDelta> first_written_audio_timestamp_;
+ base::CVal<base::TimeDelta> first_written_video_timestamp_;
+ base::CVal<base::TimeDelta> last_written_audio_timestamp_;
+ base::CVal<base::TimeDelta> last_written_video_timestamp_;
+ base::CVal<int> video_width_;
+ base::CVal<int> video_height_;
+ base::CVal<bool> is_audio_eos_written_;
+ base::CVal<bool> is_video_eos_written_;
+ base::CVal<PipelineStatus> pipeline_status_;
+ base::CVal<std::string> error_message_;
bool is_initial_config_ = true;
+ bool is_first_audio_buffer_written_ = false;
+ bool is_first_video_buffer_written_ = false;
};
} // namespace media
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 3b7fcb4..5a9b541 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -21,11 +21,13 @@
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/optional.h"
+#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
+#include "cobalt/base/c_val.h"
#include "cobalt/math/size.h"
#include "cobalt/media/base/audio_decoder_config.h"
#include "cobalt/media/base/bind_to_current_loop.h"
@@ -55,6 +57,8 @@
static const int kRetryDelayAtSuspendInMilliseconds = 100;
+unsigned int g_pipeline_identifier_counter = 0;
+
// Used to post parameters to SbPlayerPipeline::StartTask() as the number of
// parameters exceed what base::Bind() can support.
struct StartTaskParameters {
@@ -189,6 +193,10 @@
// Retrieve the statistics as a string and append to message.
std::string AppendStatisticsString(const std::string& message) const;
+ // An identifier string for the pipeline, used in CVal to identify multiple
+ // pipelines.
+ const std::string pipeline_identifier_;
+
// Message loop used to execute pipeline tasks. It is thread-safe.
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
@@ -219,12 +227,12 @@
// Current volume level (from 0.0f to 1.0f). This value is set immediately
// via SetVolume() and a task is dispatched on the message loop to notify the
// filters.
- float volume_ = 1.f;
+ base::CVal<float> volume_;
// Current playback rate (>= 0.0f). This value is set immediately via
// SetPlaybackRate() and a task is dispatched on the message loop to notify
// the filters.
- float playback_rate_ = 0.f;
+ base::CVal<float> playback_rate_;
// The saved audio and video demuxer streams. Note that it is safe to store
// raw pointers of the demuxer streams, as the Demuxer guarantees that its
@@ -259,7 +267,7 @@
bool audio_read_in_progress_ = false;
bool audio_read_delayed_ = false;
bool video_read_in_progress_ = false;
- TimeDelta duration_;
+ base::CVal<TimeDelta> duration_;
#if SB_HAS(PLAYER_WITH_URL)
TimeDelta start_date_;
@@ -276,12 +284,14 @@
// Temporary callback used for Start() and Seek().
SeekCB seek_cb_;
- base::TimeDelta seek_time_;
+ TimeDelta seek_time_;
std::unique_ptr<StarboardPlayer> player_;
bool is_initial_preroll_ = true;
- bool suspended_ = false;
- bool stopped_ = false;
- bool ended_ = false;
+ base::CVal<bool> started_;
+ base::CVal<bool> suspended_;
+ base::CVal<bool> stopped_;
+ base::CVal<bool> ended_;
+ base::CVal<SbPlayerState> player_state_;
VideoFrameProvider* video_frame_provider_;
@@ -299,11 +309,11 @@
// Timestamp for the last written audio.
SbTime timestamp_of_last_written_audio_ = 0;
// Last media time reported by GetMediaTime().
- SbTime last_media_time_ = 0;
+ base::CVal<SbTime> last_media_time_;
// Time when we last checked the media time.
SbTime last_time_media_time_retrieved_ = 0;
// The maximum video playback capabilities required for the playback.
- std::string max_video_capabilities_;
+ base::CVal<std::string> max_video_capabilities_;
PlaybackStatistics playback_statistics_;
@@ -317,14 +327,52 @@
get_decode_target_graphics_context_provider_func,
bool allow_resume_after_suspend, MediaLog* media_log,
VideoFrameProvider* video_frame_provider)
- : task_runner_(task_runner),
+ : pipeline_identifier_(
+ base::StringPrintf("%X", g_pipeline_identifier_counter++)),
+ task_runner_(task_runner),
allow_resume_after_suspend_(allow_resume_after_suspend),
window_(window),
get_decode_target_graphics_context_provider_func_(
get_decode_target_graphics_context_provider_func),
natural_size_(0, 0),
+ volume_(base::StringPrintf("Media.Pipeline.%s.Volume",
+ pipeline_identifier_.c_str()),
+ 1.0f, "Volume of the media pipeline."),
+ playback_rate_(base::StringPrintf("Media.Pipeline.%s.PlaybackRate",
+ pipeline_identifier_.c_str()),
+ 0.0f, "Playback rate of the media pipeline."),
+ duration_(base::StringPrintf("Media.Pipeline.%s.Duration",
+ pipeline_identifier_.c_str()),
+ base::TimeDelta(), "Playback duration of the media pipeline."),
set_bounds_helper_(new SbPlayerSetBoundsHelper),
- video_frame_provider_(video_frame_provider) {
+ started_(base::StringPrintf("Media.Pipeline.%s.Started",
+ pipeline_identifier_.c_str()),
+ false, "Whether the media pipeline has started."),
+ suspended_(base::StringPrintf("Media.Pipeline.%s.Suspended",
+ pipeline_identifier_.c_str()),
+ false,
+ "Whether the media pipeline has been suspended. The "
+ "pipeline is usually suspended after the app enters "
+ "background mode."),
+ stopped_(base::StringPrintf("Media.Pipeline.%s.Stopped",
+ pipeline_identifier_.c_str()),
+ false, "Whether the media pipeline has stopped."),
+ ended_(base::StringPrintf("Media.Pipeline.%s.Ended",
+ pipeline_identifier_.c_str()),
+ false, "Whether the media pipeline has ended."),
+ player_state_(base::StringPrintf("Media.Pipeline.%s.PlayerState",
+ pipeline_identifier_.c_str()),
+ kSbPlayerStateInitialized,
+ "The underlying SbPlayer state of the media pipeline."),
+ video_frame_provider_(video_frame_provider),
+ last_media_time_(base::StringPrintf("Media.Pipeline.%s.LastMediaTime",
+ pipeline_identifier_.c_str()),
+ 0, "Last media time reported by the underlying player."),
+ max_video_capabilities_(
+ base::StringPrintf("Media.Pipeline.%s.MaxVideoCapabilities",
+ pipeline_identifier_.c_str()),
+ "", "The max video capabilities required for the media pipeline."),
+ playback_statistics_(pipeline_identifier_) {
SbMediaSetAudioWriteDuration(kAudioLimit);
}
@@ -487,9 +535,9 @@
player = std::move(player_);
}
- DLOG(INFO) << "Destroying SbPlayer.";
+ LOG(INFO) << "Destroying SbPlayer.";
player.reset();
- DLOG(INFO) << "SbPlayer destroyed.";
+ LOG(INFO) << "SbPlayer destroyed.";
}
// When Stop() is in progress, we no longer need to call |error_cb_|.
@@ -773,6 +821,8 @@
BindToCurrentLoop(base::Bind(
&SbPlayerPipeline::OnDemuxerInitialized, this)),
kEnableTextTracks);
+
+ started_ = true;
}
void SbPlayerPipeline::SetVolumeTask(float volume) {
@@ -857,7 +907,7 @@
{
base::AutoLock auto_lock(lock_);
- DLOG(INFO) << "StarboardPlayer created with url: " << source_url;
+ LOG(INFO) << "Creating StarboardPlayer with url: " << source_url;
player_.reset(new StarboardPlayer(
task_runner_, source_url, window_, this, set_bounds_helper_.get(),
allow_resume_after_suspend_, *decode_to_texture_output_mode_,
@@ -867,6 +917,7 @@
SetVolumeTask(volume_);
} else {
player_.reset();
+ LOG(INFO) << "Failed to create a valid StarboardPlayer.";
}
}
@@ -891,8 +942,7 @@
base::AutoLock auto_lock(lock_);
if (!player_) {
- DLOG(INFO)
- << "Player not set before calling SbPlayerPipeline::SetDrmSystem";
+ LOG(INFO) << "Player not set before calling SbPlayerPipeline::SetDrmSystem";
return;
}
@@ -947,6 +997,7 @@
// available, reset the existing player first to reduce the number of active
// players.
player_.reset();
+ LOG(INFO) << "Creating StarboardPlayer.";
player_.reset(new StarboardPlayer(
task_runner_, get_decode_target_graphics_context_provider_func_,
audio_config, video_config, window_, drm_system, this,
@@ -958,6 +1009,7 @@
SetVolumeTask(volume_);
} else {
player_.reset();
+ LOG(INFO) << "Failed to create a valid StarboardPlayer.";
}
}
@@ -1118,15 +1170,13 @@
if (type == DemuxerStream::AUDIO) {
audio_read_in_progress_ = false;
+ playback_statistics_.OnAudioAU(buffer);
if (!buffer->end_of_stream()) {
- playback_statistics_.OnAudioAU(buffer->timestamp());
timestamp_of_last_written_audio_ = buffer->timestamp().ToSbTime();
}
} else {
video_read_in_progress_ = false;
- if (!buffer->end_of_stream()) {
- playback_statistics_.OnVideoAU(buffer->timestamp());
- }
+ playback_statistics_.OnVideoAU(buffer);
}
player_->WriteBuffer(type, buffer);
@@ -1170,7 +1220,7 @@
timestamp_of_last_written_audio_ - last_media_time_;
if (time_ahead_of_playback > (kAudioLimit + kMediaTimeCheckInterval)) {
SbTime delay_time = (time_ahead_of_playback - kAudioLimit) /
- std::max(playback_rate_, 1.0f);
+ std::max(playback_rate_.value(), 1.0f);
task_runner_->PostDelayedTask(
FROM_HERE, base::Bind(&SbPlayerPipeline::DelayedNeedData, this),
base::TimeDelta::FromMicroseconds(delay_time));
@@ -1201,6 +1251,7 @@
if (!player_) {
return;
}
+ player_state_ = state;
switch (state) {
case kSbPlayerStateInitialized:
NOTREACHED();
@@ -1338,6 +1389,11 @@
void SbPlayerPipeline::CallErrorCB(PipelineStatus status,
const std::string& error_message) {
DCHECK_NE(status, PIPELINE_OK);
+ // Only to record the first error.
+ if (error_cb_.is_null()) {
+ return;
+ }
+ playback_statistics_.OnError(status, error_message);
ResetAndRunIfNotNull(&error_cb_, status,
AppendStatisticsString(error_message));
}
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index 3f34f40..7d19612 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -517,7 +517,7 @@
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!on_encrypted_media_init_data_encountered_cb_.is_null());
- DLOG(INFO) << "CreateUrlPlayer passed url " << url;
+ LOG(INFO) << "CreateUrlPlayer passed url " << url;
player_ =
SbUrlPlayerCreate(url.c_str(), window_, &StarboardPlayer::PlayerStatusCB,
&StarboardPlayer::EncryptedMediaInitDataEncounteredCB,
@@ -778,10 +778,10 @@
break;
#if SB_API_VERSION < 12
case kSbPlayerDecoderStateBufferFull:
- DLOG(WARNING) << "kSbPlayerDecoderStateBufferFull has been deprecated.";
+ LOG(WARNING) << "kSbPlayerDecoderStateBufferFull has been deprecated.";
return;
case kSbPlayerDecoderStateDestroyed:
- DLOG(WARNING) << "kSbPlayerDecoderStateDestroyed has been deprecated.";
+ LOG(WARNING) << "kSbPlayerDecoderStateDestroyed has been deprecated.";
return;
#endif // SB_API_VERSION < 12
}
diff --git a/src/cobalt/media/formats/mp4/box_definitions.cc b/src/cobalt/media/formats/mp4/box_definitions.cc
index 0fc6975..631b713 100644
--- a/src/cobalt/media/formats/mp4/box_definitions.cc
+++ b/src/cobalt/media/formats/mp4/box_definitions.cc
@@ -1304,7 +1304,8 @@
reader->ReadChild(&decode_time) && reader->MaybeReadChildren(&runs) &&
reader->MaybeReadChild(&auxiliary_offset) &&
reader->MaybeReadChild(&auxiliary_size) &&
- reader->MaybeReadChild(&sdtp));
+ reader->MaybeReadChild(&sdtp) &&
+ reader->MaybeReadChild(&sample_encryption));
// There could be multiple SampleGroupDescription and SampleToGroup boxes with
// different grouping types. For common encryption, the relevant grouping type
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index 480d7e8..69f5bd6 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -33,9 +33,6 @@
namespace {
-// Used to ensure that there is no more than one instance of WebMediaPlayerImpl.
-WebMediaPlayerImpl* s_instance;
-
// Limits the range of playback rate.
//
// TODO(kylep): Revisit these.
@@ -137,10 +134,6 @@
video_frame_provider_ = new VideoFrameProvider();
- DLOG_IF(ERROR, s_instance)
- << "More than one WebMediaPlayerImpl has been created.";
- s_instance = this;
-
DCHECK(buffer_allocator_);
media_log_->AddEvent(
media_log_->CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED));
@@ -166,10 +159,6 @@
ON_INSTANCE_RELEASED(WebMediaPlayerImpl);
- DLOG_IF(ERROR, s_instance != this)
- << "More than one WebMediaPlayerImpl has been created.";
- s_instance = NULL;
-
if (delegate_) {
delegate_->UnregisterPlayer(this);
}
@@ -229,7 +218,7 @@
DCHECK_EQ(main_loop_, base::MessageLoop::current());
UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(url), kMaxURLScheme);
- DLOG(INFO) << "Start URL playback";
+ LOG(INFO) << "Start URL playback";
// Handle any volume changes that occured before load().
SetVolume(GetClient()->Volume());
@@ -249,7 +238,7 @@
TRACE_EVENT0("cobalt::media", "WebMediaPlayerImpl::LoadMediaSource");
DCHECK_EQ(main_loop_, base::MessageLoop::current());
- DLOG(INFO) << "Start MEDIASOURCE playback";
+ LOG(INFO) << "Start MEDIASOURCE playback";
// Handle any volume changes that occured before load().
SetVolume(GetClient()->Volume());
@@ -276,7 +265,7 @@
DCHECK_EQ(main_loop_, base::MessageLoop::current());
UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(url), kMaxURLScheme);
- DLOG(INFO) << "Start PROGRESSIVE playback";
+ LOG(INFO) << "Start PROGRESSIVE playback";
// Handle any volume changes that occured before load().
SetVolume(GetClient()->Volume());
@@ -904,11 +893,11 @@
// Note: stopping the pipeline might block for a long time.
base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
- DLOG(INFO) << "Trying to stop media pipeline.";
+ LOG(INFO) << "Trying to stop media pipeline.";
pipeline_->Stop(
base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)));
waiter.Wait();
- DLOG(INFO) << "Media pipeline stopped.";
+ LOG(INFO) << "Media pipeline stopped.";
// And then detach the proxy, it may live on the render thread for a little
// longer until all the tasks are finished.
diff --git a/src/cobalt/media_integration_tests/__init__.py b/src/cobalt/media_integration_tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/cobalt/media_integration_tests/__init__.py
diff --git a/src/cobalt/media_integration_tests/_env.py b/src/cobalt/media_integration_tests/_env.py
new file mode 100644
index 0000000..a9d83bf
--- /dev/null
+++ b/src/cobalt/media_integration_tests/_env.py
@@ -0,0 +1,26 @@
+#
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+ print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+ sys.exit(1)
+load_source('', _ENV)
diff --git a/src/cobalt/media_integration_tests/endurance/__init__.py b/src/cobalt/media_integration_tests/endurance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/cobalt/media_integration_tests/endurance/__init__.py
diff --git a/src/cobalt/media_integration_tests/endurance/_env.py b/src/cobalt/media_integration_tests/endurance/_env.py
new file mode 100644
index 0000000..a9d83bf
--- /dev/null
+++ b/src/cobalt/media_integration_tests/endurance/_env.py
@@ -0,0 +1,26 @@
+#
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+ print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+ sys.exit(1)
+load_source('', _ENV)
diff --git a/src/cobalt/media_integration_tests/endurance/endurance_test.py b/src/cobalt/media_integration_tests/endurance/endurance_test.py
new file mode 100644
index 0000000..18cc902
--- /dev/null
+++ b/src/cobalt/media_integration_tests/endurance/endurance_test.py
@@ -0,0 +1,264 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+""" Endurance test of playbacks."""
+
+import argparse
+import logging
+import random
+import time
+
+import _env # pylint: disable=unused-import
+from cobalt.media_integration_tests.test_app import Features, MediaSessionPlaybackState
+from cobalt.media_integration_tests.test_case import TestCase
+from cobalt.media_integration_tests.test_util import PlaybackUrls
+
+# The variables below are all in seconds.
+STATUS_CHECK_INTERVAL = 0.5
+SINGLE_PLAYBACK_WATCH_TIME_MAXIMUM = 2.0 * 60 * 60
+PLAYER_INITIALIZATION_WAITING_TIMEOUT = 5.0 * 60
+MEDIA_TIME_UPDATE_WAITING_TIMEOUT = 10.0
+WRITTEN_INPUT_WAITING_TIMEOUT = 30.0
+PLAYBACK_END_WAITING_TIMEOUT = 30.0
+# Needs to interact with the app regularly to keep it active. Otherwise, the
+# playback may be paused.
+IDLE_TIME_MAXIMUM = 60 * 60
+
+
+class EnduranceTest(TestCase):
+ """
+ Test case for playback endurance test.
+ """
+
+ def __init__(self, *args, **kwargs):
+ super(EnduranceTest, self).__init__(*args, **kwargs)
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--startup_url', default=PlaybackUrls.PLAYLIST, type=str)
+ # Stop the test after |max_running_time| (in hours). If |max_running_time|
+ # is 0 or negative, the test will not stop until it gets an error.
+ parser.add_argument('--max_running_time', default=20, type=float)
+ # Insert random actions if |random_action_interval| is greater than 0.
+ parser.add_argument('--random_action_interval', default=0.0, type=float)
+ args, _ = parser.parse_known_args()
+
+ self.startup_url = args.startup_url
+ # Convert |max_running_time| to seconds.
+ self.max_running_time = args.max_running_time * 60 * 60
+ self.needs_random_action = args.random_action_interval > 0
+ self.random_action_interval = args.random_action_interval
+
+ def ResetTimestamps(self):
+ self.last_media_time = -1
+ self.last_media_time_update_time = -1
+ self.last_written_audio_timestamp = -1
+ self.last_written_audio_update_time = -1
+ self.last_written_video_timestamp = -1
+ self.last_written_video_update_time = -1
+ self.audio_eos_written_time = -1
+ self.video_eos_written_time = -1
+ self.playback_end_time = -1
+
+ def OnPlayerStateChanged(self, player_state_handler, player_state):
+ current_running_time = time.time() - self.start_time
+ element_state = player_state.video_element_state
+ pipeline_state = player_state.pipeline_state
+ media_session_state = player_state.media_session_state
+
+ if not player_state_handler.IsPlayerPlaying():
+ return
+
+ # Reset timestamps after player identifier is changed.
+ if self.player_identifier != pipeline_state.identifier:
+ self.player_identifier = pipeline_state.identifier
+ self.playback_start_time = current_running_time
+ self.ResetTimestamps()
+
+ # Reset timestamps after resume. It could happen after pause, fastforward,
+ # rewind and playback switch.
+ if media_session_state.playback_state != MediaSessionPlaybackState.PLAYING:
+ self.playback_is_playing = False
+ elif not self.playback_is_playing:
+ self.playback_is_playing = True
+ self.ResetTimestamps()
+
+ # Update media time.
+ if self.last_media_time != element_state.current_time:
+ self.last_media_time = element_state.current_time
+ self.last_media_time_update_time = current_running_time
+
+ # Update written audio timestamp.
+ if (self.last_written_audio_timestamp !=
+ pipeline_state.last_written_audio_timestamp):
+ self.last_written_audio_timestamp = (
+ pipeline_state.last_written_audio_timestamp)
+ self.last_written_audio_update_time = current_running_time
+
+ # Update written video timestamp.
+ if (self.last_written_video_timestamp !=
+ pipeline_state.last_written_video_timestamp):
+ self.last_written_video_timestamp = (
+ pipeline_state.last_written_video_timestamp)
+ self.last_written_video_update_time = current_running_time
+
+ # Update audio eos timestamp.
+ if (self.audio_eos_written_time == -1 and
+ pipeline_state.is_audio_eos_written):
+ self.audio_eos_written_time = current_running_time
+
+ # Update video eos timestamp.
+ if (self.video_eos_written_time == -1 and
+ pipeline_state.is_video_eos_written):
+ self.video_eos_written_time = current_running_time
+
+ # Update playback end time.
+ if (self.audio_eos_written_time != -1 and
+ self.video_eos_written_time != -1 and self.playback_end_time == -1 and
+ element_state.current_time >= element_state.duration):
+ self.playback_end_time = current_running_time
+
+ def GenerateErrorString(self, err_msg):
+ return (
+ '%s (running time: %f, player identifier: "%s", is playing: %r, '
+ 'playback start time: %f, last media time: %f (updated at %f), '
+ 'last written audio timestamp: %d (updated at %f), '
+ 'last written video timestamp: %d (updated at %f), '
+ 'audio eos written at %f, video eos written at %f, '
+ 'playback ended at %f).' %
+ (err_msg, time.time() - self.start_time, self.player_identifier,
+ self.playback_is_playing, self.playback_start_time,
+ self.last_media_time, self.last_media_time_update_time,
+ self.last_written_audio_timestamp, self.last_written_audio_update_time,
+ self.last_written_video_timestamp, self.last_written_video_update_time,
+ self.audio_eos_written_time, self.video_eos_written_time,
+ self.playback_end_time))
+
+ def SendRandomAction(self, app):
+
+ def SuspendAndResume(app):
+ app.Suspend()
+ app.WaitUntilReachState(
+ lambda _app: not _app.ApplicationState().is_visible)
+ # Wait for 1 second before resume.
+ time.sleep(1)
+ app.Resume()
+
+ actions = {
+ 'PlayPause': lambda _app: _app.PlayOrPause(),
+ 'Fastforward': lambda _app: _app.Fastforward(),
+ 'Rewind': lambda _app: _app.Rewind(),
+ 'PlayPrevious': lambda _app: _app.PlayPrevious(),
+ 'PlayNext': lambda _app: _app.PlayNext(),
+ }
+ if TestCase.IsFeatureSupported(Features.SUSPEND_AND_RESUME):
+ actions['SuspendAndResume'] = SuspendAndResume
+
+ action_name = random.choice(list(actions.keys()))
+ logging.info('Send random action (%s).', action_name)
+ actions[action_name](app)
+
+ def test_playback_endurance(self):
+ self.start_time = time.time()
+ self.player_identifier = ''
+ self.playback_is_playing = False
+ self.playback_start_time = -1
+ self.last_state_check_time = 0
+ self.last_action_time = 0
+ self.ResetTimestamps()
+
+ app = self.CreateCobaltApp(self.startup_url)
+ app.AddPlayerStateChangeHandler(self.OnPlayerStateChanged)
+ with app:
+ while True:
+ # Put the sleep at the top of the loop.
+ time.sleep(STATUS_CHECK_INTERVAL)
+
+ current_running_time = time.time() - self.start_time
+ # Stop the test after reaching max running time.
+ if (self.max_running_time > 0 and
+ current_running_time > self.max_running_time):
+ break
+ # Skip if there's no running player.
+ if not app.player_state_handler.IsPlayerPlaying():
+ # TODO: identify network problem
+ self.assertTrue(
+ current_running_time - self.last_state_check_time <
+ PLAYER_INITIALIZATION_WAITING_TIMEOUT,
+ self.GenerateErrorString(
+ 'Timed out waiting for player initialization (waited: %f).' %
+ (current_running_time - self.last_state_check_time)))
+ continue
+ self.last_state_check_time = current_running_time
+ # Skip to next playback if it has been played for long time.
+ if (self.playback_start_time != -1 and
+ current_running_time - self.playback_start_time >
+ SINGLE_PLAYBACK_WATCH_TIME_MAXIMUM):
+ app.PlayNext()
+ # Set start time to -1 here to avoid send same command in next loop.
+ self.playback_start_time = -1
+ continue
+ if self.playback_is_playing:
+ # Check media time.
+ if self.last_media_time_update_time > 0:
+ # TODO: identify network problem
+ self.assertTrue(
+ current_running_time - self.last_media_time_update_time <
+ MEDIA_TIME_UPDATE_WAITING_TIMEOUT,
+ self.GenerateErrorString(
+ 'Timed out waiting for media time update (waited: %f).' %
+ (current_running_time - self.last_media_time_update_time)))
+ # Check written audio timestamp.
+ if (self.last_written_audio_update_time > 0 and
+ self.audio_eos_written_time == -1):
+ self.assertTrue(
+ current_running_time - self.last_written_audio_update_time <
+ WRITTEN_INPUT_WAITING_TIMEOUT,
+ self.GenerateErrorString(
+ 'Timed out waiting for new audio input (waited: %f).' %
+ (current_running_time -
+ self.last_written_audio_update_time)))
+ # Check written video timestamp.
+ if (self.last_written_video_update_time > 0 and
+ self.video_eos_written_time == -1):
+ self.assertTrue(
+ current_running_time - self.last_written_video_update_time <
+ WRITTEN_INPUT_WAITING_TIMEOUT,
+ self.GenerateErrorString(
+ 'Timed out waiting for new video input (waited: %f).' %
+ (current_running_time -
+ self.last_written_video_update_time)))
+ # Check if the playback ends properly.
+ if (self.audio_eos_written_time > 0 and
+ self.video_eos_written_time > 0 and self.playback_end_time > 0):
+ self.assertTrue(
+ current_running_time - self.playback_end_time <
+ PLAYBACK_END_WAITING_TIMEOUT,
+ self.GenerateErrorString(
+ 'Timed out waiting for playback to end (waited: %f).' %
+ (current_running_time - self.playback_end_time,)))
+
+ # Send random actions.
+ if (self.needs_random_action and
+ current_running_time - self.last_action_time >
+ self.random_action_interval):
+ self.SendRandomAction(app)
+ self.last_action_time = current_running_time
+
+ # Interact with the app to keep it active.
+ if current_running_time - self.last_action_time > IDLE_TIME_MAXIMUM:
+ # Play the previous playback again.
+ app.PlayPrevious()
+ self.last_action_time = current_running_time
+
+ app.RemovePlayerStateChangeHandler(self.OnPlayerStateChanged)
diff --git a/src/cobalt/media_integration_tests/functionality/__init__.py b/src/cobalt/media_integration_tests/functionality/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/cobalt/media_integration_tests/functionality/__init__.py
diff --git a/src/cobalt/media_integration_tests/functionality/_env.py b/src/cobalt/media_integration_tests/functionality/_env.py
new file mode 100644
index 0000000..a9d83bf
--- /dev/null
+++ b/src/cobalt/media_integration_tests/functionality/_env.py
@@ -0,0 +1,26 @@
+#
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+ print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+ sys.exit(1)
+load_source('', _ENV)
diff --git a/src/cobalt/media_integration_tests/functionality/general_playback.py b/src/cobalt/media_integration_tests/functionality/general_playback.py
new file mode 100644
index 0000000..6edc0ca
--- /dev/null
+++ b/src/cobalt/media_integration_tests/functionality/general_playback.py
@@ -0,0 +1,60 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+""" Tests for general playbacks with different formats."""
+
+import logging
+
+import _env # pylint: disable=unused-import
+from cobalt.media_integration_tests.test_case import TestCase
+from cobalt.media_integration_tests.test_util import MimeStrings, PlaybackUrls
+
+
+class GeneralPlaybackTest(TestCase):
+ """
+ Test cases for general playbacks.
+ """
+
+ def run_test(self, url, mime=None):
+ app = self.CreateCobaltApp(url)
+ with app:
+ # Skip the test if the mime is not supported.
+ if mime and not app.IsMediaTypeSupported(mime):
+ logging.info('Mime type (%s) is not supported. Skip the test.', mime)
+ return
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for 10 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 10)
+
+
+TEST_PARAMETERS = [
+ ('H264', PlaybackUrls.H264_ONLY, None),
+ ('PROGRESSIVE', PlaybackUrls.PROGRESSIVE, None),
+ ('ENCRYPTED', PlaybackUrls.ENCRYPTED, None),
+ ('VR', PlaybackUrls.VR, None),
+ ('VP9', PlaybackUrls.VP9, MimeStrings.VP9),
+ ('VP9_HFR', PlaybackUrls.VP9_HFR, MimeStrings.VP9_HFR),
+ ('AV1', PlaybackUrls.AV1, MimeStrings.AV1),
+ ('AV1_HFR', PlaybackUrls.AV1_HFR, MimeStrings.AV1_HFR),
+ ('VERTICAL', PlaybackUrls.VERTICAL, None),
+ ('SHORT', PlaybackUrls.SHORT, None),
+ ('VP9_HDR_HLG', PlaybackUrls.VP9_HDR_HLG, MimeStrings.VP9_HDR_HLG),
+ ('VP9_HDR_PQ', PlaybackUrls.VP9_HDR_PQ, MimeStrings.VP9_HDR_PQ),
+ ('HDR_PQ_HFR', PlaybackUrls.HDR_PQ_HFR, MimeStrings.VP9_HDR_PQ_HFR),
+]
+
+for name, playback_url, mime_str in TEST_PARAMETERS:
+ TestCase.CreateTest(GeneralPlaybackTest, name, GeneralPlaybackTest.run_test,
+ playback_url, mime_str)
diff --git a/src/cobalt/media_integration_tests/functionality/live_playback.py b/src/cobalt/media_integration_tests/functionality/live_playback.py
new file mode 100644
index 0000000..7390116
--- /dev/null
+++ b/src/cobalt/media_integration_tests/functionality/live_playback.py
@@ -0,0 +1,57 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+""" Tests for live playbacks."""
+
+import time
+
+import _env # pylint: disable=unused-import
+from cobalt.media_integration_tests.test_case import TestCase
+from cobalt.media_integration_tests.test_util import PlaybackUrls
+
+
+class LivePlaybackTest(TestCase):
+ """
+ Test cases for live playbacks.
+ """
+
+ def run_test(self, url):
+ app = self.CreateCobaltApp(url)
+ with app:
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for 5 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 5)
+ # Pause the playback and wait for some time.
+ app.PlayOrPause()
+ app.WaitUntilReachState(
+ lambda _app: _app.PlayerState().video_element_state.paused)
+ time.sleep(2)
+ # Resume the playback.
+ app.PlayOrPause()
+ app.WaitUntilReachState(
+ lambda _app: not _app.PlayerState().video_element_state.paused)
+ # Let the playback play for another 5 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 5)
+
+
+TEST_PARAMETERS = [
+ ('LIVE', PlaybackUrls.LIVE),
+ ('LIVE_ULL', PlaybackUrls.LIVE_ULL),
+ ('LIVE_VR', PlaybackUrls.LIVE_VR),
+]
+
+for name, playback_url in TEST_PARAMETERS:
+ TestCase.CreateTest(LivePlaybackTest, name, LivePlaybackTest.run_test,
+ playback_url)
diff --git a/src/cobalt/media_integration_tests/functionality/playback_controls.py b/src/cobalt/media_integration_tests/functionality/playback_controls.py
new file mode 100644
index 0000000..13b1ec8
--- /dev/null
+++ b/src/cobalt/media_integration_tests/functionality/playback_controls.py
@@ -0,0 +1,101 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+""" Tests for playback control shortcuts."""
+
+import logging
+
+import _env # pylint: disable=unused-import
+from cobalt.media_integration_tests.test_case import TestCase
+from cobalt.media_integration_tests.test_util import PlaybackUrls
+
+
+class PlaybackControlsTest(TestCase):
+ """
+ Test cases for playback control shortcuts.
+ """
+
+ def test_play_pause(self):
+ app = self.CreateCobaltApp(PlaybackUrls.H264_ONLY)
+ with app:
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for 2 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
+ # Pause the playback.
+ app.PlayOrPause()
+ app.WaitUntilReachState(
+ lambda _app: _app.PlayerState().video_element_state.paused)
+ # Resume the playback.
+ app.PlayOrPause()
+ app.WaitUntilReachState(
+ lambda _app: not _app.PlayerState().video_element_state.paused)
+ # Let the playback play for another 2 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
+
+ def test_rewind_fastforward(self):
+ app = self.CreateCobaltApp(PlaybackUrls.H264_ONLY)
+ with app:
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for 2 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
+
+ logging.info('Fast forward the playback.')
+ old_media_time = app.CurrentMediaTime()
+ # Fastforward the playback by 10 seconds.
+ app.Fastforward()
+ app.WaitUntilReachState(
+ lambda _app: _app.CurrentMediaTime() > old_media_time + 10)
+ # Let the playback play for another 2 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
+
+ logging.info('Rewind the playback.')
+ old_media_time = app.CurrentMediaTime()
+ # Rewind the playback by 10 seconds.
+ app.Rewind()
+ app.WaitUntilReachState(
+ lambda _app: _app.CurrentMediaTime() < old_media_time)
+ # Let the playback play for another 2 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
+
+ def test_play_next_and_previous(self):
+ app = self.CreateCobaltApp(PlaybackUrls.PLAYLIST)
+ with app:
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for 2 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
+ # Play next track.
+ logging.info('Play next playback.')
+ app.PlayNext()
+ # Wait until the player is destroyed.
+ app.WaitUntilPlayerDestroyed()
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for another 2 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
+ # Play previous track.
+ logging.info('Play previous playback.')
+ app.PlayPrevious()
+ # Wait until the player is destroyed.
+ app.WaitUntilPlayerDestroyed()
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for another 2 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
diff --git a/src/cobalt/media_integration_tests/functionality/suspend_resume.py b/src/cobalt/media_integration_tests/functionality/suspend_resume.py
new file mode 100644
index 0000000..5b7de5e
--- /dev/null
+++ b/src/cobalt/media_integration_tests/functionality/suspend_resume.py
@@ -0,0 +1,74 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+""" Tests for suspend and resume during playing."""
+
+import logging
+import time
+
+import _env # pylint: disable=unused-import
+from cobalt.media_integration_tests.test_app import Features
+from cobalt.media_integration_tests.test_case import TestCase
+from cobalt.media_integration_tests.test_util import MimeStrings, PlaybackUrls
+
+
+class SuspendResumeTest(TestCase):
+ """
+ Test cases for suspend and resume.
+ """
+
+ def run_test(self, url, mime=None):
+ app = self.CreateCobaltApp(url)
+ with app:
+ # Skip the test if the mime is not supported.
+ if mime and not app.IsMediaTypeSupported(mime):
+ logging.info('Mime type (%s) is not supported. Skip the test.', mime)
+ return
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for 2 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
+ # Suspend the app and wait the app enters background.
+ logging.info('Suspend the application.')
+ app.Suspend()
+ app.WaitUntilReachState(
+ lambda _app: not _app.ApplicationState().is_visible)
+ # Wait for 1 second before resume.
+ time.sleep(1)
+ # Resume the app and let it play for a few time.
+ logging.info('Resume the application.')
+ app.Resume()
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for 2 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 2)
+
+
+TEST_PARAMETERS = [
+ ('H264', PlaybackUrls.H264_ONLY, None),
+ ('PROGRESSIVE', PlaybackUrls.PROGRESSIVE, None),
+ ('ENCRYPTED', PlaybackUrls.ENCRYPTED, None),
+ ('LIVE', PlaybackUrls.LIVE, None),
+ ('VP9', PlaybackUrls.VP9, MimeStrings.VP9),
+ ('AV1', PlaybackUrls.AV1, MimeStrings.AV1),
+ ('VERTICAL', PlaybackUrls.VERTICAL, None),
+ ('VR', PlaybackUrls.VR, None),
+]
+
+if not TestCase.IsFeatureSupported(Features.SUSPEND_AND_RESUME):
+ logging.info('Suspend and resume is not supported on this platform.')
+else:
+ for name, playback_url, mime_str in TEST_PARAMETERS:
+ TestCase.CreateTest(SuspendResumeTest, name, SuspendResumeTest.run_test,
+ playback_url, mime_str)
diff --git a/src/cobalt/media_integration_tests/media_integration_tests_runner.py b/src/cobalt/media_integration_tests/media_integration_tests_runner.py
new file mode 100644
index 0000000..7db2ab2
--- /dev/null
+++ b/src/cobalt/media_integration_tests/media_integration_tests_runner.py
@@ -0,0 +1,143 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""The media integration test runner.
+
+Usage:
+ To run all tests of a specific type:
+ "python media_integration_tests_runner.py
+ -p android-arm64 -c devel -w noinstall -log-level info
+ --type functionality"
+ To run a specific test (suspend_resume):
+ python media_integration_tests_runner.py
+ -p android-arm64 -c devel -w noinstall -log-level info
+ --type functionality --test_name suspend_resume"
+
+"""
+
+import argparse
+import importlib
+import logging
+import pkgutil
+import sys
+import unittest
+
+import _env # pylint: disable=unused-import
+from cobalt.media_integration_tests.test_app import Features
+from cobalt.media_integration_tests.test_case import SetLauncherParams, SetSupportedFeatures
+from starboard.tools import abstract_launcher
+from starboard.tools import command_line
+from starboard.tools import log_level
+
+# Location of test files.
+_TESTS_PATH = {
+ 'functionality': 'cobalt.media_integration_tests.functionality',
+ 'endurance': 'cobalt.media_integration_tests.endurance',
+ 'performance': 'cobalt.media_integration_tests.performance'
+}
+
+
+def GetSupportedFeatures(launcher_params):
+ launcher = abstract_launcher.LauncherFactory(
+ launcher_params.platform,
+ 'cobalt',
+ launcher_params.config,
+ device_id=launcher_params.device_id,
+ target_params=None,
+ output_file=None,
+ out_directory=launcher_params.out_directory,
+ loader_platform=launcher_params.loader_platform,
+ loader_config=launcher_params.loader_config,
+ loader_out_directory=launcher_params.loader_out_directory)
+ return {
+ Features.SUSPEND_AND_RESUME: launcher.SupportsSystemSuspendResume(),
+ Features.SEND_KEYS: True,
+ }
+
+
+def GetTestPackagePath(test_type):
+ if _TESTS_PATH.has_key(test_type):
+ return _TESTS_PATH[test_type]
+ return None
+
+
+def GetAllTestNamesInPackage(package_path):
+ test_names = []
+ loader = pkgutil.get_loader(package_path)
+ for sub_module in pkgutil.walk_packages([loader.filename]):
+ _, sub_module_name, _ = sub_module
+ # Filter '_env' and '__init__'.
+ if sub_module_name[0] == '_':
+ continue
+ if not sub_module_name in test_names:
+ test_names.append(sub_module_name)
+ return test_names
+
+
+def LoadAllTests(package_path, all_test_names):
+ test_suite = unittest.TestSuite()
+ for test_name in all_test_names:
+ module = importlib.import_module(package_path + '.' + test_name)
+ test_suite.addTest(unittest.TestLoader().loadTestsFromModule(module))
+ return test_suite
+
+
+def main():
+ # TODO: Support filters.
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--type',
+ required=True,
+ type=str,
+ help=('Type of the tests to run. It must be functionality, endurance or'
+ ' performance.'))
+ parser.add_argument(
+ '--test_name',
+ default=None,
+ type=str,
+ help=('Name of test to run. If not specified, it will run all tests in'
+ 'the directory.'))
+ args, _ = parser.parse_known_args()
+
+ package_path = GetTestPackagePath(args.type)
+ if package_path is None:
+ logging.error('{%s} is not a valid test type.', args.type)
+ return 2
+
+ all_test_names = GetAllTestNamesInPackage(package_path)
+ specified_test_name = args.test_name
+ if specified_test_name is not None:
+ if not specified_test_name in all_test_names:
+ logging.error('{%s} is not a valid test name.', specified_test_name)
+ return 2
+ else:
+ all_test_names = [specified_test_name]
+
+ log_level.InitializeLogging(args)
+ launcher_params = command_line.CreateLauncherParams()
+ supported_features = GetSupportedFeatures(launcher_params)
+
+ # Update global variables in test case.
+ SetLauncherParams(launcher_params)
+ SetSupportedFeatures(supported_features)
+
+ unittest.installHandler()
+
+ test_suite = LoadAllTests(package_path, all_test_names)
+
+ return not unittest.TextTestRunner(
+ verbosity=0, stream=sys.stdout).run(test_suite).wasSuccessful()
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/src/cobalt/media_integration_tests/performance/__init__.py b/src/cobalt/media_integration_tests/performance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/cobalt/media_integration_tests/performance/__init__.py
diff --git a/src/cobalt/media_integration_tests/performance/_env.py b/src/cobalt/media_integration_tests/performance/_env.py
new file mode 100644
index 0000000..a9d83bf
--- /dev/null
+++ b/src/cobalt/media_integration_tests/performance/_env.py
@@ -0,0 +1,26 @@
+#
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Ask the parent directory to load the project environment."""
+
+from imp import load_source
+from os import path
+import sys
+
+_ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
+if not path.exists(_ENV):
+ print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+ sys.exit(1)
+load_source('', _ENV)
diff --git a/src/cobalt/media_integration_tests/performance/codec_capability.py b/src/cobalt/media_integration_tests/performance/codec_capability.py
new file mode 100644
index 0000000..c117fd2
--- /dev/null
+++ b/src/cobalt/media_integration_tests/performance/codec_capability.py
@@ -0,0 +1,78 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+""" Tests of platform codec capabilities."""
+
+import logging
+
+import _env # pylint: disable=unused-import
+from cobalt.media_integration_tests.test_case import TestCase
+from cobalt.media_integration_tests.test_util import MimeStrings, PlaybackUrls
+
+
+class CodecCapabilityTest(TestCase):
+ """
+ Test cases for platform codec capabilities.
+ """
+
+ # Returns a string which shows the max supported resolution, or "n/a" if the
+ # codec is not supported.
+ @staticmethod
+ def run_video_codec_test(app, codec_name, codec_mime):
+ if app.IsMediaTypeSupported(codec_mime):
+ for res_name, _ in reversed(MimeStrings.RESOLUTIONS.items()):
+ if app.IsMediaTypeSupported(
+ MimeStrings.create_video_mime_string(codec_mime, res_name)):
+ return '[%s, %s]' % (codec_name, res_name)
+ return '[%s, n/a]' % (codec_name)
+
+ # Returns a string which shows the max supported channels, or "n/a" if the
+ # codec is not supported.
+ @staticmethod
+ def run_audio_codec_test(app, codec_name, codec_mime):
+ if app.IsMediaTypeSupported(codec_mime):
+ for channels in [6, 4, 2]:
+ if app.IsMediaTypeSupported(
+ MimeStrings.create_audio_mime_string(codec_mime, channels)):
+ return '[%s, %s]' % (codec_name, channels)
+ return '[%s, n/a]' % (codec_name)
+
+ def test_video_codec_capability(self):
+ app = self.CreateCobaltApp(PlaybackUrls.DEFAULT)
+ mimes = [
+ ('H264', MimeStrings.H264),
+ ('VP9', MimeStrings.VP9),
+ ('VP9_HFR', MimeStrings.VP9_HFR),
+ ('AV1', MimeStrings.AV1),
+ ('AV1_HFR', MimeStrings.AV1_HFR),
+ ('VP9_HDR_HLG', MimeStrings.VP9_HDR_HLG),
+ ('VP9_HDR_PQ', MimeStrings.VP9_HDR_PQ),
+ ('VP9_HDR_PQ_HFR', MimeStrings.VP9_HDR_PQ_HFR),
+ ]
+ result = [' ***** Video codec capability test results: ']
+ with app:
+ for mime_name, mime_str in mimes:
+ result.append(self.run_video_codec_test(app, mime_name, mime_str))
+ logging.info(', '.join(result))
+
+ def test_audio_codec_capability(self):
+ app = self.CreateCobaltApp(PlaybackUrls.DEFAULT)
+ mimes = [
+ ('AAC', MimeStrings.AAC),
+ ('OPUS', MimeStrings.OPUS),
+ ]
+ result = [' ***** Audio codec capability test results: ']
+ with app:
+ for mime_name, mime_str in mimes:
+ result.append(self.run_audio_codec_test(app, mime_name, mime_str))
+ logging.info(', '.join(result))
diff --git a/src/cobalt/media_integration_tests/performance/dropped_frames.py b/src/cobalt/media_integration_tests/performance/dropped_frames.py
new file mode 100644
index 0000000..89f426c
--- /dev/null
+++ b/src/cobalt/media_integration_tests/performance/dropped_frames.py
@@ -0,0 +1,75 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+""" Performance tests of dropped frames."""
+
+import argparse
+import logging
+
+import _env # pylint: disable=unused-import
+from cobalt.media_integration_tests.test_case import TestCase
+from cobalt.media_integration_tests.test_util import MimeStrings, PlaybackUrls
+
+
+class DroppedFrameTest(TestCase):
+ """
+ Test cases for playback dropped frames.
+ """
+
+ def __init__(self, *args, **kwargs):
+ super(DroppedFrameTest, self).__init__(*args, **kwargs)
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--test_times', default=5, type=int)
+ args, _ = parser.parse_known_args()
+ self.test_times = args.test_times
+
+ def run_test_with_url(self, test_name, url, mime=None):
+ test_results = []
+ for _ in range(self.test_times):
+ app = self.CreateCobaltApp(url)
+ with app:
+ # Skip the test if the mime is not supported.
+ if mime and not app.IsMediaTypeSupported(mime):
+ logging.info(' ***** Dropped frame test result: [%s, n/a]', test_name)
+ return
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for 10 seconds.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 10)
+
+ total_frames = app.PlayerState().video_element_state.total_video_frames
+ dropped_frames = app.PlayerState(
+ ).video_element_state.dropped_video_frames
+ dropped_ratio = dropped_frames * 1.0 / total_frames
+ test_results.append([dropped_ratio, dropped_frames, total_frames])
+ logging.info(' ***** Dropped frame test result of %s : %r.', test_name,
+ test_results)
+
+
+TEST_PARAMETERS = [
+ ('H264', PlaybackUrls.H264_ONLY, MimeStrings.H264),
+ ('VP9', PlaybackUrls.VP9, MimeStrings.VP9),
+ ('VP9_HFR', PlaybackUrls.VP9_HFR, MimeStrings.VP9_HFR),
+ ('AV1', PlaybackUrls.AV1, MimeStrings.AV1),
+ ('AV1_HFR', PlaybackUrls.AV1_HFR, MimeStrings.AV1_HFR),
+ ('VP9_HDR_HLG', PlaybackUrls.VP9_HDR_HLG, MimeStrings.VP9_HDR_HLG),
+ ('VP9_HDR_PQ', PlaybackUrls.VP9_HDR_PQ, MimeStrings.VP9_HDR_PQ),
+ ('VP9_HDR_PQ_HFR', PlaybackUrls.HDR_PQ_HFR, MimeStrings.VP9_HDR_PQ_HFR),
+]
+
+for name, playback_url, mime_str in TEST_PARAMETERS:
+ TestCase.CreateTest(DroppedFrameTest, name,
+ DroppedFrameTest.run_test_with_url, name, playback_url,
+ mime_str)
diff --git a/src/cobalt/media_integration_tests/performance/start_latency.py b/src/cobalt/media_integration_tests/performance/start_latency.py
new file mode 100644
index 0000000..62b32f1
--- /dev/null
+++ b/src/cobalt/media_integration_tests/performance/start_latency.py
@@ -0,0 +1,157 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+""" Performance tests of playback operation latency."""
+
+import argparse
+import logging
+import time
+
+import _env # pylint: disable=unused-import
+from cobalt.media_integration_tests.test_app import AdsState
+from cobalt.media_integration_tests.test_case import TestCase
+from cobalt.media_integration_tests.test_util import MimeStrings, PlaybackUrls
+
+
+class StartLatencyTest(TestCase):
+ """
+ Test cases for playback operation latency.
+ """
+
+ def __init__(self, *args, **kwargs):
+ super(StartLatencyTest, self).__init__(*args, **kwargs)
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--test_times', default=5, type=int)
+ args, _ = parser.parse_known_args()
+ self.test_times = args.test_times
+
+ def run_first_start_latency_test(self, test_name, url, mime=None):
+ test_results = []
+ while len(test_results) < self.test_times:
+ app = self.CreateCobaltApp(url)
+ with app:
+ # Skip the test if the mime is not supported.
+ if mime and not app.IsMediaTypeSupported(mime):
+ logging.info(' ***** First start latency test result of %s : [n/a].',
+ test_name)
+ return
+ app.WaitUntilPlayerStart()
+ start_time = time.time()
+ start_media_time = app.CurrentMediaTime()
+ app.WaitUntilReachState(lambda _app: _app.PlayerState(
+ ).pipeline_state.first_written_video_timestamp != _app.PlayerState().
+ pipeline_state.last_written_video_timestamp)
+ # Record inputs received time to know the preliminary impact of network.
+ inputs_received_time = time.time()
+ inputs_received_latency = inputs_received_time - start_time
+ # Let the playback play for 1 second.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 1)
+
+ # If it's playing ads, wait until ads end and run the test again.
+ if app.GetAdsState() != AdsState.NONE:
+ app.WaitUntilAdsEnd()
+ continue
+ media_time = app.CurrentMediaTime() - start_media_time
+ end_time = time.time()
+ start_latency = end_time - start_time - media_time
+ test_results.append([
+ start_latency, inputs_received_latency, start_time,
+ inputs_received_time, media_time, end_time
+ ])
+ logging.info(' ***** First start latency test result of %s : %r.',
+ test_name, test_results)
+
+ def run_play_pause_latency_test(self, test_name, url, mime=None):
+ app = self.CreateCobaltApp(url)
+ with app:
+ # Skip the test if the mime is not supported.
+ if mime and not app.IsMediaTypeSupported(mime):
+ logging.info(' ***** Play/pause latency test result of %s : [n/a].',
+ test_name)
+ return
+ test_results = []
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ while len(test_results) < self.test_times:
+ # Let the playback play for 1 second.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 1)
+ start_time = time.time()
+ # Pause the playback.
+ app.PlayOrPause()
+ app.WaitUntilReachState(
+ lambda _app: _app.PlayerState().video_element_state.paused)
+ pause_latency = time.time() - start_time
+
+ start_time = time.time()
+ # Resume the playback.
+ app.PlayOrPause()
+ app.WaitUntilReachState(
+ lambda _app: not _app.PlayerState().video_element_state.paused)
+
+ play_latency = time.time() - start_time
+ test_results.append([play_latency, pause_latency])
+ logging.info(' ***** Play/pause latency test result of %s : %r.',
+ test_name, test_results)
+
+ def run_fastforward_latency_test(self, test_name, url, mime=None):
+ app = self.CreateCobaltApp(url)
+ with app:
+ # Skip the test if the mime is not supported.
+ if mime and not app.IsMediaTypeSupported(mime):
+ logging.info(' ***** Fastforward latency test result of %s : [n/a].',
+ test_name)
+ return
+ test_results = []
+ # Wait until the playback starts.
+ app.WaitUntilPlayerStart()
+ app.WaitUntilAdsEnd()
+ # Let the playback play for 1 second.
+ app.WaitUntilMediaTimeReached(app.CurrentMediaTime() + 1)
+ while len(test_results) < self.test_times:
+ old_media_time = app.CurrentMediaTime()
+ start_time = time.time()
+ app.Fastforward()
+ app.WaitUntilReachState(lambda _app: _app.PlayerState(
+ ).pipeline_state.first_written_video_timestamp != _app.PlayerState().
+ pipeline_state.last_written_video_timestamp)
+ # Record inputs received time to know the preliminary impact of network.
+ inputs_received_latency = time.time() - start_time
+ app.WaitUntilReachState(
+ lambda _app: _app.CurrentMediaTime() > old_media_time + 10)
+ fastforward_latency = time.time() - start_time
+ test_results.append([fastforward_latency, inputs_received_latency])
+
+ logging.info(' ***** Fastforward latency test result of %s : %r.',
+ test_name, test_results)
+
+
+TEST_PARAMETERS = [
+ ('H264', PlaybackUrls.H264_ONLY, MimeStrings.H264),
+ ('VP9', PlaybackUrls.VP9, MimeStrings.VP9),
+ ('AV1', PlaybackUrls.AV1, MimeStrings.AV1),
+ ('VP9_HDR_HLG', PlaybackUrls.VP9_HDR_HLG, MimeStrings.VP9_HDR_HLG),
+ ('VP9_HDR_PQ', PlaybackUrls.VP9_HDR_PQ, MimeStrings.VP9_HDR_PQ),
+]
+
+for name, playback_url, mime_str in TEST_PARAMETERS:
+ TestCase.CreateTest(StartLatencyTest, 'first_start_latency_%s' % (name),
+ StartLatencyTest.run_first_start_latency_test, name,
+ playback_url, mime_str)
+ TestCase.CreateTest(StartLatencyTest, 'play_pause_latency_%s' % (name),
+ StartLatencyTest.run_play_pause_latency_test, name,
+ playback_url, mime_str)
+ TestCase.CreateTest(StartLatencyTest, 'fastforward_latency_%s' % (name),
+ StartLatencyTest.run_fastforward_latency_test, name,
+ playback_url, mime_str)
diff --git a/src/cobalt/media_integration_tests/test_app.py b/src/cobalt/media_integration_tests/test_app.py
new file mode 100644
index 0000000..8503dd5
--- /dev/null
+++ b/src/cobalt/media_integration_tests/test_app.py
@@ -0,0 +1,855 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""A module to support fundamental communications with Cobalt application."""
+
+import json
+import logging
+import threading
+import time
+
+import _env # pylint: disable=unused-import
+from cobalt.tools.automated_testing import cobalt_runner
+from cobalt.tools.automated_testing import webdriver_utils
+
+webdriver_keys = webdriver_utils.import_selenium_module('webdriver.common.keys')
+
+PERIODIC_QUERIES_INTERVAL_SECONDS = 0.1
+THREAD_EXIT_TIMEOUT_SECONDS = 10
+WAIT_INTERVAL_SECONDS = 0.5
+WAIT_UNTIL_REACH_STATE_DEFAULT_TIMEOUT_SECONDS = 30
+WAIT_UNTIL_ADS_END_DEFAULT_TIMEOUT_SECONDS = 120
+WAIT_UNTIL_MEDIA_TIME_REACHED_DEFAULT_TIMEOUT_SECONDS = 30
+
+ACCOUNT_SELECTOR_ADD_ACCOUNT_TEXT = u'Add account'
+
+
+def GetValueFromQueryResult(query_result, key, default):
+ if query_result is None:
+ return default
+
+ if not isinstance(query_result, dict):
+ return default
+
+ if not query_result.has_key(key):
+ return default
+
+ value = query_result[key]
+ default_value_type = type(default)
+ value_type = type(value)
+
+ if value is None:
+ return default
+
+ if value_type == default_value_type:
+ return value
+
+ if value_type in (int, float) and default_value_type in (int, float):
+ return value
+
+ # CVal returns unicode, so we need to decode them before using.
+ if default_value_type in (bool, int, float):
+ if value_type in (str, unicode):
+ try:
+ # Try to parse numbers and booleans.
+ parsed_value = json.loads(value)
+ except ValueError:
+ raise RuntimeError('Failed to parse query result.')
+
+ return parsed_value
+
+ elif default_value_type is str:
+ if value_type == unicode:
+ return value.encode('utf-8')
+
+ raise NotImplementedError('Convertion from (%s) to (%s) is not suppoted.' %
+ (value_type, default_value_type))
+
+
+class AdditionalKeys():
+ """
+ Set of special keys codes for media control, corresponding to cobalt
+ webdriver AdditionalSpecialKey.
+ """
+ MEDIA_NEXT_TRACK = u'\uf000'
+ MEDIA_PREV_TRACK = u'\uf001'
+ MEDIA_STOP = u'\uf002'
+ MEDIA_PLAY_PAUSE = u'\uf003'
+ MEDIA_REWIND = u'\uf004'
+ MEDIA_FAST_FORWARD = u'\uf005'
+
+
+class Features():
+ """Set of platform features."""
+ SUSPEND_AND_RESUME = 1
+ SEND_KEYS = 2
+
+
+class ApplicationState():
+ """Set of platform features."""
+
+ def __init__(self, query_result=None):
+ if not query_result is None and not isinstance(query_result, dict):
+ raise NotImplementedError
+
+ self.is_visible = GetValueFromQueryResult(query_result, 'is_visible', False)
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ raise NotImplementedError
+ return self.is_visible == other.is_visible
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ @staticmethod
+ def QueryJsCode():
+ return """
+ return {
+ is_visible: document.visibilityState === "visible",
+ }
+ """
+
+
+class ApplicationStateHandler():
+ """The handler of applicatoin state."""
+
+ def __init__(self, app):
+ self.app = app
+ self.state_change_handlers = []
+ self.app_state = ApplicationState()
+
+ self.app.RegisterPeriodicQuery('Application',
+ ApplicationState.QueryJsCode(),
+ self._ApplicatinStateQueryHandler)
+
+ def AddAppStateChangeHandler(self, handler):
+ self.state_change_handlers.append(handler)
+
+ def RemoveAppStateChangeHandler(self, handler):
+ self.state_change_handlers.remove(handler)
+
+ def _NotifyHandlersOnStateChanged(self):
+ for handler in self.state_change_handlers:
+ handler(self, self.app, self.app_state)
+
+ def _ApplicatinStateQueryHandler(self, app, query_name, query_result):
+ del app, query_name # Unused here.
+ # Note that the callback is invoked on a different thread.
+ new_state = ApplicationState(query_result)
+ if self.app_state != new_state:
+ self.app_state = new_state
+ self._NotifyHandlersOnStateChanged()
+
+
+class PipelinePlayerState(int):
+ """Set of pipeline states, equals to SbPlayerState."""
+ INITIALIZED = 0 # kSbPlayerStateInitialized
+ PREROLLING = 1 # kSbPlayerStatePrerolling
+ PRESENTING = 2 # kSbPlayerStatePresenting
+ ENDOFSTREAM = 3 # kSbPlayerStateEndOfStream
+ DESTROYED = 4 # kSbPlayerStateDestroyed
+
+
+class PipelineState():
+ """
+ The states of SbPlayerPipeline. We update the states periodically via
+ webdriver and logs. Note that the duration and timestamps are in
+ milliseconds.
+ """
+
+ def __init__(self, query_result=None):
+ # If there's no player existing, the query return unicode code "null".
+ if (not query_result is None and not query_result == u'null' and
+ not isinstance(query_result, dict)):
+ raise NotImplementedError
+
+ self.identifier = GetValueFromQueryResult(query_result, 'identifier', '')
+ self.is_started = GetValueFromQueryResult(query_result, 'is_started', False)
+ self.is_suspended = GetValueFromQueryResult(query_result, 'is_suspended',
+ False)
+ self.is_stopped = GetValueFromQueryResult(query_result, 'is_stopped', False)
+ self.is_ended = GetValueFromQueryResult(query_result, 'is_ended', False)
+ self.player_state = PipelinePlayerState(
+ GetValueFromQueryResult(query_result, 'player_state', 0))
+ self.volume = GetValueFromQueryResult(query_result, 'volume', 1.0)
+ self.playback_rate = GetValueFromQueryResult(query_result, 'playback_rate',
+ 1.0)
+ self.duration = GetValueFromQueryResult(query_result, 'duration', 0)
+ self.last_media_time = GetValueFromQueryResult(query_result,
+ 'last_media_time', 0)
+ self.max_video_capabilities = GetValueFromQueryResult(
+ query_result, 'max_video_capabilities', '')
+ self.seek_time = GetValueFromQueryResult(query_result, 'seek_time', 0)
+ self.first_written_audio_timestamp = GetValueFromQueryResult(
+ query_result, 'first_written_audio_timestamp', 0)
+ self.first_written_video_timestamp = GetValueFromQueryResult(
+ query_result, 'first_written_video_timestamp', 0)
+ self.last_written_audio_timestamp = GetValueFromQueryResult(
+ query_result, 'last_written_audio_timestamp', 0)
+ self.last_written_video_timestamp = GetValueFromQueryResult(
+ query_result, 'last_written_video_timestamp', 0)
+ self.video_width = GetValueFromQueryResult(query_result, 'video_width', 0)
+ self.video_height = GetValueFromQueryResult(query_result, 'video_height', 0)
+ self.is_audio_eos_written = GetValueFromQueryResult(query_result,
+ 'is_audio_eos_written',
+ False)
+ self.is_video_eos_written = GetValueFromQueryResult(query_result,
+ 'is_video_eos_written',
+ False)
+ # If |pipeline_status| is not 0, it indicates there's an error
+ # in the pipeline.
+ self.pipeline_status = GetValueFromQueryResult(query_result,
+ 'pipeline_status', 0)
+ self.error_message = GetValueFromQueryResult(query_result, 'error_message',
+ '')
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ raise NotImplementedError
+ return (self.identifier == other.identifier and
+ self.is_started == other.is_started and
+ self.is_suspended == other.identifier and
+ self.is_stopped == other.identifier and
+ self.is_ended == other.is_ended and
+ self.player_state == other.player_state and
+ self.volume == other.volume and
+ self.playback_rate == other.playback_rate and
+ self.duration == other.duration and
+ self.last_media_time == other.last_media_time and
+ self.max_video_capabilities == other.max_video_capabilities and
+ self.seek_time == other.seek_time and
+ self.first_written_audio_timestamp
+ == other.first_written_audio_timestamp and
+ self.first_written_video_timestamp
+ == other.first_written_video_timestamp and
+ self.last_written_audio_timestamp
+ == other.last_written_audio_timestamp and
+ self.last_written_video_timestamp
+ == other.last_written_video_timestamp and
+ self.video_width == other.video_width and
+ self.video_height == other.video_height and
+ self.is_audio_eos_written == other.is_audio_eos_written and
+ self.is_video_eos_written == other.is_video_eos_written and
+ self.pipeline_status == other.pipeline_status and
+ self.error_message == other.error_message)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ @staticmethod
+ def QueryJsCode():
+ return """
+ const primary_pipeline_keys = h5vcc.cVal.keys().filter(key =>
+ key.startsWith("Media.Pipeline.") &&
+ key.endsWith("MaxVideoCapabilities") &&
+ h5vcc.cVal.getValue(key).length === 0);
+ if (primary_pipeline_keys.length == 0) {
+ return "null";
+ }
+ const key_prefix = primary_pipeline_keys[0].slice(0,
+ -".MaxVideoCapabilities".length);
+ return {
+ identifier: key_prefix.slice("Media.Pipeline.".length),
+ is_started: h5vcc.cVal.getValue(key_prefix + '.Started'),
+ is_suspended: h5vcc.cVal.getValue(key_prefix + '.Suspended'),
+ is_stopped: h5vcc.cVal.getValue(key_prefix + '.Stopped'),
+ is_ended: h5vcc.cVal.getValue(key_prefix + '.Ended'),
+ player_state: h5vcc.cVal.getValue(key_prefix + '.PlayerState'),
+ volume: h5vcc.cVal.getValue(key_prefix + '.Volume'),
+ playback_rate: h5vcc.cVal.getValue(key_prefix + '.PlaybackRate'),
+ duration: h5vcc.cVal.getValue(key_prefix + '.Duration'),
+ last_media_time: h5vcc.cVal.getValue(key_prefix + '.LastMediaTime'),
+ max_video_capabilities: h5vcc.cVal.getValue(
+ key_prefix + '.MaxVideoCapabilities'),
+ seek_time: h5vcc.cVal.getValue(key_prefix + '.SeekTime'),
+ first_written_audio_timestamp:
+ h5vcc.cVal.getValue(key_prefix + '.FirstWrittenAudioTimestamp'),
+ first_written_video_timestamp:
+ h5vcc.cVal.getValue(key_prefix + '.FirstWrittenVideoTimestamp'),
+ last_written_audio_timestamp:
+ h5vcc.cVal.getValue(key_prefix + '.LastWrittenAudioTimestamp'),
+ last_written_video_timestamp:
+ h5vcc.cVal.getValue(key_prefix + '.LastWrittenVideoTimestamp'),
+ video_width: h5vcc.cVal.getValue(key_prefix + '.VideoWidth'),
+ video_height: h5vcc.cVal.getValue(key_prefix + '.VideoHeight'),
+ is_audio_eos_written: h5vcc.cVal.getValue(key_prefix +
+ '.IsAudioEOSWritten'),
+ is_video_eos_written: h5vcc.cVal.getValue(key_prefix +
+ '.IsVideoEOSWritten'),
+ pipeline_status: h5vcc.cVal.getValue(key_prefix + '.PipelineStatus'),
+ error_message: h5vcc.cVal.getValue(key_prefix + '.ErrorMessage'),
+ };
+ """
+
+ def IsPlaying(self):
+ return (len(self.identifier) > 0 and self.is_started and
+ not self.is_ended and not self.is_suspended and not self.is_stopped)
+
+
+class MediaSessionPlaybackState(int):
+ """Set of media session playback states."""
+ NONE = 0
+ PAUSED = 1
+ PLAYING = 2
+
+ @staticmethod
+ def FromString(str):
+ if str == 'none':
+ return 0
+ if str == 'paused':
+ return 1
+ if str == 'playing':
+ return 2
+ raise NotImplementedError(
+ '"%s" is not a valid media session playback state.' % str)
+
+
+class MediaSessionState():
+ """
+ The info we get from HTML MediaSession. We update the states periodically
+ via webdriver.
+ """
+
+ def __init__(self, query_result=None):
+ if not query_result is None and not isinstance(query_result, dict):
+ raise NotImplementedError
+
+ metadata = None
+ if isinstance(query_result, dict) and query_result.has_key('metadata'):
+ metadata = query_result['metadata']
+
+ self.title = GetValueFromQueryResult(metadata, 'title', '')
+ self.artist = GetValueFromQueryResult(metadata, 'artist', '')
+ self.album = GetValueFromQueryResult(metadata, 'album', '')
+ self.playback_state = MediaSessionPlaybackState.FromString(
+ GetValueFromQueryResult(query_result, 'playbackState', 'none'))
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ raise NotImplementedError
+ return (self.title == other.title and self.artist == other.artist and
+ self.album == other.album and
+ self.playback_state == other.playback_state)
+
+ def __ne__(self, other):
+ if self.__eq__(other):
+ return False
+ return True
+
+ @staticmethod
+ def QueryJsCode():
+ return """
+ return {
+ metadata: navigator.mediaSession.metadata,
+ playbackState: navigator.mediaSession.playbackState,
+ };
+ """
+
+
+class VideoElementState():
+ """
+ The info we get from HTML video element. We update the states periodically
+ via webdriver.
+ """
+
+ def __init__(self, query_result=None):
+ # If there's no player existing, the query return unicode code "null".
+ if (not query_result is None and not query_result == u'null' and
+ not isinstance(query_result, dict)):
+ raise NotImplementedError
+
+ self.has_player = isinstance(query_result, dict)
+ self.current_time = GetValueFromQueryResult(query_result, 'current_time',
+ 0.0)
+ self.duration = GetValueFromQueryResult(query_result, 'duration', 0.0)
+ self.paused = GetValueFromQueryResult(query_result, 'paused', False)
+ self.playback_rate = GetValueFromQueryResult(query_result, 'playback_rate',
+ 0.0)
+ self.volume = GetValueFromQueryResult(query_result, 'volume', 0.0)
+ self.dropped_video_frames = GetValueFromQueryResult(query_result,
+ 'dropped_video_frames',
+ 0)
+ self.total_video_frames = GetValueFromQueryResult(query_result,
+ 'total_video_frames', 0)
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ raise NotImplementedError
+ return (self.has_player == other.has_player and
+ self.current_time == other.current_time and
+ self.duration == other.duration and self.paused == other.paused and
+ self.playback_rate == other.playback_rate and
+ self.volume == other.volume and
+ self.dropped_video_frames == other.dropped_video_frames and
+ self.total_video_frames == other.total_video_frames)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ @staticmethod
+ def QueryJsCode():
+ # Looking for a full screen video tag and return that as the primary player.
+ return """
+ const players = document.querySelectorAll("video");
+ if (players && players.length > 0) {
+ for (let i = 0; i < players.length; i++) {
+ const player = players[i];
+ const rect = player.getBoundingClientRect();
+ if (rect.width === window.innerWidth ||
+ rect.height === window.innerHeight) {
+ const quality = player.getVideoPlaybackQuality();
+ return {
+ current_time: player.currentTime,
+ duration: player.duration,
+ paused: player.paused,
+ playback_rate: player.playbackRate,
+ volume: player.volume,
+ dropped_video_frames: quality.droppedVideoFrames,
+ total_video_frames: quality.totalVideoFrames,
+ };
+ }
+ }
+ }
+ return "null";
+ """
+
+
+class PlayerStateHandler():
+ """The handler of player state."""
+
+ def __init__(self, app):
+ self.app = app
+ self.state_change_handlers = []
+ self.player_state = type('', (), {})()
+ self.player_state.pipeline_state = PipelineState()
+ self.player_state.media_session_state = MediaSessionState()
+ self.player_state.video_element_state = VideoElementState()
+
+ self.app.RegisterPeriodicQuery('MediaPipeline', PipelineState.QueryJsCode(),
+ self._PipelineStateQueryHandler)
+ self.app.RegisterPeriodicQuery('MediaSession',
+ MediaSessionState.QueryJsCode(),
+ self._MediaSessionStateQueryHandler)
+ self.app.RegisterPeriodicQuery('VideoElement',
+ VideoElementState.QueryJsCode(),
+ self._VideoElementStateQueryHandler)
+
+ def AddPlayerStateChangeHandler(self, handler):
+ self.state_change_handlers.append(handler)
+
+ def RemovePlayerStateChangeHandler(self, handler):
+ self.state_change_handlers.remove(handler)
+
+ def IsPlayerPlaying(self):
+ return self.player_state.pipeline_state.IsPlaying()
+
+ def _NotifyHandlersOnStateChanged(self):
+ for handler in self.state_change_handlers:
+ handler(self, self.player_state)
+
+ def _PipelineStateQueryHandler(self, app, query_name, query_result):
+ # Note that the callback is invoked on a different thread.
+ del app, query_name # Unused here.
+ new_state = PipelineState(query_result)
+ if self.player_state.pipeline_state != new_state:
+ self.player_state.pipeline_state = new_state
+ self._NotifyHandlersOnStateChanged()
+
+ def _MediaSessionStateQueryHandler(self, app, query_name, query_result):
+ # Note that the callback is invoked on a different thread.
+ del app, query_name # Unused here.
+ new_state = MediaSessionState(query_result)
+ if self.player_state.media_session_state != new_state:
+ self.player_state.media_session_state = new_state
+ self._NotifyHandlersOnStateChanged()
+
+ def _VideoElementStateQueryHandler(self, app, query_name, query_result):
+ # Note that the callback is invoked on a different thread.
+ del app, query_name # Unused here.
+ new_state = VideoElementState(query_result)
+ if self.player_state.video_element_state != new_state:
+ self.player_state.video_element_state = new_state
+ self._NotifyHandlersOnStateChanged()
+
+
+class AdsState(int):
+ """Set of ads states. The numeric values are used in ads state query."""
+ NONE = 0
+ PLAYING = 1
+ PLAYING_AND_SKIPPABLE = 2
+
+
+class TestApp():
+ """
+ A class encapsulating fundamental functions to control and query data
+ from Cobalt application.
+ """
+
+ def __init__(self,
+ launcher_params,
+ url,
+ supported_features,
+ log_file=None,
+ target_params=None,
+ success_message=None):
+ self.supported_features = supported_features
+ self.runner = cobalt_runner.CobaltRunner(
+ launcher_params=launcher_params,
+ url=url,
+ log_handler=self._OnNewLogLine,
+ log_file=log_file,
+ target_params=target_params,
+ success_message=success_message)
+ self.log_handlers = []
+ self.is_queries_changed = False
+ self.periodic_queries = {}
+ self.periodic_queries_lock = threading.Lock()
+ self.should_exit = threading.Event()
+ self.periodic_query_thread = threading.Thread(
+ target=self._RunPeriodicQueries)
+
+ self.app_state_handler = ApplicationStateHandler(self)
+ self.player_state_handler = PlayerStateHandler(self)
+
+ def __enter__(self):
+ try:
+ self.runner.__enter__()
+ except Exception:
+ # Potentially from thread.interrupt_main(). No need to start
+ # query thread.
+ return self
+
+ self.periodic_query_thread.start()
+ return self
+
+ def __exit__(self, *args):
+ self.should_exit.set()
+ if self.periodic_query_thread.is_alive():
+ self.periodic_query_thread.join(THREAD_EXIT_TIMEOUT_SECONDS)
+ return self.runner.__exit__(*args)
+
+ def ExecuteScript(self, script):
+ try:
+ result = self.runner.webdriver.execute_script(script)
+ except Exception as e:
+ raise RuntimeError('Fail to excute script with error (%s).' % (str(e)))
+ return result
+
+ def _OnNewLogLine(self, line):
+ # Note that the function is called on cobalt runner reader thread.
+ # pass
+ self._NotifyHandlersOnNewLogLine(line)
+
+ def _NotifyHandlersOnNewLogLine(self, line):
+ for handler in self.log_handlers:
+ handler(self, line)
+
+ def ApplicationState(self):
+ return self.app_state_handler.app_state
+
+ def PlayerState(self):
+ return self.player_state_handler.player_state
+
+ def CurrentMediaTime(self):
+ # Convert timestamp from millisecond to second.
+ return float(self.PlayerState().pipeline_state.last_media_time) / 1000000.0
+
+ def Suspend(self):
+ if not self.supported_features[Features.SUSPEND_AND_RESUME]:
+ raise RuntimeError('Suspend is not supported.')
+ if not self.runner.launcher_is_running:
+ raise RuntimeError('Runner is not running.')
+ self.runner.SendSystemSuspend()
+
+ def Resume(self):
+ if not self.supported_features[Features.SUSPEND_AND_RESUME]:
+ raise RuntimeError('Resume is not supported.')
+ if not self.runner.launcher_is_running:
+ raise RuntimeError('Runner is not running.')
+ self.runner.SendSystemResume()
+
+ def SendKeys(self, keys):
+ if not self.supported_features[Features.SEND_KEYS]:
+ raise RuntimeError('SendKeys is not supported.')
+ if not self.runner.launcher_is_running:
+ raise RuntimeError('Runner is not running.')
+ self.runner.SendKeys(keys)
+
+ # The handler will receive parameters (TestApp, String).
+ def AddLogHandler(self, handler):
+ self.log_handlers.append(handler)
+
+ def RemoveLogHandler(self, handler):
+ self.log_handlers.remove(handler)
+
+ # The handler will receive parameters (TestApp, ApplicationState).
+ def AddAppStateChangeHandler(self, handler):
+ self.app_state_handler.AddAppStateChangeHandler(handler)
+
+ def RemoveAppStateChangeHandler(self, handler):
+ self.app_state_handler.RemoveAppStateChangeHandler(handler)
+
+ # The handler will receive parameters (TestApp, PlayerState).
+ def AddPlayerStateChangeHandler(self, handler):
+ self.player_state_handler.AddPlayerStateChangeHandler(handler)
+
+ def RemovePlayerStateChangeHandler(self, handler):
+ self.player_state_handler.RemovePlayerStateChangeHandler(handler)
+
+ # The handler will receive parameters (TestApp, String, Dictionary).
+ def RegisterPeriodicQuery(self, query_name, query_js_code, result_handler):
+ if not isinstance(query_name, str):
+ raise RuntimeError('Query name must be a string.')
+ if self.periodic_queries.get(query_name):
+ raise RuntimeError('Duplicate query name is not allowed.')
+ # TODO: Validate js code.
+ with self.periodic_queries_lock:
+ self.is_queries_changed = True
+ self.periodic_queries[query_name] = (query_js_code, result_handler)
+
+ def UnregisterPeriodicQuery(self, query_name):
+ if not isinstance(query_name, str):
+ raise RuntimeError('Query name must be a string.')
+ with self.periodic_queries_lock:
+ self.is_queries_changed = True
+ self.periodic_queries.pop(query_name)
+
+ def _RunPeriodicQueries(self):
+ while True:
+ if self.should_exit.is_set():
+ break
+ # Wait until webdriver client attached to Cobalt.
+ if self.runner.test_script_started.is_set():
+ with self.periodic_queries_lock:
+ local_is_queries_changed = self.is_queries_changed
+ local_periodic_queries = self.periodic_queries
+ self.is_queries_changed = False
+ # Skip if there's no registered query.
+ if len(local_periodic_queries) == 0:
+ continue
+ # Generate javascript code and execute it.
+ js_code = self._GeneratePeriodicQueryJsCode(local_is_queries_changed,
+ local_periodic_queries)
+ result = self.ExecuteScript(js_code)
+ for query_name in local_periodic_queries.keys():
+ if not result.get(query_name):
+ raise RuntimeError(
+ 'There\'s something wrong in the queries result.')
+ local_periodic_queries[query_name][1](self, query_name,
+ result[query_name])
+
+ time.sleep(PERIODIC_QUERIES_INTERVAL_SECONDS)
+
+ _PERIODIC_QUERIES_JS_CODE = """
+ let ret = {};
+ for(let key in _media_integration_testing_queries) {
+ ret[key] = _media_integration_testing_queries[key]();
+ }
+ return ret;
+ """
+
+ def _GeneratePeriodicQueryJsCode(self, is_queries_changed, periodic_queries):
+ js_code = ''
+ # Update registered queries at host side.
+ if is_queries_changed:
+ js_code += '_media_integration_testing_queries = {'
+ for query_name in periodic_queries:
+ js_code += query_name + ': function() {'
+ js_code += periodic_queries[query_name][0]
+ js_code += '},'
+ js_code += '}'
+ # Run queries and collect results.
+ js_code += self._PERIODIC_QUERIES_JS_CODE
+ return js_code
+
+ # The first input of |state_lambda| is an instance of TestApp.
+ def WaitUntilReachState(
+ self,
+ state_lambda,
+ timeout=WAIT_UNTIL_REACH_STATE_DEFAULT_TIMEOUT_SECONDS):
+ start_time = time.time()
+ while not state_lambda(self) and time.time() - start_time < timeout:
+ time.sleep(WAIT_INTERVAL_SECONDS)
+ execute_interval = time.time() - start_time
+ if execute_interval > timeout:
+ raise RuntimeError('WaitUntilReachState timed out after (%f) seconds.' %
+ (execute_interval))
+
+ # The result is an array of overlay header text contents.
+ _OVERLAY_QUERY_JS_CODE = """
+ let result = [];
+ if (document.getElementsByTagName(
+ "YTLR-OVERLAY-PANEL-HEADER-RENDERER").length > 0) {
+ let childNodes = document.getElementsByTagName(
+ "YTLR-OVERLAY-PANEL-HEADER-RENDERER")[0].childNodes;
+ for (let i = 0; i < childNodes.length; i++) {
+ result.push(childNodes[i].textContent);
+ }
+ }
+ return result;
+ """
+
+ def WaitUntilPlayerStart(
+ self, timeout=WAIT_UNTIL_REACH_STATE_DEFAULT_TIMEOUT_SECONDS):
+
+ def PlayerStateCheckCallback(app):
+ # Check if it's showing account selector page.
+ is_showing_account_selector = self.ExecuteScript(
+ 'return document.getElementsByTagName("YTLR-ACCOUNT-SELECTOR")'
+ '.length > 0;')
+ if is_showing_account_selector:
+ active_element_label_attr = self.ExecuteScript(
+ 'return document.activeElement.getAttribute("aria-label");')
+ if active_element_label_attr != ACCOUNT_SELECTOR_ADD_ACCOUNT_TEXT:
+ logging.info('Select an account (%s) to continue the test.',
+ active_element_label_attr)
+ self.SendKeys(webdriver_keys.Keys.ENTER)
+ else:
+ logging.info('Current selected item is "Add acount", move to next'
+ ' item.')
+ self.SendKeys(webdriver_keys.Keys.ARROW_RIGHT)
+ return False
+ # Check if it's showing a playback survey.
+ is_showing_skip_button = self.ExecuteScript(
+ 'return document.getElementsByTagName("YTLR-SKIP-BUTTON-RENDERER")'
+ '.length > 0;')
+ # When there's a skip button and no running player, it's showing a
+ # survey.
+ if (is_showing_skip_button and
+ not app.player_state_handler.IsPlayerPlaying()):
+ self.SendKeys(webdriver_keys.Keys.ENTER)
+ logging.info('Send enter key event to skip the survey.')
+ return False
+ # Check if it's showing an overlay.
+ overlay_query_result = self.ExecuteScript(self._OVERLAY_QUERY_JS_CODE)
+ if len(overlay_query_result) > 0:
+ # Note that if there're playback errors, after close the overlay,
+ # the test will end with timeout error.
+ self.SendKeys(webdriver_keys.Keys.ENTER)
+ logging.info(
+ 'Send enter key event to close the overlay. Overlay '
+ 'headers : %r', overlay_query_result)
+ return False
+
+ return app.player_state_handler.IsPlayerPlaying()
+
+ self.WaitUntilReachState(PlayerStateCheckCallback, timeout)
+
+ def WaitUntilPlayerDestroyed(
+ self, timeout=WAIT_UNTIL_REACH_STATE_DEFAULT_TIMEOUT_SECONDS):
+ current_identifier = self.PlayerState().pipeline_state.identifier
+ if not current_identifier or len(current_identifier) == 0:
+ raise RuntimeError('No existing player when calling'
+ 'WaitUntilPlayerDestroyed.')
+ self.WaitUntilReachState(
+ lambda _app: _app.PlayerState().pipeline_state.identifier !=
+ current_identifier, timeout)
+
+ """
+ The return values of the query, exact mapping of AdsState defined earlier
+ in this file.
+ """
+ _GET_ADS_STATE_QUERY_JS_CODE = """
+ if (document.getElementsByTagName("YTLR-AD-PREVIEW-RENDERER").length > 0) {
+ return 1;
+ }
+ if (document.getElementsByTagName("YTLR-SKIP-BUTTON-RENDERER").length > 0) {
+ return 2;
+ }
+ return 0;
+ """
+
+ def GetAdsState(self):
+ if not self.runner.test_script_started.is_set():
+ raise RuntimeError('Webdriver is not ready yet')
+ result = self.ExecuteScript(self._GET_ADS_STATE_QUERY_JS_CODE)
+ return AdsState(result)
+
+ def WaitUntilAdsEnd(self, timeout=WAIT_UNTIL_ADS_END_DEFAULT_TIMEOUT_SECONDS):
+ start_time = time.time()
+ # There may be multiple ads and we need to wait all of them finished.
+ while time.time() - start_time < timeout:
+ # Wait until the content starts, otherwise the ads state may not be right.
+ self.WaitUntilReachState(lambda _app: _app.CurrentMediaTime() > 0,
+ timeout)
+ ads_state = self.GetAdsState()
+ if ads_state == AdsState.NONE:
+ break
+ while ads_state != AdsState.NONE and time.time() - start_time < timeout:
+ ads_state = self.GetAdsState()
+ if ads_state == AdsState.PLAYING_AND_SKIPPABLE:
+ self.SendKeys(webdriver_keys.Keys.ENTER)
+ logging.info('Send enter key event to skip the ad.')
+ time.sleep(WAIT_INTERVAL_SECONDS)
+
+ execute_interval = time.time() - start_time
+ if execute_interval > timeout:
+ raise RuntimeError(
+ 'WaitUntilReachState timed out after (%f) seconds, ads_state: (%d).' %
+ (execute_interval, ads_state))
+
+ def WaitUntilMediaTimeReached(
+ self,
+ media_time,
+ timeout=WAIT_UNTIL_MEDIA_TIME_REACHED_DEFAULT_TIMEOUT_SECONDS):
+ start_media_time = self.CurrentMediaTime()
+ if start_media_time > media_time:
+ return
+
+ # Wait until playback starts.
+ self.WaitUntilReachState(
+ lambda _app: _app.PlayerState().pipeline_state.playback_rate > 0,
+ timeout)
+
+ duration = self.PlayerState().video_element_state.duration
+ if media_time > duration:
+ logging.info(
+ 'Requested media time (%f) is greater than the duration (%f)',
+ media_time, duration)
+ media_time = duration
+
+ time_to_play = max(media_time - self.CurrentMediaTime(), 0)
+ playback_rate = self.PlayerState().pipeline_state.playback_rate
+ adjusted_timeout = time_to_play / playback_rate + timeout
+ self.WaitUntilReachState(lambda _app: _app.CurrentMediaTime() > media_time,
+ adjusted_timeout)
+
+ def IsMediaTypeSupported(self, mime):
+ return self.ExecuteScript('return MediaSource.isTypeSupported("%s");' %
+ (mime))
+
+ def PlayOrPause(self):
+ self.SendKeys(AdditionalKeys.MEDIA_PLAY_PAUSE)
+
+ def Fastforward(self):
+ # The first fastforward will only bring up the progress bar.
+ self.SendKeys(AdditionalKeys.MEDIA_FAST_FORWARD)
+ # The second fastforward will forward the playback by 10 seconds.
+ self.SendKeys(AdditionalKeys.MEDIA_FAST_FORWARD)
+ # Press play button to start the playback.
+ self.SendKeys(AdditionalKeys.MEDIA_PLAY_PAUSE)
+
+ def Rewind(self):
+ # The first rewind will only bring up the progress bar.
+ self.SendKeys(AdditionalKeys.MEDIA_REWIND)
+ # The second rewind will rewind the playback by 10 seconds.
+ self.SendKeys(AdditionalKeys.MEDIA_REWIND)
+ # It needs to press play button to start the playback.
+ self.SendKeys(AdditionalKeys.MEDIA_PLAY_PAUSE)
+
+ def PlayPrevious(self):
+ self.SendKeys(AdditionalKeys.MEDIA_PREV_TRACK)
+
+ def PlayNext(self):
+ self.SendKeys(AdditionalKeys.MEDIA_NEXT_TRACK)
diff --git a/src/cobalt/media_integration_tests/test_case.py b/src/cobalt/media_integration_tests/test_case.py
new file mode 100644
index 0000000..04b942a
--- /dev/null
+++ b/src/cobalt/media_integration_tests/test_case.py
@@ -0,0 +1,67 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""The module of base media integration test case."""
+
+import logging
+import unittest
+
+import _env # pylint: disable=unused-import
+from cobalt.media_integration_tests.test_app import TestApp
+
+# Global variables
+_launcher_params = None
+_supported_features = None
+
+
+def SetLauncherParams(launcher_params):
+ global _launcher_params
+ _launcher_params = launcher_params
+
+
+def SetSupportedFeatures(supported_features):
+ global _supported_features
+ _supported_features = supported_features
+
+
+class TestCase(unittest.TestCase):
+ """The base class for media integration test cases."""
+
+ def __init__(self, *args, **kwargs):
+ super(TestCase, self).__init__(*args, **kwargs)
+ self.launcher_params = _launcher_params
+ self.supported_features = _supported_features
+
+ def setUp(self):
+ logging.info('Running %s', str(self.id()))
+
+ def tearDown(self):
+ logging.info('Done %s', str(self.id()))
+
+ def CreateCobaltApp(self, url):
+ app = TestApp(
+ launcher_params=self.launcher_params,
+ supported_features=self.supported_features,
+ url=url)
+ return app
+
+ @staticmethod
+ def IsFeatureSupported(feature):
+ global _supported_features
+ return _supported_features[feature]
+
+ @staticmethod
+ def CreateTest(test_class, test_name, test_function, *args):
+ test_method = lambda self: test_function(self, *args)
+ test_method.__name__ = 'test_%s' % test_name
+ setattr(test_class, test_method.__name__, test_method)
diff --git a/src/cobalt/media_integration_tests/test_util.py b/src/cobalt/media_integration_tests/test_util.py
new file mode 100644
index 0000000..6bd08d9
--- /dev/null
+++ b/src/cobalt/media_integration_tests/test_util.py
@@ -0,0 +1,88 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""The module of base media integration test case."""
+
+from collections import OrderedDict
+
+import _env # pylint: disable=unused-import
+
+
+class MimeStrings():
+ """
+ Set of playback mime strings.
+ """
+
+ H264 = 'video/mp4; codecs=\\"avc1.4d4015\\"'
+ VP9 = 'video/webm; codecs=\\"vp9\\"'
+ VP9_HFR = 'video/webm; codecs=\\"vp9\\"; framerate=60'
+ AV1 = 'video/mp4; codecs=\\"av01.0.08M.08\\"'
+ AV1_HFR = 'video/mp4; codecs=\\"av01.0.08M.08\\"; framerate=60'
+ VP9_HDR_HLG = 'video/webm; codecs=\\"vp09.02.51.10.01.09.18.09.00\\"'
+ VP9_HDR_PQ = 'video/webm; codecs=\\"vp09.02.51.10.01.09.16.09.00\\"'
+ VP9_HDR_PQ_HFR = ('video/webm; codecs=\\"vp09.02.51.10.01.09.16.09.00\\";'
+ 'framerate=60')
+
+ AAC = 'audio/mp4; codecs=\\"mp4a.40.2\\"'
+ OPUS = 'audio/webm; codecs=\\"opus\\"'
+
+ RESOLUTIONS = OrderedDict([('140P', 'width=256; height=144'),
+ ('240P', 'width=352; height=240'),
+ ('360P', 'width=480; height=360'),
+ ('480P', 'width=640; height=480'),
+ ('720P', 'width=1280; height=720'),
+ ('1080P', 'width=1920; height=1080'),
+ ('2K', 'width=2560; height=1440'),
+ ('4K', 'width=3840; height=2160'),
+ ('8K', 'width=7680; height=4320')])
+
+ @staticmethod
+ def create_video_mime_string(codec, resolution):
+ return '%s; %s' % (codec, MimeStrings.RESOLUTIONS[resolution])
+
+ @staticmethod
+ def create_audio_mime_string(codec, channels):
+ return '%s; channels=%d' % (codec, channels)
+
+
+class PlaybackUrls():
+ """
+ Set of testing video urls.
+ """
+
+ DEFAULT = 'https://www.youtube.com/tv'
+ H264_ONLY = 'https://www.youtube.com/tv#/watch?v=RACW52qnJMI'
+ PROGRESSIVE = 'https://www.youtube.com/tv#/watch?v=w5tbgPJxyGA'
+ ENCRYPTED = 'https://www.youtube.com/tv#/watch?v=iNvUS1dnwfw'
+
+ VP9 = 'https://www.youtube.com/tv#/watch?v=x7GkebUe6XQ'
+ VP9_HFR = 'https://www.youtube.com/tv#/watch?v=Jsjtt5dWDYU'
+ AV1 = 'https://www.youtube.com/tv#/watch?v=iXvy8ZeCs5M'
+ AV1_HFR = 'https://www.youtube.com/tv#/watch?v=9jZ01i92JI8'
+ VERTICAL = 'https://www.youtube.com/tv#/watch?v=jNQXAC9IVRw'
+ SHORT = 'https://www.youtube.com/tv#/watch?v=NEf8Ug49FEw'
+ VP9_HDR_HLG = 'https://www.youtube.com/tv#/watch?v=ebhEiRWGvZM'
+ VP9_HDR_PQ = 'https://www.youtube.com/tv#/watch?v=Rw-qEKR5uv8'
+ HDR_PQ_HFR = 'https://www.youtube.com/tv#/watch?v=LXb3EKWsInQ'
+ VR = 'https://www.youtube.com/tv#/watch?v=Ei0fgLfJ6Tk'
+
+ LIVE = 'https://www.youtube.com/tv#/watch?v=o7UP6i4PAbk'
+ LIVE_ULL = 'https://www.youtube.com/tv#/watch?v=KI1XlTQrsa0'
+ LIVE_VR = 'https://www.youtube.com/tv#/watch?v=soeo5OPv7CA'
+
+ PLAYLIST = ('https://www.youtube.com/tv#/watch?list='
+ 'PLynQTqo4blSSCMPUGcH2YnbSrV84AnsRD')
+ # The link refers to a video in the middle of the playlist, so that
+ # it can be used to test both play previous and next.
+ PLAYLIST_MID = ('https://www.youtube.com/tv#/watch?'
+ 'v=9WrgRpOJr0I&list=PLynQTqo4blSSCMPUGcH2YnbSrV84AnsRD')
diff --git a/src/cobalt/media_session/media_session_client.cc b/src/cobalt/media_session/media_session_client.cc
index 99097ef..7dd8853 100644
--- a/src/cobalt/media_session/media_session_client.cc
+++ b/src/cobalt/media_session/media_session_client.cc
@@ -175,6 +175,15 @@
return result;
}
+void MediaSessionClient::PostDelayedTaskForMaybeFreezeCallback() {
+ if (is_active()) return;
+
+ media_session_->task_runner_->PostDelayedTask(
+ FROM_HERE, base::Bind(&MediaSessionClient::RunMaybeFreezeCallback,
+ base::Unretained(this), ++sequence_number_),
+ kMaybeFreezeDelay);
+}
+
void MediaSessionClient::UpdatePlatformPlaybackState(
CobaltExtensionMediaSessionPlaybackState state) {
DCHECK(media_session_->task_runner_);
@@ -190,12 +199,7 @@
UpdateMediaSessionState();
}
- if (!is_active()) {
- media_session_->task_runner_->PostDelayedTask(
- FROM_HERE, base::Bind(&MediaSessionClient::RunMaybeFreezeCallback,
- base::Unretained(this), ++sequence_number_),
- kMaybeFreezeDelay);
- }
+ PostDelayedTaskForMaybeFreezeCallback();
}
void MediaSessionClient::RunMaybeFreezeCallback(int sequence_number) {
diff --git a/src/cobalt/media_session/media_session_client.h b/src/cobalt/media_session/media_session_client.h
index 115b852..35a9a31 100644
--- a/src/cobalt/media_session/media_session_client.h
+++ b/src/cobalt/media_session/media_session_client.h
@@ -95,9 +95,8 @@
media_session_ = media_session;
}
- // If the media session is not active, then run MaybeFreezeCallback to
- // suspend the App.
- void RunMaybeFreezeCallback(int sequence_number);
+ // Post a delayed task for running MaybeFreeze callback.
+ void PostDelayedTaskForMaybeFreezeCallback();
private:
THREAD_CHECKER(thread_checker_);
@@ -135,6 +134,10 @@
MediaSessionAction ConvertMediaSessionAction(
CobaltExtensionMediaSessionAction action);
+ // If the media session is not active, then run MaybeFreezeCallback to
+ // suspend the App.
+ void RunMaybeFreezeCallback(int sequence_number);
+
base::Closure maybe_freeze_callback_;
// This is for checking the sequence number of PostDelayedTask. It should be
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl
index 1380682..1be401c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_2.glsl
@@ -14,7 +14,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_3.glsl
index 8ca842b..02f89e1 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_threshold_3.glsl
@@ -14,7 +14,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl
index 63ff1ed..11db997 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_circle_color_scale_thresholds.glsl
@@ -19,7 +19,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl
index 9c5c1f3..e7447a2 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_2.glsl
@@ -14,7 +14,7 @@
varying mediump float vinCoverage_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_3.glsl
index 3d40e8a..1ee4967 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_threshold_3.glsl
@@ -14,7 +14,7 @@
varying highp float vcoverage_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl
index cbcba0c..7bf9ef3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds.glsl
@@ -19,7 +19,7 @@
varying highp float vcoverage_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl
index 083f5e4..e6a6613 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_2.glsl
@@ -14,7 +14,7 @@
varying highp float vcoverage_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl
index e737a75..a5049c7 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_coverage_scale_thresholds_3.glsl
@@ -21,7 +21,7 @@
varying mediump float vinCoverage_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl
index d3a03a4..e338bcf 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_decal_scaletranslate_texdom_texturew_threshold.glsl
@@ -18,7 +18,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl
index 5f2b1d3..667a564 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold.glsl
@@ -15,7 +15,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl
index 084c066..35a3010 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_2.glsl
@@ -15,7 +15,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl
index cb27f80..1f47fae 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_threshold_3.glsl
@@ -15,7 +15,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl
index 1ba24f6..b7223a3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds.glsl
@@ -18,7 +18,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl
index da56a36..ba5cadd 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_quad_scale_thresholds_2.glsl
@@ -20,7 +20,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl
index e2ec055..8bcc27a 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold.glsl
@@ -16,7 +16,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl
index fe3a8ae..2ac7fcc 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_2.glsl
@@ -16,7 +16,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl
index f1bbe9d..1cb8137 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_threshold_3.glsl
@@ -16,7 +16,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl
index b073544..ba8e732 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texindex_texturew_thresholds.glsl
@@ -19,7 +19,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texturew_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texturew_threshold.glsl
index 987a9fd..8ea773f 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texturew_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_texturew_threshold.glsl
@@ -15,7 +15,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl
index 3657419..2cec90c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold.glsl
@@ -13,7 +13,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold_2.glsl
index a80e7d6..8335e68 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_threshold_2.glsl
@@ -13,7 +13,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl
index 6e1c100..7685597 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds.glsl
@@ -18,7 +18,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl
index e055c58..a1f1060 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_2.glsl
@@ -18,7 +18,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_3.glsl
index be9d988..ecc4cb4 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_3.glsl
@@ -18,7 +18,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_4.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_4.glsl
index cb3bcb1..71ce693 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_4.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_4.glsl
@@ -22,7 +22,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_5.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_5.glsl
index 68b87cd..4fb093a 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_5.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_5.glsl
@@ -18,7 +18,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_6.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_6.glsl
index 36f18a2..bffd97d 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_6.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_6.glsl
@@ -22,7 +22,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_7.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_7.glsl
index a76b32a..9324401 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_7.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_7.glsl
@@ -18,7 +18,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_8.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_8.glsl
index f5c5d14..39b4e76 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_8.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_8.glsl
@@ -16,7 +16,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_9.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_9.glsl
index 58cdb65..0e27646 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_9.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_border_color_scale_thresholds_9.glsl
@@ -22,7 +22,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold.glsl
index 62db91a..482238d 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold.glsl
@@ -25,7 +25,7 @@
}
mediump vec4 stage_Stage1_c1_c0_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample453_c1_c0_c0_c0;
- mediump float t = vTransformedCoords_1_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_1_Stage0.x + 9.999999747378752e-06;
_sample453_c1_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample453_c1_c0_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_2.glsl
index 77855e7..563d103 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_2.glsl
@@ -25,7 +25,7 @@
}
mediump vec4 stage_Stage1_c1_c0_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample453_c1_c0_c0_c0;
- mediump float t = vTransformedCoords_1_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_1_Stage0.x + 9.999999747378752e-06;
_sample453_c1_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample453_c1_c0_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_3.glsl
index 52553ba..37c926c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_3.glsl
@@ -18,7 +18,7 @@
}
mediump vec4 stage_Stage2_c1_c0_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample453_c1_c0_c0_c0;
- mediump float t = vTransformedCoords_1_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_1_Stage0.x + 9.999999747378752e-06;
_sample453_c1_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample453_c1_c0_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_4.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_4.glsl
index 6522fd0..aa9cd37 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_4.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_threshold_4.glsl
@@ -11,7 +11,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample453_c0_c0_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample453_c0_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample453_c0_c0_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds.glsl
index a35702c..5eedd12 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds.glsl
@@ -21,7 +21,7 @@
}
mediump vec4 stage_Stage2_c1_c0_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample453_c1_c0_c0_c0;
- mediump float t = vTransformedCoords_1_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_1_Stage0.x + 9.999999747378752e-06;
_sample453_c1_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample453_c1_c0_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds_2.glsl
index 28eab3a..ddacd34 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_texture_thresholds_2.glsl
@@ -23,7 +23,7 @@
}
mediump vec4 stage_Stage2_c1_c0_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample453_c1_c0_c0_c0;
- mediump float t = vTransformedCoords_1_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_1_Stage0.x + 9.999999747378752e-06;
_sample453_c1_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample453_c1_c0_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_thresholds.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_thresholds.glsl
index e435cc9..8dd97f4 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_thresholds.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_bias_color_scale_thresholds.glsl
@@ -14,7 +14,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample453_c0_c0_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample453_c0_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample453_c0_c0_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color.glsl
index 3dcf693..d0e6ce0 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color.glsl
@@ -10,7 +10,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage.glsl
index aa5970f..6adacd9 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage.glsl
@@ -11,7 +11,7 @@
varying highp float vcoverage_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl
index 224674d..07a5ac3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_coverage_texture.glsl
@@ -10,7 +10,7 @@
varying highp float vcoverage_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_decal_ellipse_scaletranslate_texdom_texturew.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_decal_ellipse_scaletranslate_texdom_texturew.glsl
index 572a984..70771b9 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_decal_ellipse_scaletranslate_texdom_texturew.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_decal_ellipse_scaletranslate_texdom_texturew.glsl
@@ -51,7 +51,7 @@
highp float test = dot(offset, offset) - 1.0;
highp vec2 grad = (2.0 * offset) * (vEllipseOffsets_Stage0.z * vEllipseRadii_Stage0.xy);
highp float grad_dot = dot(grad, grad);
- grad_dot = max(grad_dot, 6.1036000261083245e-05);
+ grad_dot = max(grad_dot, 6.103600026108325e-05);
highp float invlen = vEllipseOffsets_Stage0.z * inversesqrt(grad_dot);
highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
outputCoverage_Stage0 = vec4(edgeAlpha);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl
index 1ea6bd8..3c26f70 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture.glsl
@@ -11,7 +11,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl
index d8c1e31..298ccc0 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_quad_texture_2.glsl
@@ -11,7 +11,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl
index 4e004af..92cba89 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texindex_texturew.glsl
@@ -12,7 +12,7 @@
varying highp vec2 vTransformedCoords_0_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture.glsl
index 4b56cd0..6f7f457 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture.glsl
@@ -9,7 +9,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture_2.glsl
index fa2dffc..edebf92 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texture_2.glsl
@@ -9,7 +9,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texturew_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texturew_2.glsl
index 84896ca..78b3466 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texturew_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_border_color_texturew_2.glsl
@@ -12,7 +12,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample1099_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample1099_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample1099_c0_c0;
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_decal_ellipse_scaletranslate_texdom_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_decal_ellipse_scaletranslate_texdom_texture.glsl
index 98af413..b1749e3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_decal_ellipse_scaletranslate_texdom_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_decal_ellipse_scaletranslate_texdom_texture.glsl
@@ -21,14 +21,14 @@
highp float test = dot(offset, offset) - 1.0;
highp vec2 grad = (2.0 * offset) * (vEllipseOffsets_Stage0.z * vEllipseRadii_Stage0.xy);
highp float grad_dot = dot(grad, grad);
- grad_dot = max(grad_dot, 6.1036000261083245e-05);
+ grad_dot = max(grad_dot, 6.103600026108325e-05);
highp float invlen = vEllipseOffsets_Stage0.z * inversesqrt(grad_dot);
highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
offset = vEllipseOffsets_Stage0.xy * vEllipseRadii_Stage0.zw;
test = dot(offset, offset) - 1.0;
grad = (2.0 * offset) * (vEllipseOffsets_Stage0.z * vEllipseRadii_Stage0.zw);
grad_dot = dot(grad, grad);
- grad_dot = max(grad_dot, 6.1036000261083245e-05);
+ grad_dot = max(grad_dot, 6.103600026108325e-05);
invlen = vEllipseOffsets_Stage0.z * inversesqrt(grad_dot);
edgeAlpha *= clamp(0.5 + test * invlen, 0.0, 1.0);
outputCoverage_Stage0 = vec4(edgeAlpha);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl
index e7cd090..fda3d24 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_edges_texindex_texturew_2.glsl
@@ -21,9 +21,9 @@
{
texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
}
- mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+ mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
mediump float afwidth;
- afwidth = abs(0.64999997615814209 * -dFdy(vIntTextureCoords_Stage0.y));
+ afwidth = abs(0.6499999761581421 * -dFdy(vIntTextureCoords_Stage0.y));
mediump float val = smoothstep(-afwidth, afwidth, distance);
outputCoverage_Stage0 = vec4(val);
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl
index ee26f8b..8b55db4 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse.glsl
@@ -14,7 +14,7 @@
highp float test = dot(offset, offset) - 1.0;
highp vec2 grad = (2.0 * offset) * (vEllipseOffsets_Stage0.z * vEllipseRadii_Stage0.xy);
highp float grad_dot = dot(grad, grad);
- grad_dot = max(grad_dot, 6.1036000261083245e-05);
+ grad_dot = max(grad_dot, 6.103600026108325e-05);
highp float invlen = vEllipseOffsets_Stage0.z * inversesqrt(grad_dot);
highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
outputCoverage_Stage0 = vec4(edgeAlpha);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl
index 0cb3d54..dd2801e 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_2.glsl
@@ -18,7 +18,7 @@
highp vec2 grad = vec2(vEllipseOffsets0_Stage0.x * duvdx.x + vEllipseOffsets0_Stage0.y * duvdx.y, vEllipseOffsets0_Stage0.x * duvdy.x + vEllipseOffsets0_Stage0.y * duvdy.y);
grad *= vEllipseOffsets0_Stage0.z;
highp float grad_dot = 4.0 * dot(grad, grad);
- grad_dot = max(grad_dot, 6.1036000261083245e-05);
+ grad_dot = max(grad_dot, 6.103600026108325e-05);
highp float invlen = inversesqrt(grad_dot);
invlen *= vEllipseOffsets0_Stage0.z;
highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
@@ -29,7 +29,7 @@
grad = vec2(vEllipseOffsets1_Stage0.x * duvdx.x + vEllipseOffsets1_Stage0.y * duvdx.y, vEllipseOffsets1_Stage0.x * duvdy.x + vEllipseOffsets1_Stage0.y * duvdy.y);
grad *= vEllipseOffsets0_Stage0.z;
grad_dot = 4.0 * dot(grad, grad);
- grad_dot = max(grad_dot, 6.1036000261083245e-05);
+ grad_dot = max(grad_dot, 6.103600026108325e-05);
invlen = inversesqrt(grad_dot);
invlen *= vEllipseOffsets0_Stage0.z;
edgeAlpha *= clamp(0.5 + test * invlen, 0.0, 1.0);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl
index 6148054..ff7654e 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_3.glsl
@@ -18,7 +18,7 @@
highp vec2 grad = vec2(vEllipseOffsets0_Stage0.x * duvdx.x + vEllipseOffsets0_Stage0.y * duvdx.y, vEllipseOffsets0_Stage0.x * duvdy.x + vEllipseOffsets0_Stage0.y * duvdy.y);
grad *= vEllipseOffsets0_Stage0.z;
highp float grad_dot = 4.0 * dot(grad, grad);
- grad_dot = max(grad_dot, 6.1036000261083245e-05);
+ grad_dot = max(grad_dot, 6.103600026108325e-05);
highp float invlen = inversesqrt(grad_dot);
invlen *= vEllipseOffsets0_Stage0.z;
highp float edgeAlpha = clamp(0.5 - test * invlen, 0.0, 1.0);
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl
index 0b4a811..4676e51 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_ellipse_scale_texture.glsl
@@ -27,7 +27,7 @@
highp float implicit = dot(Z, d) - 1.0;
highp float grad_dot = 4.0 * dot(Z, Z);
{
- grad_dot = max(grad_dot, 6.1036000261083245e-05);
+ grad_dot = max(grad_dot, 6.103600026108325e-05);
}
highp float approx_dist = implicit * inversesqrt(grad_dot);
{
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl
index 53a0a74..32a6af2 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew.glsl
@@ -18,9 +18,9 @@
{
texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
}
- mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+ mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
mediump float afwidth;
- afwidth = abs(0.64999997615814209 * -dFdy(vIntTextureCoords_Stage0.y));
+ afwidth = abs(0.6499999761581421 * -dFdy(vIntTextureCoords_Stage0.y));
mediump float val = smoothstep(-afwidth, afwidth, distance);
outputCoverage_Stage0 = vec4(val);
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl
index 1b61907..7c4db08 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_2.glsl
@@ -18,9 +18,9 @@
{
texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
}
- mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+ mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
mediump float afwidth;
- afwidth = abs(0.64999997615814209 * -dFdy(vIntTextureCoords_Stage0.y));
+ afwidth = abs(0.6499999761581421 * -dFdy(vIntTextureCoords_Stage0.y));
mediump float val = smoothstep(-afwidth, afwidth, distance);
outputCoverage_Stage0 = vec4(val);
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl
index f4db076..421494c 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_3.glsl
@@ -18,19 +18,19 @@
{
texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
}
- mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+ mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
mediump float afwidth;
mediump vec2 dist_grad = vec2(dFdx(distance), -dFdy(distance));
mediump float dg_len2 = dot(dist_grad, dist_grad);
- if (dg_len2 < 9.9999997473787516e-05) {
- dist_grad = vec2(0.70709997415542603, 0.70709997415542603);
+ if (dg_len2 < 9.999999747378752e-05) {
+ dist_grad = vec2(0.707099974155426, 0.707099974155426);
} else {
dist_grad = dist_grad * inversesqrt(dg_len2);
}
mediump vec2 Jdx = dFdx(vIntTextureCoords_Stage0);
mediump vec2 Jdy = -dFdy(vIntTextureCoords_Stage0);
mediump vec2 grad = vec2(dist_grad.x * Jdx.x + dist_grad.y * Jdy.x, dist_grad.x * Jdx.y + dist_grad.y * Jdy.y);
- afwidth = 0.64999997615814209 * length(grad);
+ afwidth = 0.6499999761581421 * length(grad);
mediump float val = smoothstep(-afwidth, afwidth, distance);
outputCoverage_Stage0 = vec4(val);
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl
index 20eef11..663553b 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_4.glsl
@@ -18,10 +18,10 @@
{
texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
}
- mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+ mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
mediump float afwidth;
mediump float st_grad_len = length(-dFdy(vIntTextureCoords_Stage0));
- afwidth = abs(0.64999997615814209 * st_grad_len);
+ afwidth = abs(0.6499999761581421 * st_grad_len);
mediump float val = smoothstep(-afwidth, afwidth, distance);
outputCoverage_Stage0 = vec4(val);
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl
index ac92c03..80cd4a3 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_5.glsl
@@ -18,10 +18,10 @@
{
texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
}
- mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+ mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
mediump float afwidth;
mediump float st_grad_len = length(-dFdy(vIntTextureCoords_Stage0));
- afwidth = abs(0.64999997615814209 * st_grad_len);
+ afwidth = abs(0.6499999761581421 * st_grad_len);
mediump float val = smoothstep(-afwidth, afwidth, distance);
outputCoverage_Stage0 = vec4(val);
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_7.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_7.glsl
index 85b6c4e..165aeed 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_7.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texindex_texturew_7.glsl
@@ -18,19 +18,19 @@
{
texColor = texture2D(uTextureSampler_0_Stage0, uv).wwww;
}
- mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
+ mediump float distance = 7.96875 * (texColor.x - 0.501960813999176);
mediump float afwidth;
mediump vec2 dist_grad = vec2(dFdx(distance), -dFdy(distance));
mediump float dg_len2 = dot(dist_grad, dist_grad);
- if (dg_len2 < 9.9999997473787516e-05) {
- dist_grad = vec2(0.70709997415542603, 0.70709997415542603);
+ if (dg_len2 < 9.999999747378752e-05) {
+ dist_grad = vec2(0.707099974155426, 0.707099974155426);
} else {
dist_grad = dist_grad * inversesqrt(dg_len2);
}
mediump vec2 Jdx = dFdx(vIntTextureCoords_Stage0);
mediump vec2 Jdy = -dFdy(vIntTextureCoords_Stage0);
mediump vec2 grad = vec2(dist_grad.x * Jdx.x + dist_grad.y * Jdy.x, dist_grad.x * Jdx.y + dist_grad.y * Jdy.y);
- afwidth = 0.64999997615814209 * length(grad);
+ afwidth = 0.6499999761581421 * length(grad);
mediump float val = smoothstep(-afwidth, afwidth, distance);
outputCoverage_Stage0 = vec4(val);
}
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_10.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_10.glsl
index 07ec93a..525fe12 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_10.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_10.glsl
@@ -19,7 +19,7 @@
}
mediump vec4 output_Stage2;
{
- mediump float luma = clamp(dot(vec3(0.2125999927520752, 0.71520000696182251, 0.072200000286102295), output_Stage1.xyz), 0.0, 1.0);
+ mediump float luma = clamp(dot(vec3(0.2125999927520752, 0.7152000069618225, 0.0722000002861023), output_Stage1.xyz), 0.0, 1.0);
output_Stage2 = vec4(0.0, 0.0, 0.0, luma);
}
{
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_11.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_11.glsl
index bc07de6..e8d7417 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_11.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_11.glsl
@@ -18,7 +18,7 @@
mediump vec4 out0_c1_c0;
mediump vec4 inputColor = _input;
{
- mediump float nonZeroAlpha = max(inputColor.w, 9.9999997473787516e-05);
+ mediump float nonZeroAlpha = max(inputColor.w, 9.999999747378752e-05);
inputColor = vec4(inputColor.xyz / nonZeroAlpha, nonZeroAlpha);
}
out0_c1_c0 = um_Stage2_c1_c0_c0_c0 * inputColor + uv_Stage2_c1_c0_c0_c0;
@@ -32,7 +32,7 @@
}
mediump vec4 stage_Stage2_c1_c0_c1_c0(mediump vec4 _input) {
mediump vec4 _sample1278;
- mediump float nonZeroAlpha = max(_input.w, 9.9999997473787516e-05);
+ mediump float nonZeroAlpha = max(_input.w, 9.999999747378752e-05);
mediump vec4 coord = vec4(_input.xyz / nonZeroAlpha, nonZeroAlpha);
coord = coord * 0.9960939884185791 + vec4(0.0019529999699443579, 0.0019529999699443579, 0.0019529999699443579, 0.0019529999699443579);
_sample1278.w = texture2D(uTextureSampler_0_Stage2, vec2(coord.w, 0.125)).w;
diff --git a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_12.glsl b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_12.glsl
index a29ab60..1db4bf2 100644
--- a/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_12.glsl
+++ b/src/cobalt/renderer/glimp_shaders/glsl/fragment_skia_color_texture_12.glsl
@@ -7,7 +7,7 @@
varying mediump vec4 vcolor_Stage0;
mediump vec4 stage_Stage1_c0_c0_c0_c0_c0_c0(mediump vec4 _input) {
mediump vec4 _sample453_c0_c0_c0_c0;
- mediump float t = vTransformedCoords_0_Stage0.x + 9.9999997473787516e-06;
+ mediump float t = vTransformedCoords_0_Stage0.x + 9.999999747378752e-06;
_sample453_c0_c0_c0_c0 = vec4(t, 1.0, 0.0, 0.0);
return _sample453_c0_c0_c0_c0;
}
diff --git a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
index 7a4a543..eaf9838 100644
--- a/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/shader_program_manager.cc
@@ -28,12 +28,17 @@
ShaderProgramManager::ShaderProgramManager() {
// These are shaders that get instantiated during video playback when the
- // users starts interacting with the transport controls. They are preloaded
+ // user 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>();
+ Preload<ShaderVertexRcornerTexcoord,
+ ShaderFragmentRcornerTexcoordColorYuv3>();
+ Preload<ShaderVertexRcorner, ShaderFragmentRcornerColor>();
+ Preload<ShaderVertexColorTexcoord, ShaderFragmentColorTexcoord>();
+ Preload<ShaderVertexColorTexcoord, ShaderFragmentColorTexcoordYuv3>();
}
ShaderProgramManager::~ShaderProgramManager() {
diff --git a/src/cobalt/site/docs/contributors/index.md b/src/cobalt/site/docs/contributors/index.md
index ea93cc6..77811fa 100644
--- a/src/cobalt/site/docs/contributors/index.md
+++ b/src/cobalt/site/docs/contributors/index.md
@@ -52,7 +52,7 @@
* Ensure you or your company have signed the appropriate CLA as discussed
in the [Before You Contribute](#before-you-contribute) section above.
* Rebase your changes down into a single git commit.
-* Run `git cl upload` to upload the review to
+* Run `git push origin HEAD:refs/for/master` to upload the review to
[Cobalt's Gerrit instance](https://cobalt-review.googlesource.com/).
* Someone from the maintainers team reviews the code, adding comments on
any things that need to change before the code can be submitted.
diff --git a/src/cobalt/tools/automated_testing/cobalt_runner.py b/src/cobalt/tools/automated_testing/cobalt_runner.py
index d1c878b..1c26ae3 100644
--- a/src/cobalt/tools/automated_testing/cobalt_runner.py
+++ b/src/cobalt/tools/automated_testing/cobalt_runner.py
@@ -85,7 +85,8 @@
url,
log_file=None,
target_params=None,
- success_message=None):
+ success_message=None,
+ log_handler=None):
"""CobaltRunner constructor.
Args:
@@ -112,6 +113,8 @@
'webdriver')
self.launcher_params = launcher_params
+ self.log_handler = log_handler
+
if log_file:
self.log_file = open(log_file)
logging.basicConfig(stream=self.log_file, level=logging.INFO)
@@ -152,6 +155,14 @@
"""Sends a system signal to put Cobalt into stopped state."""
self.launcher.SendStop()
+ def SendSystemSuspend(self):
+ """Ask the system to suspend Cobalt."""
+ self.launcher.SendSystemSuspend()
+
+ def SendSystemResume(self):
+ """Ask the system to resume Cobalt."""
+ self.launcher.SendSystemResume()
+
def SendDeepLink(self, link):
"""Sends a deep link to Cobalt."""
return self.launcher.SendDeepLink(link)
@@ -175,17 +186,21 @@
while True:
line = self.launcher_read_pipe.readline()
if line:
+ if self.log_handler is not None:
+ self.log_handler(line)
self.log_file.write(line)
# Calling flush() to ensure the logs are delivered timely.
self.log_file.flush()
else:
break
- if RE_WINDOWDRIVER_CREATED.search(line):
+ if not self.windowdriver_created.set() and \
+ RE_WINDOWDRIVER_CREATED.search(line):
self.windowdriver_created.set()
continue
- if RE_WEBMODULE_LOADED.search(line):
+ if not self.webmodule_loaded.set() and \
+ RE_WEBMODULE_LOADED.search(line):
self.webmodule_loaded.set()
continue
diff --git a/src/cobalt/webdriver/keyboard.cc b/src/cobalt/webdriver/keyboard.cc
index 285dc9e..59331ac 100644
--- a/src/cobalt/webdriver/keyboard.cc
+++ b/src/cobalt/webdriver/keyboard.cc
@@ -162,6 +162,32 @@
dom::keycode::kLwin, // kSpecialKey_Meta
};
+// Besides of selenium defined special keys, we need additional special keys
+// for media control. The following utf-8 code could be provided as "keys"
+// sent to WebDriver, and should be mapped to the corresponding keyboard code.
+enum AdditionalSpecialKey {
+ kFirstAdditionalSpecialKey = 0xF000,
+ kSpecialKey_MediaNextTrack = kFirstAdditionalSpecialKey,
+ kSpecialKey_MediaPrevTrack,
+ kSpecialKey_MediaStop,
+ kSpecialKey_MediaPlayPause,
+ kSpecialKey_MediaRewind,
+ kSpecialKey_MediaFastForward,
+ kLastAdditionalSpecialKey = kSpecialKey_MediaFastForward,
+};
+
+// Mapping from an additional special keycode to virtual keycode. Subtract
+// kFirstAdditionalSpecialKey from the integer value of the WebDriver keycode
+// and index into this table.
+const int32 additional_special_keycode_mapping[] = {
+ dom::keycode::kMediaNextTrack, // kMediaNextTrack,
+ dom::keycode::kMediaPrevTrack, // kMediaPrevTrack,
+ dom::keycode::kMediaStop, // kMediaStop,
+ dom::keycode::kMediaPlayPause, // kMediaPlayPause,
+ dom::keycode::kMediaRewind, // kMediaRewind,
+ dom::keycode::kMediaFastForward, // kMediaFastForward,
+};
+
// Check that the mapping is the expected size.
const int kLargestMappingIndex = kLastSpecialKey - kFirstSpecialKey;
COMPILE_ASSERT(arraysize(special_keycode_mapping) == kLargestMappingIndex + 1,
@@ -249,6 +275,11 @@
return webdriver_key >= kFirstSpecialKey && webdriver_key < kLastSpecialKey;
}
+bool IsAdditionalSpecialKey(int webdriver_key) {
+ return webdriver_key >= kFirstAdditionalSpecialKey &&
+ webdriver_key <= kLastAdditionalSpecialKey;
+}
+
bool IsModifierKey(int webdriver_key) {
return webdriver_key == kSpecialKey_Alt ||
webdriver_key == kSpecialKey_Shift ||
@@ -294,6 +325,15 @@
return KeyboardEvent::kDomKeyLocationStandard;
}
+// Returns the keycode that corresponds to this additional special key.
+int32 GetAdditionalSpecialKeycode(int32 webdriver_key) {
+ DCHECK(IsAdditionalSpecialKey(webdriver_key));
+ int index = webdriver_key - kFirstAdditionalSpecialKey;
+ DCHECK_GE(index, 0);
+ DCHECK_LT(index, arraysize(additional_special_keycode_mapping));
+ return additional_special_keycode_mapping[index];
+}
+
class KeyTranslator {
public:
explicit KeyTranslator(Keyboard::KeyboardEventVector* event_vector)
@@ -327,6 +367,14 @@
KeyLocationCode location = GetSpecialKeyLocation(webdriver_key);
AddKeyDownEvent(key_code, char_code, location);
AddKeyUpEvent(key_code, char_code, location);
+ } else if (IsAdditionalSpecialKey(webdriver_key)) {
+ // Else if it's an additional special key, translate to key_code and
+ // send key events.
+ int32 key_code = GetAdditionalSpecialKeycode(webdriver_key);
+ int32 char_code = 0;
+ KeyLocationCode location = KeyboardEvent::kDomKeyLocationStandard;
+ AddKeyDownEvent(key_code, char_code, location);
+ AddKeyUpEvent(key_code, char_code, location);
} else {
DCHECK_GE(webdriver_key, 0);
DCHECK_LT(webdriver_key, std::numeric_limits<char>::max());
diff --git a/src/cobalt/webdriver/window_driver.cc b/src/cobalt/webdriver/window_driver.cc
index 1cb4432..6a33518 100644
--- a/src/cobalt/webdriver/window_driver.cc
+++ b/src/cobalt/webdriver/window_driver.cc
@@ -479,9 +479,6 @@
script_executor_ = base::AsWeakPtr(script_executor.get());
}
- DLOG(INFO) << "Executing: " << script.function_body();
- DLOG(INFO) << "Arguments: " << script.argument_array();
-
auto gc_prevented_params =
ScriptExecutorParams::Create(global_environment, script.function_body(),
script.argument_array(), async_timeout);
diff --git a/src/docker-compose.yml b/src/docker-compose.yml
index 7cb3f94..2401d96 100644
--- a/src/docker-compose.yml
+++ b/src/docker-compose.yml
@@ -259,6 +259,17 @@
PLATFORM: ${PLATFORM:-raspi-2}
CONFIG: ${CONFIG:-debug}
+ raspi-gn:
+ <<: *build-common-definitions
+ build:
+ context: ./docker/linux/raspi
+ dockerfile: ./gn/Dockerfile
+ image: cobalt-build-raspi-gn
+ environment:
+ <<: *shared-build-env
+ PLATFORM: ${PLATFORM:-raspi-2}
+ CONFIG: ${CONFIG:-debug}
+
# Define common build container for Evergreen
build-evergreen:
<<: *build-common-definitions
diff --git a/src/docker/linux/raspi/Dockerfile b/src/docker/linux/raspi/Dockerfile
index 5483068..5f61683 100644
--- a/src/docker/linux/raspi/Dockerfile
+++ b/src/docker/linux/raspi/Dockerfile
@@ -24,6 +24,7 @@
&& apt install -qqy --no-install-recommends \
g++-multilib \
bzip2 \
+ libglib2.0-dev \
&& apt-get clean autoclean \
&& apt-get autoremove -y --purge \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
diff --git a/src/docker/linux/raspi/gn/Dockerfile b/src/docker/linux/raspi/gn/Dockerfile
new file mode 100644
index 0000000..2952fef
--- /dev/null
+++ b/src/docker/linux/raspi/gn/Dockerfile
@@ -0,0 +1,18 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM cobalt-build-raspi
+
+CMD gn gen ${OUTDIR}/${PLATFORM}_${CONFIG} --args="target_platform=\"${PLATFORM}\" build_type=\"${CONFIG}\" target_cpu=\"arm\" treat_warnings_as_errors=false" && \
+ ninja -j ${NINJA_PARALLEL} -C ${OUTDIR}/${PLATFORM}_${CONFIG}
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java
index e5a4254..1f96ccd 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java
@@ -14,10 +14,16 @@
package dev.cobalt.coat;
+import static dev.cobalt.util.Log.TAG;
+
+import dev.cobalt.util.Log;
import dev.cobalt.util.UsedByNative;
/** Abstract class that provides an interface for Cobalt to interact with a platform service. */
public abstract class CobaltService {
+ // Indicate is the service opened, and be able to send data to client
+ protected boolean opened = true;
+
/** Interface that returns an object that extends CobaltService. */
public interface Factory {
/** Create the service. */
@@ -59,14 +65,41 @@
@UsedByNative
public abstract ResponseToClient receiveFromClient(byte[] data);
- /** Close the service. */
+ /**
+ * Close the service.
+ *
+ * <p>Once this function returns, it is invalid to call sendToClient for the nativeService, so
+ * synchronization must be used to protect against this.
+ */
@SuppressWarnings("unused")
@UsedByNative
+ public void onClose() {
+ synchronized (this) {
+ opened = false;
+ close();
+ }
+ }
+
public abstract void close();
- /** Send data from the service to the client. */
+ /**
+ * Send data from the service to the client.
+ *
+ * <p>This may be called from a separate thread, do not call nativeSendToClient() once onClose()
+ * is processed.
+ */
protected void sendToClient(long nativeService, byte[] data) {
- nativeSendToClient(nativeService, data);
+ synchronized (this) {
+ if (!opened) {
+ Log.w(
+ TAG,
+ "Platform service did not send data to client, because client already closed the"
+ + " platform service.");
+ return;
+ }
+
+ nativeSendToClient(nativeService, data);
+ }
}
private native void nativeSendToClient(long nativeService, byte[] data);
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
index 455f47b..68d95ef 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.hardware.input.InputManager;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.net.ConnectivityManager;
@@ -33,6 +34,7 @@
import android.util.Size;
import android.util.SizeF;
import android.view.Display;
+import android.view.InputDevice;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.CaptioningManager;
import androidx.annotation.Nullable;
@@ -172,7 +174,7 @@
Service service = serviceHolder.get();
if (service == null) {
if (appContext == null) {
- Log.w(TAG, "Activiy already destoryed.");
+ Log.w(TAG, "Activiy already destroyed.");
return;
}
Log.i(TAG, "Cold start - Instantiating a MediaPlaybackService.");
@@ -250,6 +252,7 @@
@UsedByNative
public void requestStop(int errorLevel) {
if (!starboardStopped) {
+ Log.i(TAG, "Request to stop");
nativeStopApp(errorLevel);
}
}
@@ -261,6 +264,7 @@
public void requestSuspend() {
Activity activity = activityHolder.get();
if (activity != null) {
+ Log.i(TAG, "Request to suspend");
activity.finish();
}
}
@@ -435,7 +439,21 @@
// connected input audio device is a microphone.
AudioManager audioManager = (AudioManager) appContext.getSystemService(AUDIO_SERVICE);
AudioDeviceInfo[] devices = audioManager.getDevices(GET_DEVICES_INPUTS);
- return devices.length > 0;
+ if (devices.length > 0) {
+ return true;
+ }
+
+ // fallback to check for BT voice capable RCU
+ InputManager inputManager = (InputManager) appContext.getSystemService(Context.INPUT_SERVICE);
+ final int[] inputDeviceIds = inputManager.getInputDeviceIds();
+ for (int inputDeviceId : inputDeviceIds) {
+ final InputDevice inputDevice = inputManager.getInputDevice(inputDeviceId);
+ final boolean hasMicrophone = inputDevice.hasMicrophone();
+ if (hasMicrophone) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -682,9 +700,7 @@
cobaltServices.remove(serviceName);
}
- /**
- * Returns the application start timestamp.
- */
+ /** Returns the application start timestamp. */
@SuppressWarnings("unused")
@UsedByNative
protected long getAppStartTimestamp() {
@@ -693,8 +709,8 @@
long javaStartTimestamp = ((CobaltActivity) activity).getAppStartTimestamp();
long cppTimestamp = nativeSbTimeGetMonotonicNow();
long javaStopTimestamp = System.nanoTime();
- return cppTimestamp -
- (javaStartTimestamp - javaStopTimestamp) / timeNanosecondsPerMicrosecond;
+ return cppTimestamp
+ - (javaStartTimestamp - javaStopTimestamp) / timeNanosecondsPerMicrosecond;
}
return 0;
}
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
index 18418e8..f265e98 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
@@ -97,6 +97,11 @@
|| info.getType() == AudioDeviceInfo.TYPE_HDMI)) {
// TODO: Avoid destroying the AudioTrack if the new devices can support the current
// AudioFormat.
+ Log.v(
+ TAG,
+ String.format(
+ "Setting |hasAudioDeviceChanged| to true for audio device %s, %s.",
+ info.getProductName(), getDeviceTypeNameV23(info.getType())));
hasAudioDeviceChanged.set(true);
break;
}
@@ -105,6 +110,11 @@
@Override
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ Log.v(
+ TAG,
+ String.format(
+ "onAudioDevicesAdded() called, |initialDevicesAdded| is: %b.",
+ initialDevicesAdded));
if (initialDevicesAdded) {
handleConnectedDeviceChange(addedDevices);
return;
@@ -114,6 +124,7 @@
@Override
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ Log.v(TAG, "onAudioDevicesRemoved() called.");
handleConnectedDeviceChange(removedDevices);
}
},
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
index eaeb513..c31e017 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
@@ -464,7 +464,8 @@
// focus if |explicitUserActionRequired| is true. Currently we're not able to recognize
// it. But if we don't have window focus, we know the user is not interacting with our app
// and we should not request media focus.
- if (!explicitUserActionRequired || activityHolder.get().hasWindowFocus()) {
+ if (!explicitUserActionRequired
+ || (activityHolder.get() != null && activityHolder.get().hasWindowFocus())) {
explicitUserActionRequired = false;
configureMediaFocus(playbackState);
} else {
@@ -478,7 +479,7 @@
}
// Ignore updates to the MediaSession metadata if playback is stopped.
- if (playbackState == PLAYBACK_STATE_NONE) {
+ if (playbackState == PLAYBACK_STATE_NONE || mediaSession == null) {
return;
}
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
index 0ca198c..2473e28 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
@@ -351,7 +351,8 @@
float maxMasteringLuminance,
float minMasteringLuminance,
int maxCll,
- int maxFall) {
+ int maxFall,
+ boolean forceBigEndianHdrMetadata) {
this.colorRange = colorRange;
this.colorStandard = colorStandard;
this.colorTransfer = colorTransfer;
@@ -365,7 +366,14 @@
// This logic is inspired by
// https://github.com/google/ExoPlayer/blob/deb9b301b2c7ef66fdd7d8a3e58298a79ba9c619/library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java#L1803.
- ByteBuffer hdrStaticInfo = ByteBuffer.allocateDirect(25).order(ByteOrder.LITTLE_ENDIAN);
+ ByteBuffer hdrStaticInfo = ByteBuffer.allocateDirect(25);
+ // Force big endian in case the HDR metadata causes problems in production.
+ if (forceBigEndianHdrMetadata) {
+ hdrStaticInfo.order(ByteOrder.BIG_ENDIAN);
+ } else {
+ hdrStaticInfo.order(ByteOrder.LITTLE_ENDIAN);
+ }
+
hdrStaticInfo.put((byte) 0);
hdrStaticInfo.putShort((short) ((primaryRChromaticityX * MAX_CHROMATICITY) + 0.5f));
hdrStaticInfo.putShort((short) ((primaryRChromaticityY * MAX_CHROMATICITY) + 0.5f));
diff --git a/src/starboard/android/shared/BUILD.gn b/src/starboard/android/shared/BUILD.gn
index 66628a2..a84353e 100644
--- a/src/starboard/android/shared/BUILD.gn
+++ b/src/starboard/android/shared/BUILD.gn
@@ -180,7 +180,6 @@
"//starboard/shared/signal/suspend_signals.cc",
"//starboard/shared/signal/suspend_signals.h",
"//starboard/shared/signal/system_request_conceal.cc",
- "//starboard/shared/signal/system_request_freeze_no_freezedone_callback.cc",
"//starboard/shared/starboard/application.cc",
"//starboard/shared/starboard/application.h",
"//starboard/shared/starboard/audio_sink/audio_sink_create.cc",
@@ -381,6 +380,7 @@
"system_has_capability.cc",
"system_network_is_disconnected.cc",
"system_platform_error.cc",
+ "system_request_freeze_no_freezedone_callback.cc",
"system_request_stop.cc",
"system_request_suspend.cc",
"thread_create.cc",
diff --git a/src/starboard/android/shared/launcher.py b/src/starboard/android/shared/launcher.py
index 5f591dc..abd2787 100644
--- a/src/starboard/android/shared/launcher.py
+++ b/src/starboard/android/shared/launcher.py
@@ -279,7 +279,11 @@
# Setup for running executable
self._CheckCallAdb('wait-for-device')
self._Shutdown()
-
+ # TODO: Need to wait until cobalt fully shutdown. Otherwise, it may get
+ # dirty logs from previous test, and logs like "***Application Stopped***"
+ # will cause unexpected errors.
+ # Simply wait 2s as a temperary solution.
+ time.sleep(2)
# Clear logcat
self._CheckCallAdb('logcat', '-c')
diff --git a/src/starboard/android/shared/media_codec_bridge.cc b/src/starboard/android/shared/media_codec_bridge.cc
index 0415adc..3b5427d 100644
--- a/src/starboard/android/shared/media_codec_bridge.cc
+++ b/src/starboard/android/shared/media_codec_bridge.cc
@@ -198,6 +198,7 @@
const SbMediaColorMetadata* color_metadata,
bool require_software_codec,
int tunnel_mode_audio_session_id,
+ bool force_big_endian_hdr_metadata,
std::string* error_message) {
SB_DCHECK(error_message);
@@ -222,7 +223,7 @@
color_range != COLOR_VALUE_UNKNOWN) {
const auto& mastering_metadata = color_metadata->mastering_metadata;
j_color_info.Reset(env->NewObjectOrAbort(
- "dev/cobalt/media/MediaCodecBridge$ColorInfo", "(IIIFFFFFFFFFFII)V",
+ "dev/cobalt/media/MediaCodecBridge$ColorInfo", "(IIIFFFFFFFFFFIIZ)V",
color_range, color_standard, color_transfer,
mastering_metadata.primary_r_chromaticity_x,
mastering_metadata.primary_r_chromaticity_y,
@@ -233,7 +234,8 @@
mastering_metadata.white_point_chromaticity_x,
mastering_metadata.white_point_chromaticity_y,
mastering_metadata.luminance_max, mastering_metadata.luminance_min,
- color_metadata->max_cll, color_metadata->max_fall));
+ color_metadata->max_cll, color_metadata->max_fall,
+ force_big_endian_hdr_metadata));
}
}
diff --git a/src/starboard/android/shared/media_codec_bridge.h b/src/starboard/android/shared/media_codec_bridge.h
index 8ec1f94..60a6487 100644
--- a/src/starboard/android/shared/media_codec_bridge.h
+++ b/src/starboard/android/shared/media_codec_bridge.h
@@ -112,6 +112,7 @@
const SbMediaColorMetadata* color_metadata,
bool require_software_codec,
int tunnel_mode_audio_session_id,
+ bool force_big_endian_hdr_metadata,
std::string* error_message);
~MediaCodecBridge();
diff --git a/src/starboard/android/shared/media_decoder.cc b/src/starboard/android/shared/media_decoder.cc
index 29567ed..69293d1 100644
--- a/src/starboard/android/shared/media_decoder.cc
+++ b/src/starboard/android/shared/media_decoder.cc
@@ -111,6 +111,7 @@
bool require_software_codec,
const FrameRenderedCB& frame_rendered_cb,
int tunnel_mode_audio_session_id,
+ bool force_big_endian_hdr_metadata,
std::string* error_message)
: media_type_(kSbMediaTypeVideo),
host_(host),
@@ -125,7 +126,7 @@
media_codec_bridge_ = MediaCodecBridge::CreateVideoMediaCodecBridge(
video_codec, width, height, fps, this, j_output_surface, j_media_crypto,
color_metadata, require_software_codec, tunnel_mode_audio_session_id,
- error_message);
+ force_big_endian_hdr_metadata, error_message);
if (!media_codec_bridge_) {
SB_LOG(ERROR) << "Failed to create video media codec bridge with error: "
<< *error_message;
diff --git a/src/starboard/android/shared/media_decoder.h b/src/starboard/android/shared/media_decoder.h
index 6550cdd..569036a 100644
--- a/src/starboard/android/shared/media_decoder.h
+++ b/src/starboard/android/shared/media_decoder.h
@@ -85,6 +85,7 @@
bool require_software_codec,
const FrameRenderedCB& frame_rendered_cb,
int tunnel_mode_audio_session_id,
+ bool force_big_endian_hdr_metadata,
std::string* error_message);
~MediaDecoder();
diff --git a/src/starboard/android/shared/media_is_audio_supported.cc b/src/starboard/android/shared/media_is_audio_supported.cc
index e7b04de..a9c63f6 100644
--- a/src/starboard/android/shared/media_is_audio_supported.cc
+++ b/src/starboard/android/shared/media_is_audio_supported.cc
@@ -45,71 +45,54 @@
if (!mime) {
return false;
}
+
MimeType mime_type(content_type);
- // Allows for disabling the use of the AudioDeviceCallback API to detect when
- // audio peripherals are connected. Enabled by default.
- // (https://developer.android.com/reference/android/media/AudioDeviceCallback)
- auto enable_audio_device_callback_parameter_value =
- mime_type.GetParamStringValue("enableaudiodevicecallback", "");
- if (!enable_audio_device_callback_parameter_value.empty() &&
- enable_audio_device_callback_parameter_value != "true" &&
- enable_audio_device_callback_parameter_value != "false") {
- SB_LOG(INFO) << "Invalid value for audio mime parameter "
- "\"enableaudiodevicecallback\": "
- << enable_audio_device_callback_parameter_value << ".";
- return false;
+ if (strlen(content_type) > 0) {
+ // Allows for disabling the use of the AudioDeviceCallback API to detect
+ // when audio peripherals are connected. Enabled by default.
+ // (https://developer.android.com/reference/android/media/AudioDeviceCallback)
+ mime_type.RegisterBoolParameter("enableaudiodevicecallback");
+ // Allows for enabling tunneled playback. Disabled by default.
+ // (https://source.android.com/devices/tv/multimedia-tunneling)
+ mime_type.RegisterBoolParameter("tunnelmode");
+ // Enables audio passthrough if the codec supports it.
+ mime_type.RegisterBoolParameter("audiopassthrough");
+
+ if (!mime_type.is_valid()) {
+ return false;
+ }
}
- // Allows for enabling tunneled playback. Disabled by default.
- // (https://source.android.com/devices/tv/multimedia-tunneling)
- auto enable_tunnel_mode_parameter_value =
- mime_type.GetParamStringValue("tunnelmode", "");
- if (!enable_tunnel_mode_parameter_value.empty() &&
- enable_tunnel_mode_parameter_value != "true" &&
- enable_tunnel_mode_parameter_value != "false") {
- SB_LOG(INFO) << "Invalid value for audio mime parameter \"tunnelmode\": "
- << enable_tunnel_mode_parameter_value << ".";
- return false;
- } else if (enable_tunnel_mode_parameter_value == "true" &&
- !SbAudioSinkIsAudioSampleTypeSupported(
- kSbMediaAudioSampleTypeInt16Deprecated)) {
+
+ bool enable_tunnel_mode = mime_type.GetParamBoolValue("tunnelmode", false);
+ if (enable_tunnel_mode && !SbAudioSinkIsAudioSampleTypeSupported(
+ kSbMediaAudioSampleTypeInt16Deprecated)) {
SB_LOG(WARNING)
<< "Tunnel mode is rejected because int16 sample is required "
"but not supported.";
return false;
}
- auto audio_passthrough_parameter_value =
- mime_type.GetParamStringValue("audiopassthrough", "");
- if (!audio_passthrough_parameter_value.empty() &&
- audio_passthrough_parameter_value != "true" &&
- audio_passthrough_parameter_value != "false") {
- SB_LOG(INFO) << "Invalid value for audio mime parameter "
- "\"audiopassthrough\": "
- << audio_passthrough_parameter_value
- << ". Passthrough is disabled.";
- return false;
- }
- if (audio_passthrough_parameter_value == "false" && is_passthrough) {
- SB_LOG(INFO) << "Passthrough is rejected because audio mime parameter "
- "\"audiopassthrough\" == false.";
- return false;
- }
-
JniEnvExt* env = JniEnvExt::Get();
ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
- const bool must_support_tunnel_mode =
- enable_tunnel_mode_parameter_value == "true";
auto media_codec_supported =
env->CallStaticBooleanMethodOrAbort(
"dev/cobalt/media/MediaCodecUtil", "hasAudioDecoderFor",
"(Ljava/lang/String;IZ)Z", j_mime.Get(), static_cast<jint>(bitrate),
- must_support_tunnel_mode) == JNI_TRUE;
+ enable_tunnel_mode) == JNI_TRUE;
if (!media_codec_supported) {
return false;
}
+
if (!is_passthrough) {
return true;
}
+
+ if (!mime_type.GetParamBoolValue("audiopassthrough", true)) {
+ SB_LOG(INFO) << "Passthrough codec is rejected because passthrough is "
+ "disabled through mime param.";
+ return false;
+ }
+
SbMediaAudioCodingType coding_type;
switch (audio_codec) {
case kSbMediaAudioCodecAc3:
diff --git a/src/starboard/android/shared/media_is_video_supported.cc b/src/starboard/android/shared/media_is_video_supported.cc
index 332e4b2..095a698 100644
--- a/src/starboard/android/shared/media_is_video_supported.cc
+++ b/src/starboard/android/shared/media_is_video_supported.cc
@@ -95,29 +95,32 @@
if (!mime) {
return false;
}
+ // Check extended parameters for correctness and return false if any invalid
+ // invalid params are found.
MimeType mime_type(content_type);
// Allows for enabling tunneled playback. Disabled by default.
- // (https://source.android.com/devices/tv/multimedia-tunneling)
- auto enable_tunnel_mode_parameter_value =
- mime_type.GetParamStringValue("tunnelmode", "");
- if (!enable_tunnel_mode_parameter_value.empty() &&
- enable_tunnel_mode_parameter_value != "true" &&
- enable_tunnel_mode_parameter_value != "false") {
- SB_LOG(INFO) << "Invalid value for video mime parameter \"tunnelmode\": "
- << enable_tunnel_mode_parameter_value << ".";
+ // https://source.android.com/devices/tv/multimedia-tunneling
+ mime_type.RegisterBoolParameter("tunnelmode");
+ // Override endianness on HDR Info header. Defaults to little.
+ mime_type.RegisterStringParameter("hdrinfoendianness", "big|little");
+
+ if (!mime_type.is_valid()) {
return false;
- } else if (enable_tunnel_mode_parameter_value == "true" &&
- decode_to_texture_required) {
+ }
+
+ bool must_support_tunnel_mode =
+ mime_type.GetParamBoolValue("tunnelmode", false);
+ if (must_support_tunnel_mode && decode_to_texture_required) {
SB_LOG(WARNING) << "Tunnel mode is rejected because output mode decode to "
"texture is required but not supported.";
return false;
}
+
JniEnvExt* env = JniEnvExt::Get();
ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
const bool must_support_hdr = (transfer_id != kSbMediaTransferIdBt709 &&
transfer_id != kSbMediaTransferIdUnspecified);
- const bool must_support_tunnel_mode =
- enable_tunnel_mode_parameter_value == "true";
+
// We assume that if a device supports a format for clear playback, it will
// also support it for encrypted playback. However, some devices require
// tunneled playback to be encrypted, so we must align the tunnel mode
diff --git a/src/starboard/android/shared/platform_configuration/BUILD.gn b/src/starboard/android/shared/platform_configuration/BUILD.gn
index 27b0a08..75b13be 100644
--- a/src/starboard/android/shared/platform_configuration/BUILD.gn
+++ b/src/starboard/android/shared/platform_configuration/BUILD.gn
@@ -60,27 +60,6 @@
cflags += [ "-g" ]
}
- if (sb_pedantic_warnings) {
- cflags += [
- "-Wall",
- "-Wextra",
- "-Wunreachable-code",
-
- # Don"t get pedantic about warnings from base macros. These must be
- # disabled after the -Wall above, so this has to be done here rather
- # than in the platform"s target toolchain.
- # TODO: Rebase base and use static_assert instead of COMPILE_ASSERT
-
- "-Wno-unused-local-typedef", # COMPILE_ASSERT
- "-Wno-missing-field-initializers", # LAZY_INSTANCE_INITIALIZER
-
- # It"s OK not to use some input parameters. Note that the order
- # matters: Wall implies Wunused-parameter and Wno-unused-parameter
- # has no effect if specified before Wall.
- "-Wno-unused-parameter",
- ]
- }
-
if (use_asan) {
cflags += [
"-fsanitize=address",
@@ -206,3 +185,23 @@
config("library_config") {
cflags = [ "-fPIC" ]
}
+
+config("pedantic_warnings") {
+ cflags = [
+ "-Wall",
+ "-Wextra",
+ "-Wunreachable-code",
+
+ # Don't get pedantic about warnings from base macros. These must be
+ # disabled after the -Wall above, so this has to be done here rather
+ # than in the platform's target toolchain.
+ # TODO: Rebase base and use static_assert instead of COMPILE_ASSERT
+ "-Wno-unused-local-typedef", # COMPILE_ASSERT
+ "-Wno-missing-field-initializers", # LAZY_INSTANCE_INITIALIZER
+
+ # It's OK not to use some input parameters. Note that the order
+ # matters: Wall implies Wunused-parameter and Wno-unused-parameter
+ # has no effect if specified before Wall.
+ "-Wno-unused-parameter",
+ ]
+}
diff --git a/src/starboard/android/shared/platform_configuration/configuration.gni b/src/starboard/android/shared/platform_configuration/configuration.gni
index 19850b6..f1c28dd 100644
--- a/src/starboard/android/shared/platform_configuration/configuration.gni
+++ b/src/starboard/android/shared/platform_configuration/configuration.gni
@@ -39,6 +39,9 @@
size_config_path = "//starboard/android/shared/platform_configuration:size"
speed_config_path = "//starboard/android/shared/platform_configuration:speed"
+pedantic_warnings_config_path =
+ "//starboard/android/shared/platform_configuration:pedantic_warnings"
+
executable_configs +=
[ "//starboard/android/shared/platform_configuration:executable_config" ]
shared_library_configs +=
diff --git a/src/starboard/android/shared/platform_service.cc b/src/starboard/android/shared/platform_service.cc
index 7e3a479..0670bc8 100644
--- a/src/starboard/android/shared/platform_service.cc
+++ b/src/starboard/android/shared/platform_service.cc
@@ -78,7 +78,7 @@
void Close(CobaltExtensionPlatformService service) {
JniEnvExt* env = JniEnvExt::Get();
- env->CallVoidMethodOrAbort(service->cobalt_service, "close", "()V");
+ env->CallVoidMethodOrAbort(service->cobalt_service, "onClose", "()V");
ScopedLocalJavaRef<jstring> j_name(
env->NewStringStandardUTFOrAbort(service->name));
env->CallStarboardVoidMethodOrAbort("closeCobaltService",
diff --git a/src/starboard/android/shared/player_components_factory.h b/src/starboard/android/shared/player_components_factory.h
index b2cbfed..037e757 100644
--- a/src/starboard/android/shared/player_components_factory.h
+++ b/src/starboard/android/shared/player_components_factory.h
@@ -183,24 +183,6 @@
return (value + alignment - 1) / alignment * alignment;
}
- static bool IsAudioDeviceCallbackEnabled(
- const CreationParameters& creation_parameters) {
- using starboard::shared::starboard::media::MimeType;
-
- MimeType mime_type(creation_parameters.audio_mime());
- auto enable_audio_device_callback_parameter_value =
- mime_type.GetParamStringValue("enableaudiodevicecallback", "");
- if (enable_audio_device_callback_parameter_value.empty() ||
- enable_audio_device_callback_parameter_value == "true") {
- SB_LOG(INFO) << "AudioDeviceCallback is enabled.";
- return true;
- }
- SB_LOG(INFO) << "Mime attribute \"enableaudiodevicecallback\" is set to: "
- << enable_audio_device_callback_parameter_value
- << ". AudioDeviceCallback is disabled.";
- return false;
- }
-
scoped_ptr<PlayerComponents> CreateComponents(
const CreationParameters& creation_parameters,
std::string* error_message) override {
@@ -215,8 +197,21 @@
}
MimeType audio_mime_type(creation_parameters.audio_mime());
- if (audio_mime_type.GetParamStringValue("audiopassthrough", "") ==
- "false") {
+
+ if (strlen(creation_parameters.audio_mime()) > 0) {
+ audio_mime_type.RegisterBoolParameter("enableaudiodevicecallback");
+ audio_mime_type.RegisterBoolParameter("audiopassthrough");
+ if (!audio_mime_type.is_valid()) {
+ return scoped_ptr<PlayerComponents>();
+ }
+ }
+
+ bool enable_audio_device_callback =
+ audio_mime_type.GetParamBoolValue("enableaudiodevicecallback", true);
+ SB_LOG(INFO) << "AudioDeviceCallback is "
+ << (enable_audio_device_callback ? "enabled." : "disabled.");
+
+ if (!audio_mime_type.GetParamBoolValue("audiopassthrough", true)) {
SB_LOG(INFO) << "Mime attribute \"audiopassthrough\" is set to: "
"false. Passthrough is disabled.";
return scoped_ptr<PlayerComponents>();
@@ -228,7 +223,7 @@
audio_renderer.reset(new AudioRendererPassthrough(
creation_parameters.audio_sample_info(),
GetExtendedDrmSystem(creation_parameters.drm_system()),
- IsAudioDeviceCallbackEnabled(creation_parameters)));
+ enable_audio_device_callback));
if (!audio_renderer->is_valid()) {
return scoped_ptr<PlayerComponents>();
}
@@ -272,31 +267,52 @@
using starboard::shared::starboard::media::MimeType;
SB_DCHECK(error_message);
+ const char* audio_mime =
+ creation_parameters.audio_codec() != kSbMediaAudioCodecNone
+ ? creation_parameters.audio_mime()
+ : "";
+ MimeType audio_mime_type(audio_mime);
+ if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone &&
+ strlen(creation_parameters.audio_mime()) > 0) {
+ audio_mime_type.RegisterBoolParameter("tunnelmode");
+ audio_mime_type.RegisterBoolParameter("enableaudiodevicecallback");
+
+ if (!audio_mime_type.is_valid()) {
+ return false;
+ }
+ }
+
+ const char* video_mime =
+ creation_parameters.video_codec() != kSbMediaVideoCodecNone
+ ? creation_parameters.video_mime()
+ : "";
+ MimeType video_mime_type(video_mime);
+ if (creation_parameters.video_codec() != kSbMediaVideoCodecNone &&
+ strlen(creation_parameters.video_mime()) > 0) {
+ video_mime_type.RegisterBoolParameter("tunnelmode");
+
+ if (!video_mime_type.is_valid()) {
+ return false;
+ }
+ }
+
int tunnel_mode_audio_session_id = -1;
bool enable_tunnel_mode = false;
if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone &&
creation_parameters.video_codec() != kSbMediaVideoCodecNone) {
- MimeType audio_mime_type(creation_parameters.audio_mime());
- MimeType video_mime_type(creation_parameters.video_mime());
- auto enable_tunnel_mode_audio_parameter_value =
- audio_mime_type.GetParamStringValue("tunnelmode", "");
- auto enable_tunnel_mode_video_parameter_value =
- video_mime_type.GetParamStringValue("tunnelmode", "");
- if (enable_tunnel_mode_audio_parameter_value == "true" &&
- enable_tunnel_mode_video_parameter_value == "true") {
- enable_tunnel_mode = true;
- } else {
- if (enable_tunnel_mode_audio_parameter_value.empty()) {
- enable_tunnel_mode_audio_parameter_value = "not provided";
- }
- if (enable_tunnel_mode_video_parameter_value.empty()) {
- enable_tunnel_mode_video_parameter_value = "not provided";
- }
- SB_LOG(INFO) << "Tunnel mode is disabled. Audio mime parameter "
- "\"tunnelmode\" value: "
- << enable_tunnel_mode_audio_parameter_value
+ bool enable_tunnel_mode =
+ audio_mime_type.GetParamBoolValue("tunnelmode", false) &&
+ video_mime_type.GetParamBoolValue("tunnelmode", false);
+
+ if (!enable_tunnel_mode) {
+ SB_LOG(INFO) << "Tunnel mode is disabled. "
+ << "Audio mime parameter \"tunnelmode\" value: "
+ << audio_mime_type.GetParamStringValue("tunnelmode",
+ "<not provided>")
<< ", video mime parameter \"tunnelmode\" value: "
- << enable_tunnel_mode_video_parameter_value << ".";
+ << video_mime_type.GetParamStringValue("tunnelmode",
+ "<not provided>")
+ << ".";
}
} else {
SB_LOG(INFO) << "Tunnel mode requires both an audio and video stream. "
@@ -359,7 +375,10 @@
decoder_creator));
bool enable_audio_device_callback =
- IsAudioDeviceCallbackEnabled(creation_parameters);
+ audio_mime_type.GetParamBoolValue("enableaudiodevicecallback", true);
+ SB_LOG(INFO) << "AudioDeviceCallback is "
+ << (enable_audio_device_callback ? "enabled." : "disabled.");
+
if (tunnel_mode_audio_session_id != -1) {
*audio_renderer_sink = TryToCreateTunnelModeAudioRendererSink(
tunnel_mode_audio_session_id, creation_parameters,
@@ -431,6 +450,20 @@
int tunnel_mode_audio_session_id,
bool force_secure_pipeline_under_tunnel_mode,
std::string* error_message) {
+ using starboard::shared::starboard::media::MimeType;
+ // Use mime param to determine endianness of HDR metadata. If param is
+ // missing or invalid it defaults to Little Endian.
+ MimeType video_mime_type(creation_parameters.video_mime());
+
+ if (strlen(creation_parameters.video_mime()) > 0) {
+ video_mime_type.RegisterStringParameter("hdrinfoendianness",
+ "big|little");
+ }
+ const std::string& hdr_info_endianness =
+ video_mime_type.GetParamStringValue("hdrinfoendianness",
+ /*default=*/"little");
+ bool force_big_endian_hdr_metadata = hdr_info_endianness == "big";
+
scoped_ptr<VideoDecoder> video_decoder(new VideoDecoder(
creation_parameters.video_codec(),
GetExtendedDrmSystem(creation_parameters.drm_system()),
@@ -438,7 +471,7 @@
creation_parameters.decode_target_graphics_context_provider(),
creation_parameters.max_video_capabilities(),
tunnel_mode_audio_session_id, force_secure_pipeline_under_tunnel_mode,
- error_message));
+ force_big_endian_hdr_metadata, error_message));
if (creation_parameters.video_codec() == kSbMediaVideoCodecAv1 ||
video_decoder->is_decoder_created()) {
return video_decoder.Pass();
diff --git a/src/starboard/android/shared/starboard_platform.gypi b/src/starboard/android/shared/starboard_platform.gypi
index 8338cbc..6e61339 100644
--- a/src/starboard/android/shared/starboard_platform.gypi
+++ b/src/starboard/android/shared/starboard_platform.gypi
@@ -164,6 +164,7 @@
'system_has_capability.cc',
'system_network_is_disconnected.cc',
'system_platform_error.cc',
+ 'system_request_freeze_no_freezedone_callback.cc',
'system_request_stop.cc',
'system_request_suspend.cc',
'thread_create.cc',
@@ -357,7 +358,6 @@
'<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
'<(DEPTH)/starboard/shared/signal/suspend_signals.h',
'<(DEPTH)/starboard/shared/signal/system_request_conceal.cc',
- '<(DEPTH)/starboard/shared/signal/system_request_freeze_no_freezedone_callback.cc',
'<(DEPTH)/starboard/shared/starboard/application.cc',
'<(DEPTH)/starboard/shared/starboard/application.h',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
diff --git a/src/starboard/android/shared/system_get_property.cc b/src/starboard/android/shared/system_get_property.cc
index fa9b8ce..9d17a69 100644
--- a/src/starboard/android/shared/system_get_property.cc
+++ b/src/starboard/android/shared/system_get_property.cc
@@ -106,10 +106,9 @@
value_length, kUnknownValue);
case kSbSystemPropertyModelYear: {
char key1[PROP_VALUE_MAX] = "";
- SB_DCHECK(GetAndroidSystemProperty("ro.oem.key1", key1, PROP_VALUE_MAX,
- kUnknownValue));
- if (strcmp(key1, kUnknownValue) == 0 ||
- strlen(key1) < 10) {
+ GetAndroidSystemProperty("ro.oem.key1", key1, PROP_VALUE_MAX,
+ kUnknownValue);
+ if (strcmp(key1, kUnknownValue) == 0 || strlen(key1) < 10) {
return CopyStringAndTestIfSuccess(out_value, value_length,
kUnknownValue);
}
diff --git a/src/starboard/shared/signal/system_request_freeze_no_freezedone_callback.cc b/src/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
similarity index 74%
rename from src/starboard/shared/signal/system_request_freeze_no_freezedone_callback.cc
rename to src/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
index ad245d6..d245e9e 100644
--- a/src/starboard/shared/signal/system_request_freeze_no_freezedone_callback.cc
+++ b/src/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
@@ -14,9 +14,12 @@
#include "starboard/system.h"
+#include "starboard/android/shared/jni_env_ext.h"
#include "starboard/shared/signal/signal_internal.h"
#include "starboard/shared/starboard/application.h"
+using starboard::android::shared::JniEnvExt;
+
#if SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
#include "starboard/loader_app/pending_restart.h"
#endif // SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
@@ -29,16 +32,22 @@
SbLogFlush();
starboard::shared::starboard::Application::Get()->Stop(0);
} else {
- // Let the platform decide if directly transit into Frozen. There
- // is no FreezeDone callback for stopping all thread execution
+ // There is no FreezeDone callback for stopping all thread execution
// after fully transitioning into Frozen.
starboard::shared::starboard::Application::Get()->Freeze(NULL, NULL);
+
+ // Let Android platform directly transit into Frozen.
+ JniEnvExt* env = JniEnvExt::Get();
+ env->CallStarboardVoidMethodOrAbort("requestSuspend", "()V");
}
#else
- // Let the platform decide if directly transit into Frozen. There
- // is no FreezeDone callback for stopping all thread execution
+ // There is no FreezeDone callback for stopping all thread execution
// after fully transitioning into Frozen.
starboard::shared::starboard::Application::Get()->Freeze(NULL, NULL);
+
+ // Let Android platform directly transit into Frozen.
+ JniEnvExt* env = JniEnvExt::Get();
+ env->CallStarboardVoidMethodOrAbort("requestSuspend", "()V");
#endif // SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
}
#endif // SB_API_VERSION >= 13
diff --git a/src/starboard/android/shared/video_decoder.cc b/src/starboard/android/shared/video_decoder.cc
index bdd2226..775496c 100644
--- a/src/starboard/android/shared/video_decoder.cc
+++ b/src/starboard/android/shared/video_decoder.cc
@@ -218,6 +218,7 @@
const char* max_video_capabilities,
int tunnel_mode_audio_session_id,
bool force_secure_pipeline_under_tunnel_mode,
+ bool force_big_endian_hdr_metadata,
std::string* error_message)
: video_codec_(video_codec),
drm_system_(static_cast<DrmSystem*>(drm_system)),
@@ -228,7 +229,8 @@
has_new_texture_available_(false),
surface_condition_variable_(surface_destroy_mutex_),
require_software_codec_(max_video_capabilities &&
- strlen(max_video_capabilities) > 0) {
+ strlen(max_video_capabilities) > 0),
+ force_big_endian_hdr_metadata_(force_big_endian_hdr_metadata) {
SB_DCHECK(error_message);
if (tunnel_mode_audio_session_id != -1) {
@@ -556,7 +558,8 @@
drm_system_, color_metadata_ ? &*color_metadata_ : nullptr,
require_software_codec_,
std::bind(&VideoDecoder::OnTunnelModeFrameRendered, this, _1),
- tunnel_mode_audio_session_id_, error_message));
+ tunnel_mode_audio_session_id_, force_big_endian_hdr_metadata_,
+ error_message));
if (media_decoder_->is_valid()) {
if (error_cb_) {
media_decoder_->Initialize(
diff --git a/src/starboard/android/shared/video_decoder.h b/src/starboard/android/shared/video_decoder.h
index 69c2cfd..cc5dc1e 100644
--- a/src/starboard/android/shared/video_decoder.h
+++ b/src/starboard/android/shared/video_decoder.h
@@ -67,6 +67,7 @@
const char* max_video_capabilities,
int tunnel_mode_audio_session_id,
bool force_secure_pipeline_under_tunnel_mode,
+ bool force_big_endian_hdr_metadata,
std::string* error_message);
~VideoDecoder() override;
@@ -135,6 +136,9 @@
// the main player and SW decoder for sub players.
const bool require_software_codec_;
+ // Force endianness of HDR Metadata.
+ const bool force_big_endian_hdr_metadata_;
+
const int tunnel_mode_audio_session_id_ = -1;
// On some platforms tunnel mode is only supported in the secure pipeline. So
// we create a dummy drm system to force the video playing in secure pipeline
diff --git a/src/starboard/android/x86/gyp_configuration.py b/src/starboard/android/x86/gyp_configuration.py
index 2caad96..80de3ec 100644
--- a/src/starboard/android/x86/gyp_configuration.py
+++ b/src/starboard/android/x86/gyp_configuration.py
@@ -47,9 +47,23 @@
'.SunnyDaySourceForDestination/*',
'SbMediaSetAudioWriteDurationTests/SbMediaSetAudioWriteDurationTest'
'.WriteContinuedLimitedInput/*',
+ 'SbMediaSetAudioWriteDurationTests/SbMediaSetAudioWriteDurationTest'
+ '.WriteLimitedInput/*',
],
'player_filter_tests': [
- 'VideoDecoderTests/*',
'AudioDecoderTests/*',
+ 'VideoDecoderTests/*',
+
+ 'PlayerComponentsTests/PlayerComponentsTest.Preroll/*',
+ 'PlayerComponentsTests/PlayerComponentsTest.Pause/*',
+
+ 'PlayerComponentsTests/PlayerComponentsTest.*/2',
+ 'PlayerComponentsTests/PlayerComponentsTest.*/4',
+ 'PlayerComponentsTests/PlayerComponentsTest.*/9',
+ 'PlayerComponentsTests/PlayerComponentsTest.*/11',
+ 'PlayerComponentsTests/PlayerComponentsTest.*/16',
+ 'PlayerComponentsTests/PlayerComponentsTest.*/17',
+ 'PlayerComponentsTests/PlayerComponentsTest.*/20',
+ 'PlayerComponentsTests/PlayerComponentsTest.*/21',
],
}
diff --git a/src/starboard/build/config/BUILD.gn b/src/starboard/build/config/BUILD.gn
index 05310b8..e903cb3 100644
--- a/src/starboard/build/config/BUILD.gn
+++ b/src/starboard/build/config/BUILD.gn
@@ -149,3 +149,15 @@
configs = [ size_config_path ]
}
}
+
+config("pedantic_warnings") {
+ if (defined(pedantic_warnings_config_path)) {
+ configs = [ pedantic_warnings_config_path ]
+ }
+}
+
+config("no_pedantic_warnings") {
+ if (defined(no_pedantic_warnings_config_path)) {
+ configs = [ no_pedantic_warnings_config_path ]
+ }
+}
diff --git a/src/starboard/build/config/BUILDCONFIG.gn b/src/starboard/build/config/BUILDCONFIG.gn
index 31a8fd5..38169b8 100644
--- a/src/starboard/build/config/BUILDCONFIG.gn
+++ b/src/starboard/build/config/BUILDCONFIG.gn
@@ -101,11 +101,12 @@
default_compiler_configs = [
"//build/config/compiler:default_include_dirs",
"//build/config/compiler:no_exceptions",
+ "//$starboard_path/platform_configuration",
"//starboard/build/config:base",
"//starboard/build/config:host",
"//starboard/build/config:size",
"//starboard/build/config:target",
- "//$starboard_path/platform_configuration",
+ "//starboard/build/config:no_pedantic_warnings",
]
if (is_starboard) {
diff --git a/src/starboard/build/config/base_configuration.gni b/src/starboard/build/config/base_configuration.gni
index 3b798ea..f2ea066 100644
--- a/src/starboard/build/config/base_configuration.gni
+++ b/src/starboard/build/config/base_configuration.gni
@@ -23,10 +23,6 @@
# value is meant to be overridden by a Starboard ABI file.
sb_api_version = 13
- # Enabling this variable enables pedantic levels of warnings for the current
- # toolchain.
- sb_pedantic_warnings = false
-
# Enables embedding Cobalt as a shared library within another app. This
# requires a 'lib' starboard implementation for the corresponding platform.
sb_enable_lib = false
diff --git a/src/starboard/build/doc/gn_migrate_stub_to_platform.md b/src/starboard/build/doc/gn_migrate_stub_to_platform.md
new file mode 100644
index 0000000..f07e338
--- /dev/null
+++ b/src/starboard/build/doc/gn_migrate_stub_to_platform.md
@@ -0,0 +1,128 @@
+# Stub to Platform GN Migration
+
+This document outlines a step by step process for converting the stub platform's
+GN files to GN files that will be able to be built for your platform. It assumes
+you have an already working port of Starboard using GYP.
+
+## Steps to Migrate Stub Files to your platform's GN Files
+
+This is **one** way for migrating your platform from GYP to GN. The benefit of
+following this is that you can have regular checkpoints to see if your migration
+is going correctly, rather than trying to do the entire migration at once where
+it's uncertain how much progress is being made. \
+Here are the steps to do your migration:
+
+1. [Copy stub files over to your platform and build them](#copy-stub-files-over-to-your-platform-and-build-them).
+2. [Replace stub toolchain with your platform's toolchain](#replace-stub-toolchain-with-your-platforms-toolchain).
+3. [Replace stub configuration with your platform's configuration](#replace-stub-configuration-with-your-platforms-configuration).
+4. [Replace stubbed starboard_platform target sources with your platform's
+ sources](#replace-stubbed-starboardplatform-sources-with-your-platforms-sources).
+
+After each step, you should be able to build the starboard_platform target.
+For example, you would build raspi2 starboard_platform target with the following
+commands:
+```
+$gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel"'
+$ninja -C out/raspi-2gn_devel/ starboard
+```
+
+### Copy Stub Files Over to Your Platform and Build Them
+
+Here is a list of steps outlining which files to copy over and how to build
+those files:
+
+1. Copy over files from the stub implementation to the platform folder. This
+ list gives you an example of which files to copy over for your platform.
+ This is an example for files to be copied over for your platform's port at
+ starboard/YOUR_PLATFORM
+ * starboard/stub/BUILD.gn > starboard/YOUR_PLATFORM/BUILD.gn
+ * starboard/stub/platform_configuration/BUILD.gn >
+ starboard/YOUR_PLATFORM/platform_configuration/BUILD.gn
+ * starboard/stub/platform_configuration/configuration.gni >
+ starboard/YOUR_PLATFORM/platform_configuration/configuration.gni
+ * starboard/stub/toolchain/BUILD.gn >
+ starboard/YOUR_PLATFORM/toolchain/BUILD.gn
+2. Add your platform path to starboard/build/platforms.gni as referenced
+ [here](../migrating_gyp_to_gn.md#adding-your-platform-to-starboard)
+3. Resolve any errors which come up for missing/incorrect file paths. Then, you
+ should be able to build your platform target with the stubbed out files
+ suggested in the above section.
+
+### Replace Stub Toolchain with Your Platform's Toolchain
+
+Follow instructions [here](../migrating_gyp_to_gn.md#migrating-a-toolchain) for
+migrating the toolchain. Resolve errors and build the starboard_platform target
+with the stubbed files.
+
+### Replace Stub Configuration with Your Platform's Configuration
+
+This involves migrating the compiler flags and build variables as referenced
+[here](../migrating_gyp_to_gn.md#migrating-a-platform).
+
+> **Highly recommended** \
+> It’s good to turn off the `treat_warnings_as_errors flag` until you can compile
+> the starboard_platform target with the platform files.
+> If this flag is not disabled you might run into a lot of
+> warnings turned errors and it might take time to solve all those errors.
+> Meanwhile you won't be in a buildable state which might make it uncertain as to
+> how much progress you are actually making.
+> For disabling the flag you can pass that as an argument to gn.
+> Here's an example for disabling the flag for raspi2:
+> ```
+> $gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel" treat_warnings_as_errors=false'
+> ```
+
+Resolve errors and build the starboard_platform target with the stubbed files.
+
+### Replace Stubbed starboard_platform Sources with Your Platform's Sources
+
+This involves adding files for the starboard_platform target as suggested
+[here](../migrating_gyp_to_gn.md#migrating-a-platform).
+
+While building any target, follow the recommendation above of building the
+target with `treat_warnings_as_errors=false`.
+
+Once you can build your platform files, you can remove the
+`treat_warnings_as_errors=false` flag and resolve the warning errors.
+
+## FAQ
+
+1. **I’m getting a build error! What should I do?** \
+ Some common questions to ask yourself:
+
+ * Is the same target building with GYP + ninja (as GN + Ninja)?
+
+ > For example if the `nplb` target is not being built by GN, check first
+ > if it can be built with GYP. If GYP cannot build it, this indicates
+ > that some flags are missing in GYP itself so it might be prudent to
+ > solve that first before migrating to GN.
+
+ * Am I missing a config/dependency to include the missing file?
+
+ > [gn check](https://cobalt.googlesource.com/third_party/gn/+/refs/heads/main/docs/reference.md#cmd_check)
+ > can help point out missing dependencies.
+
+ * Is the same file being included in the build by GYP?
+
+ > Add a preprocessor directive like #error "This file is included" in
+ > that file and see if GYP + Ninja prints out that error message.
+
+ * Is the same code path being followed by GYP + ninja ?
+
+ > Use the same method as above.
+
+ * Are the compiler flags for this file the same as in GYP ?
+
+ > To compare flags for GYP vs GN refer
+ > [section](../migrating_gyp_to_gn.md#validating-a-target). To check if
+ > the variables/flags you are compiling have changed since GYP, refer
+ > [page](../migration_changes.md).
+
+ * Have you passed in the default arguments for your platform correctly?
+
+ > Default variables such as `target_cpu`, `target_os` and others can be
+ > overridden by passing arguments to gn while building. Here's an
+ > example of passing the default argument `target_cpu` for raspi2:
+ > ```
+ > $gn gen out/raspi-2gn_devel --args='target_platform="raspi-2" build_type="devel" target_cpu="arm"'
+ > ```
diff --git a/src/starboard/build/doc/migrating_gyp_to_gn.md b/src/starboard/build/doc/migrating_gyp_to_gn.md
index 2333987..1a3d4bb 100644
--- a/src/starboard/build/doc/migrating_gyp_to_gn.md
+++ b/src/starboard/build/doc/migrating_gyp_to_gn.md
@@ -203,6 +203,21 @@
comparison tool, i.e. [meld](https://meldmerge.org/). This will allow you to see
any changes in commands, i.e. with flags or otherwise.
+The name of the intermediate .o, .d files is different in both cases: this
+doesn't cause any issues. Keep this in mind while comparing the ninja flags for
+GYP vs GN. Here is an example for raspi2 while compiling the same source file
+```
+starboard/common/new.cc
+```
+GYP generates:
+```
+obj/starboard/common/common.new.cc.o
+```
+GN generates:
+```
+obj/starboard/common/common/new.o
+```
+
### Validating a Platform
Checking that an entire platform has been migrated successfully is slightly more
@@ -214,6 +229,12 @@
You can use the same comparison method of using `format_ninja.py` as discussed
[in the section above](#validating-a-target).
+### Step by Step Stub to Your Platform Migration Guide
+
+This [document](../gn_migrate_stub_to_platform.md) outlines a step by step
+process for converting the stub platform's GN files to GN files that will be
+able to be built for your platform.
+
[cobalt_porting_guide]: https://cobalt.dev/starboard/porting.html
[gn_check_tool]: https://cobalt.googlesource.com/third_party/gn/+/refs/heads/main/docs/reference.md#cmd_check
[gn_doc_home]: https://cobalt.googlesource.com/third_party/gn/+/refs/heads/main/docs
diff --git a/src/starboard/build/doc/migration_changes.md b/src/starboard/build/doc/migration_changes.md
index 145ed7b..433d1a3 100644
--- a/src/starboard/build/doc/migration_changes.md
+++ b/src/starboard/build/doc/migration_changes.md
@@ -38,6 +38,8 @@
`optimize_target_for_speed` (1) | `"//starboard/build/config:speed"` | Optimizations
`compiler_flags_*_speed` | `speed_config_path` | Optimizations
`compiler_flags_*_size` | `size_config_path` | Optimizations
+`sb_pedantic_warnings` | `pedantic_warnings_config_path` | Compiler Options
+`sb_pedantic_warnings` | `no_pedantic_warnings_config_path` | Compiler Options
Notes:
@@ -53,3 +55,14 @@
configurations for your platform by creating `config`s and pointing to the
correct ones for `speed_config_path` and `size_config_path` in your
platform's `platform_configuration/configuration.gni` file.
+
+* *Compiler Options:* Cobalt compiles some targets with stricter settings
+ than others, depending on the platform. Before these targets would opt into
+ the stricter settings by settings `sb_pedantic_warnings: 1` in their
+ `variables` section. Now they will add the appropriate config like so:
+ `configs += [ "//starboard/build/config:pedantic_warnings" ]` and remove
+ the default: `configs -= [ "//starboard/build/config:no_pedantic_warnings"
+ ]`. The additional config that is used to compile these targets is
+ specified with the `pedantic_warnings_config_path` and
+ `no_pedantic_warnings_config_path` variables in your platform's
+ `platform_configuration/configuration.gni` file.
diff --git a/src/starboard/build/platforms.gni b/src/starboard/build/platforms.gni
index b3793c2..ed9af54 100644
--- a/src/starboard/build/platforms.gni
+++ b/src/starboard/build/platforms.gni
@@ -57,4 +57,8 @@
name = "raspi-2"
path = "starboard/raspi/2"
},
+ {
+ name = "raspi-2-skia"
+ path = "starboard/raspi/2/skia"
+ },
]
diff --git a/src/starboard/doc/evergreen/cobalt_evergreen_lite.md b/src/starboard/doc/evergreen/cobalt_evergreen_lite.md
index 1dcbbc1..06dacf9 100644
--- a/src/starboard/doc/evergreen/cobalt_evergreen_lite.md
+++ b/src/starboard/doc/evergreen/cobalt_evergreen_lite.md
@@ -75,7 +75,7 @@
## How is Evergreen different from porting Cobalt previously?
-Same as the [Evergreen full doc](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/doc/evergreen/cobalt_evergreen_overview.md).
+Same as the [Evergreen full doc](cobalt_evergreen_overview.md).
## Building Cobalt Evergreen Components
diff --git a/src/starboard/doc/evergreen/cobalt_evergreen_overview.md b/src/starboard/doc/evergreen/cobalt_evergreen_overview.md
index 75b6940..51130f1 100644
--- a/src/starboard/doc/evergreen/cobalt_evergreen_overview.md
+++ b/src/starboard/doc/evergreen/cobalt_evergreen_overview.md
@@ -408,7 +408,7 @@
The number of installation slots is directly controlled using
`kMaxNumInstallations`, defined in
-[loader\_app.cc](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/loader_app/loader_app.cc).
+[loader\_app.cc](../../loader_app/loader_app.cc).
It is worth noting that all slot configurations specify that the first
installation slot (`SLOT_0`) will always be the read-only factory system image.
@@ -549,7 +549,7 @@
suspending if there is a pending restart.
Please see
-[`suspend_signals.cc`](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/shared/signal/suspend_signals.cc)
+[`suspend_signals.cc`](../../shared/signal/suspend_signals.cc)
for an example.
### Multi-App Support
@@ -644,7 +644,7 @@
```
Please see
-[`loader_app_switches.cc`](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/loader_app/loader_app.cc)
+[`loader_app_switches.cc`](../../loader_app/loader_app.cc)
for full list of available command-line flags.
### Platform Security
diff --git a/src/starboard/doc/evergreen/cobalt_evergreen_reference_port_raspi2.md b/src/starboard/doc/evergreen/cobalt_evergreen_reference_port_raspi2.md
index b72d0f7..a9fec25 100644
--- a/src/starboard/doc/evergreen/cobalt_evergreen_reference_port_raspi2.md
+++ b/src/starboard/doc/evergreen/cobalt_evergreen_reference_port_raspi2.md
@@ -53,8 +53,15 @@
$ cp -r out/evergreen-arm-hardfp-sbversion-12_qa/lib $COEG_PATH/content/app/cobalt/
$ cp -r out/evergreen-arm-hardfp-sbversion-12_qa/content $COEG_PATH/content/app/cobalt/
-## Download the manifest file
-$ curl https://storage.googleapis.com/evergreen_public/latest/manifest.json -o $COEG_PATH/content/app/cobalt/manifest.json
+## Create a file named manifest.json with the following content, and put it under $COEG_PATH/content/app/cobalt/
+$ cat > $COEG_PATH/content/app/cobalt/manifest.json <<EOF
+{
+ "manifest_version": 2,
+ "name": "Cobalt",
+ "description": "Cobalt",
+ "version": "1.0.0"
+}
+EOF
```
## Deployment instructions
diff --git a/src/starboard/doc/starboard_abi.md b/src/starboard/doc/starboard_abi.md
index f97a788..bd0d309 100644
--- a/src/starboard/doc/starboard_abi.md
+++ b/src/starboard/doc/starboard_abi.md
@@ -36,18 +36,18 @@
With the Starboard ABI being the source of truth for all things architecture
related, each platform must now include a Starboard ABI file in its build (see
-[//starboard/sabi](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/sabi)
+[//starboard/sabi](../sabi)
for examples). Starboard ABI files are JSON, and should all contain identical
keys with the values being appropriate for the architecture. Each platform must
override the new
-[GetPathToSabiJsonFile](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/build/platform_configuration.py##339)
+[GetPathToSabiJsonFile](../build/platform_configuration.py##339)
method in its platform configuration to return the path to the desired Starboard
ABI file (e.g.
-[//starboard/linux/shared/gyp\_configuration.py](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/linux/shared/gyp_configuration.py)).
+[//starboard/linux/shared/gyp\_configuration.py](../linux/shared/gyp_configuration.py)).
By default, an empty and invalid Starboard ABI file is provided.
Additionally, all platforms must include the
-[sabi.gypi](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/sabi/sabi.gypi)
+[sabi.gypi](../sabi/sabi.gypi)
in their build configuration. This file will consume the specified Starboard ABI
file, resulting in the creation of a set of GYP variables and preprocessor
macros. The preprocessor macros are passed directly to the compiler and replace
@@ -56,7 +56,7 @@
The newly defined GYP variables need to be transformed into toolchain specific
flags; these flags are what actually makes the build result in a binary for the
desired architecture. These flags will, in most cases, be identical to the flags
-already being used for building.
+already being used for building.
The process outlined above is shown in the diagram below.
@@ -106,14 +106,14 @@
1. When configuring your build, the Starboard ABI file that was specified will
have its values sanity checked against a provided
- [schema](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/sabi/sabi.schema.json).
+ [schema](../sabi/sabi.schema.json).
1. When building, a number of static assertions will assert correctness of a
number of features generated from the Starboard ABI file against the
features of the binary.
1. The NPLB test suite has been expanded to include [additional
- tests](https://cobalt.googlesource.com/cobalt/+/refs/heads/master/src/starboard/nplb/sabi/)
+ tests](../nplb/sabi/)
capable of verifying the remaining features of the binary.
Finally, binaries produced by the Cobalt team for your architecture, including
NPLB, will be made available to ensure end-to-end correctness of the produced
-binaries.
\ No newline at end of file
+binaries.
diff --git a/src/starboard/elf_loader/elf_loader.gyp b/src/starboard/elf_loader/elf_loader.gyp
index 27cdce7..b93ed1d 100644
--- a/src/starboard/elf_loader/elf_loader.gyp
+++ b/src/starboard/elf_loader/elf_loader.gyp
@@ -142,6 +142,7 @@
],
'dependencies': [
'elf_loader_sys',
+ '<(DEPTH)/starboard/elf_loader/sabi_string.gyp:sabi_string',
'<(DEPTH)/starboard/starboard.gyp:starboard',
],
'sources': [
diff --git a/src/starboard/linux/shared/platform_configuration/BUILD.gn b/src/starboard/linux/shared/platform_configuration/BUILD.gn
index e6bfc37..9920863 100644
--- a/src/starboard/linux/shared/platform_configuration/BUILD.gn
+++ b/src/starboard/linux/shared/platform_configuration/BUILD.gn
@@ -99,33 +99,6 @@
"-Wl,-gc-sections",
]
- if (sb_pedantic_warnings) {
- cflags += [
- "-Wall",
- "-Wextra",
- "-Wunreachable-code",
- ]
- } else {
- cflags += [
- # 'this' pointer cannot be NULL...pointer may be assumed
- # to always convert to true.
- "-Wno-undefined-bool-conversion",
-
- # Skia doesn't use overrides.
- "-Wno-inconsistent-missing-override",
-
- # Do not warn for implicit type conversions that may change a value.
- "-Wno-conversion",
-
- # shifting a negative signed value is undefined
- "-Wno-shift-negative-value",
-
- # Width of bit-field exceeds width of its type- value will be truncated
- "-Wno-bitfield-width",
- "-Wno-undefined-var-template",
- ]
- }
-
if (use_asan) {
cflags += [
"-fsanitize=address",
@@ -212,3 +185,32 @@
"-fdata-sections",
]
}
+
+config("pedantic_warnings") {
+ cflags = [
+ "-Wall",
+ "-Wextra",
+ "-Wunreachable-code",
+ ]
+}
+
+config("no_pedantic_warnings") {
+ cflags = [
+ # 'this' pointer cannot be NULL...pointer may be assumed
+ # to always convert to true.
+ "-Wno-undefined-bool-conversion",
+
+ # Skia doesn't use overrides.
+ "-Wno-inconsistent-missing-override",
+
+ # Do not warn for implicit type conversions that may change a value.
+ "-Wno-conversion",
+
+ # shifting a negative signed value is undefined
+ "-Wno-shift-negative-value",
+
+ # Width of bit-field exceeds width of its type- value will be truncated
+ "-Wno-bitfield-width",
+ "-Wno-undefined-var-template",
+ ]
+}
diff --git a/src/starboard/linux/shared/platform_configuration/configuration.gni b/src/starboard/linux/shared/platform_configuration/configuration.gni
index 3618b00..eda9b87 100644
--- a/src/starboard/linux/shared/platform_configuration/configuration.gni
+++ b/src/starboard/linux/shared/platform_configuration/configuration.gni
@@ -27,4 +27,9 @@
speed_config_path = "//starboard/linux/shared/platform_configuration:speed"
size_config_path = "//starboard/linux/shared/platform_configuration:size"
+pedantic_warnings_config_path =
+ "//starboard/linux/shared/platform_configuration:pedantic_warnings"
+no_pedantic_warnings_config_path =
+ "//starboard/linux/shared/platform_configuration:no_pedantic_warnings"
+
sb_widevine_platform = "linux"
diff --git a/src/starboard/nplb/drm_helpers.h b/src/starboard/nplb/drm_helpers.h
index be29b72..2b43c12 100644
--- a/src/starboard/nplb/drm_helpers.h
+++ b/src/starboard/nplb/drm_helpers.h
@@ -132,75 +132,15 @@
0xbc, 0x6a, 0x6b, 0xed, 0x13, 0xfb, 0x0d, 0x49, 0xd3, 0x8a, 0x45, 0xeb,
0x87, 0xa5, 0xf4};
+// Widevine-specific CENC Initialization data.
+// https://www.w3.org/TR/eme-stream-mp4/
+// https://www.w3.org/TR/eme-initdata-cenc/#common-system
static constexpr uint8_t kCencInitData[] = {
- 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00,
- 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
- 0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x14, 0x08, 0x01, 0x12, 0x10,
- 0x31, 0xfd, 0x5b, 0x66, 0x19, 0xfc, 0x5e, 0xad, 0x86, 0x7c, 0xff, 0xb5,
- 0x84, 0xed, 0x4c, 0x19, 0x00, 0x00, 0x02, 0xf4, 0x70, 0x73, 0x73, 0x68,
- 0x00, 0x00, 0x00, 0x00, 0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86,
- 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95, 0x00, 0x00, 0x02, 0xd4,
- 0xd4, 0x02, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xca, 0x02, 0x3c, 0x00,
- 0x57, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x48, 0x00, 0x45, 0x00, 0x41, 0x00,
- 0x44, 0x00, 0x45, 0x00, 0x52, 0x00, 0x20, 0x00, 0x78, 0x00, 0x6d, 0x00,
- 0x6c, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x3d, 0x00, 0x22, 0x00, 0x68, 0x00,
- 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00,
- 0x73, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x61, 0x00,
- 0x73, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00,
- 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x2e, 0x00,
- 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x44, 0x00, 0x52, 0x00,
- 0x4d, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x37, 0x00,
- 0x2f, 0x00, 0x30, 0x00, 0x33, 0x00, 0x2f, 0x00, 0x50, 0x00, 0x6c, 0x00,
- 0x61, 0x00, 0x79, 0x00, 0x52, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00,
- 0x79, 0x00, 0x48, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00,
- 0x72, 0x00, 0x22, 0x00, 0x20, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00,
- 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x3d, 0x00, 0x22, 0x00,
- 0x34, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00,
- 0x30, 0x00, 0x22, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x44, 0x00, 0x41, 0x00,
- 0x54, 0x00, 0x41, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x50, 0x00, 0x52, 0x00,
- 0x4f, 0x00, 0x54, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x49, 0x00,
- 0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4b, 0x00,
- 0x45, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x3e, 0x00,
- 0x31, 0x00, 0x36, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4b, 0x00, 0x45, 0x00,
- 0x59, 0x00, 0x4c, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x3c, 0x00,
- 0x41, 0x00, 0x4c, 0x00, 0x47, 0x00, 0x49, 0x00, 0x44, 0x00, 0x3e, 0x00,
- 0x41, 0x00, 0x45, 0x00, 0x53, 0x00, 0x43, 0x00, 0x54, 0x00, 0x52, 0x00,
- 0x3c, 0x00, 0x2f, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x47, 0x00, 0x49, 0x00,
- 0x44, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x50, 0x00, 0x52, 0x00,
- 0x4f, 0x00, 0x54, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x49, 0x00,
- 0x4e, 0x00, 0x46, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x4b, 0x00,
- 0x49, 0x00, 0x44, 0x00, 0x3e, 0x00, 0x5a, 0x00, 0x6c, 0x00, 0x76, 0x00,
- 0x39, 0x00, 0x4d, 0x00, 0x66, 0x00, 0x77, 0x00, 0x5a, 0x00, 0x72, 0x00,
- 0x56, 0x00, 0x36, 0x00, 0x47, 0x00, 0x66, 0x00, 0x50, 0x00, 0x2b, 0x00,
- 0x31, 0x00, 0x68, 0x00, 0x4f, 0x00, 0x31, 0x00, 0x4d, 0x00, 0x47, 0x00,
- 0x51, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x4b, 0x00,
- 0x49, 0x00, 0x44, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x43, 0x00, 0x48, 0x00,
- 0x45, 0x00, 0x43, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x55, 0x00, 0x4d, 0x00,
- 0x3e, 0x00, 0x4a, 0x00, 0x58, 0x00, 0x46, 0x00, 0x36, 0x00, 0x57, 0x00,
- 0x38, 0x00, 0x41, 0x00, 0x64, 0x00, 0x51, 0x00, 0x2b, 0x00, 0x49, 0x00,
- 0x3d, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x43, 0x00, 0x48, 0x00, 0x45, 0x00,
- 0x43, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x55, 0x00, 0x4d, 0x00, 0x3e, 0x00,
- 0x3c, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x52, 0x00,
- 0x4c, 0x00, 0x3e, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00,
- 0x73, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x77, 0x00, 0x77, 0x00,
- 0x77, 0x00, 0x2e, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00,
- 0x75, 0x00, 0x62, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00,
- 0x6d, 0x00, 0x2f, 0x00, 0x61, 0x00, 0x70, 0x00, 0x69, 0x00, 0x2f, 0x00,
- 0x64, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x2f, 0x00, 0x70, 0x00, 0x6c, 0x00,
- 0x61, 0x00, 0x79, 0x00, 0x72, 0x00, 0x65, 0x00, 0x61, 0x00, 0x64, 0x00,
- 0x79, 0x00, 0x3f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00,
- 0x63, 0x00, 0x65, 0x00, 0x3d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x55, 0x00,
- 0x54, 0x00, 0x55, 0x00, 0x42, 0x00, 0x45, 0x00, 0x26, 0x00, 0x61, 0x00,
- 0x6d, 0x00, 0x70, 0x00, 0x3b, 0x00, 0x76, 0x00, 0x69, 0x00, 0x64, 0x00,
- 0x65, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x3d, 0x00,
- 0x39, 0x00, 0x33, 0x00, 0x39, 0x00, 0x35, 0x00, 0x62, 0x00, 0x34, 0x00,
- 0x36, 0x00, 0x64, 0x00, 0x37, 0x00, 0x64, 0x00, 0x63, 0x00, 0x64, 0x00,
- 0x37, 0x00, 0x38, 0x00, 0x38, 0x00, 0x66, 0x00, 0x3c, 0x00, 0x2f, 0x00,
- 0x4c, 0x00, 0x41, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x52, 0x00, 0x4c, 0x00,
- 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00,
- 0x41, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x57, 0x00, 0x52, 0x00,
- 0x4d, 0x00, 0x48, 0x00, 0x45, 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00,
- 0x52, 0x00, 0x3e, 0x00, 0x00};
+ 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00,
+ 0x00, 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8,
+ 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x14, 0x08,
+ 0x01, 0x12, 0x10, 0x31, 0xfd, 0x5b, 0x66, 0x19, 0xfc, 0x5e, 0xad,
+ 0x86, 0x7c, 0xff, 0xb5, 0x84, 0xed, 0x4c, 0x19};
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn b/src/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
index b6efbad..05753b8 100644
--- a/src/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
@@ -18,6 +18,7 @@
sources = [
"//starboard/common/test_main.cc",
"checks.h",
+ "crashpad_config_test.cc",
"executable_memory_test.cc",
"fonts_test.cc",
"sabi_test.cc",
diff --git a/src/starboard/raspi/2/BUILD.gn b/src/starboard/raspi/2/BUILD.gn
index 3cf94e9..92dbb16 100644
--- a/src/starboard/raspi/2/BUILD.gn
+++ b/src/starboard/raspi/2/BUILD.gn
@@ -14,7 +14,11 @@
static_library("starboard_platform") {
check_includes = false
+ sources = [
+ "//starboard/raspi/shared/configuration.cc",
+ "//starboard/raspi/shared/configuration.h",
+ "//starboard/raspi/shared/system_get_extensions.cc",
+ ]
configs += [ "//starboard/build/config:starboard_implementation" ]
-
public_deps = [ "//starboard/raspi/shared:starboard_platform" ]
}
diff --git a/src/starboard/raspi/2/skia/BUILD.gn b/src/starboard/raspi/2/skia/BUILD.gn
new file mode 100644
index 0000000..bcb5b92
--- /dev/null
+++ b/src/starboard/raspi/2/skia/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+static_library("starboard_platform") {
+ check_includes = false
+ sources = [
+ "//starboard/raspi/2/skia/configuration.cc",
+ "//starboard/raspi/2/skia/configuration.h",
+ "//starboard/raspi/2/skia/system_get_extensions.cc",
+ ]
+ configs += [ "//starboard/build/config:starboard_implementation" ]
+ public_deps = [ "//starboard/raspi/shared:starboard_platform" ]
+}
diff --git a/src/starboard/raspi/2/skia/platform_configuration/BUILD.gn b/src/starboard/raspi/2/skia/platform_configuration/BUILD.gn
new file mode 100644
index 0000000..17a93b7
--- /dev/null
+++ b/src/starboard/raspi/2/skia/platform_configuration/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+config("platform_configuration") {
+ configs = [
+ "//starboard/raspi/2/platform_configuration",
+ ]
+}
diff --git a/src/starboard/raspi/2/skia/platform_configuration/configuration.gni b/src/starboard/raspi/2/skia/platform_configuration/configuration.gni
new file mode 100644
index 0000000..4cd4761
--- /dev/null
+++ b/src/starboard/raspi/2/skia/platform_configuration/configuration.gni
@@ -0,0 +1,15 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/raspi/shared/platform_configuration/configuration.gni")
diff --git a/src/starboard/raspi/2/skia/toolchain/BUILD.gn b/src/starboard/raspi/2/skia/toolchain/BUILD.gn
new file mode 100644
index 0000000..653ad00
--- /dev/null
+++ b/src/starboard/raspi/2/skia/toolchain/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/toolchain/gcc_toolchain.gni")
+import("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
+
+clang_toolchain("host") {
+ clang_base_path = raspi_clang_base_path
+}
+
+gcc_toolchain("target") {
+ cc = "$gcc_toolchain_cc"
+ cxx = "$gcc_toolchain_cxx"
+ ld = cxx
+
+ ar = "$gcc_toolchain_ar"
+ strip = "$gcc_toolchain_strip"
+
+ toolchain_args = {
+ is_clang = false
+ }
+}
diff --git a/src/starboard/raspi/2/toolchain/BUILD.gn b/src/starboard/raspi/2/toolchain/BUILD.gn
index 57a35ca..7e872a6 100644
--- a/src/starboard/raspi/2/toolchain/BUILD.gn
+++ b/src/starboard/raspi/2/toolchain/BUILD.gn
@@ -13,23 +13,20 @@
# limitations under the License.
import("//build/toolchain/gcc_toolchain.gni")
-
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
-raspi_toolchain_path = "$_home_dir/raspi_tools/tools/arm-bcm2708/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin"
+import("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
clang_toolchain("host") {
- clang_base_path = _clang_base_path
+ clang_base_path = raspi_clang_base_path
}
gcc_toolchain("target") {
- cc = "$raspi_toolchain_path/arm-linux-gnueabihf-gcc"
- cxx = "$raspi_toolchain_path/arm-linux-gnueabihf-g++"
+ cc = "$gcc_toolchain_cc"
+ cxx = "$gcc_toolchain_cxx"
ld = cxx
# We use whatever 'ar' resolves to in gyp.
- ar = "ar"
- strip = "$raspi_toolchain_path/arm-linux-gnueabihf-strip"
+ ar = "$gcc_toolchain_ar"
+ strip = "$gcc_toolchain_strip"
toolchain_args = {
is_clang = false
diff --git a/src/starboard/raspi/shared/BUILD.gn b/src/starboard/raspi/shared/BUILD.gn
index 37c97e7..3828973 100644
--- a/src/starboard/raspi/shared/BUILD.gn
+++ b/src/starboard/raspi/shared/BUILD.gn
@@ -18,6 +18,8 @@
}
static_library("starboard_platform_sources") {
+ check_includes = false
+
sources = [
"//starboard/linux/shared/atomic_public.h",
"//starboard/linux/shared/configuration_constants.cc",
@@ -31,8 +33,6 @@
"//starboard/linux/shared/system_has_capability.cc",
"//starboard/raspi/shared/application_dispmanx.cc",
"//starboard/raspi/shared/audio_sink_type_dispatcher.cc",
- "//starboard/raspi/shared/configuration.cc",
- "//starboard/raspi/shared/configuration.h",
"//starboard/raspi/shared/dispmanx_util.cc",
"//starboard/raspi/shared/dispmanx_util.h",
"//starboard/raspi/shared/graphics.cc",
@@ -62,7 +62,6 @@
"//starboard/raspi/shared/open_max/video_decoder.h",
"//starboard/raspi/shared/player_components_factory.cc",
"//starboard/raspi/shared/system_get_device_type.cc",
- "//starboard/raspi/shared/system_get_extensions.cc",
"//starboard/raspi/shared/system_get_property.cc",
"//starboard/raspi/shared/system_gles2.cc",
"//starboard/raspi/shared/thread_create_priority.cc",
@@ -376,7 +375,11 @@
sources += common_player_sources
- configs += [ "//starboard/build/config:starboard_implementation" ]
+ configs += [
+ "//starboard/build/config:pedantic_warnings",
+ "//starboard/build/config:starboard_implementation",
+ ]
+ configs -= [ "//starboard/build/config:no_pedantic_warnings" ]
public_deps = [
":starboard_base_symbolize",
@@ -386,9 +389,7 @@
"//starboard/shared/starboard/media:media_util",
"//starboard/shared/starboard/player/filter:filter_based_player_sources",
]
- if (sb_is_evergreen_compatible) {
- public_deps += [ "//starboard/elf_loader:evergreen_config" ]
- }
+
if (sb_is_evergreen_compatible && !sb_evergreen_compatible_enable_lite) {
public_deps += [ "//starboard/loader_app:pending_restart" ]
}
@@ -397,9 +398,17 @@
"//third_party/libevent",
"//third_party/opus",
]
+
if (sb_evergreen_compatible_use_libunwind) {
deps += [ "//third_party/llvm-project/libunwind:unwind_starboard" ]
}
+
+ if (sb_is_evergreen_compatible) {
+ public_deps += [ "//starboard/elf_loader:evergreen_config" ]
+ deps += [ "//third_party/crashpad/wrapper" ]
+ } else {
+ deps += [ "//third_party/crashpad/wrapper:wrapper_stub" ]
+ }
}
static_library("starboard_base_symbolize") {
diff --git a/src/starboard/raspi/shared/platform_configuration/BUILD.gn b/src/starboard/raspi/shared/platform_configuration/BUILD.gn
index fbbab79..98d7b28 100644
--- a/src/starboard/raspi/shared/platform_configuration/BUILD.gn
+++ b/src/starboard/raspi/shared/platform_configuration/BUILD.gn
@@ -125,29 +125,6 @@
"-std=gnu++14",
"-Wno-literal-suffix",
]
-
- if (sb_pedantic_warnings) {
- cflags += [
- "-Wall",
- "-Wextra",
- "-Wunreachable-code",
-
- # Raspi toolchain is based off an old version of gcc, which
- # falsely flags some code. That same code does not warn with gcc 6.3.
- # This decision should be revisited after raspi toolchain is upgraded.
- "-Wno-maybe-uninitialized",
-
- #TODO: Renable -Werror after fixing all warnings.
- #"-Werror",
- "-Wno-expansion-to-defined",
- "-Wno-implicit-fallthrough",
- ]
- } else {
- cflags += [
- # Do not warn for implicit type conversions that may change a value.
- "-Wno-conversion",
- ]
- }
}
config("platform_configuration") {
@@ -193,3 +170,28 @@
"-fdata-sections",
]
}
+
+config("pedantic_warnings") {
+ cflags = [
+ "-Wall",
+ "-Wextra",
+ "-Wunreachable-code",
+
+ # Raspi toolchain is based off an old version of gcc, which
+ # falsely flags some code. That same code does not warn with gcc 6.3.
+ # This decision should be revisited after raspi toolchain is upgraded.
+ "-Wno-maybe-uninitialized",
+
+ #TODO: Renable -Werror after fixing all warnings.
+ #"-Werror",
+ "-Wno-expansion-to-defined",
+ "-Wno-implicit-fallthrough",
+ ]
+}
+
+config("no_pedantic_warnings") {
+ cflags = [
+ # Do not warn for implicit type conversions that may change a value.
+ "-Wno-conversion",
+ ]
+}
diff --git a/src/starboard/raspi/shared/platform_configuration/configuration.gni b/src/starboard/raspi/shared/platform_configuration/configuration.gni
index c96fc14..fd4a3ce 100644
--- a/src/starboard/raspi/shared/platform_configuration/configuration.gni
+++ b/src/starboard/raspi/shared/platform_configuration/configuration.gni
@@ -14,7 +14,13 @@
import("//starboard/build/config/base_configuration.gni")
+arm_float_abi = "hard"
sb_pedantic_warnings = true
sb_static_contents_output_data_dir = "$root_out_dir/content"
+pedantic_warnings_config_path =
+ "//starboard/raspi/shared/platform_configuration:pedantic_warnings"
+no_pedantic_warnings_config_path =
+ "//starboard/raspi/shared/platform_configuration:no_pedantic_warnings"
+
sabi_path = "//starboard/sabi/arm/hardfp/sabi-v$sb_api_version.json"
diff --git a/src/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni b/src/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni
new file mode 100644
index 0000000..d773598
--- /dev/null
+++ b/src/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni
@@ -0,0 +1,26 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+_home_dir = getenv("HOME")
+_raspi_home_dir = getenv("RASPI_HOME")
+assert(_raspi_home_dir != "",
+ "RasPi builds require the 'RASPI_HOME' environment variable to be set.")
+
+raspi_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
+_raspi_toolchain_path = "$_raspi_home_dir/tools/arm-bcm2708/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin"
+
+gcc_toolchain_ar = "ar"
+gcc_toolchain_cc = "$_raspi_toolchain_path/arm-linux-gnueabihf-gcc"
+gcc_toolchain_cxx = "$_raspi_toolchain_path/arm-linux-gnueabihf-g++"
+gcc_toolchain_strip = "$_raspi_toolchain_path/arm-linux-gnueabihf-strip"
diff --git a/src/starboard/sabi/sabi.py b/src/starboard/sabi/sabi.py
index a49ae2e..b7eab07 100644
--- a/src/starboard/sabi/sabi.py
+++ b/src/starboard/sabi/sabi.py
@@ -11,6 +11,6 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Source of truth of the default/experimental Starboard API version."""
+"""Source of truth of the default Starboard API version."""
-SB_API_VERSION = 14
+SB_API_VERSION = 13
diff --git a/src/starboard/shared/starboard/media/mime_type.cc b/src/starboard/shared/starboard/media/mime_type.cc
index 0008844..035de69 100644
--- a/src/starboard/shared/starboard/media/mime_type.cc
+++ b/src/starboard/shared/starboard/media/mime_type.cc
@@ -14,8 +14,12 @@
#include "starboard/shared/starboard/media/mime_type.h"
+#include <algorithm>
+#include <iosfwd>
#include <locale>
-#include <sstream>
+#include <numeric>
+#include <string>
+#include <vector>
#include "starboard/common/log.h"
#include "starboard/common/string.h"
@@ -43,6 +47,11 @@
if (!buffer.fail() && buffer.rdbuf()->in_avail() == 0) {
return MimeType::kParamTypeFloat;
}
+
+ if (value == "true" || value == "false") {
+ return MimeType::kParamTypeBoolean;
+ }
+
return MimeType::kParamTypeString;
}
@@ -85,6 +94,22 @@
return result;
}
+const char* ParamTypeToString(MimeType::ParamType param_type) {
+ switch (param_type) {
+ case MimeType::kParamTypeInteger:
+ return "Integer";
+ case MimeType::kParamTypeFloat:
+ return "Float";
+ case MimeType::kParamTypeString:
+ return "String";
+ case MimeType::kParamTypeBoolean:
+ return "Boolean";
+ default:
+ SB_NOTREACHED();
+ return "Unknown";
+ }
+}
+
} // namespace
const int MimeType::kInvalidParamIndex = -1;
@@ -117,8 +142,11 @@
for (Strings::iterator iter = components.begin(); iter != components.end();
++iter) {
std::vector<std::string> name_and_value = SplitAndTrim(*iter, '=');
+ // The parameter must be on the format 'name=value' and neither |name| nor
+ // |value| can be empty. |value| must also not contain '|'.
if (name_and_value.size() != 2 || name_and_value[0].empty() ||
- name_and_value[1].empty()) {
+ name_and_value[1].empty() ||
+ name_and_value[1].find('|') != std::string::npos) {
return;
}
Param param;
@@ -215,6 +243,17 @@
return params_[index].value;
}
+bool MimeType::GetParamBoolValue(int index) const {
+ SB_DCHECK(is_valid());
+ SB_DCHECK(index < GetParamCount());
+
+ if (GetParamType(index) != kParamTypeBoolean) {
+ return false;
+ }
+
+ return params_[index].value == "true";
+}
+
int MimeType::GetParamIntValue(const char* name, int default_value) const {
int index = GetParamIndexByName(name);
if (index != kInvalidParamIndex) {
@@ -242,6 +281,60 @@
return default_value;
}
+bool MimeType::GetParamBoolValue(const char* name, bool default_value) const {
+ int index = GetParamIndexByName(name);
+ if (index != kInvalidParamIndex) {
+ return GetParamBoolValue(index);
+ }
+ return default_value;
+}
+
+bool MimeType::RegisterBoolParameter(const char* name) {
+ return RegisterParameter(name, kParamTypeBoolean);
+}
+
+bool MimeType::RegisterStringParameter(const char* name,
+ const std::string& pattern /* = "" */) {
+ if (!RegisterParameter(name, kParamTypeString)) {
+ return false;
+ }
+
+ int param_index = GetParamIndexByName(name);
+ if (param_index == kInvalidParamIndex || pattern.empty()) {
+ return true;
+ }
+
+ // Compare the parameter value with the provided pattern.
+ const std::string& param_value = GetParamStringValue(param_index);
+ bool matches = false;
+ size_t match_start = 0;
+ while (!matches) {
+ match_start = pattern.find(param_value, match_start);
+ if (match_start == std::string::npos) {
+ break;
+ }
+
+ size_t match_end = match_start + param_value.length();
+ matches =
+ // Matches if the match is at the start of the string or
+ // if the preceding character is the divider _and_
+ (match_start <= 0 || pattern[match_start - 1] == '|') &&
+ // if the end of the match is the end of the pattern or
+ // if the succeeding character is the divider.
+ (match_end >= pattern.length() || pattern[match_end] == '|');
+ match_start = match_end + 1;
+ }
+
+ if (matches) {
+ return true;
+ }
+
+ SB_LOG(INFO) << "Extended Parameter '" << name << "=" << param_value
+ << "' does not match the supplied pattern: '" << pattern << "'";
+ is_valid_ = false;
+ return false;
+}
+
int MimeType::GetParamIndexByName(const char* name) const {
for (size_t i = 0; i < params_.size(); ++i) {
if (SbStringCompareNoCase(params_[i].name.c_str(), name) == 0) {
@@ -251,6 +344,36 @@
return kInvalidParamIndex;
}
+bool MimeType::RegisterParameter(const char* name, ParamType param_type) {
+ if (!is_valid()) {
+ return false;
+ }
+
+ int index = GetParamIndexByName(name);
+ if (index == kInvalidParamIndex) {
+ return true;
+ }
+
+ const std::string& param_value = GetParamStringValue(index);
+ ParamType parsed_type = GetParamType(index);
+
+ // Check that the parameter can be returned as the requested type.
+ // Allowed conversions:
+ // Any Type -> String, Int -> Float
+ bool convertible =
+ param_type == parsed_type || param_type == kParamTypeString ||
+ (param_type == kParamTypeFloat && parsed_type == kParamTypeInteger);
+ if (!convertible) {
+ SB_LOG(INFO) << "Extended Parameter '" << name << "=" << param_value
+ << "' can't be converted to " << ParamTypeToString(param_type);
+ is_valid_ = false;
+ return false;
+ }
+
+ // All validations succeeded.
+ return true;
+}
+
} // namespace media
} // namespace starboard
} // namespace shared
diff --git a/src/starboard/shared/starboard/media/mime_type.h b/src/starboard/shared/starboard/media/mime_type.h
index 7c3d619..a05a301 100644
--- a/src/starboard/shared/starboard/media/mime_type.h
+++ b/src/starboard/shared/starboard/media/mime_type.h
@@ -50,6 +50,7 @@
kParamTypeInteger,
kParamTypeFloat,
kParamTypeString,
+ kParamTypeBoolean,
};
static const int kInvalidParamIndex;
@@ -71,12 +72,30 @@
int GetParamIntValue(int index) const;
float GetParamFloatValue(int index) const;
const std::string& GetParamStringValue(int index) const;
+ bool GetParamBoolValue(int index) const;
int GetParamIntValue(const char* name, int default_value) const;
float GetParamFloatValue(const char* name, float default_value) const;
const std::string& GetParamStringValue(
const char* name,
const std::string& default_value) const;
+ bool GetParamBoolValue(const char* name, bool default_value) const;
+
+ // Pre-register a mime parameter of type boolean.
+ // Returns true if the mime type is valid and the value passes validation.
+ // If the parameter validation fails this MimeType will be marked invalid.
+ // NOTE: The function returns true for missing parameters.
+ bool RegisterBoolParameter(const char* name);
+
+ // Pre-register a mime parameter of type string.
+ // Returns true if the mime type is valid and the value passes validation.
+ // Allows passing a pattern on the format "value_1|...|value_n"
+ // where the parameter value must match one of the values in the pattern in
+ // order to be considered valid.
+ // If the parameter validation fails this MimeType will be marked invalid.
+ // NOTE: The function returns true for missing parameters.
+ bool RegisterStringParameter(const char* name,
+ const std::string& pattern = "");
private:
struct Param {
@@ -90,6 +109,7 @@
typedef std::vector<Param> Params;
int GetParamIndexByName(const char* name) const;
+ bool RegisterParameter(const char* name, ParamType type);
const std::string raw_content_type_;
bool is_valid_;
diff --git a/src/starboard/shared/starboard/media/mime_type_test.cc b/src/starboard/shared/starboard/media/mime_type_test.cc
index 54dfdfc..854444d 100644
--- a/src/starboard/shared/starboard/media/mime_type_test.cc
+++ b/src/starboard/shared/starboard/media/mime_type_test.cc
@@ -41,7 +41,7 @@
}
// Valid mime type must have a type/subtype without space in between.
-TEST(MimeTypeTest, InvalidType) {
+TEST(MimeTypeTest, InvalidMimeType) {
{
MimeType mime_type("video");
EXPECT_FALSE(mime_type.is_valid());
@@ -83,6 +83,21 @@
"param1=\" value1 \";codecs=\"abc, def\"");
EXPECT_FALSE(mime_type.is_valid());
}
+ {
+ // Parameter name must not be empty.
+ MimeType mime_type("video/mp4; =value");
+ EXPECT_FALSE(mime_type.is_valid());
+ }
+ {
+ // Parameter value must not be empty.
+ MimeType mime_type("video/mp4; name=");
+ EXPECT_FALSE(mime_type.is_valid());
+ }
+ {
+ // No '|' allowed.
+ MimeType mime_type("video/mp4; name=ab|c");
+ EXPECT_FALSE(mime_type.is_valid());
+ }
}
TEST(MimeTypeTest, ValidContentTypeWithTypeAndSubtypeOnly) {
@@ -165,10 +180,13 @@
TEST(MimeTypeTest, GetParamType) {
{
- MimeType mime_type("video/mp4; name0=123; name1=1.2; name2=xyz");
+ MimeType mime_type(
+ "video/mp4; name0=123; name1=1.2; name2=xyz; name3=true; name4=false");
EXPECT_EQ(MimeType::kParamTypeInteger, mime_type.GetParamType(0));
EXPECT_EQ(MimeType::kParamTypeFloat, mime_type.GetParamType(1));
EXPECT_EQ(MimeType::kParamTypeString, mime_type.GetParamType(2));
+ EXPECT_EQ(MimeType::kParamTypeBoolean, mime_type.GetParamType(3));
+ EXPECT_EQ(MimeType::kParamTypeBoolean, mime_type.GetParamType(4));
}
{
@@ -230,6 +248,12 @@
}
}
+TEST(MimeTypeTest, GetParamBoolValueWithIndex) {
+ MimeType mime_type("video/mp4; name0=true; name1=false");
+ EXPECT_TRUE(mime_type.GetParamBoolValue(0));
+ EXPECT_FALSE(mime_type.GetParamBoolValue(1));
+}
+
TEST(MimeTypeTest, GetParamValueInInvalidType) {
MimeType mime_type("video/mp4; name0=abc; name1=123.4");
EXPECT_FLOAT_EQ(0, mime_type.GetParamIntValue(0));
@@ -280,6 +304,110 @@
}
}
+TEST(MimeTypeTest, GetParamBooleanValueWithName) {
+ MimeType mime_type("video/mp4; name0=true; name1=false; name2=trues");
+ EXPECT_TRUE(mime_type.GetParamBoolValue("name0", false));
+ EXPECT_FALSE(mime_type.GetParamBoolValue("name1", true));
+ // Default value
+ EXPECT_FALSE(mime_type.GetParamBoolValue("name2", false));
+}
+
+TEST(MimeTypeTest, ReadParamOfDifferentTypes) {
+ // Ensure that the parameter type is enforced correctly.
+ MimeType mime_type("video/mp4; string=value; int=1; float=1.0; bool=true");
+ ASSERT_TRUE(mime_type.is_valid());
+ // Read params as their original types.
+ EXPECT_EQ(mime_type.GetParamStringValue("string", ""), "value");
+ EXPECT_EQ(mime_type.GetParamIntValue("int", 0), 1);
+ EXPECT_EQ(mime_type.GetParamFloatValue("float", 0.0), 1.0);
+ EXPECT_EQ(mime_type.GetParamBoolValue("bool", false), true);
+ // All param types can be read as strings.
+ EXPECT_EQ(mime_type.GetParamStringValue("int", ""), "1");
+ EXPECT_EQ(mime_type.GetParamStringValue("float", ""), "1.0");
+ EXPECT_EQ(mime_type.GetParamStringValue("bool", ""), "true");
+ // Int can also be read as float.
+ EXPECT_EQ(mime_type.GetParamFloatValue("int", 0.0), 1.0);
+
+ // Test failing validation for ints,
+ EXPECT_EQ(mime_type.GetParamIntValue("string", 0), 0);
+ EXPECT_EQ(mime_type.GetParamIntValue("float", 0), 0);
+ EXPECT_EQ(mime_type.GetParamIntValue("bool", 0), 0);
+
+ // floats,
+ EXPECT_EQ(mime_type.GetParamFloatValue("string", 0.0), 0.0);
+ EXPECT_EQ(mime_type.GetParamFloatValue("bool", 0.0), 0.0);
+
+ // and bools.
+ EXPECT_FALSE(mime_type.GetParamBoolValue("string", false));
+ EXPECT_FALSE(mime_type.GetParamBoolValue("int", false));
+ EXPECT_FALSE(mime_type.GetParamBoolValue("float", false));
+}
+
+TEST(MimeTypeTest, RegisterAndValidateParamsWithPatterns) {
+ MimeType mime_type("video/mp4; string=yes");
+ EXPECT_TRUE(mime_type.RegisterStringParameter("string", "yes"));
+ EXPECT_TRUE(mime_type.RegisterStringParameter("string", "yes|no"));
+ EXPECT_TRUE(mime_type.RegisterStringParameter("string", "no|yes|no"));
+ EXPECT_TRUE(mime_type.RegisterStringParameter("string", "no|no|yes"));
+ EXPECT_TRUE(mime_type.RegisterStringParameter("string", "noyes|yes"));
+ EXPECT_TRUE(mime_type.is_valid());
+}
+
+TEST(MimeTypeTest, RegisterAndValidateParamsWithShortPatterns) {
+ MimeType mime_type("video/mp4; string=y");
+ EXPECT_TRUE(mime_type.RegisterStringParameter("string", "y"));
+ EXPECT_TRUE(mime_type.RegisterStringParameter("string", "y|n"));
+ EXPECT_TRUE(mime_type.RegisterStringParameter("string", "n|y"));
+ EXPECT_TRUE(mime_type.is_valid());
+}
+
+TEST(MimeTypeTest, RegisterAndValidateParamsWithPartialMatches) {
+ {
+ MimeType mime_type("video/mp4; string=yes");
+ EXPECT_FALSE(mime_type.RegisterStringParameter("string", "yesno|no"));
+ EXPECT_FALSE(mime_type.is_valid());
+ }
+ {
+ MimeType mime_type("video/mp4; string=yes");
+ EXPECT_FALSE(mime_type.RegisterStringParameter("string", "noyes|no"));
+ EXPECT_FALSE(mime_type.is_valid());
+ }
+ {
+ MimeType mime_type("video/mp4; string=yes");
+ EXPECT_FALSE(mime_type.RegisterStringParameter("string", "no|yesno"));
+ EXPECT_FALSE(mime_type.is_valid());
+ }
+ {
+ MimeType mime_type("video/mp4; string=yes");
+ EXPECT_FALSE(mime_type.RegisterStringParameter("string", "no|noyes"));
+ EXPECT_FALSE(mime_type.is_valid());
+ }
+}
+
+TEST(MimeTypeTest, MissingParamReturnsTrueOnRegistration) {
+ MimeType mime_type("video/mp4");
+ EXPECT_TRUE(mime_type.RegisterStringParameter("string"));
+ EXPECT_TRUE(mime_type.is_valid());
+ EXPECT_EQ(mime_type.GetParamStringValue("string", "default"), "default");
+}
+
+TEST(MimeTypeTest, RegisterAndValidateParamsWithEmptyishPattern) {
+ {
+ MimeType mime_type("video/mp4; string=yes");
+ EXPECT_FALSE(mime_type.RegisterStringParameter("string", "|"));
+ }
+ {
+ MimeType mime_type("video/mp4; string=yes");
+ EXPECT_FALSE(mime_type.RegisterStringParameter("string", "||"));
+ }
+}
+
+TEST(MimeTypeTest, CannotRegisterParamWithInvalidMimeType) {
+ MimeType mime_type("video/mp4; string=");
+ ASSERT_FALSE(mime_type.is_valid());
+ EXPECT_FALSE(mime_type.RegisterStringParameter("string"));
+}
+
} // namespace
} // namespace media
} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
index 5127ae4..e1ca12b 100644
--- a/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
@@ -362,8 +362,8 @@
return test_params;
}
- vector<const char*> supported_files = GetSupportedAudioTestFiles(
- kExcludeHeaac, 6, "audiopassthrough=\"false\"");
+ vector<const char*> supported_files =
+ GetSupportedAudioTestFiles(kExcludeHeaac, 6, "audiopassthrough=false");
// Generate test cases. For example, we have |supported_files| [A, B, C].
// Add tests A->A, A->B, A->C, B->A, B->B, B->C, C->A, C->B and C->C.
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
index 00933f3..eee3730 100644
--- a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
@@ -637,7 +637,7 @@
AudioDecoderTest,
Combine(ValuesIn(GetSupportedAudioTestFiles(kIncludeHeaac,
6,
- "audiopassthrough=\"false\"")),
+ "audiopassthrough=false")),
Bool()));
} // namespace
diff --git a/src/starboard/shared/starboard/player/filter/testing/player_components_test.cc b/src/starboard/shared/starboard/player/filter/testing/player_components_test.cc
index fe7932f..afa4fad 100644
--- a/src/starboard/shared/starboard/player/filter/testing/player_components_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/player_components_test.cc
@@ -38,6 +38,8 @@
namespace {
using ::starboard::testing::FakeGraphicsContextProvider;
+using std::placeholders::_1;
+using std::placeholders::_2;
using std::string;
using std::unique_ptr;
using std::vector;
@@ -121,7 +123,7 @@
if (GetAudioRenderer()) {
GetAudioRenderer()->Initialize(
- std::bind(&PlayerComponentsTest::OnError, this),
+ std::bind(&PlayerComponentsTest::OnError, this, _1, _2),
std::bind(&PlayerComponentsTest::OnPrerolled, this,
kSbMediaTypeAudio),
std::bind(&PlayerComponentsTest::OnEnded, this, kSbMediaTypeAudio));
@@ -129,7 +131,7 @@
SetPlaybackRate(playback_rate_);
if (GetVideoRenderer()) {
GetVideoRenderer()->Initialize(
- std::bind(&PlayerComponentsTest::OnError, this),
+ std::bind(&PlayerComponentsTest::OnError, this, _1, _2),
std::bind(&PlayerComponentsTest::OnPrerolled, this,
kSbMediaTypeVideo),
std::bind(&PlayerComponentsTest::OnEnded, this, kSbMediaTypeVideo));
@@ -401,7 +403,11 @@
// 1s in the tests.
const SbTime kMaxWriteAheadDuration = kSbTimeSecond;
- void OnError() { has_error_ = true; }
+ void OnError(SbPlayerError error, const std::string& error_message) {
+ has_error_ = true;
+ SB_LOG(ERROR) << "Caught renderer error (" << error
+ << "): " << error_message;
+ }
void OnPrerolled(SbMediaType media_type) {
if (media_type == kSbMediaTypeAudio) {
audio_prerolled_ = true;
diff --git a/src/starboard/stub/platform_configuration/BUILD.gn b/src/starboard/stub/platform_configuration/BUILD.gn
index ace2a65..a5bdb8f 100644
--- a/src/starboard/stub/platform_configuration/BUILD.gn
+++ b/src/starboard/stub/platform_configuration/BUILD.gn
@@ -85,30 +85,32 @@
# Defined to get format macro constants from <inttypes.h>.
defines = [ "__STDC_FORMAT_MACROS" ]
+}
- if (sb_pedantic_warnings) {
- cflags += [
- "-Wall",
- "-Wextra",
- "-Wunreachable-code",
- ]
- } else {
- cflags += [
- # 'this' pointer cannot be NULL...pointer may be assumed
- # to always convert to true.
- "-Wno-undefined-bool-conversion",
+config("pedantic_warnings") {
+ cflags = [
+ "-Wall",
+ "-Wextra",
+ "-Wunreachable-code",
+ ]
+}
- # Skia doesn't use overrides.
- "-Wno-inconsistent-missing-override",
+config("no_pedantic_warnings") {
+ cflags = [
+ # 'this' pointer cannot be NULL...pointer may be assumed
+ # to always convert to true.
+ "-Wno-undefined-bool-conversion",
- # Do not warn for implicit type conversions that may change a value.
- "-Wno-conversion",
+ # Skia doesn't use overrides.
+ "-Wno-inconsistent-missing-override",
- # shifting a negative signed value is undefined
- "-Wno-shift-negative-value",
+ # Do not warn for implicit type conversions that may change a value.
+ "-Wno-conversion",
- # Width of bit-field exceeds width of its type- value will be truncated
- "-Wno-bitfield-width",
- ]
- }
+ # shifting a negative signed value is undefined
+ "-Wno-shift-negative-value",
+
+ # Width of bit-field exceeds width of its type- value will be truncated
+ "-Wno-bitfield-width",
+ ]
}
diff --git a/src/starboard/stub/platform_configuration/configuration.gni b/src/starboard/stub/platform_configuration/configuration.gni
index 61a778f..7386286 100644
--- a/src/starboard/stub/platform_configuration/configuration.gni
+++ b/src/starboard/stub/platform_configuration/configuration.gni
@@ -23,3 +23,8 @@
sabi_path = "//starboard/sabi/x64/sysv/sabi-v$sb_api_version.json"
install_target_path = "//starboard/build/install/install_target.gni"
+
+pedantic_warnings_config_path =
+ "//starboard/stub/platform_configuration:pedantic_warnings"
+no_pedantic_warnings_config_path =
+ "//starboard/stub/platform_configuration:no_pedantic_warnings"
diff --git a/src/third_party/crashpad/handler/handler.gyp b/src/third_party/crashpad/handler/handler.gyp
index 1275a4d..d862d70 100644
--- a/src/third_party/crashpad/handler/handler.gyp
+++ b/src/third_party/crashpad/handler/handler.gyp
@@ -102,6 +102,7 @@
'ldflags': [
'-Wl,--as-needed',
'-Wl,-gc-sections',
+ '-Wl,-z,noexecstack',
],
}],
['OS=="win"', {
diff --git a/src/third_party/web_platform_tests/tools/wpt/requirements.txt b/src/third_party/web_platform_tests/tools/wpt/requirements.txt
index 7369cb8..a8ed785 100644
--- a/src/third_party/web_platform_tests/tools/wpt/requirements.txt
+++ b/src/third_party/web_platform_tests/tools/wpt/requirements.txt
@@ -1 +1 @@
-requests==2.14.2
+requests==2.26.0