Import Cobalt 16.154703
diff --git a/src/CONTRIBUTING.md b/src/CONTRIBUTING.md index bd99ed6..71f6b83 100644 --- a/src/CONTRIBUTING.md +++ b/src/CONTRIBUTING.md
@@ -50,6 +50,9 @@ 1. Ensure you or your company have signed the appropriate CLA (see "Before You Contribute" above). 1. Rebase your changes down into a single git commit. + 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 [Cobalt's Gerrit instance](https://cobalt-review.googlesource.com/). 1. Someone from the maintainers team will review the code, putting up comments
diff --git a/src/base/base.gyp b/src/base/base.gyp index 6b048fc..47f7437 100644 --- a/src/base/base.gyp +++ b/src/base/base.gyp
@@ -294,6 +294,7 @@ ], 'dependencies': [ 'base', + 'base_copy_test_data', 'base_i18n', 'base_static', 'run_all_unittests', @@ -306,21 +307,8 @@ ], 'variables': { # TODO(ajwong): Is there a way to autodetect this? - 'module_dir': 'base' + 'module_dir': 'base', }, - 'actions': [ - { - 'action_name': 'copy_test_data', - 'variables': { - 'input_files': [ - 'data/json', - 'data/file_util_unittest', - ], - 'output_dir': 'base/data', - }, - 'includes': [ '../cobalt/build/copy_test_data.gypi' ], - }, - ], 'msvs_disabled_warnings': [ # forcing value to bool 'true' or 'false' (performance warning) 4800, @@ -329,6 +317,18 @@ ], }, { + 'target_name': 'base_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + 'data/json', + 'data/file_util_unittest', + ], + 'content_test_output_subdir': 'base/data', + }, + 'includes': [ '<(DEPTH)/starboard/build/copy_test_data.gypi' ], + }, + { 'target_name': 'test_support_base', 'type': 'static_library', 'dependencies': [
diff --git a/src/base/base_paths.h b/src/base/base_paths.h index 7bc23a8..c0226be 100644 --- a/src/base/base_paths.h +++ b/src/base/base_paths.h
@@ -37,11 +37,14 @@ FILE_MODULE, // Path and filename of the module containing the code for the // PathService (which could differ from FILE_EXE if the // PathService were compiled into a shared object, for example). - DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful - // for tests that need to locate various resources. It - // should not be used outside of test code. + DIR_SOURCE_ROOT, // Returns the root of the source tree. Only for utility + // code that runs on the host to locate various resources. + // Not available in release builds. DIR_USER_DESKTOP, // The current user's Desktop. + DIR_TEST_DATA, // Directory holding static input for testing. + // Not available in release builds. + PATH_END };
diff --git a/src/base/base_paths_shell.cc b/src/base/base_paths_shell.cc deleted file mode 100644 index aae1d80..0000000 --- a/src/base/base_paths_shell.cc +++ /dev/null
@@ -1,76 +0,0 @@ -/* - * Copyright 2012 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "base/base_paths.h" -#include "base/file_path.h" -#include "base/file_util.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "cobalt/deprecated/platform_delegate.h" - -namespace base { - -// this is where we can control the path for placement -// of a lot of file resources for lbshell. -bool PathProviderShell(int key, FilePath* result) { - cobalt::deprecated::PlatformDelegate* plat = - cobalt::deprecated::PlatformDelegate::Get(); - switch (key) { - case base::DIR_EXE: - case base::DIR_MODULE: - DCHECK(!plat->game_content_path().empty()); - *result = FilePath(plat->game_content_path()); - return true; - -#if defined(ENABLE_DIR_SOURCE_ROOT_ACCESS) - case base::DIR_SOURCE_ROOT: - if (plat->dir_source_root().length() > 0) { - *result = FilePath(plat->dir_source_root()); - return true; - } else { - DLOG(INFO) << "DIR_SOURCE_ROOT not defined - skipping."; - return false; - } -#endif // ENABLE_DIR_SOURCE_ROOT_ACCESS - - case base::DIR_TEMP: - DCHECK(!plat->temp_path().empty()); - *result = FilePath(plat->temp_path()); - return true; - - case base::DIR_CACHE: - DCHECK(!plat->temp_path().empty()); - *result = FilePath(plat->temp_path()).Append("cache"); - return true; - case base::DIR_HOME: { -#if defined(COBALT_LINUX) - const char* home_dir = getenv("HOME"); - if (home_dir && home_dir[0]) { - *result = FilePath(home_dir); - return true; - } else { - return PathProviderShell(base::DIR_TEMP, result); - } -#else - return PathProviderShell(base::DIR_EXE, result); -#endif // defined(COBALT_LINUX) - } - } - - return false; -} - -} // namespace base -
diff --git a/src/base/base_paths_starboard.cc b/src/base/base_paths_starboard.cc index b3b6e8e..cd12eaf 100644 --- a/src/base/base_paths_starboard.cc +++ b/src/base/base_paths_starboard.cc
@@ -49,19 +49,20 @@ return false; } -#if defined(ENABLE_DIR_SOURCE_ROOT_ACCESS) - case base::DIR_SOURCE_ROOT: { - bool success = SbSystemGetPath(kSbSystemPathSourceDirectory, path, +#if defined(ENABLE_TEST_DATA) + case base::DIR_TEST_DATA: { + bool success = SbSystemGetPath(kSbSystemPathContentDirectory, path, SB_ARRAY_SIZE_INT(path)); DCHECK(success); if (success) { - *result = FilePath(path); + // Append "test" to match the output of copy_test_data.gypi + *result = FilePath(path).Append(FILE_PATH_LITERAL("test")); return true; } - DLOG(ERROR) << "DIR_SOURCE_ROOT not defined."; + DLOG(ERROR) << "DIR_TEST_DATA not defined."; return false; } -#endif // ENABLE_DIR_SOURCE_ROOT_ACCESS +#endif // ENABLE_TEST_DATA case base::DIR_CACHE: { bool success = SbSystemGetPath(kSbSystemPathCacheDirectory, path,
diff --git a/src/base/callback_helpers.h b/src/base/callback_helpers.h index ada0090..3db2ed1 100644 --- a/src/base/callback_helpers.h +++ b/src/base/callback_helpers.h
@@ -35,14 +35,15 @@ return true; } -template <typename Sig, typename ParamType> -bool ResetAndRunIfNotNull(base::Callback<Sig>* cb, const ParamType& param) { +template <typename Sig, typename... ParamTypes> +bool ResetAndRunIfNotNull(base::Callback<Sig>* cb, + const ParamTypes&... params) { if (cb->is_null()) { return false; } base::Callback<Sig> ret(*cb); cb->Reset(); - ret.Run(param); + ret.Run(params...); return true; }
diff --git a/src/base/containers/small_map_unittest.cc b/src/base/containers/small_map_unittest.cc index af8126c..95a22b2 100644 --- a/src/base/containers/small_map_unittest.cc +++ b/src/base/containers/small_map_unittest.cc
@@ -137,33 +137,24 @@ } } -template<class inner> -static void SmallMapToMap(SmallMap<inner> const& src, inner* dest) { +template <class inner> +static bool SmallMapIsSubset(SmallMap<inner> const& a, + SmallMap<inner> const& b) { typename SmallMap<inner>::const_iterator it; - for (it = src.begin(); it != src.end(); ++it) { - dest->insert(std::make_pair(it->first, it->second)); - } -} - -template<class inner> -static bool SmallMapEqual(SmallMap<inner> const& a, - SmallMap<inner> const& b) { - inner ia, ib; - SmallMapToMap(a, &ia); - SmallMapToMap(b, &ib); - - // On most systems we can use operator== here, but under some lesser STL - // implementations it doesn't seem to work. So we manually compare. - if (ia.size() != ib.size()) - return false; - for (typename inner::iterator ia_it = ia.begin(), ib_it = ib.begin(); - ia_it != ia.end(); ++ia_it, ++ib_it) { - if (*ia_it != *ib_it) + for (it = a.begin(); it != a.end(); ++it) { + typename SmallMap<inner>::const_iterator it_in_b = b.find(it->first); + if (it_in_b == b.end() || it_in_b->second != it->second) return false; } return true; } +template <class inner> +static bool SmallMapEqual(SmallMap<inner> const& a, + SmallMap<inner> const& b) { + return SmallMapIsSubset(a, b) && SmallMapIsSubset(b, a); +} + TEST(SmallMap, AssignmentOperator) { SmallMap<hash_map<int, int> > src_small; SmallMap<hash_map<int, int> > src_large;
diff --git a/src/base/file_util_unittest.cc b/src/base/file_util_unittest.cc index 6d0c25c..6500e32 100644 --- a/src/base/file_util_unittest.cc +++ b/src/base/file_util_unittest.cc
@@ -1578,7 +1578,7 @@ #if !defined(__LB_WIIU__) && !defined(OS_STARBOARD) TEST_F(ReadOnlyFileUtilTest, ContentsEqual) { FilePath data_dir; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); + ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir)); data_dir = data_dir.Append(FILE_PATH_LITERAL("base")) .Append(FILE_PATH_LITERAL("data")) .Append(FILE_PATH_LITERAL("file_util_unittest")); @@ -1629,7 +1629,7 @@ #if !defined(__LB_SHELL__) && !defined(OS_STARBOARD) TEST_F(ReadOnlyFileUtilTest, TextContentsEqual) { FilePath data_dir; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); + ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir)); data_dir = data_dir.Append(FILE_PATH_LITERAL("base")) .Append(FILE_PATH_LITERAL("data")) .Append(FILE_PATH_LITERAL("file_util_unittest"));
diff --git a/src/base/file_version_info_unittest.cc b/src/base/file_version_info_unittest.cc index 916e7bc..a4eeb73 100644 --- a/src/base/file_version_info_unittest.cc +++ b/src/base/file_version_info_unittest.cc
@@ -17,7 +17,7 @@ #if defined(OS_WIN) FilePath GetTestDataPath() { FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); + PathService::Get(base::DIR_TEST_DATA, &path); path = path.AppendASCII("base"); path = path.AppendASCII("data"); path = path.AppendASCII("file_version_info_unittest");
diff --git a/src/base/id_map_unittest.cc b/src/base/id_map_unittest.cc index 53ca656..2547489 100644 --- a/src/base/id_map_unittest.cc +++ b/src/base/id_map_unittest.cc
@@ -94,31 +94,41 @@ const int kCount = 5; TestObject obj[kCount]; - int32 ids[kCount]; for (int i = 0; i < kCount; i++) - ids[i] = map.Add(&obj[i]); + map.Add(&obj[i]); + // IDMap uses a hash_map, which has no predictable iteration order. + int32 ids_in_iteration_order[kCount]; + const TestObject* objs_in_iteration_order[kCount]; int counter = 0; for (IDMap<TestObject>::const_iterator iter(&map); !iter.IsAtEnd(); iter.Advance()) { + ids_in_iteration_order[counter] = iter.GetCurrentKey(); + objs_in_iteration_order[counter] = iter.GetCurrentValue(); + counter++; + } + + counter = 0; + for (IDMap<TestObject>::const_iterator iter(&map); + !iter.IsAtEnd(); iter.Advance()) { EXPECT_EQ(1, map.iteration_depth()); switch (counter) { case 0: - EXPECT_EQ(ids[0], iter.GetCurrentKey()); - EXPECT_EQ(&obj[0], iter.GetCurrentValue()); - map.Remove(ids[1]); + EXPECT_EQ(ids_in_iteration_order[0], iter.GetCurrentKey()); + EXPECT_EQ(objs_in_iteration_order[0], iter.GetCurrentValue()); + map.Remove(ids_in_iteration_order[1]); break; case 1: - EXPECT_EQ(ids[2], iter.GetCurrentKey()); - EXPECT_EQ(&obj[2], iter.GetCurrentValue()); - map.Remove(ids[3]); + EXPECT_EQ(ids_in_iteration_order[2], iter.GetCurrentKey()); + EXPECT_EQ(objs_in_iteration_order[2], iter.GetCurrentValue()); + map.Remove(ids_in_iteration_order[3]); break; case 2: - EXPECT_EQ(ids[4], iter.GetCurrentKey()); - EXPECT_EQ(&obj[4], iter.GetCurrentValue()); - map.Remove(ids[0]); + EXPECT_EQ(ids_in_iteration_order[4], iter.GetCurrentKey()); + EXPECT_EQ(objs_in_iteration_order[4], iter.GetCurrentValue()); + map.Remove(ids_in_iteration_order[0]); break; default: FAIL() << "should not have that many elements"; @@ -189,32 +199,37 @@ EXPECT_EQ(0, map.iteration_depth()); } -// This test relies on specific ordering of items in a hash map to expect a -// given ID at a given iteration point. This is a bad expectation for a -// hash_map, and one that does not hold on lbshell platforms. This test -// should be rewritten. -#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD) TEST(IDMapTest, IteratorRemainsValidWhenClearing) { IDMap<TestObject> map; const int kCount = 5; TestObject obj[kCount]; - int32 ids[kCount]; for (int i = 0; i < kCount; i++) - ids[i] = map.Add(&obj[i]); + map.Add(&obj[i]); + // IDMap uses a hash_map, which has no predictable iteration order. int counter = 0; + int32 ids_in_iteration_order[kCount]; + const TestObject* objs_in_iteration_order[kCount]; + for (IDMap<TestObject>::const_iterator iter(&map); + !iter.IsAtEnd(); iter.Advance()) { + ids_in_iteration_order[counter] = iter.GetCurrentKey(); + objs_in_iteration_order[counter] = iter.GetCurrentValue(); + counter++; + } + + counter = 0; for (IDMap<TestObject>::const_iterator iter(&map); !iter.IsAtEnd(); iter.Advance()) { switch (counter) { case 0: - EXPECT_EQ(ids[0], iter.GetCurrentKey()); - EXPECT_EQ(&obj[0], iter.GetCurrentValue()); + EXPECT_EQ(ids_in_iteration_order[0], iter.GetCurrentKey()); + EXPECT_EQ(objs_in_iteration_order[0], iter.GetCurrentValue()); break; case 1: - EXPECT_EQ(ids[1], iter.GetCurrentKey()); - EXPECT_EQ(&obj[1], iter.GetCurrentValue()); + EXPECT_EQ(ids_in_iteration_order[1], iter.GetCurrentKey()); + EXPECT_EQ(objs_in_iteration_order[1], iter.GetCurrentValue()); map.Clear(); EXPECT_TRUE(map.IsEmpty()); EXPECT_EQ(0U, map.size()); @@ -229,7 +244,6 @@ EXPECT_TRUE(map.IsEmpty()); EXPECT_EQ(0U, map.size()); } -#endif TEST(IDMapTest, OwningPointersDeletesThemOnRemove) { const int kCount = 3;
diff --git a/src/base/json/json_reader_unittest.cc b/src/base/json/json_reader_unittest.cc index 38bf590..0a9f5f8 100644 --- a/src/base/json/json_reader_unittest.cc +++ b/src/base/json/json_reader_unittest.cc
@@ -529,7 +529,7 @@ TEST(JSONReaderTest, ReadFromFile) { FilePath path; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &path)); + ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &path)); path = path.Append(FILE_PATH_LITERAL("base")) .Append(FILE_PATH_LITERAL("data")) .Append(FILE_PATH_LITERAL("json"));
diff --git a/src/base/message_pump_io_starboard.cc b/src/base/message_pump_io_starboard.cc index 2ef146d..45e8215 100644 --- a/src/base/message_pump_io_starboard.cc +++ b/src/base/message_pump_io_starboard.cc
@@ -28,7 +28,8 @@ namespace base { MessagePumpIOStarboard::SocketWatcher::SocketWatcher() - : socket_(kSbSocketInvalid), + : interests_(kSbSocketWaiterInterestNone), + socket_(kSbSocketInvalid), pump_(NULL), watcher_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {} @@ -48,6 +49,7 @@ } pump_ = NULL; watcher_ = NULL; + interests_ = kSbSocketWaiterInterestNone; return result; } @@ -147,6 +149,7 @@ controller->Init(socket, persistent); controller->set_watcher(delegate); controller->set_pump(this); + controller->set_interests(interests); return true; }
diff --git a/src/base/message_pump_io_starboard.h b/src/base/message_pump_io_starboard.h index 53b8662..a0e7564 100644 --- a/src/base/message_pump_io_starboard.h +++ b/src/base/message_pump_io_starboard.h
@@ -83,6 +83,7 @@ SbSocket Release(); int interests() const { return interests_; } + void set_interests(int interests) { interests_ = interests; } void set_pump(MessagePumpIOStarboard* pump) { pump_ = pump; } MessagePumpIOStarboard* pump() const { return pump_; }
diff --git a/src/base/path_service_unittest.cc b/src/base/path_service_unittest.cc index 51e5ccc..1f76b43 100644 --- a/src/base/path_service_unittest.cc +++ b/src/base/path_service_unittest.cc
@@ -31,6 +31,9 @@ // Some paths might not exist on some platforms in which case confirming // |result| is true and !path.empty() is the best we can do. bool check_path_exists = true; + // With tests using DIR_TEST_DATA, there is no longer a fake source-root. + if (dir_type == base::DIR_SOURCE_ROOT) + check_path_exists = false; #if defined(__LB_SHELL__) if (dir_type == base::DIR_USER_DESKTOP) check_path_exists = false; @@ -92,6 +95,9 @@ // correct value while returning false.) TEST_F(PathServiceTest, Get) { for (int key = base::PATH_START + 1; key < base::PATH_END; ++key) { + // With tests using DIR_TEST_DATA, there is no longer a fake source-root. + if (key == base::DIR_SOURCE_ROOT) + continue; #if defined(OS_ANDROID) if (key == base::FILE_MODULE || key == base::DIR_USER_DESKTOP) continue; // Android doesn't implement FILE_MODULE and DIR_USER_DESKTOP;
diff --git a/src/base/prefs/json_pref_store_unittest.cc b/src/base/prefs/json_pref_store_unittest.cc index 959007a..c45266b 100644 --- a/src/base/prefs/json_pref_store_unittest.cc +++ b/src/base/prefs/json_pref_store_unittest.cc
@@ -41,7 +41,7 @@ virtual void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir_)); + ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir_)); data_dir_ = data_dir_.AppendASCII("base"); data_dir_ = data_dir_.AppendASCII("prefs"); data_dir_ = data_dir_.AppendASCII("test");
diff --git a/src/build/copy_test_data_ios.gypi b/src/build/copy_test_data_ios.gypi deleted file mode 100644 index 56a222f..0000000 --- a/src/build/copy_test_data_ios.gypi +++ /dev/null
@@ -1,48 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# This file is meant to be included into an action to copy test data files into -# an iOS app bundle. To use this the following variables need to be defined: -# test_data_files: list: paths to test data files or directories -# test_data_prefix: string: a directory prefix that will be prepended to each -# output path. Generally, this should be the base -# directory of the gypi file containing the unittest -# target (e.g. "base" or "chrome"). -# -# To use this, create a gyp target with the following form: -# { -# 'target_name': 'my_unittests', -# 'conditions': [ -# ['OS == "ios"', { -# 'actions': [ -# { -# 'action_name': 'copy_test_data', -# 'variables': { -# 'test_data_files': [ -# 'path/to/datafile.txt', -# 'path/to/data/directory/', -# ] -# 'test_data_prefix' : 'prefix', -# }, -# 'includes': ['path/to/this/gypi/file'], -# }, -# ], -# }], -# } -# - -{ - 'inputs': [ - '<!@pymod_do_main(copy_test_data_ios --inputs <(test_data_files))', - ], - 'outputs': [ - '<!@pymod_do_main(copy_test_data_ios -o <(PRODUCT_DIR)/<(_target_name).app/<(test_data_prefix) --outputs <(test_data_files))', - ], - 'action': [ - 'python', - '<(DEPTH)/build/copy_test_data_ios.py', - '-o', '<(PRODUCT_DIR)/<(_target_name).app/<(test_data_prefix)', - '<@(_inputs)', - ], -}
diff --git a/src/build/copy_test_data_ios.py b/src/build/copy_test_data_ios.py deleted file mode 100755 index 6f0302f..0000000 --- a/src/build/copy_test_data_ios.py +++ /dev/null
@@ -1,105 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Copies test data files or directories into a given output directory.""" - -import optparse -import os -import shutil -import sys - -class WrongNumberOfArgumentsException(Exception): - pass - -def EscapePath(path): - """Returns a path with spaces escaped.""" - return path.replace(" ", "\\ ") - -def ListFilesForPath(path): - """Returns a list of all the files under a given path.""" - output = [] - # Ignore revision control metadata directories. - if (os.path.basename(path).startswith('.git') or - os.path.basename(path).startswith('.svn')): - return output - - # Files get returned without modification. - if not os.path.isdir(path): - output.append(path) - return output - - # Directories get recursively expanded. - contents = os.listdir(path) - for item in contents: - full_path = os.path.join(path, item) - output.extend(ListFilesForPath(full_path)) - return output - -def CalcInputs(inputs): - """Computes the full list of input files for a set of command-line arguments. - """ - # |inputs| is a list of paths, which may be directories. - output = [] - for input in inputs: - output.extend(ListFilesForPath(input)) - return output - -def CopyFiles(relative_filenames, output_basedir): - """Copies files to the given output directory.""" - for file in relative_filenames: - relative_dirname = os.path.dirname(file) - output_dir = os.path.join(output_basedir, relative_dirname) - output_filename = os.path.join(output_basedir, file) - - # In cases where a directory has turned into a file or vice versa, delete it - # before copying it below. - if os.path.exists(output_dir) and not os.path.isdir(output_dir): - os.remove(output_dir) - if os.path.exists(output_filename) and os.path.isdir(output_filename): - shutil.rmtree(output_filename) - - if not os.path.exists(output_dir): - os.makedirs(output_dir) - shutil.copy(file, output_filename) - -def DoMain(argv): - parser = optparse.OptionParser() - usage = 'Usage: %prog -o <output_dir> [--inputs] [--outputs] <input_files>' - parser.set_usage(usage) - parser.add_option('-o', dest='output_dir') - parser.add_option('--inputs', action='store_true', dest='list_inputs') - parser.add_option('--outputs', action='store_true', dest='list_outputs') - options, arglist = parser.parse_args(argv) - - if len(arglist) == 0: - raise WrongNumberOfArgumentsException('<input_files> required.') - - files_to_copy = CalcInputs(arglist) - escaped_files = [EscapePath(x) for x in CalcInputs(arglist)] - if options.list_inputs: - return '\n'.join(escaped_files) - - if not options.output_dir: - raise WrongNumberOfArgumentsException('-o required.') - - if options.list_outputs: - outputs = [os.path.join(options.output_dir, x) for x in escaped_files] - return '\n'.join(outputs) - - CopyFiles(files_to_copy, options.output_dir) - return - -def main(argv): - try: - result = DoMain(argv[1:]) - except WrongNumberOfArgumentsException, e: - print >>sys.stderr, e - return 1 - if result: - print result - return 0 - -if __name__ == '__main__': - sys.exit(main(sys.argv))
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md index 1d2cc6e..b6ba91c 100644 --- a/src/cobalt/CHANGELOG.md +++ b/src/cobalt/CHANGELOG.md
@@ -2,6 +2,65 @@ This document records all notable changes made to Cobalt since the last release. +## Version 16 + - **Move test data** + + Static test data is now copied to `content/data/test` instead of + `content/dir_source_root`. Tests looking for the path to this data should + use `BasePathKey::DIR_TEST_DATA` instead of `BasePathKey::DIR_SOURCE_ROOT`. + Tests in Starboard can find the static data in the `test/` subdirectory of + `kSbSystemPathContentDirectory`. + + - **Add support for cobalt_media_buffer_max_capacity** + + Allow bounding the max capacity allocated by decoder buffers, by setting the + gypi variables cobalt_media_buffer_max_capacity_1080p and + cobalt_media_buffer_max_capacity_4k. 1080p applies to all resolutions 1080p + and below. Those values default to 0, which imposes no bounds. If non-zero, + each capacity must be greater than or equal to the sum of the video budget + and non video budget for that resolution (see + cobalt_media_buffer_video_buffer_1080p, cobalt_media_buffer_non_video_budget, + etc.), and the max capacities must be greater than or equal to the + corresponding initial capacities: cobalt_media_buffer_initial_capacity_1080p + and cobalt_media_buffer_initial_capacity_4k. + +- **Fix issue with CSS animations not working with the 'outline' property** + + There was a bug in previous versions that resulted in incorrect behavior when + applying a CSS animation or CSS transition to the 'outline' property. This + is fixed now. + +- **Change default minimum frame time to 16.0ms instead of 16.4ms.** + + In case a platform can only wait on millisecond resolution, we would prefer + that it wait 16ms instead of 17ms, so we round this down. Many platforms + will pace themselves to 16.6ms regardless. + +- **Make new rasterizer type "direct-gles" default** + + This is an optimized OpenGLES 2.0 rasterizer that provides a fast path for + rendering most of the skia primitives that Cobalt uses. While it was + available since Version 11, it is now polished and set as the default + rasterizer. + +- **Added support for the fetch and streams Web APIs** + + A subset of ReadableStream and Fetch Web APIs have been implemented. Fetch + may be used to get progressive results via a ReadableStream, or the full + result can be accessed as text, from the Response class. + +- **HTMLMediaElement::loop is supported** + + Now the video plays in a loop if the loop attribute of the video element is + set. + +- **Add support for MediaDevices.enumerateDevices()** + + It is now possible to enumerate microphone devices from JavaScript via a + call to navigator.mediaDevices.enumerateDevices(). This returns a promise + of an array of MediaDeviceInfo objects, each partially implemented to have + valid `label` and `kind` attributes. + ## Version 14 - **Add support for document.hasFocus()** @@ -10,9 +69,9 @@ - **Implemented Same Origin Policy and removed navigation whitelist** - - Added Same Origin Policy and Cross Origin Resource Sharing to vulnerable - code areas including XHR, script elements, link elements, style elements, - media elements and @font-face CSS rules. + - Added Same Origin Policy (SOP) and Cross Origin Resource Sharing (CORS) + suport to Cobalt. In particular, it is added to XHR, script elements, + link elements, style elements, media elements and @font-face CSS rules. - Removed hardcoded YouTube navigation whitelist in favor of SOP and CSP. ## Version 12
diff --git a/src/cobalt/accessibility/accessibility_test.gyp b/src/cobalt/accessibility/accessibility_test.gyp index 8db8e78..3da8086 100644 --- a/src/cobalt/accessibility/accessibility_test.gyp +++ b/src/cobalt/accessibility/accessibility_test.gyp
@@ -34,18 +34,13 @@ { 'target_name': 'accessibility_test_data', 'type': 'none', - 'actions': [ - { - 'action_name': 'accessibility_test_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/accessibility/testdata/', - ], - 'output_dir': 'cobalt/accessibility/testdata/', - }, - 'includes': ['../build/copy_test_data.gypi'], - } - ], + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/cobalt/accessibility/testdata/', + ], + 'content_test_output_subdir': 'cobalt/accessibility/testdata/', + }, + 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], }, { 'target_name': 'accessibility_test_deploy',
diff --git a/src/cobalt/accessibility/screen_reader_tests.cc b/src/cobalt/accessibility/screen_reader_tests.cc index e6631ff..be8adfd 100644 --- a/src/cobalt/accessibility/screen_reader_tests.cc +++ b/src/cobalt/accessibility/screen_reader_tests.cc
@@ -55,7 +55,7 @@ const std::string& subdir) { std::vector<TestInfo> infos; FilePath root_directory; - PathService::Get(base::DIR_SOURCE_ROOT, &root_directory); + PathService::Get(base::DIR_TEST_DATA, &root_directory); root_directory = root_directory.Append("cobalt") .Append("accessibility") .Append("testdata")
diff --git a/src/cobalt/audio/audio_buffer.cc b/src/cobalt/audio/audio_buffer.cc index 3f72c56..6e9e804 100644 --- a/src/cobalt/audio/audio_buffer.cc +++ b/src/cobalt/audio/audio_buffer.cc
@@ -36,8 +36,8 @@ const uint32 length = number_of_frames * number_of_channels * (sample_type == kSampleTypeFloat32 ? sizeof(float) : sizeof(int16)); - scoped_refptr<dom::ArrayBuffer> array_buffer(new dom::ArrayBuffer( - settings, dom::ArrayBuffer::kFromHeap, channels_data.Pass(), length)); + scoped_refptr<dom::ArrayBuffer> array_buffer( + new dom::ArrayBuffer(settings, channels_data.Pass(), length)); // Each channel should have |number_of_frames * size_of_sample_type| bytes. // We create |number_of_channels| of {Float32,Int16}Array as views into the
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc index 53d041d..3e68fe5 100644 --- a/src/cobalt/audio/audio_device.cc +++ b/src/cobalt/audio/audio_device.cc
@@ -225,7 +225,7 @@ const bool is_input_int16 = input_audio_bus_.sample_type() == media::ShellAudioBus::kInt16; const bool is_output_int16 = - output_sample_type_ == kSbMediaAudioSampleTypeInt16; + output_sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated; if (is_input_int16 && is_output_int16) { FillOutputAudioBusForType<int16, int16>();
diff --git a/src/cobalt/audio/audio_helpers.h b/src/cobalt/audio/audio_helpers.h index a1bbe1b..2d36bec 100644 --- a/src/cobalt/audio/audio_helpers.h +++ b/src/cobalt/audio/audio_helpers.h
@@ -47,7 +47,7 @@ switch (sample_type) { case kSbMediaAudioSampleTypeFloat32: return sizeof(float); - case kSbMediaAudioSampleTypeInt16: + case kSbMediaAudioSampleTypeInt16Deprecated: return sizeof(int16); } NOTREACHED(); @@ -77,7 +77,8 @@ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) { return kSampleTypeFloat32; } - DCHECK(SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) + DCHECK(SbAudioSinkIsAudioSampleTypeSupported( + kSbMediaAudioSampleTypeInt16Deprecated)) << "At least one starboard audio sample type must be supported if using " "starboard media pipeline."; return kSampleTypeInt16; @@ -96,7 +97,7 @@ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) { return kSbMediaAudioSampleTypeFloat32; } - return kSbMediaAudioSampleTypeInt16; + return kSbMediaAudioSampleTypeInt16Deprecated; } #endif
diff --git a/src/cobalt/base/copy_i18n_data.gypi b/src/cobalt/base/copy_i18n_data.gypi index 77567f2..00fe6ae 100644 --- a/src/cobalt/base/copy_i18n_data.gypi +++ b/src/cobalt/base/copy_i18n_data.gypi
@@ -30,4 +30,10 @@ 'files': ['<@(inputs_i18n)'], }, ], + + 'all_dependent_settings': { + 'variables': { + 'content_deploy_subdirs': [ 'icu' ] + } + }, }
diff --git a/src/cobalt/bindings/contexts.py b/src/cobalt/bindings/contexts.py index 061a44d..690189a 100644 --- a/src/cobalt/bindings/contexts.py +++ b/src/cobalt/bindings/contexts.py
@@ -558,7 +558,7 @@ if operation.name ] - # Create overload sets for static and non-static methods seperately. + # Create overload sets for static and non-static methods separately. # Each item in the list is a pair of (name, [method_contexts]) where for # each method_context m in the list, m['name'] == name. static_method_overloads = method_overloads_by_name(
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/derived_dictionary.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/derived_dictionary.h index beb3930..78e7e1a 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/derived_dictionary.h +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/derived_dictionary.h
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off @@ -63,6 +61,7 @@ additional_member_ = value; } + private: bool additional_member_; };
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h index b4737b0..5b2ce57 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off @@ -66,6 +64,7 @@ nested_dictionary_ = value; } + private: bool has_nested_dictionary_; TestDictionary nested_dictionary_;
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc index b430f43..93ed68d 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_callback_interface_interface.cc
@@ -347,7 +347,7 @@ return false; } // Non-optional arguments - TypeTraits<::cobalt::script::CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType callback_interface; + TypeTraits<::cobalt::script::CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType callbackInterface; DCHECK_LT(0, args.length()); JS::RootedValue non_optional_value0( @@ -355,12 +355,12 @@ FromJSValue(context, non_optional_value0, kNoConversionFlags, - &exception_state, &callback_interface); + &exception_state, &callbackInterface); if (exception_state.is_exception_set()) { return false; } - impl->RegisterCallback(callback_interface); + impl->RegisterCallback(callbackInterface); result_value.set(JS::UndefinedHandleValue); return !exception_state.is_exception_set(); }
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc index f99a86c..5dea92b 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.cc
@@ -555,7 +555,7 @@ TypeTraits<int32_t >::ConversionType arg1; TypeTraits<bool >::ConversionType arg2; // Optional arguments with default values - TypeTraits<std::string >::ConversionType default_arg = + TypeTraits<std::string >::ConversionType defaultArg = "default"; DCHECK_LT(0, args.length()); @@ -587,14 +587,14 @@ optional_value0, kNoConversionFlags, &exception_state, - &default_arg); + &defaultArg); if (exception_state.is_exception_set()) { return false; } } scoped_refptr<ConstructorWithArgumentsInterface> new_object = - new ConstructorWithArgumentsInterface(arg1, arg2, default_arg); + new ConstructorWithArgumentsInterface(arg1, arg2, defaultArg); JS::RootedValue result_value(context); ToJSValue(context, new_object, &result_value); DCHECK(result_value.isObject());
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_convert_simple_object_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_convert_simple_object_interface.cc new file mode 100644 index 0000000..c2704b8 --- /dev/null +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_convert_simple_object_interface.cc
@@ -0,0 +1,473 @@ + + +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// clang-format off + +// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY! +// Auto-generated from template: bindings/mozjs45/templates/interface.cc.template + +#include "cobalt/bindings/testing/mozjs_convert_simple_object_interface.h" + +#include "base/debug/trace_event.h" +#include "cobalt/base/polymorphic_downcast.h" +#include "cobalt/script/global_environment.h" +#include "cobalt/script/script_value.h" +#include "cobalt/script/value_handle.h" + +#include "mozjs_gen_type_conversion.h" + +#include "base/lazy_instance.h" +#include "cobalt/script/exception_state.h" +#include "cobalt/script/mozjs-45/callback_function_conversion.h" +#include "cobalt/script/mozjs-45/conversion_helpers.h" +#include "cobalt/script/mozjs-45/mozjs_callback_function.h" +#include "cobalt/script/mozjs-45/mozjs_exception_state.h" +#include "cobalt/script/mozjs-45/mozjs_global_environment.h" +#include "cobalt/script/mozjs-45/mozjs_property_enumerator.h" +#include "cobalt/script/mozjs-45/mozjs_user_object_holder.h" +#include "cobalt/script/mozjs-45/mozjs_value_handle.h" +#include "cobalt/script/mozjs-45/native_promise.h" +#include "cobalt/script/mozjs-45/proxy_handler.h" +#include "cobalt/script/mozjs-45/type_traits.h" +#include "cobalt/script/mozjs-45/wrapper_factory.h" +#include "cobalt/script/mozjs-45/wrapper_private.h" +#include "cobalt/script/property_enumerator.h" +#include "cobalt/script/sequence.h" +#include "third_party/mozjs-45/js/src/jsapi.h" +#include "third_party/mozjs-45/js/src/jsfriendapi.h" + + +namespace { +using cobalt::bindings::testing::ConvertSimpleObjectInterface; +using cobalt::bindings::testing::MozjsConvertSimpleObjectInterface; +using cobalt::script::CallbackInterfaceTraits; +using cobalt::script::GlobalEnvironment; +using cobalt::script::ScriptValue; +using cobalt::script::ValueHandle; +using cobalt::script::ValueHandle; +using cobalt::script::ValueHandleHolder; +using cobalt::script::Wrappable; + +using cobalt::script::CallbackFunction; +using cobalt::script::CallbackInterfaceTraits; +using cobalt::script::ExceptionState; +using cobalt::script::Wrappable; +using cobalt::script::mozjs::FromJSValue; +using cobalt::script::mozjs::InterfaceData; +using cobalt::script::mozjs::MozjsCallbackFunction; +using cobalt::script::mozjs::MozjsExceptionState; +using cobalt::script::mozjs::MozjsGlobalEnvironment; +using cobalt::script::mozjs::MozjsPropertyEnumerator; +using cobalt::script::mozjs::MozjsUserObjectHolder; +using cobalt::script::mozjs::ProxyHandler; +using cobalt::script::mozjs::ToJSValue; +using cobalt::script::mozjs::TypeTraits; +using cobalt::script::mozjs::WrapperFactory; +using cobalt::script::mozjs::WrapperPrivate; +using cobalt::script::mozjs::kConversionFlagClamped; +using cobalt::script::mozjs::kConversionFlagNullable; +using cobalt::script::mozjs::kConversionFlagRestricted; +using cobalt::script::mozjs::kConversionFlagTreatNullAsEmptyString; +using cobalt::script::mozjs::kConversionFlagTreatUndefinedAsEmptyString; +using cobalt::script::mozjs::kConversionFlagObjectOnly; +using cobalt::script::mozjs::kNoConversionFlags; +} // namespace + +namespace cobalt { +namespace bindings { +namespace testing { + + +namespace { + + + + + + + +class MozjsConvertSimpleObjectInterfaceHandler : public ProxyHandler { + public: + MozjsConvertSimpleObjectInterfaceHandler() + : ProxyHandler(indexed_property_hooks, named_property_hooks) {} + + private: + static NamedPropertyHooks named_property_hooks; + static IndexedPropertyHooks indexed_property_hooks; +}; + +ProxyHandler::NamedPropertyHooks +MozjsConvertSimpleObjectInterfaceHandler::named_property_hooks = { + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +ProxyHandler::IndexedPropertyHooks +MozjsConvertSimpleObjectInterfaceHandler::indexed_property_hooks = { + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static base::LazyInstance<MozjsConvertSimpleObjectInterfaceHandler> + proxy_handler; + +bool Constructor(JSContext* context, unsigned int argc, JS::Value* vp); + + +bool HasInstance(JSContext *context, JS::HandleObject type, + JS::MutableHandleValue vp, bool *success) { + JS::RootedObject global_object( + context, JS_GetGlobalForObject(context, type)); + DCHECK(global_object); + + JS::RootedObject prototype( + context, MozjsConvertSimpleObjectInterface::GetPrototype(context, global_object)); + + // |IsDelegate| walks the prototype chain of an object returning true if + // .prototype is found. + bool is_delegate; + if (!IsDelegate(context, prototype, vp, &is_delegate)) { + *success = false; + return false; + } + + *success = is_delegate; + return true; +} + +const JSClass instance_class_definition = { + "ConvertSimpleObjectInterface", + 0 | JSCLASS_HAS_PRIVATE, + NULL, // addProperty + NULL, // delProperty + NULL, // getProperty + NULL, // setProperty + NULL, // enumerate + NULL, // resolve + NULL, // mayResolve + &WrapperPrivate::Finalizer, // finalize + NULL, // call + NULL, // hasInstance + NULL, // construct + &WrapperPrivate::Trace, // trace +}; + +const JSClass prototype_class_definition = { + "ConvertSimpleObjectInterfacePrototype", +}; + +const JSClass interface_object_class_definition = { + "ConvertSimpleObjectInterfaceConstructor", + 0, + NULL, // addProperty + NULL, // delProperty + NULL, // getProperty + NULL, // setProperty + NULL, // enumerate + NULL, // resolve + NULL, // mayResolve + NULL, // finalize + NULL, // call + &HasInstance, + Constructor, +}; + + +bool fcn_convertSimpleObjectToMapTest( + JSContext* context, uint32_t argc, JS::Value *vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + // Compute the 'this' value. + JS::RootedValue this_value(context, JS_ComputeThis(context, vp)); + // 'this' should be an object. + JS::RootedObject object(context); + if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) { + return false; + } + if (!JS_ValueToObject(context, this_value, &object)) { + NOTREACHED(); + return false; + } + const JSClass* proto_class = + MozjsConvertSimpleObjectInterface::PrototypeClass(context); + if (proto_class == JS_GetClass(object)) { + // Simply returns true if the object is this class's prototype object. + // There is no need to return any value due to the object is not a platform + // object. The execution reaches here when Object.getOwnPropertyDescriptor + // gets called on native object prototypes. + return true; + } + + MozjsGlobalEnvironment* global_environment = + static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); + WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); + if (!wrapper_factory->DoesObjectImplementInterface( + object, base::GetTypeId<ConvertSimpleObjectInterface>())) { + MozjsExceptionState exception(context); + exception.SetSimpleException(script::kDoesNotImplementInterface); + return false; + } + MozjsExceptionState exception_state(context); + JS::RootedValue result_value(context); + + WrapperPrivate* wrapper_private = + WrapperPrivate::GetFromObject(context, object); + ConvertSimpleObjectInterface* impl = + wrapper_private->wrappable<ConvertSimpleObjectInterface>().get(); + const size_t kMinArguments = 1; + if (args.length() < kMinArguments) { + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); + return false; + } + // Non-optional arguments + TypeTraits<::cobalt::script::ValueHandle >::ConversionType value; + + DCHECK_LT(0, args.length()); + JS::RootedValue non_optional_value0( + context, args[0]); + FromJSValue(context, + non_optional_value0, + kNoConversionFlags, + &exception_state, &value); + if (exception_state.is_exception_set()) { + return false; + } + + impl->ConvertSimpleObjectToMapTest(value, &exception_state); + result_value.set(JS::UndefinedHandleValue); + return !exception_state.is_exception_set(); +} + + + +const JSPropertySpec prototype_properties[] = { + + JS_PS_END +}; + +const JSFunctionSpec prototype_functions[] = { + JS_FNSPEC( + "convertSimpleObjectToMapTest", fcn_convertSimpleObjectToMapTest, NULL, + 1, JSPROP_ENUMERATE, NULL), + JS_FS_END +}; + +const JSPropertySpec interface_object_properties[] = { + + JS_PS_END +}; + +const JSFunctionSpec interface_object_functions[] = { + JS_FS_END +}; + +const JSPropertySpec own_properties[] = { + JS_PS_END +}; + +void InitializePrototypeAndInterfaceObject( + InterfaceData* interface_data, JSContext* context, + JS::HandleObject global_object) { + DCHECK(!interface_data->prototype); + DCHECK(!interface_data->interface_object); + DCHECK(JS_IsGlobalObject(global_object)); + + JS::RootedObject parent_prototype( + context, JS_GetObjectPrototype(context, global_object)); + DCHECK(parent_prototype); + + interface_data->prototype = JS_NewObjectWithGivenProto( + context, &prototype_class_definition, parent_prototype + ); + + JS::RootedObject rooted_prototype(context, interface_data->prototype); + bool success = JS_DefineProperties( + context, + rooted_prototype, + prototype_properties); + + DCHECK(success); + success = JS_DefineFunctions( + context, rooted_prototype, prototype_functions); + DCHECK(success); + + JS::RootedObject function_prototype( + context, JS_GetFunctionPrototype(context, global_object)); + DCHECK(function_prototype); + + const char name[] = + "ConvertSimpleObjectInterface"; + + JSFunction* function = js::NewFunctionWithReserved( + context, + Constructor, + 0, + JSFUN_CONSTRUCTOR, + name); + interface_data->interface_object = JS_GetFunctionObject(function); + + // Add the InterfaceObject.name property. + JS::RootedObject rooted_interface_object( + context, interface_data->interface_object); + JS::RootedValue name_value(context); + + js::SetPrototype(context, rooted_interface_object, function_prototype); + + name_value.setString(JS_NewStringCopyZ(context, name)); + success = JS_DefineProperty( + context, rooted_interface_object, "name", name_value, JSPROP_READONLY, + NULL, NULL); + DCHECK(success); + + // Add the InterfaceObject.length property. It is set to the length of the + // shortest argument list of all overload constructors. + JS::RootedValue length_value(context); + length_value.setInt32(0); + success = JS_DefineProperty( + context, rooted_interface_object, "length", length_value, + JSPROP_READONLY, NULL, NULL); + DCHECK(success); + + // Define interface object properties (including constants). + success = JS_DefineProperties(context, rooted_interface_object, + interface_object_properties); + DCHECK(success); + // Define interface object functions (static). + success = JS_DefineFunctions(context, rooted_interface_object, + interface_object_functions); + DCHECK(success); + + // Set the Prototype.constructor and Constructor.prototype properties. + DCHECK(interface_data->interface_object); + DCHECK(interface_data->prototype); + success = JS_LinkConstructorAndPrototype( + context, + rooted_interface_object, + rooted_prototype); + DCHECK(success); +} + +inline InterfaceData* GetInterfaceData(JSContext* context) { + const int kInterfaceUniqueId = 12; + MozjsGlobalEnvironment* global_environment = + static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); + // By convention, the |MozjsGlobalEnvironment| that we are associated with + // will hold our |InterfaceData| at index |kInterfaceUniqueId|, as we asked + // for it to be there in the first place, and could not have conflicted with + // any other interface. + return global_environment->GetInterfaceData(kInterfaceUniqueId); +} + +} // namespace + +// static +JSObject* MozjsConvertSimpleObjectInterface::CreateProxy( + JSContext* context, const scoped_refptr<Wrappable>& wrappable) { + DCHECK(MozjsGlobalEnvironment::GetFromContext(context)); + JS::RootedObject global_object( + context, + MozjsGlobalEnvironment::GetFromContext(context)->global_object()); + DCHECK(global_object); + + InterfaceData* interface_data = GetInterfaceData(context); + JS::RootedObject prototype(context, GetPrototype(context, global_object)); + DCHECK(prototype); + JS::RootedObject new_object( + context, + JS_NewObjectWithGivenProto( + context, &instance_class_definition, prototype)); + DCHECK(new_object); + JS::RootedObject proxy(context, + ProxyHandler::NewProxy( + context, proxy_handler.Pointer(), new_object, prototype)); + WrapperPrivate::AddPrivateData(context, proxy, wrappable); + return proxy; +} + +// static +const JSClass* MozjsConvertSimpleObjectInterface::PrototypeClass( + JSContext* context) { + DCHECK(MozjsGlobalEnvironment::GetFromContext(context)); + JS::RootedObject global_object( + context, + MozjsGlobalEnvironment::GetFromContext(context)->global_object()); + DCHECK(global_object); + + JS::RootedObject prototype(context, GetPrototype(context, global_object)); + const JSClass* proto_class = JS_GetClass(prototype); + return proto_class; +} + +// static +JSObject* MozjsConvertSimpleObjectInterface::GetPrototype( + JSContext* context, JS::HandleObject global_object) { + DCHECK(JS_IsGlobalObject(global_object)); + + InterfaceData* interface_data = GetInterfaceData(context); + if (!interface_data->prototype) { + // Create new prototype that has all the props and methods + InitializePrototypeAndInterfaceObject( + interface_data, context, global_object); + } + DCHECK(interface_data->prototype); + return interface_data->prototype; +} + +// static +JSObject* MozjsConvertSimpleObjectInterface::GetInterfaceObject( + JSContext* context, JS::HandleObject global_object) { + DCHECK(JS_IsGlobalObject(global_object)); + + InterfaceData* interface_data = GetInterfaceData(context); + if (!interface_data->interface_object) { + InitializePrototypeAndInterfaceObject( + interface_data, context, global_object); + } + DCHECK(interface_data->interface_object); + return interface_data->interface_object; +} + +namespace { + + + +bool Constructor(JSContext* context, unsigned int argc, JS::Value* vp) { + MozjsExceptionState exception_state(context); + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + + scoped_refptr<ConvertSimpleObjectInterface> new_object = + new ConvertSimpleObjectInterface(); + JS::RootedValue result_value(context); + ToJSValue(context, new_object, &result_value); + DCHECK(result_value.isObject()); + args.rval().setObject(result_value.toObject()); + return true; +} + + +} // namespace + + +} // namespace testing +} // namespace bindings +} // namespace cobalt + +
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_convert_simple_object_interface.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_convert_simple_object_interface.h new file mode 100644 index 0000000..392d5a8 --- /dev/null +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_convert_simple_object_interface.h
@@ -0,0 +1,52 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// clang-format off + +// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY! +// Auto-generated from template: bindings/mozjs45/templates/interface.h.template + +#ifndef MozjsConvertSimpleObjectInterface_h +#define MozjsConvertSimpleObjectInterface_h + +#include "base/hash_tables.h" +#include "base/lazy_instance.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "cobalt/base/polymorphic_downcast.h" +#include "cobalt/script/wrappable.h" +#include "cobalt/bindings/testing/convert_simple_object_interface.h" + +#include "third_party/mozjs-45/js/src/jsapi.h" + +namespace cobalt { +namespace bindings { +namespace testing { + +class MozjsConvertSimpleObjectInterface { + public: + static JSObject* CreateProxy(JSContext* context, + const scoped_refptr<script::Wrappable>& wrappable); + static const JSClass* PrototypeClass(JSContext* context); + static JSObject* GetPrototype(JSContext* context, + JS::HandleObject global_object); + static JSObject* GetInterfaceObject(JSContext* context, + JS::HandleObject global_object); +}; + +} // namespace testing +} // namespace bindings +} // namespace cobalt + +#endif // MozjsConvertSimpleObjectInterface_h
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_dictionary.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_dictionary.cc index 68c45e7..cca4db3 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_dictionary.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_dictionary.cc
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc index beeed87..f7fe523 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
@@ -858,7 +858,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 14; + const int kInterfaceUniqueId = 15; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc index 56b576d..1705331 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
@@ -406,7 +406,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 15; + const int kInterfaceUniqueId = 16; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc index af42eb9..bce080c 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_interface.cc
@@ -610,7 +610,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 16; + const int kInterfaceUniqueId = 17; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc index e4b23a6..438e531 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dictionary_with_dictionary_member.cc
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc index be0503a..ec676ae 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
@@ -452,7 +452,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 18; + const int kInterfaceUniqueId = 19; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc index 721d4ad..ad7378f 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_dom_string_test_interface.cc
@@ -826,7 +826,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 12; + const int kInterfaceUniqueId = 13; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc index b6010a8..2bf155e 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_enumeration_interface.cc
@@ -472,7 +472,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 19; + const int kInterfaceUniqueId = 20; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc index 93cc11b..2853596 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exception_object_interface.cc
@@ -406,7 +406,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 20; + const int kInterfaceUniqueId = 21; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc index 5b2e7c3..55475f2 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_exceptions_interface.cc
@@ -454,7 +454,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 21; + const int kInterfaceUniqueId = 22; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc index fd4f75c..a9f4c6a 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_extended_idl_attributes_interface.cc
@@ -520,7 +520,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 22; + const int kInterfaceUniqueId = 23; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc index 29c0d00..3d4bc35 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_garbage_collection_test_interface.cc
@@ -514,7 +514,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 23; + const int kInterfaceUniqueId = 24; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc index 4d46379..fd8827a 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_global_interface_parent.cc
@@ -344,7 +344,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 24; + const int kInterfaceUniqueId = 25; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc index 87f1bdf..0b3c64e 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_indexed_getter_interface.cc
@@ -683,7 +683,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 26; + const int kInterfaceUniqueId = 27; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc index 0253e92..7267dd6 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any.cc
@@ -422,7 +422,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 27; + const int kInterfaceUniqueId = 28; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.cc index 4e4587a..81cc7f8 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_any_dictionary.cc
@@ -534,7 +534,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 28; + const int kInterfaceUniqueId = 29; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_date.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_date.cc index 165ab40..5b256be 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_date.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_date.cc
@@ -422,7 +422,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 29; + const int kInterfaceUniqueId = 30; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc index 6961c4b..07233d6 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_interface_with_unsupported_properties.cc
@@ -348,7 +348,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 30; + const int kInterfaceUniqueId = 31; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc index c097e06..f57483d 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_constructor_interface.cc
@@ -298,7 +298,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 31; + const int kInterfaceUniqueId = 32; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc index 90ae15d..7ec66d7 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_getter_interface.cc
@@ -631,7 +631,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 32; + const int kInterfaceUniqueId = 33; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc index 6d5a610..6761bbb 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_named_indexed_getter_interface.cc
@@ -1008,7 +1008,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 33; + const int kInterfaceUniqueId = 34; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc index 04cc839..97e295f 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nested_put_forwards_interface.cc
@@ -426,7 +426,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 34; + const int kInterfaceUniqueId = 35; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc index 2b3459a..183514f 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_constructor_interface.cc
@@ -294,7 +294,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 35; + const int kInterfaceUniqueId = 36; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc index 15b0fdf..582d177 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_no_interface_object_interface.cc
@@ -249,7 +249,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 36; + const int kInterfaceUniqueId = 37; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc index 89b0ac0..59de7cd 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_nullable_types_test_interface.cc
@@ -1218,7 +1218,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 37; + const int kInterfaceUniqueId = 38; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc index d86cd37..db32800 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_numeric_types_test_interface.cc
@@ -3442,7 +3442,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 38; + const int kInterfaceUniqueId = 39; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc index 9e10cc0..6e6e15c 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_object_type_bindings_interface.cc
@@ -678,7 +678,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 39; + const int kInterfaceUniqueId = 40; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc index 6021a3f..85259f0 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_operations_test_interface.cc
@@ -1302,7 +1302,7 @@ OperationsTestInterface* impl = wrapper_private->wrappable<OperationsTestInterface>().get(); // Optional arguments - TypeTraits<bool >::ConversionType optional_arg; + TypeTraits<bool >::ConversionType optionalArg; // Variadic argument TypeTraits<std::vector<std::string> >::ConversionType strings; size_t num_set_arguments = 0; @@ -1313,7 +1313,7 @@ optional_value0, kNoConversionFlags, &exception_state, - &optional_arg); + &optionalArg); if (exception_state.is_exception_set()) { return false; } @@ -1354,7 +1354,7 @@ break; case 2: { - impl->VariadicStringArgumentsAfterOptionalArgument(optional_arg, strings); + impl->VariadicStringArgumentsAfterOptionalArgument(optionalArg, strings); result_value.set(JS::UndefinedHandleValue); return !exception_state.is_exception_set(); } @@ -1852,7 +1852,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 40; + const int kInterfaceUniqueId = 41; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc index b0dfda6..cd5df8a 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_promise_interface.cc
@@ -568,7 +568,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 41; + const int kInterfaceUniqueId = 42; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc index 6f4c4f8..70819d5 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_put_forwards_interface.cc
@@ -485,7 +485,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 42; + const int kInterfaceUniqueId = 43; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc index e55a911..1c5fed2 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_sequence_user.cc
@@ -1170,7 +1170,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 43; + const int kInterfaceUniqueId = 44; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc index 2e3a926..0cb7128 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_static_properties_interface.cc
@@ -606,7 +606,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 45; + const int kInterfaceUniqueId = 46; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc index 5d81015..1aa7cec 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_anonymous_operation_interface.cc
@@ -354,7 +354,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 46; + const int kInterfaceUniqueId = 47; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc index 27051e7..fce9bd1 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_attribute_interface.cc
@@ -460,7 +460,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 47; + const int kInterfaceUniqueId = 48; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc index b041ca5..0373f11 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_stringifier_operation_interface.cc
@@ -410,7 +410,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 48; + const int kInterfaceUniqueId = 49; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc index 6337cbc..f361b59 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_target_interface.cc
@@ -394,7 +394,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 49; + const int kInterfaceUniqueId = 50; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc index 9e98c40..2fb1b7d 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_dictionary.cc
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_enum.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_enum.cc index a859d1c..d64fee9 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_enum.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_test_enum.cc
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc index 673f84e..59b9b49 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_union_types_interface.cc
@@ -726,7 +726,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 51; + const int kInterfaceUniqueId = 52; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc index 4a585c6..bf69b82 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
@@ -38,6 +38,7 @@ #include "cobalt/bindings/testing/constants_interface.h" #include "cobalt/bindings/testing/constructor_interface.h" #include "cobalt/bindings/testing/constructor_with_arguments_interface.h" +#include "cobalt/bindings/testing/convert_simple_object_interface.h" #include "cobalt/bindings/testing/derived_getter_setter_interface.h" #include "cobalt/bindings/testing/derived_interface.h" #include "cobalt/bindings/testing/dictionary_interface.h" @@ -67,6 +68,7 @@ #include "cobalt/bindings/testing/mozjs_constants_interface.h" #include "cobalt/bindings/testing/mozjs_constructor_interface.h" #include "cobalt/bindings/testing/mozjs_constructor_with_arguments_interface.h" +#include "cobalt/bindings/testing/mozjs_convert_simple_object_interface.h" #include "cobalt/bindings/testing/mozjs_derived_getter_setter_interface.h" #include "cobalt/bindings/testing/mozjs_derived_interface.h" #include "cobalt/bindings/testing/mozjs_dictionary_interface.h" @@ -167,6 +169,7 @@ using cobalt::bindings::testing::ConstantsInterface; using cobalt::bindings::testing::ConstructorInterface; using cobalt::bindings::testing::ConstructorWithArgumentsInterface; +using cobalt::bindings::testing::ConvertSimpleObjectInterface; using cobalt::bindings::testing::DOMStringTestInterface; using cobalt::bindings::testing::DerivedGetterSetterInterface; using cobalt::bindings::testing::DerivedInterface; @@ -200,6 +203,7 @@ using cobalt::bindings::testing::MozjsConstantsInterface; using cobalt::bindings::testing::MozjsConstructorInterface; using cobalt::bindings::testing::MozjsConstructorWithArgumentsInterface; +using cobalt::bindings::testing::MozjsConvertSimpleObjectInterface; using cobalt::bindings::testing::MozjsDOMStringTestInterface; using cobalt::bindings::testing::MozjsDerivedGetterSetterInterface; using cobalt::bindings::testing::MozjsDerivedInterface; @@ -992,7 +996,7 @@ } inline InterfaceData* GetInterfaceData(JSContext* context) { - const int kInterfaceUniqueId = 53; + const int kInterfaceUniqueId = 54; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); // By convention, the |MozjsGlobalEnvironment| that we are associated with @@ -1168,6 +1172,10 @@ base::Bind(MozjsConstructorWithArgumentsInterface::CreateProxy), base::Bind(MozjsConstructorWithArgumentsInterface::PrototypeClass)); wrapper_factory_->RegisterWrappableType( + ConvertSimpleObjectInterface::ConvertSimpleObjectInterfaceWrappableType(), + base::Bind(MozjsConvertSimpleObjectInterface::CreateProxy), + base::Bind(MozjsConvertSimpleObjectInterface::PrototypeClass)); + wrapper_factory_->RegisterWrappableType( DOMStringTestInterface::DOMStringTestInterfaceWrappableType(), base::Bind(MozjsDOMStringTestInterface::CreateProxy), base::Bind(MozjsDOMStringTestInterface::PrototypeClass));
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h index 1989c88..b60f6c3 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_dictionary.h
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off @@ -75,14 +73,14 @@ non_default_member_ = other.non_default_member_; if (other.any_member_with_default_) { any_member_with_default_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference( - other.any_member_with_default_->referenced_value())); + new script::Handle<::cobalt::script::ValueHandle>( + *other.any_member_with_default_)); } has_any_member_ = other.has_any_member_; if (other.any_member_) { any_member_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference( - other.any_member_->referenced_value())); + new script::Handle<::cobalt::script::ValueHandle>( + *other.any_member_)); } } @@ -104,16 +102,16 @@ non_default_member_ = other.non_default_member_; if (other.any_member_with_default_) { any_member_with_default_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference( - other.any_member_with_default_->referenced_value())); + new script::Handle<::cobalt::script::ValueHandle>( + *other.any_member_with_default_)); } else { any_member_with_default_.reset(); } has_any_member_ = other.has_any_member_; if (other.any_member_) { any_member_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference( - other.any_member_->referenced_value())); + new script::Handle<::cobalt::script::ValueHandle>( + *other.any_member_)); } else { any_member_.reset(); } @@ -221,12 +219,12 @@ if (!any_member_with_default_) { return NULL; } - return &(any_member_with_default_->referenced_value()); + return (any_member_with_default_->GetScriptValue()); } void set_any_member_with_default(const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* value) { if (value) { any_member_with_default_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(*value)); + new script::Handle<::cobalt::script::ValueHandle>(*value)); } else { any_member_with_default_.reset(); } @@ -240,18 +238,19 @@ if (!any_member_) { return NULL; } - return &(any_member_->referenced_value()); + return (any_member_->GetScriptValue()); } void set_any_member(const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* value) { has_any_member_ = true; if (value) { any_member_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(*value)); + new script::Handle<::cobalt::script::ValueHandle>(*value)); } else { any_member_.reset(); } } + private: bool has_boolean_member_; bool boolean_member_; @@ -268,9 +267,9 @@ int32_t member_with_default_; bool has_non_default_member_; int32_t non_default_member_; - scoped_ptr<script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference> any_member_with_default_; + scoped_ptr<script::Handle<::cobalt::script::ValueHandle>> any_member_with_default_; bool has_any_member_; - scoped_ptr<script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference> any_member_; + scoped_ptr<script::Handle<::cobalt::script::ValueHandle>> any_member_; }; // This ostream override is necessary for MOCK_METHODs commonly used
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_enum.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_enum.h index b688cc7..7fdb86e 100644 --- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_enum.h +++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/test_enum.h
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/derived_dictionary.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/derived_dictionary.h index beb3930..78e7e1a 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/derived_dictionary.h +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/derived_dictionary.h
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off @@ -63,6 +61,7 @@ additional_member_ = value; } + private: bool additional_member_; };
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h index b4737b0..5b2ce57 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/dictionary_with_dictionary_member.h
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off @@ -66,6 +64,7 @@ nested_dictionary_ = value; } + private: bool has_nested_dictionary_; TestDictionary nested_dictionary_;
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/test_dictionary.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/test_dictionary.h index 1989c88..b60f6c3 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/test_dictionary.h +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/test_dictionary.h
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off @@ -75,14 +73,14 @@ non_default_member_ = other.non_default_member_; if (other.any_member_with_default_) { any_member_with_default_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference( - other.any_member_with_default_->referenced_value())); + new script::Handle<::cobalt::script::ValueHandle>( + *other.any_member_with_default_)); } has_any_member_ = other.has_any_member_; if (other.any_member_) { any_member_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference( - other.any_member_->referenced_value())); + new script::Handle<::cobalt::script::ValueHandle>( + *other.any_member_)); } } @@ -104,16 +102,16 @@ non_default_member_ = other.non_default_member_; if (other.any_member_with_default_) { any_member_with_default_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference( - other.any_member_with_default_->referenced_value())); + new script::Handle<::cobalt::script::ValueHandle>( + *other.any_member_with_default_)); } else { any_member_with_default_.reset(); } has_any_member_ = other.has_any_member_; if (other.any_member_) { any_member_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference( - other.any_member_->referenced_value())); + new script::Handle<::cobalt::script::ValueHandle>( + *other.any_member_)); } else { any_member_.reset(); } @@ -221,12 +219,12 @@ if (!any_member_with_default_) { return NULL; } - return &(any_member_with_default_->referenced_value()); + return (any_member_with_default_->GetScriptValue()); } void set_any_member_with_default(const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* value) { if (value) { any_member_with_default_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(*value)); + new script::Handle<::cobalt::script::ValueHandle>(*value)); } else { any_member_with_default_.reset(); } @@ -240,18 +238,19 @@ if (!any_member_) { return NULL; } - return &(any_member_->referenced_value()); + return (any_member_->GetScriptValue()); } void set_any_member(const ::cobalt::script::ScriptValue<::cobalt::script::ValueHandle>* value) { has_any_member_ = true; if (value) { any_member_.reset( - new script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference(*value)); + new script::Handle<::cobalt::script::ValueHandle>(*value)); } else { any_member_.reset(); } } + private: bool has_boolean_member_; bool boolean_member_; @@ -268,9 +267,9 @@ int32_t member_with_default_; bool has_non_default_member_; int32_t non_default_member_; - scoped_ptr<script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference> any_member_with_default_; + scoped_ptr<script::Handle<::cobalt::script::ValueHandle>> any_member_with_default_; bool has_any_member_; - scoped_ptr<script::ScriptValue<::cobalt::script::ValueHandle>::StrongReference> any_member_; + scoped_ptr<script::Handle<::cobalt::script::ValueHandle>> any_member_; }; // This ostream override is necessary for MOCK_METHODs commonly used
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/test_enum.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/test_enum.h index b688cc7..7fdb86e 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/test_enum.h +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/test_enum.h
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc index 47af2a9..b529fa5 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_indexed_getter_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -94,7 +95,7 @@ uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -129,7 +130,7 @@ void IndexedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -160,7 +161,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -192,17 +193,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "AnonymousIndexedGetterInterface is not constructible."); +} + + void lengthAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<AnonymousIndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cAnonymousIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -219,14 +227,16 @@ AnonymousIndexedGetterInterface* impl = wrapper_private->wrappable<AnonymousIndexedGetterInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->length(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } @@ -258,7 +268,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -305,18 +315,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, lengthAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - lengthAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -338,6 +349,8 @@ static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + + { v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { IndexedPropertyGetterCallback,
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc index ab85a2f..1f72f1a 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_getter_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -91,7 +92,7 @@ v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -120,11 +121,18 @@ DCHECK(!exception_state.is_exception_set()); } +void IndexedPropertyGetterCallback( + uint32_t index, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); + NamedPropertyGetterCallback(as_string, info); +} + void NamedPropertyQueryCallback( v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -138,19 +146,29 @@ if (!result) { return; } + // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty + int properties = v8::None; // 2.7. If |O| implements an interface with a named property setter, then set // desc.[[Writable]] to true, otherwise set it to false. // 2.8. If |O| implements an interface with the // [LegacyUnenumerableNamedProperties] extended attribute, then set // desc.[[Enumerable]] to false, otherwise set it to true. - info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly); + + info.GetReturnValue().Set(properties); +} + +void IndexedPropertyDescriptorCallback( + uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { + // TODO: Figure out under what conditions this gets called. It's not + // getting called in our tests. + NOTIMPLEMENTED(); } void NamedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -171,7 +189,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -193,15 +211,32 @@ impl->AnonymousNamedSetter(property_name, native_value); result_value = v8::Undefined(isolate); + info.GetReturnValue().Set(value); DCHECK(!exception_state.is_exception_set()); } +void IndexedPropertySetterCallback( + uint32_t index, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); + NamedPropertySetterCallback(as_string, value, info); +} + +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "AnonymousNamedGetterInterface is not constructible."); +} + + + void InitializeTemplate(v8::Isolate* isolate) { // https://heycam.github.io/webidl/#interface-object @@ -229,7 +264,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -274,6 +309,7 @@ NewInternalString(isolate, "AnonymousNamedGetterInterface"), static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + { v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = { NamedPropertyGetterCallback, @@ -287,6 +323,18 @@ instance_template->SetHandler(named_property_handler_configuration); } + { + v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { + IndexedPropertyGetterCallback, + IndexedPropertySetterCallback, + IndexedPropertyDescriptorCallback, + nullptr, + nullptr, + nullptr + }; + instance_template->SetHandler(indexed_property_handler_configuration); + } + }
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc index 3947292..ae2eaf5 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_anonymous_named_indexed_getter_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -91,7 +92,7 @@ v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -120,11 +121,12 @@ DCHECK(!exception_state.is_exception_set()); } + void NamedPropertyQueryCallback( v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -138,19 +140,23 @@ if (!result) { return; } + // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty + int properties = v8::None; // 2.7. If |O| implements an interface with a named property setter, then set // desc.[[Writable]] to true, otherwise set it to false. // 2.8. If |O| implements an interface with the // [LegacyUnenumerableNamedProperties] extended attribute, then set // desc.[[Enumerable]] to false, otherwise set it to true. - info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly); + + info.GetReturnValue().Set(properties); } + void NamedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -171,7 +177,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -193,16 +199,19 @@ impl->AnonymousNamedSetter(property_name, native_value); result_value = v8::Undefined(isolate); + info.GetReturnValue().Set(value); DCHECK(!exception_state.is_exception_set()); } + + void IndexedPropertyGetterCallback( uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -237,7 +246,7 @@ void IndexedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -268,7 +277,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -300,17 +309,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "AnonymousNamedIndexedGetterInterface is not constructible."); +} + + void lengthAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<AnonymousNamedIndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cAnonymousNamedIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -327,14 +343,16 @@ AnonymousNamedIndexedGetterInterface* impl = wrapper_private->wrappable<AnonymousNamedIndexedGetterInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->length(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } @@ -366,7 +384,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -413,18 +431,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, lengthAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - lengthAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -445,6 +464,7 @@ NewInternalString(isolate, "AnonymousNamedIndexedGetterInterface"), static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + { v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = { NamedPropertyGetterCallback, @@ -458,6 +478,7 @@ instance_template->SetHandler(named_property_handler_configuration); } + { v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { IndexedPropertyGetterCallback,
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc index 627c95d..9439609 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_arbitrary_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -111,16 +112,17 @@ } + void arbitraryPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ArbitraryInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cArbitraryInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -137,27 +139,28 @@ ArbitraryInterface* impl = wrapper_private->wrappable<ArbitraryInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->arbitrary_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void arbitraryPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ArbitraryInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cArbitraryInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -189,11 +192,11 @@ void arbitraryFunctionMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ArbitraryInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cArbitraryInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -291,18 +294,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, arbitraryPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, arbitraryPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - arbitraryPropertyAttributeGetter, - arbitraryPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -328,16 +333,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, arbitraryFunctionMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "arbitraryFunction"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -358,6 +363,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc index e447a9c..5dc0a30 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_base_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -111,16 +112,17 @@ } + void baseAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<BaseInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cBaseInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -137,14 +139,16 @@ BaseInterface* impl = wrapper_private->wrappable<BaseInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->base_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } @@ -152,11 +156,11 @@ void baseOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<BaseInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cBaseInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -254,18 +258,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, baseAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - baseAttributeAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -291,16 +296,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, baseOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "baseOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -321,6 +326,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc index a3706ab..fdfd3e5 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_boolean_type_test_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -93,17 +94,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "BooleanTypeTestInterface is not constructible."); +} + + void booleanPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<BooleanTypeTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cBooleanTypeTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -120,27 +128,28 @@ BooleanTypeTestInterface* impl = wrapper_private->wrappable<BooleanTypeTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->boolean_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void booleanPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<BooleanTypeTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cBooleanTypeTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -172,11 +181,11 @@ void booleanArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<BooleanTypeTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cBooleanTypeTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -218,11 +227,11 @@ void booleanReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<BooleanTypeTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cBooleanTypeTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -278,7 +287,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -325,18 +334,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, booleanPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, booleanPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - booleanPropertyAttributeGetter, - booleanPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -362,16 +373,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, booleanArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "booleanArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -395,16 +406,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, booleanReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "booleanReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -425,6 +436,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc index 1328e44..0cbf4e8 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_function_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -97,17 +98,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "CallbackFunctionInterface is not constructible."); +} + + void callbackAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackFunctionInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackFunctionInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -124,27 +132,28 @@ CallbackFunctionInterface* impl = wrapper_private->wrappable<CallbackFunctionInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->callback_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void callbackAttributeAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackFunctionInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackFunctionInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -174,15 +183,15 @@ void nullableCallbackAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackFunctionInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackFunctionInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -199,27 +208,28 @@ CallbackFunctionInterface* impl = wrapper_private->wrappable<CallbackFunctionInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->nullable_callback_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nullableCallbackAttributeAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackFunctionInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackFunctionInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -251,11 +261,11 @@ void takesFunctionThatReturnsStringMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackFunctionInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackFunctionInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -297,11 +307,11 @@ void takesFunctionWithNullableParametersMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackFunctionInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackFunctionInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -343,11 +353,11 @@ void takesFunctionWithOneParameterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackFunctionInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackFunctionInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -389,11 +399,11 @@ void takesFunctionWithSeveralParametersMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackFunctionInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackFunctionInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -435,11 +445,11 @@ void takesVoidFunctionMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackFunctionInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackFunctionInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -505,7 +515,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -552,18 +562,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, callbackAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, callbackAttributeAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - callbackAttributeAttributeGetter, - callbackAttributeAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -586,18 +598,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, nullableCallbackAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, nullableCallbackAttributeAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - nullableCallbackAttributeAttributeGetter, - nullableCallbackAttributeAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -623,16 +637,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionThatReturnsStringMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "takesFunctionThatReturnsString"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -656,16 +670,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionWithNullableParametersMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "takesFunctionWithNullableParameters"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -689,16 +703,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionWithOneParameterMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "takesFunctionWithOneParameter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -722,16 +736,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesFunctionWithSeveralParametersMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "takesFunctionWithSeveralParameters"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -755,16 +769,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, takesVoidFunctionMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "takesVoidFunction"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -785,6 +799,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc index 6f254dc..d9a7bff 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_callback_interface_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -97,17 +98,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "CallbackInterfaceInterface is not constructible."); +} + + void callbackAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackInterfaceInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackInterfaceInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -124,27 +132,28 @@ CallbackInterfaceInterface* impl = wrapper_private->wrappable<CallbackInterfaceInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->callback_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void callbackAttributeAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackInterfaceInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackInterfaceInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -176,11 +185,11 @@ void registerCallbackMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackInterfaceInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackInterfaceInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -202,18 +211,18 @@ return; } // Non-optional arguments - TypeTraits<::cobalt::script::CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType callback_interface; + TypeTraits<::cobalt::script::CallbackInterfaceTraits<SingleOperationInterface > >::ConversionType callbackInterface; DCHECK_LT(0, info.Length()); v8::Local<v8::Value> non_optional_value0 = info[0]; FromJSValue(isolate, non_optional_value0, kNoConversionFlags, - &exception_state, &callback_interface); + &exception_state, &callbackInterface); if (exception_state.is_exception_set()) { return; } - impl->RegisterCallback(callback_interface); + impl->RegisterCallback(callbackInterface); result_value = v8::Undefined(isolate); } @@ -222,11 +231,11 @@ void someOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<CallbackInterfaceInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cCallbackInterfaceInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -276,7 +285,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -323,18 +332,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, callbackAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, callbackAttributeAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - callbackAttributeAttributeGetter, - callbackAttributeAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -360,16 +371,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, registerCallbackMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "registerCallback"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -393,16 +404,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, someOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "someOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -423,6 +434,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc index 28b4236..bcdaf92 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -95,18 +96,25 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "ConditionalInterface is not constructible."); +} + + #if defined(ENABLE_CONDITIONAL_PROPERTY) void enabledAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ConditionalInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cConditionalInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -123,27 +131,28 @@ ConditionalInterface* impl = wrapper_private->wrappable<ConditionalInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->enabled_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void enabledAttributeAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ConditionalInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cConditionalInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -175,15 +184,15 @@ #if defined(NO_ENABLE_CONDITIONAL_PROPERTY) void disabledAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ConditionalInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cConditionalInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -200,27 +209,28 @@ ConditionalInterface* impl = wrapper_private->wrappable<ConditionalInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->disabled_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void disabledAttributeAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ConditionalInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cConditionalInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -254,11 +264,11 @@ void disabledOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ConditionalInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cConditionalInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -286,11 +296,11 @@ void enabledOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ConditionalInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cConditionalInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -341,7 +351,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -389,18 +399,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, enabledAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, enabledAttributeAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - enabledAttributeAttributeGetter, - enabledAttributeAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } #endif // ENABLE_CONDITIONAL_PROPERTY @@ -425,18 +437,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, disabledAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, disabledAttributeAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - disabledAttributeAttributeGetter, - disabledAttributeAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } #endif // NO_ENABLE_CONDITIONAL_PROPERTY @@ -464,16 +478,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, disabledOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "disabledOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -499,16 +513,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, enabledOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "enabledOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -530,6 +544,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc index ea5e9af..47be197 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constants_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -93,6 +94,13 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "ConstantsInterface is not constructible."); +} + + void InitializeTemplate(v8::Isolate* isolate) { @@ -121,7 +129,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -220,6 +228,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc index 48c0906..a1621a5 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -177,6 +178,7 @@ + void InitializeTemplate(v8::Isolate* isolate) { // https://heycam.github.io/webidl/#interface-object // 3.6.1. Interface object @@ -251,6 +253,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc index 0903cc0..e4d1e89 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_constructor_with_arguments_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -106,7 +107,7 @@ TypeTraits<int32_t >::ConversionType arg1; TypeTraits<bool >::ConversionType arg2; // Optional arguments with default values - TypeTraits<std::string >::ConversionType default_arg = + TypeTraits<std::string >::ConversionType defaultArg = "default"; DCHECK_LT(0, info.Length()); v8::Local<v8::Value> non_optional_value0 = info[0]; @@ -133,7 +134,7 @@ optional_value0, kNoConversionFlags, &exception_state, - &default_arg); + &defaultArg); if (exception_state.is_exception_set()) { return; } @@ -144,7 +145,7 @@ } scoped_refptr<ConstructorWithArgumentsInterface> new_object = - new ConstructorWithArgumentsInterface(arg1, arg2, default_arg); + new ConstructorWithArgumentsInterface(arg1, arg2, defaultArg); v8::Local<v8::Value> result_value; ToJSValue(isolate, new_object, &result_value); DCHECK(result_value->IsObject()); @@ -152,16 +153,17 @@ } + void longArgAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ConstructorWithArgumentsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cConstructorWithArgumentsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -178,28 +180,30 @@ ConstructorWithArgumentsInterface* impl = wrapper_private->wrappable<ConstructorWithArgumentsInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->long_arg(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void booleanArgAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ConstructorWithArgumentsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cConstructorWithArgumentsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -216,28 +220,30 @@ ConstructorWithArgumentsInterface* impl = wrapper_private->wrappable<ConstructorWithArgumentsInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->boolean_arg(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void stringArgAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ConstructorWithArgumentsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cConstructorWithArgumentsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -254,14 +260,16 @@ ConstructorWithArgumentsInterface* impl = wrapper_private->wrappable<ConstructorWithArgumentsInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->string_arg(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } @@ -341,18 +349,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, longArgAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - longArgAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -375,18 +384,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, booleanArgAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - booleanArgAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -409,18 +419,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, stringArgAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - stringArgAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -443,6 +454,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_convert_simple_object_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_convert_simple_object_interface.cc new file mode 100644 index 0000000..529f2b1 --- /dev/null +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_convert_simple_object_interface.cc
@@ -0,0 +1,311 @@ + + +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// clang-format off + +// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY! +// Auto-generated from template: bindings/v8c/templates/interface.cc.template + +#include "cobalt/bindings/testing/v8c_convert_simple_object_interface.h" + +#include "base/debug/trace_event.h" +#include "cobalt/base/polymorphic_downcast.h" +#include "cobalt/script/global_environment.h" +#include "cobalt/script/script_value.h" +#include "cobalt/script/value_handle.h" + +#include "v8c_gen_type_conversion.h" + +#include "cobalt/script/callback_interface_traits.h" +#include "cobalt/script/v8c/callback_function_conversion.h" +#include "cobalt/script/v8c/conversion_helpers.h" +#include "cobalt/script/v8c/entry_scope.h" +#include "cobalt/script/v8c/helpers.h" +#include "cobalt/script/v8c/native_promise.h" +#include "cobalt/script/v8c/type_traits.h" +#include "cobalt/script/v8c/v8c_callback_function.h" +#include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" +#include "cobalt/script/v8c/v8c_exception_state.h" +#include "cobalt/script/v8c/v8c_global_environment.h" +#include "cobalt/script/v8c/v8c_property_enumerator.h" +#include "cobalt/script/v8c/v8c_value_handle.h" +#include "cobalt/script/v8c/wrapper_private.h" +#include "v8/include/v8.h" + + +namespace { +using cobalt::bindings::testing::ConvertSimpleObjectInterface; +using cobalt::bindings::testing::V8cConvertSimpleObjectInterface; +using cobalt::script::CallbackInterfaceTraits; +using cobalt::script::GlobalEnvironment; +using cobalt::script::ScriptValue; +using cobalt::script::ValueHandle; +using cobalt::script::ValueHandle; +using cobalt::script::ValueHandleHolder; +using cobalt::script::Wrappable; + +using cobalt::script::v8c::EntryScope; +using cobalt::script::v8c::EscapableEntryScope; +using cobalt::script::v8c::FromJSValue; +using cobalt::script::v8c::kConversionFlagClamped; +using cobalt::script::v8c::kConversionFlagNullable; +using cobalt::script::v8c::kConversionFlagObjectOnly; +using cobalt::script::v8c::kConversionFlagRestricted; +using cobalt::script::v8c::kConversionFlagTreatNullAsEmptyString; +using cobalt::script::v8c::kConversionFlagTreatUndefinedAsEmptyString; +using cobalt::script::v8c::kNoConversionFlags; +using cobalt::script::v8c::NewInternalString; +using cobalt::script::v8c::ToJSValue; +using cobalt::script::v8c::TypeTraits; +using cobalt::script::v8c::V8cExceptionState; +using cobalt::script::v8c::V8cGlobalEnvironment; +using cobalt::script::v8c::V8cPropertyEnumerator; +using cobalt::script::v8c::WrapperFactory; +using cobalt::script::v8c::WrapperPrivate; +} // namespace + +namespace cobalt { +namespace bindings { +namespace testing { + + +namespace { + +const int kInterfaceUniqueId = 12; + + + + + + + + + +void Constructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + V8cExceptionState exception_state(isolate); + if (!info.IsConstructCall()) { + exception_state.SetSimpleException(script::kTypeError, "Illegal constructor"); + return; + } + + scoped_refptr<ConvertSimpleObjectInterface> new_object = + new ConvertSimpleObjectInterface(); + v8::Local<v8::Value> result_value; + ToJSValue(isolate, new_object, &result_value); + DCHECK(result_value->IsObject()); + info.GetReturnValue().Set(result_value); +} + + + + +void convertSimpleObjectToMapTestMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + v8::Local<v8::Object> object = info.Holder(); + V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); + WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cConvertSimpleObjectInterface::GetTemplate(isolate)->HasInstance(object)) { + V8cExceptionState exception(isolate); + exception.SetSimpleException(script::kDoesNotImplementInterface); + return; + } + V8cExceptionState exception_state{isolate}; + v8::Local<v8::Value> result_value; + + WrapperPrivate* wrapper_private = + WrapperPrivate::GetFromWrapperObject(object); + if (!wrapper_private) { + NOTIMPLEMENTED(); + return; + } + ConvertSimpleObjectInterface* impl = + wrapper_private->wrappable<ConvertSimpleObjectInterface>().get(); + const size_t kMinArguments = 1; + if (info.Length() < kMinArguments) { + exception_state.SetSimpleException(script::kInvalidNumberOfArguments); + return; + } + // Non-optional arguments + TypeTraits<::cobalt::script::ValueHandle >::ConversionType value; + DCHECK_LT(0, info.Length()); + v8::Local<v8::Value> non_optional_value0 = info[0]; + FromJSValue(isolate, + non_optional_value0, + kNoConversionFlags, + &exception_state, &value); + if (exception_state.is_exception_set()) { + return; + } + + impl->ConvertSimpleObjectToMapTest(value, &exception_state); + result_value = v8::Undefined(isolate); + +} + + + +void InitializeTemplate(v8::Isolate* isolate) { + // https://heycam.github.io/webidl/#interface-object + // 3.6.1. Interface object + // + // The interface object for a given interface is a built-in function object. + // It has properties that correspond to the constants and static operations + // defined on that interface, as described in sections 3.6.6 Constants and + // 3.6.8 Operations. + // + // If the interface is declared with a [Constructor] extended attribute, + // then the interface object can be called as a constructor to create an + // object that implements that interface. Calling that interface as a + // function will throw an exception. + // + // Interface objects whose interfaces are not declared with a [Constructor] + // extended attribute will throw when called, both as a function and as a + // constructor. + // + // An interface object for a non-callback interface has an associated object + // called the interface prototype object. This object has properties that + // correspond to the regular attributes and regular operations defined on + // the interface, and is described in more detail in 3.6.3 Interface + // prototype object. + v8::Local<v8::FunctionTemplate> function_template = + v8::FunctionTemplate::New( + isolate, + Constructor, + v8::Local<v8::Value>(), + v8::Local<v8::Signature>(), + 0); + function_template->SetLength(0); + function_template->SetClassName(NewInternalString(isolate, "ConvertSimpleObjectInterface")); + function_template->ReadOnlyPrototype(); + + v8::Local<v8::ObjectTemplate> prototype_template = function_template->PrototypeTemplate(); + v8::Local<v8::ObjectTemplate> instance_template = function_template->InstanceTemplate(); + instance_template->SetInternalFieldCount(WrapperPrivate::kInternalFieldCount); + + V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); + global_environment->AddInterfaceData(kInterfaceUniqueId, function_template); + + + // https://heycam.github.io/webidl/#es-constants + // 3.6.6. Constants + // + // For each exposed constant defined on an interface A, there must be a + // corresponding property. The property has the following characteristics: + + // https://heycam.github.io/webidl/#es-attributes + // 3.6.7. Attributes + // + // For each exposed attribute of the interface there must exist a + // corresponding property. The characteristics of this property are as + // follows: + + // https://heycam.github.io/webidl/#es-operations + // 3.6.8. Operations + // + // For each unique identifier of an exposed operation defined on the + // interface, there must exist a corresponding property, unless the effective + // overload set for that identifier and operation and with an argument count + // of 0 has no entries. + // + // The characteristics of this property are as follows: + { + // The name of the property is the identifier. + v8::Local<v8::String> name = NewInternalString( + isolate, + "convertSimpleObjectToMapTest"); + + // The property has attributes { [[Writable]]: B, [[Enumerable]]: true, + // [[Configurable]]: B }, where B is false if the operation is unforgeable + // on the interface, and true otherwise. + bool B = true; + v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( + B ? v8::None : (v8::ReadOnly | v8::DontDelete)); + + v8::Local<v8::FunctionTemplate> method_template = + v8::FunctionTemplate::New(isolate, convertSimpleObjectToMapTestMethod); + method_template->RemovePrototype(); + method_template->SetLength(1); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); + + // The value of the property is the result of creating an operation function + // given the operation, the interface, and the relevant Realm of the object + // that is the location of the property. + + // Note: that is, even if an includes statement was used to make an + // operation available on the interface, we pass in the interface which + // includes the interface mixin, and not the interface mixin on which the + // operation was originally declared. + } + + // https://heycam.github.io/webidl/#es-stringifier + // 3.6.8.2. Stringifiers + prototype_template->Set( + v8::Symbol::GetToStringTag(isolate), + NewInternalString(isolate, "ConvertSimpleObjectInterface"), + static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + + + + + +} + +} // namespace + + +v8::Local<v8::Object> V8cConvertSimpleObjectInterface::CreateWrapper( + v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable) { + EscapableEntryScope entry_scope(isolate); + v8::Local<v8::Context> context = isolate->GetCurrentContext(); + + V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); + if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) { + InitializeTemplate(isolate); + } + v8::Local<v8::FunctionTemplate> function_template = global_environment->GetInterfaceData(kInterfaceUniqueId); + + DCHECK(function_template->InstanceTemplate()->InternalFieldCount() == WrapperPrivate::kInternalFieldCount); + v8::Local<v8::Object> object = function_template->InstanceTemplate()->NewInstance(context).ToLocalChecked(); + DCHECK(object->InternalFieldCount() == WrapperPrivate::kInternalFieldCount); + + // This |WrapperPrivate|'s lifetime will be managed by V8. + new WrapperPrivate(isolate, wrappable, object); + return entry_scope.Escape(object); +} + + +v8::Local<v8::FunctionTemplate> V8cConvertSimpleObjectInterface::GetTemplate(v8::Isolate* isolate) { + V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); + if (!global_environment->HasInterfaceData(kInterfaceUniqueId)) { + InitializeTemplate(isolate); + } + return global_environment->GetInterfaceData(kInterfaceUniqueId); +} + + +} // namespace testing +} // namespace bindings +} // namespace cobalt + +
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_convert_simple_object_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_convert_simple_object_interface.h new file mode 100644 index 0000000..aa2134e --- /dev/null +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_convert_simple_object_interface.h
@@ -0,0 +1,49 @@ + +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// clang-format off + +// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY! +// Auto-generated from template: bindings/v8c/templates/interface.h.template + +#ifndef V8cConvertSimpleObjectInterface_h +#define V8cConvertSimpleObjectInterface_h + +#include "base/hash_tables.h" +#include "base/lazy_instance.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "cobalt/base/polymorphic_downcast.h" +#include "cobalt/script/wrappable.h" +#include "cobalt/bindings/testing/convert_simple_object_interface.h" + +#include "cobalt/script/v8c/v8c_global_environment.h" +#include "v8/include/v8.h" + +namespace cobalt { +namespace bindings { +namespace testing { + +class V8cConvertSimpleObjectInterface { + public: + static v8::Local<v8::Object> CreateWrapper(v8::Isolate* isolate, const scoped_refptr<script::Wrappable>& wrappable); + static v8::Local<v8::FunctionTemplate> GetTemplate(v8::Isolate* isolate); +}; + +} // namespace testing +} // namespace bindings +} // namespace cobalt + +#endif // V8cConvertSimpleObjectInterface_h
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_dictionary.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_dictionary.cc index 1921f08..6b0a824 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_dictionary.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_dictionary.cc
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc index f2d42d6..7d7dd42 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -88,14 +89,14 @@ namespace { -const int kInterfaceUniqueId = 14; +const int kInterfaceUniqueId = 15; void NamedPropertyGetterCallback( v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -124,11 +125,12 @@ DCHECK(!exception_state.is_exception_set()); } + void NamedPropertyQueryCallback( v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -142,19 +144,23 @@ if (!result) { return; } + // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty + int properties = v8::None; // 2.7. If |O| implements an interface with a named property setter, then set // desc.[[Writable]] to true, otherwise set it to false. // 2.8. If |O| implements an interface with the // [LegacyUnenumerableNamedProperties] extended attribute, then set // desc.[[Enumerable]] to false, otherwise set it to true. - info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly); + + info.GetReturnValue().Set(properties); } + void NamedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -175,7 +181,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -197,16 +203,19 @@ impl->AnonymousNamedSetter(property_name, native_value); result_value = v8::Undefined(isolate); + info.GetReturnValue().Set(value); DCHECK(!exception_state.is_exception_set()); } + + void IndexedPropertyGetterCallback( uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -241,7 +250,7 @@ void IndexedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -272,7 +281,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -304,17 +313,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "DerivedGetterSetterInterface is not constructible."); +} + + void lengthAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DerivedGetterSetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDerivedGetterSetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -331,28 +347,30 @@ DerivedGetterSetterInterface* impl = wrapper_private->wrappable<DerivedGetterSetterInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->length(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void propertyOnDerivedClassAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DerivedGetterSetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDerivedGetterSetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -369,27 +387,28 @@ DerivedGetterSetterInterface* impl = wrapper_private->wrappable<DerivedGetterSetterInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->property_on_derived_class(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void propertyOnDerivedClassAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DerivedGetterSetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDerivedGetterSetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -421,11 +440,11 @@ void derivedIndexedGetterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DerivedGetterSetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDerivedGetterSetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -473,11 +492,11 @@ void derivedIndexedSetterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DerivedGetterSetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDerivedGetterSetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -529,11 +548,11 @@ void operationOnDerivedClassMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DerivedGetterSetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDerivedGetterSetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -583,7 +602,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -641,18 +660,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, lengthAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - lengthAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -675,18 +695,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, propertyOnDerivedClassAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, propertyOnDerivedClassAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - propertyOnDerivedClassAttributeGetter, - propertyOnDerivedClassAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -712,16 +734,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedIndexedGetterMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "derivedIndexedGetter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -745,16 +767,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedIndexedSetterMethod); method_template->RemovePrototype(); method_template->SetLength(2); - prototype_template->Set( - NewInternalString(isolate, "derivedIndexedSetter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -778,16 +800,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, operationOnDerivedClassMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "operationOnDerivedClass"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -806,6 +828,7 @@ NewInternalString(isolate, "DerivedGetterSetterInterface"), static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + { v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = { NamedPropertyGetterCallback, @@ -819,6 +842,7 @@ instance_template->SetHandler(named_property_handler_configuration); } + { v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { IndexedPropertyGetterCallback,
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc index db63e88..e7a1eca 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -88,7 +89,7 @@ namespace { -const int kInterfaceUniqueId = 15; +const int kInterfaceUniqueId = 16; @@ -115,16 +116,17 @@ } + void derivedAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DerivedInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDerivedInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -141,14 +143,16 @@ DerivedInterface* impl = wrapper_private->wrappable<DerivedInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->derived_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } @@ -156,11 +160,11 @@ void derivedOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DerivedInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDerivedInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -269,18 +273,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, derivedAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - derivedAttributeAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -306,16 +311,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "derivedOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -336,6 +341,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc index 48ee0bd..578780f 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_interface.cc
@@ -41,6 +41,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -90,7 +91,7 @@ namespace { -const int kInterfaceUniqueId = 16; +const int kInterfaceUniqueId = 17; @@ -99,17 +100,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "DictionaryInterface is not constructible."); +} + + void dictionarySequenceAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DictionaryInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDictionaryInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -126,27 +134,28 @@ DictionaryInterface* impl = wrapper_private->wrappable<DictionaryInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->dictionary_sequence(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void dictionarySequenceAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DictionaryInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDictionaryInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -178,11 +187,11 @@ void derivedDictionaryOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DictionaryInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDictionaryInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -224,11 +233,11 @@ void dictionaryOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DictionaryInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDictionaryInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -270,11 +279,11 @@ void testOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DictionaryInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDictionaryInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -340,7 +349,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -387,18 +396,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, dictionarySequenceAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, dictionarySequenceAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - dictionarySequenceAttributeGetter, - dictionarySequenceAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -424,16 +435,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, derivedDictionaryOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "derivedDictionaryOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -457,16 +468,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, dictionaryOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "dictionaryOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -490,16 +501,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, testOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "testOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -520,6 +531,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_with_dictionary_member.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_with_dictionary_member.cc index f3c3cc5..40932e7 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_with_dictionary_member.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dictionary_with_dictionary_member.cc
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc index d14ac28..16f758d 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -86,7 +87,7 @@ namespace { -const int kInterfaceUniqueId = 18; +const int kInterfaceUniqueId = 19; @@ -95,17 +96,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "DisabledInterface is not constructible."); +} + + void disabledPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DisabledInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDisabledInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -122,27 +130,28 @@ DisabledInterface* impl = wrapper_private->wrappable<DisabledInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->disabled_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void disabledPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DisabledInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDisabledInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -174,11 +183,11 @@ void disabledFunctionMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DisabledInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDisabledInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -228,7 +237,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -275,18 +284,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, disabledPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, disabledPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - disabledPropertyAttributeGetter, - disabledPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -312,16 +323,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, disabledFunctionMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "disabledFunction"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -342,6 +353,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc index d9fb0da..1fc563d 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_dom_string_test_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 12; +const int kInterfaceUniqueId = 13; @@ -93,17 +94,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "DOMStringTestInterface is not constructible."); +} + + void propertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DOMStringTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDOMStringTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -120,27 +128,28 @@ DOMStringTestInterface* impl = wrapper_private->wrappable<DOMStringTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void propertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DOMStringTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDOMStringTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -170,15 +179,15 @@ void readOnlyPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DOMStringTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDOMStringTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -195,28 +204,30 @@ DOMStringTestInterface* impl = wrapper_private->wrappable<DOMStringTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->read_only_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void readOnlyTokenPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DOMStringTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDOMStringTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -233,28 +244,30 @@ DOMStringTestInterface* impl = wrapper_private->wrappable<DOMStringTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->read_only_token_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nullIsEmptyPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DOMStringTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDOMStringTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -271,27 +284,28 @@ DOMStringTestInterface* impl = wrapper_private->wrappable<DOMStringTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->null_is_empty_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nullIsEmptyPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DOMStringTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDOMStringTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -321,15 +335,15 @@ void undefinedIsEmptyPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DOMStringTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDOMStringTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -346,27 +360,28 @@ DOMStringTestInterface* impl = wrapper_private->wrappable<DOMStringTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->undefined_is_empty_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void undefinedIsEmptyPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DOMStringTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDOMStringTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -396,15 +411,15 @@ void nullableUndefinedIsEmptyPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DOMStringTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDOMStringTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -421,27 +436,28 @@ DOMStringTestInterface* impl = wrapper_private->wrappable<DOMStringTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->nullable_undefined_is_empty_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nullableUndefinedIsEmptyPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<DOMStringTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cDOMStringTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -497,7 +513,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -544,18 +560,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, propertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, propertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - propertyAttributeGetter, - propertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -578,18 +596,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, readOnlyPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - readOnlyPropertyAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -612,18 +631,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, readOnlyTokenPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - readOnlyTokenPropertyAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -646,18 +666,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, nullIsEmptyPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, nullIsEmptyPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - nullIsEmptyPropertyAttributeGetter, - nullIsEmptyPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -680,18 +702,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, undefinedIsEmptyPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, undefinedIsEmptyPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - undefinedIsEmptyPropertyAttributeGetter, - undefinedIsEmptyPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -714,18 +738,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, nullableUndefinedIsEmptyPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, nullableUndefinedIsEmptyPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - nullableUndefinedIsEmptyPropertyAttributeGetter, - nullableUndefinedIsEmptyPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -748,6 +774,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc index ba0038b..dbf37ec 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_enumeration_interface.cc
@@ -39,6 +39,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -86,7 +87,7 @@ namespace { -const int kInterfaceUniqueId = 19; +const int kInterfaceUniqueId = 20; @@ -113,16 +114,17 @@ } + void enumPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<EnumerationInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cEnumerationInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -139,27 +141,28 @@ EnumerationInterface* impl = wrapper_private->wrappable<EnumerationInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->enum_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void enumPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<EnumerationInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cEnumerationInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -191,11 +194,11 @@ void optionalEnumWithDefaultMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<EnumerationInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cEnumerationInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -308,18 +311,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, enumPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, enumPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - enumPropertyAttributeGetter, - enumPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -345,16 +350,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, optionalEnumWithDefaultMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "optionalEnumWithDefault"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -375,6 +380,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc index 8c19687..925043b 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exception_object_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 20; +const int kInterfaceUniqueId = 21; @@ -93,17 +94,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "ExceptionObjectInterface is not constructible."); +} + + void errorAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ExceptionObjectInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cExceptionObjectInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -120,28 +128,30 @@ ExceptionObjectInterface* impl = wrapper_private->wrappable<ExceptionObjectInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->error(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void messageAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ExceptionObjectInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cExceptionObjectInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -158,14 +168,16 @@ ExceptionObjectInterface* impl = wrapper_private->wrappable<ExceptionObjectInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->message(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } @@ -197,7 +209,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -255,18 +267,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, errorAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - errorAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -289,18 +302,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, messageAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - messageAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -323,6 +337,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc index 2476ba1..ea6e41c 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_exceptions_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 21; +const int kInterfaceUniqueId = 22; @@ -115,16 +116,17 @@ } + void attributeThrowsExceptionAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ExceptionsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cExceptionsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -141,27 +143,28 @@ ExceptionsInterface* impl = wrapper_private->wrappable<ExceptionsInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->attribute_throws_exception(&exception_state), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void attributeThrowsExceptionAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ExceptionsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cExceptionsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -193,11 +196,11 @@ void functionThrowsExceptionMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ExceptionsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cExceptionsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -295,18 +298,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, attributeThrowsExceptionAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, attributeThrowsExceptionAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - attributeThrowsExceptionAttributeGetter, - attributeThrowsExceptionAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -332,16 +337,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, functionThrowsExceptionMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "functionThrowsException"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -362,6 +367,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc index 5de530d..bb636bd 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_extended_idl_attributes_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 22; +const int kInterfaceUniqueId = 23; @@ -93,17 +94,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "ExtendedIDLAttributesInterface is not constructible."); +} + + void defaultAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ExtendedIDLAttributesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cExtendedIDLAttributesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -120,27 +128,28 @@ ExtendedIDLAttributesInterface* impl = wrapper_private->wrappable<ExtendedIDLAttributesInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->attribute_default(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void defaultAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ExtendedIDLAttributesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cExtendedIDLAttributesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -172,11 +181,11 @@ void callWithSettingsMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ExtendedIDLAttributesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cExtendedIDLAttributesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -203,11 +212,11 @@ void clampArgumentMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ExtendedIDLAttributesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cExtendedIDLAttributesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -273,7 +282,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -320,18 +329,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, defaultAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, defaultAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - defaultAttributeGetter, - defaultAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -357,16 +368,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, callWithSettingsMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "callWithSettings"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -390,16 +401,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, clampArgumentMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "clampArgument"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -420,6 +431,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc index d6ded64..7f8db74 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_garbage_collection_test_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -88,7 +89,7 @@ namespace { -const int kInterfaceUniqueId = 23; +const int kInterfaceUniqueId = 24; @@ -115,16 +116,17 @@ } + void previousAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<GarbageCollectionTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cGarbageCollectionTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -141,27 +143,28 @@ GarbageCollectionTestInterface* impl = wrapper_private->wrappable<GarbageCollectionTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->previous(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void previousAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<GarbageCollectionTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cGarbageCollectionTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -191,15 +194,15 @@ void nextAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<GarbageCollectionTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cGarbageCollectionTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -216,27 +219,28 @@ GarbageCollectionTestInterface* impl = wrapper_private->wrappable<GarbageCollectionTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->next(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nextAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<GarbageCollectionTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cGarbageCollectionTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -340,18 +344,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, previousAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, previousAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - previousAttributeGetter, - previousAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -374,18 +380,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, nextAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, nextAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - nextAttributeGetter, - nextAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -408,6 +416,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc index d25a0d6..b066935 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_global_interface_parent.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 24; +const int kInterfaceUniqueId = 25; @@ -93,15 +94,22 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "GlobalInterfaceParent is not constructible."); +} + + void parentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<GlobalInterfaceParent>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cGlobalInterfaceParent::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -151,7 +159,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -201,16 +209,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, parentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "parentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -231,6 +239,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc index 92f94ea..1070633 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_indexed_getter_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 26; +const int kInterfaceUniqueId = 27; @@ -94,7 +95,7 @@ uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -129,7 +130,7 @@ void IndexedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -160,7 +161,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -194,7 +195,7 @@ uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -219,17 +220,24 @@ } +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "IndexedGetterInterface is not constructible."); +} + + void lengthAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<IndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -246,14 +254,16 @@ IndexedGetterInterface* impl = wrapper_private->wrappable<IndexedGetterInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->length(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } @@ -261,11 +271,11 @@ void indexedDeleterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<IndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -307,11 +317,11 @@ void indexedGetterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<IndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -359,11 +369,11 @@ void indexedSetterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<IndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -439,7 +449,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -486,18 +496,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, lengthAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - lengthAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -523,16 +534,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedDeleterMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "indexedDeleter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -556,16 +567,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedGetterMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "indexedGetter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -589,16 +600,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedSetterMethod); method_template->RemovePrototype(); method_template->SetLength(2); - prototype_template->Set( - NewInternalString(isolate, "indexedSetter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -618,6 +629,8 @@ static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + + { v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { IndexedPropertyGetterCallback,
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc index 22fbc8e..f102817 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 27; +const int kInterfaceUniqueId = 28; @@ -112,13 +113,14 @@ + void getAnyMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<InterfaceWithAny>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cInterfaceWithAny::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -150,11 +152,11 @@ void setAnyMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<InterfaceWithAny>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cInterfaceWithAny::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -271,16 +273,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getAnyMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "getAny"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -304,16 +306,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setAnyMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "setAny"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -334,6 +336,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc index d707a52..ce204d7 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_any_dictionary.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 28; +const int kInterfaceUniqueId = 29; @@ -112,13 +113,14 @@ + void getAnyMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<InterfaceWithAnyDictionary>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cInterfaceWithAnyDictionary::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -150,11 +152,11 @@ void hasAnyMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<InterfaceWithAnyDictionary>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cInterfaceWithAnyDictionary::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -186,11 +188,11 @@ void hasAnyDefaultMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<InterfaceWithAnyDictionary>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cInterfaceWithAnyDictionary::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -222,11 +224,11 @@ void setAnyMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<InterfaceWithAnyDictionary>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cInterfaceWithAnyDictionary::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -343,16 +345,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getAnyMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "getAny"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -376,16 +378,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, hasAnyMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "hasAny"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -409,16 +411,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, hasAnyDefaultMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "hasAnyDefault"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -442,16 +444,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setAnyMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "setAny"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -472,6 +474,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc index 4539d83..c94f599 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_date.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 29; +const int kInterfaceUniqueId = 30; @@ -112,13 +113,14 @@ + void getDateMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<InterfaceWithDate>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cInterfaceWithDate::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -150,11 +152,11 @@ void setDateMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<InterfaceWithDate>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cInterfaceWithDate::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -271,16 +273,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getDateMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "getDate"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -304,16 +306,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setDateMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "setDate"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -334,6 +336,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc index c815e50..77bf07f 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_interface_with_unsupported_properties.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 30; +const int kInterfaceUniqueId = 31; @@ -93,17 +94,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "InterfaceWithUnsupportedProperties is not constructible."); +} + + void supportedAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<InterfaceWithUnsupportedProperties>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cInterfaceWithUnsupportedProperties::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -120,14 +128,16 @@ InterfaceWithUnsupportedProperties* impl = wrapper_private->wrappable<InterfaceWithUnsupportedProperties>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->supported_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } @@ -159,7 +169,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -206,18 +216,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, supportedAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - supportedAttributeAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -240,6 +251,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc index 692da5b..7587b51 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_constructor_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 31; +const int kInterfaceUniqueId = 32; @@ -112,6 +113,7 @@ + void InitializeTemplate(v8::Isolate* isolate) { // https://heycam.github.io/webidl/#interface-object // 3.6.1. Interface object @@ -186,6 +188,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc index 9d39173..72017ae 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_getter_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,14 +85,14 @@ namespace { -const int kInterfaceUniqueId = 32; +const int kInterfaceUniqueId = 33; void NamedPropertyGetterCallback( v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -120,11 +121,18 @@ DCHECK(!exception_state.is_exception_set()); } +void IndexedPropertyGetterCallback( + uint32_t index, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); + NamedPropertyGetterCallback(as_string, info); +} + void NamedPropertyQueryCallback( v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -138,19 +146,29 @@ if (!result) { return; } + // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty + int properties = v8::None; // 2.7. If |O| implements an interface with a named property setter, then set // desc.[[Writable]] to true, otherwise set it to false. // 2.8. If |O| implements an interface with the // [LegacyUnenumerableNamedProperties] extended attribute, then set // desc.[[Enumerable]] to false, otherwise set it to true. - info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly); + + info.GetReturnValue().Set(properties); +} + +void IndexedPropertyDescriptorCallback( + uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { + // TODO: Figure out under what conditions this gets called. It's not + // getting called in our tests. + NOTIMPLEMENTED(); } void NamedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -171,7 +189,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -193,14 +211,24 @@ impl->NamedSetter(property_name, native_value); result_value = v8::Undefined(isolate); + info.GetReturnValue().Set(value); DCHECK(!exception_state.is_exception_set()); } +void IndexedPropertySetterCallback( + uint32_t index, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); + NamedPropertySetterCallback(as_string, value, info); +} + + void NamedPropertyDeleterCallback( v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -219,7 +247,18 @@ impl->NamedDeleter(property_name); result_value = v8::Undefined(isolate); - DCHECK(!exception_state.is_exception_set()); + if (exception_state.is_exception_set()) { + return; + } + info.GetReturnValue().Set(true); +} + +void IndexedPropertyDeleterCallback( + uint32_t index, + const v8::PropertyCallbackInfo<v8::Boolean>& info) { + v8::Isolate* isolate = info.GetIsolate(); + v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); + NamedPropertyDeleterCallback(as_string, info); } @@ -227,14 +266,22 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "NamedGetterInterface is not constructible."); +} + + + void namedDeleterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -276,11 +323,11 @@ void namedGetterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -328,11 +375,11 @@ void namedSetterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -408,7 +455,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -458,16 +505,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedDeleterMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "namedDeleter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -491,16 +538,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedGetterMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "namedGetter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -524,16 +571,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedSetterMethod); method_template->RemovePrototype(); method_template->SetLength(2); - prototype_template->Set( - NewInternalString(isolate, "namedSetter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -552,6 +599,7 @@ NewInternalString(isolate, "NamedGetterInterface"), static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + { v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = { NamedPropertyGetterCallback, @@ -565,6 +613,18 @@ instance_template->SetHandler(named_property_handler_configuration); } + { + v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { + IndexedPropertyGetterCallback, + IndexedPropertySetterCallback, + IndexedPropertyDescriptorCallback, + IndexedPropertyDeleterCallback, + nullptr, + nullptr + }; + instance_template->SetHandler(indexed_property_handler_configuration); + } + }
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc index ff6c171..496da3d 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_named_indexed_getter_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,14 +85,14 @@ namespace { -const int kInterfaceUniqueId = 33; +const int kInterfaceUniqueId = 34; void NamedPropertyGetterCallback( v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -120,11 +121,12 @@ DCHECK(!exception_state.is_exception_set()); } + void NamedPropertyQueryCallback( v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -138,19 +140,23 @@ if (!result) { return; } + // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty + int properties = v8::None; // 2.7. If |O| implements an interface with a named property setter, then set // desc.[[Writable]] to true, otherwise set it to false. // 2.8. If |O| implements an interface with the // [LegacyUnenumerableNamedProperties] extended attribute, then set // desc.[[Enumerable]] to false, otherwise set it to true. - info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly); + + info.GetReturnValue().Set(properties); } + void NamedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -171,7 +177,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -193,16 +199,19 @@ impl->NamedSetter(property_name, native_value); result_value = v8::Undefined(isolate); + info.GetReturnValue().Set(value); DCHECK(!exception_state.is_exception_set()); } + + void IndexedPropertyGetterCallback( uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -237,7 +246,7 @@ void IndexedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrapperObject(object); if (!wrapper_private) { @@ -268,7 +277,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -300,17 +309,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "NamedIndexedGetterInterface is not constructible."); +} + + void lengthAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedIndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -327,28 +343,30 @@ NamedIndexedGetterInterface* impl = wrapper_private->wrappable<NamedIndexedGetterInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->length(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void propertyOnBaseClassAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedIndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -365,27 +383,28 @@ NamedIndexedGetterInterface* impl = wrapper_private->wrappable<NamedIndexedGetterInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->property_on_base_class(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void propertyOnBaseClassAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedIndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -417,11 +436,11 @@ void indexedGetterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedIndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -469,11 +488,11 @@ void indexedSetterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedIndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -525,11 +544,11 @@ void namedGetterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedIndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -577,11 +596,11 @@ void namedSetterMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedIndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -633,11 +652,11 @@ void operationOnBaseClassMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NamedIndexedGetterInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNamedIndexedGetterInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -687,7 +706,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -734,18 +753,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, lengthAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - lengthAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -768,18 +788,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, propertyOnBaseClassAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, propertyOnBaseClassAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - propertyOnBaseClassAttributeGetter, - propertyOnBaseClassAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -805,16 +827,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedGetterMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "indexedGetter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -838,16 +860,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, indexedSetterMethod); method_template->RemovePrototype(); method_template->SetLength(2); - prototype_template->Set( - NewInternalString(isolate, "indexedSetter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -871,16 +893,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedGetterMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "namedGetter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -904,16 +926,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, namedSetterMethod); method_template->RemovePrototype(); method_template->SetLength(2); - prototype_template->Set( - NewInternalString(isolate, "namedSetter"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -937,16 +959,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, operationOnBaseClassMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "operationOnBaseClass"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -965,6 +987,7 @@ NewInternalString(isolate, "NamedIndexedGetterInterface"), static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + { v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = { NamedPropertyGetterCallback, @@ -978,6 +1001,7 @@ instance_template->SetHandler(named_property_handler_configuration); } + { v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { IndexedPropertyGetterCallback,
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc index e0f5669..ff2343f 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nested_put_forwards_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -88,7 +89,7 @@ namespace { -const int kInterfaceUniqueId = 34; +const int kInterfaceUniqueId = 35; @@ -97,17 +98,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "NestedPutForwardsInterface is not constructible."); +} + + void nestedForwardingAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NestedPutForwardsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNestedPutForwardsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -124,27 +132,28 @@ NestedPutForwardsInterface* impl = wrapper_private->wrappable<NestedPutForwardsInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->nested_forwarding_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nestedForwardingAttributeAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NestedPutForwardsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNestedPutForwardsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -222,7 +231,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -269,18 +278,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, nestedForwardingAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, nestedForwardingAttributeAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - nestedForwardingAttributeAttributeGetter, - nestedForwardingAttributeAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -303,6 +314,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc index 570064a..98f95c6 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_constructor_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 35; +const int kInterfaceUniqueId = 36; @@ -93,6 +94,13 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "NoConstructorInterface is not constructible."); +} + + void InitializeTemplate(v8::Isolate* isolate) { @@ -121,7 +129,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -168,6 +176,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc index 3bc962c..dc8ff25 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_no_interface_object_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 36; +const int kInterfaceUniqueId = 37; @@ -93,6 +94,13 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "NoInterfaceObjectInterface is not constructible."); +} + + void InitializeTemplate(v8::Isolate* isolate) { @@ -121,7 +129,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -168,6 +176,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc index 8ab1042..b469eef 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_nullable_types_test_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -88,7 +89,7 @@ namespace { -const int kInterfaceUniqueId = 37; +const int kInterfaceUniqueId = 38; @@ -97,17 +98,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "NullableTypesTestInterface is not constructible."); +} + + void nullableBooleanPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -124,27 +132,28 @@ NullableTypesTestInterface* impl = wrapper_private->wrappable<NullableTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->nullable_boolean_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nullableBooleanPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -174,15 +183,15 @@ void nullableNumericPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -199,27 +208,28 @@ NullableTypesTestInterface* impl = wrapper_private->wrappable<NullableTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->nullable_numeric_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nullableNumericPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -249,15 +259,15 @@ void nullableStringPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -274,27 +284,28 @@ NullableTypesTestInterface* impl = wrapper_private->wrappable<NullableTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->nullable_string_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nullableStringPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -324,15 +335,15 @@ void nullableObjectPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -349,27 +360,28 @@ NullableTypesTestInterface* impl = wrapper_private->wrappable<NullableTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->nullable_object_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nullableObjectPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -401,11 +413,11 @@ void nullableBooleanArgumentMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -447,11 +459,11 @@ void nullableBooleanOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -483,11 +495,11 @@ void nullableNumericArgumentMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -529,11 +541,11 @@ void nullableNumericOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -565,11 +577,11 @@ void nullableObjectArgumentMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -611,11 +623,11 @@ void nullableObjectOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -647,11 +659,11 @@ void nullableStringArgumentMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -693,11 +705,11 @@ void nullableStringOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NullableTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNullableTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -753,7 +765,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -800,18 +812,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, nullableBooleanPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, nullableBooleanPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - nullableBooleanPropertyAttributeGetter, - nullableBooleanPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -834,18 +848,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, nullableNumericPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, nullableNumericPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - nullableNumericPropertyAttributeGetter, - nullableNumericPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -868,18 +884,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, nullableStringPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, nullableStringPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - nullableStringPropertyAttributeGetter, - nullableStringPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -902,18 +920,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, nullableObjectPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, nullableObjectPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - nullableObjectPropertyAttributeGetter, - nullableObjectPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -939,16 +959,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableBooleanArgumentMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "nullableBooleanArgument"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -972,16 +992,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableBooleanOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "nullableBooleanOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1005,16 +1025,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableNumericArgumentMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "nullableNumericArgument"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1038,16 +1058,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableNumericOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "nullableNumericOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1071,16 +1091,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableObjectArgumentMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "nullableObjectArgument"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1104,16 +1124,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableObjectOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "nullableObjectOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1137,16 +1157,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableStringArgumentMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "nullableStringArgument"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1170,16 +1190,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, nullableStringOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "nullableStringOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1200,6 +1220,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.cc index e866d90..4bb1e6d 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_numeric_types_test_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 38; +const int kInterfaceUniqueId = 39; @@ -93,17 +94,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "NumericTypesTestInterface is not constructible."); +} + + void bytePropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -120,27 +128,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->byte_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void bytePropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -170,15 +179,15 @@ void byteClampPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -195,27 +204,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->byte_clamp_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void byteClampPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -245,15 +255,15 @@ void octetPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -270,27 +280,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->octet_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void octetPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -320,15 +331,15 @@ void octetClampPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -345,27 +356,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->octet_clamp_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void octetClampPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -395,15 +407,15 @@ void shortPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -420,27 +432,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->short_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void shortPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -470,15 +483,15 @@ void shortClampPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -495,27 +508,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->short_clamp_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void shortClampPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -545,15 +559,15 @@ void unsignedShortPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -570,27 +584,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->unsigned_short_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void unsignedShortPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -620,15 +635,15 @@ void unsignedShortClampPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -645,27 +660,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->unsigned_short_clamp_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void unsignedShortClampPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -695,15 +711,15 @@ void longPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -720,27 +736,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->long_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void longPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -770,15 +787,15 @@ void longClampPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -795,27 +812,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->long_clamp_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void longClampPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -845,15 +863,15 @@ void unsignedLongPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -870,27 +888,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->unsigned_long_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void unsignedLongPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -920,15 +939,15 @@ void unsignedLongClampPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -945,27 +964,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->unsigned_long_clamp_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void unsignedLongClampPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -995,15 +1015,15 @@ void longLongPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1020,27 +1040,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->long_long_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void longLongPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1070,15 +1091,15 @@ void longLongClampPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1095,27 +1116,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->long_long_clamp_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void longLongClampPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1145,15 +1167,15 @@ void unsignedLongLongPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1170,27 +1192,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->unsigned_long_long_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void unsignedLongLongPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1220,15 +1243,15 @@ void unsignedLongLongClampPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1245,27 +1268,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->unsigned_long_long_clamp_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void unsignedLongLongClampPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1295,15 +1319,15 @@ void doublePropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1320,27 +1344,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->double_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void doublePropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1370,15 +1395,15 @@ void unrestrictedDoublePropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1395,27 +1420,28 @@ NumericTypesTestInterface* impl = wrapper_private->wrappable<NumericTypesTestInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->unrestricted_double_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void unrestrictedDoublePropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1447,11 +1473,11 @@ void byteArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1493,11 +1519,11 @@ void byteReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1529,11 +1555,11 @@ void doubleArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1575,11 +1601,11 @@ void doubleReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1611,11 +1637,11 @@ void longArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1657,11 +1683,11 @@ void longLongArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1703,11 +1729,11 @@ void longLongReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1739,11 +1765,11 @@ void longReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1775,11 +1801,11 @@ void octetArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1821,11 +1847,11 @@ void octetReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1857,11 +1883,11 @@ void shortArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1903,11 +1929,11 @@ void shortReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1939,11 +1965,11 @@ void unrestrictedDoubleArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1985,11 +2011,11 @@ void unrestrictedDoubleReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -2021,11 +2047,11 @@ void unsignedLongArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -2067,11 +2093,11 @@ void unsignedLongLongArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -2113,11 +2139,11 @@ void unsignedLongLongReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -2149,11 +2175,11 @@ void unsignedLongReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -2185,11 +2211,11 @@ void unsignedShortArgumentOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -2231,11 +2257,11 @@ void unsignedShortReturnOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<NumericTypesTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cNumericTypesTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -2291,7 +2317,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -2338,18 +2364,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, bytePropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, bytePropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - bytePropertyAttributeGetter, - bytePropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2372,18 +2400,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, byteClampPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, byteClampPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - byteClampPropertyAttributeGetter, - byteClampPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2406,18 +2436,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, octetPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, octetPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - octetPropertyAttributeGetter, - octetPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2440,18 +2472,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, octetClampPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, octetClampPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - octetClampPropertyAttributeGetter, - octetClampPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2474,18 +2508,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, shortPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, shortPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - shortPropertyAttributeGetter, - shortPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2508,18 +2544,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, shortClampPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, shortClampPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - shortClampPropertyAttributeGetter, - shortClampPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2542,18 +2580,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, unsignedShortPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, unsignedShortPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - unsignedShortPropertyAttributeGetter, - unsignedShortPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2576,18 +2616,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, unsignedShortClampPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, unsignedShortClampPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - unsignedShortClampPropertyAttributeGetter, - unsignedShortClampPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2610,18 +2652,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, longPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, longPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - longPropertyAttributeGetter, - longPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2644,18 +2688,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, longClampPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, longClampPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - longClampPropertyAttributeGetter, - longClampPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2678,18 +2724,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, unsignedLongPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, unsignedLongPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - unsignedLongPropertyAttributeGetter, - unsignedLongPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2712,18 +2760,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, unsignedLongClampPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, unsignedLongClampPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - unsignedLongClampPropertyAttributeGetter, - unsignedLongClampPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2746,18 +2796,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, longLongPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, longLongPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - longLongPropertyAttributeGetter, - longLongPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2780,18 +2832,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, longLongClampPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, longLongClampPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - longLongClampPropertyAttributeGetter, - longLongClampPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2814,18 +2868,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, unsignedLongLongPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, unsignedLongLongPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - unsignedLongLongPropertyAttributeGetter, - unsignedLongLongPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2848,18 +2904,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, unsignedLongLongClampPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, unsignedLongLongClampPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - unsignedLongLongClampPropertyAttributeGetter, - unsignedLongLongClampPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2882,18 +2940,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, doublePropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, doublePropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - doublePropertyAttributeGetter, - doublePropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -2916,18 +2976,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, unrestrictedDoublePropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, unrestrictedDoublePropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - unrestrictedDoublePropertyAttributeGetter, - unrestrictedDoublePropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -2953,16 +3015,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, byteArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "byteArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -2986,16 +3048,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, byteReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "byteReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3019,16 +3081,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, doubleArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "doubleArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3052,16 +3114,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, doubleReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "doubleReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3085,16 +3147,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, longArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "longArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3118,16 +3180,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, longLongArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "longLongArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3151,16 +3213,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, longLongReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "longLongReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3184,16 +3246,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, longReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "longReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3217,16 +3279,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, octetArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "octetArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3250,16 +3312,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, octetReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "octetReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3283,16 +3345,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, shortArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "shortArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3316,16 +3378,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, shortReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "shortReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3349,16 +3411,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unrestrictedDoubleArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "unrestrictedDoubleArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3382,16 +3444,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unrestrictedDoubleReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "unrestrictedDoubleReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3415,16 +3477,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedLongArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "unsignedLongArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3448,16 +3510,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedLongLongArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "unsignedLongLongArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3481,16 +3543,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedLongLongReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "unsignedLongLongReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3514,16 +3576,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedLongReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "unsignedLongReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3547,16 +3609,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedShortArgumentOperationMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "unsignedShortArgumentOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3580,16 +3642,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, unsignedShortReturnOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "unsignedShortReturnOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -3610,6 +3672,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.cc index de2e838..9d91f69 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_object_type_bindings_interface.cc
@@ -44,6 +44,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -96,7 +97,7 @@ namespace { -const int kInterfaceUniqueId = 39; +const int kInterfaceUniqueId = 40; @@ -105,17 +106,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "ObjectTypeBindingsInterface is not constructible."); +} + + void arbitraryObjectAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ObjectTypeBindingsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cObjectTypeBindingsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -132,27 +140,28 @@ ObjectTypeBindingsInterface* impl = wrapper_private->wrappable<ObjectTypeBindingsInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->arbitrary_object(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void arbitraryObjectAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ObjectTypeBindingsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cObjectTypeBindingsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -182,15 +191,15 @@ void baseInterfaceAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ObjectTypeBindingsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cObjectTypeBindingsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -207,28 +216,30 @@ ObjectTypeBindingsInterface* impl = wrapper_private->wrappable<ObjectTypeBindingsInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->base_interface(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void derivedInterfaceAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ObjectTypeBindingsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cObjectTypeBindingsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -245,27 +256,28 @@ ObjectTypeBindingsInterface* impl = wrapper_private->wrappable<ObjectTypeBindingsInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->derived_interface(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void derivedInterfaceAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ObjectTypeBindingsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cObjectTypeBindingsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -295,15 +307,15 @@ void objectPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ObjectTypeBindingsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cObjectTypeBindingsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -320,27 +332,28 @@ ObjectTypeBindingsInterface* impl = wrapper_private->wrappable<ObjectTypeBindingsInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->object_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void objectPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<ObjectTypeBindingsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cObjectTypeBindingsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -396,7 +409,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -443,18 +456,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, arbitraryObjectAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, arbitraryObjectAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - arbitraryObjectAttributeGetter, - arbitraryObjectAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -477,18 +492,19 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, baseInterfaceAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - baseInterfaceAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -511,18 +527,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, derivedInterfaceAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, derivedInterfaceAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - derivedInterfaceAttributeGetter, - derivedInterfaceAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -545,18 +563,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, objectPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, objectPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - objectPropertyAttributeGetter, - objectPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -579,6 +599,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.cc index 93a3e46..9ce6bff 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_operations_test_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -88,7 +89,7 @@ namespace { -const int kInterfaceUniqueId = 40; +const int kInterfaceUniqueId = 41; @@ -97,15 +98,22 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "OperationsTestInterface is not constructible."); +} + + void longFunctionNoArgsMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -137,11 +145,11 @@ void objectFunctionNoArgsMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -173,11 +181,11 @@ void optionalArgumentWithDefaultMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -218,11 +226,11 @@ void optionalArgumentsMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -311,11 +319,11 @@ void optionalNullableArgumentsWithDefaultsMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -369,11 +377,11 @@ void overloadedFunctionMethod1( const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -397,11 +405,11 @@ void overloadedFunctionMethod2( const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -441,11 +449,11 @@ void overloadedFunctionMethod3( const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -485,11 +493,11 @@ void overloadedFunctionMethod4( const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -549,11 +557,11 @@ void overloadedFunctionMethod5( const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -689,11 +697,11 @@ void overloadedNullableMethod1( const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -733,11 +741,11 @@ void overloadedNullableMethod2( const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -815,11 +823,11 @@ void stringFunctionNoArgsMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -851,11 +859,11 @@ void variadicPrimitiveArgumentsMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -900,11 +908,11 @@ void variadicStringArgumentsAfterOptionalArgumentMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -921,7 +929,7 @@ OperationsTestInterface* impl = wrapper_private->wrappable<OperationsTestInterface>().get(); // Optional arguments - TypeTraits<bool >::ConversionType optional_arg; + TypeTraits<bool >::ConversionType optionalArg; // Variadic argument TypeTraits<std::vector<std::string> >::ConversionType strings; size_t num_set_arguments = 0; @@ -931,7 +939,7 @@ optional_value0, kNoConversionFlags, &exception_state, - &optional_arg); + &optionalArg); if (exception_state.is_exception_set()) { return; } @@ -970,7 +978,7 @@ break; case 2: { - impl->VariadicStringArgumentsAfterOptionalArgument(optional_arg, strings); + impl->VariadicStringArgumentsAfterOptionalArgument(optionalArg, strings); result_value = v8::Undefined(isolate); } break; @@ -984,11 +992,11 @@ void voidFunctionLongArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1030,11 +1038,11 @@ void voidFunctionNoArgsMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1060,11 +1068,11 @@ void voidFunctionObjectArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1106,11 +1114,11 @@ void voidFunctionStringArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<OperationsTestInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cOperationsTestInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -1275,7 +1283,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -1325,16 +1333,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, longFunctionNoArgsMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "longFunctionNoArgs"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1358,16 +1366,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, objectFunctionNoArgsMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "objectFunctionNoArgs"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1391,16 +1399,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, optionalArgumentWithDefaultMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "optionalArgumentWithDefault"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1424,16 +1432,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, optionalArgumentsMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "optionalArguments"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1457,16 +1465,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, optionalNullableArgumentsWithDefaultsMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "optionalNullableArgumentsWithDefaults"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1490,16 +1498,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, overloadedFunctionMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "overloadedFunction"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1523,16 +1531,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, overloadedNullableMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "overloadedNullable"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1556,16 +1564,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, stringFunctionNoArgsMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "stringFunctionNoArgs"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1589,16 +1597,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, variadicPrimitiveArgumentsMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "variadicPrimitiveArguments"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1622,16 +1630,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, variadicStringArgumentsAfterOptionalArgumentMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "variadicStringArgumentsAfterOptionalArgument"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1655,16 +1663,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, voidFunctionLongArgMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "voidFunctionLongArg"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1688,16 +1696,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, voidFunctionNoArgsMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "voidFunctionNoArgs"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1721,16 +1729,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, voidFunctionObjectArgMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "voidFunctionObjectArg"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1754,16 +1762,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, voidFunctionStringArgMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "voidFunctionStringArg"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1787,17 +1795,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // If the operation is static, then the property exists on the interface - // object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, overloadedFunctionStaticMethod); method_template->RemovePrototype(); method_template->SetLength(1); - function_template->Set( - NewInternalString(isolate, "overloadedFunction"), - method_template); + // The location of the property is determined as follows: + // If the operation is static, then the property exists on the interface + // object. + function_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1818,6 +1825,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.cc index 88abc4d..29232f0 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_promise_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 41; +const int kInterfaceUniqueId = 42; @@ -93,15 +94,22 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "PromiseInterface is not constructible."); +} + + void onSuccessMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<PromiseInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cPromiseInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -127,11 +135,11 @@ void returnBooleanPromiseMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<PromiseInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cPromiseInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -163,11 +171,11 @@ void returnInterfacePromiseMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<PromiseInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cPromiseInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -199,11 +207,11 @@ void returnStringPromiseMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<PromiseInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cPromiseInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -235,11 +243,11 @@ void returnVoidPromiseMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<PromiseInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cPromiseInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -295,7 +303,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -345,16 +353,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, onSuccessMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "onSuccess"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -378,16 +386,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, returnBooleanPromiseMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "returnBooleanPromise"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -411,16 +419,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, returnInterfacePromiseMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "returnInterfacePromise"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -444,16 +452,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, returnStringPromiseMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "returnStringPromise"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -477,16 +485,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, returnVoidPromiseMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "returnVoidPromise"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -507,6 +515,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.cc index ee735c0..cbfc0dc 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_put_forwards_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -88,7 +89,7 @@ namespace { -const int kInterfaceUniqueId = 42; +const int kInterfaceUniqueId = 43; @@ -97,17 +98,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "PutForwardsInterface is not constructible."); +} + + void forwardingAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<PutForwardsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cPutForwardsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -124,27 +132,28 @@ PutForwardsInterface* impl = wrapper_private->wrappable<PutForwardsInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->forwarding_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void forwardingAttributeAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<PutForwardsInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cPutForwardsInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -184,31 +193,32 @@ } -void staticForwardingAttributeStaticAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { +void staticForwardingAttributeAttributeGetter( + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; + if (!exception_state.is_exception_set()) { ToJSValue(isolate, PutForwardsInterface::static_forwarding_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } -void staticForwardingAttributeStaticAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { +void staticForwardingAttributeAttributeSetter( + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -264,7 +274,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -311,18 +321,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, forwardingAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, forwardingAttributeAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - forwardingAttributeAttributeGetter, - forwardingAttributeAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -345,7 +357,10 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, staticForwardingAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, staticForwardingAttributeAttributeSetter); // The location of the property is determined as follows: // Operations installed on the interface object must be static methods, so @@ -354,13 +369,12 @@ // If the attribute is a static attribute, then there is a single // corresponding property and it exists on the interface's interface object. - function_template->SetNativeDataProperty( - name, - staticForwardingAttributeStaticAttributeGetter, - staticForwardingAttributeStaticAttributeSetter, - v8::Local<v8::Value>(), - attributes); - + function_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -383,6 +397,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.cc index ec73494..ce167e9 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_sequence_user.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -88,7 +89,7 @@ namespace { -const int kInterfaceUniqueId = 43; +const int kInterfaceUniqueId = 44; @@ -116,13 +117,14 @@ + void getInterfaceSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -154,11 +156,11 @@ void getInterfaceSequenceSequenceSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -190,11 +192,11 @@ void getLongSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -226,11 +228,11 @@ void getStringSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -262,11 +264,11 @@ void getStringSequenceSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -298,11 +300,11 @@ void getUnionOfStringAndStringSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -334,11 +336,11 @@ void getUnionSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -370,11 +372,11 @@ void setInterfaceSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -416,11 +418,11 @@ void setInterfaceSequenceSequenceSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -462,11 +464,11 @@ void setLongSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -508,11 +510,11 @@ void setStringSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -554,11 +556,11 @@ void setStringSequenceSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -600,11 +602,11 @@ void setUnionOfStringAndStringSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -646,11 +648,11 @@ void setUnionSequenceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<SequenceUser>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cSequenceUser::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -767,16 +769,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getInterfaceSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "getInterfaceSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -800,16 +802,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getInterfaceSequenceSequenceSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "getInterfaceSequenceSequenceSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -833,16 +835,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getLongSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "getLongSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -866,16 +868,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getStringSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "getStringSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -899,16 +901,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getStringSequenceSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "getStringSequenceSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -932,16 +934,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getUnionOfStringAndStringSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "getUnionOfStringAndStringSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -965,16 +967,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getUnionSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "getUnionSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -998,16 +1000,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setInterfaceSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "setInterfaceSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1031,16 +1033,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setInterfaceSequenceSequenceSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "setInterfaceSequenceSequenceSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1064,16 +1066,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setLongSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "setLongSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1097,16 +1099,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setStringSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "setStringSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1130,16 +1132,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setStringSequenceSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "setStringSequenceSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1163,16 +1165,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setUnionOfStringAndStringSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "setUnionOfStringAndStringSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1196,16 +1198,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setUnionSequenceMethod); method_template->RemovePrototype(); method_template->SetLength(1); - prototype_template->Set( - NewInternalString(isolate, "setUnionSequence"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -1226,6 +1228,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_single_operation_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_single_operation_interface.cc index f5b78d7..2a04c11 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_single_operation_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_single_operation_interface.cc
@@ -58,9 +58,7 @@ base::optional<int32_t > cobalt_return_value; DCHECK(isolate_); - if (this->IsEmpty()) { - goto done; - } + DCHECK(!this->IsEmpty()); { EntryScope entry_scope(isolate_); v8::Local<v8::Context> context = isolate_->GetCurrentContext();
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.cc index 491198b..1eeccff 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_static_properties_interface.cc
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -88,7 +89,7 @@ namespace { -const int kInterfaceUniqueId = 45; +const int kInterfaceUniqueId = 46; @@ -97,32 +98,40 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "StaticPropertiesInterface is not constructible."); +} -void staticAttributeStaticAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + + +void staticAttributeAttributeGetter( + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; + if (!exception_state.is_exception_set()) { ToJSValue(isolate, StaticPropertiesInterface::static_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } -void staticAttributeStaticAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { +void staticAttributeAttributeSetter( + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cExceptionState exception_state{isolate}; v8::Local<v8::Value> result_value; @@ -402,7 +411,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -449,7 +458,10 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, staticAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, staticAttributeAttributeSetter); // The location of the property is determined as follows: // Operations installed on the interface object must be static methods, so @@ -458,13 +470,12 @@ // If the attribute is a static attribute, then there is a single // corresponding property and it exists on the interface's interface object. - function_template->SetNativeDataProperty( - name, - staticAttributeStaticAttributeGetter, - staticAttributeStaticAttributeSetter, - v8::Local<v8::Value>(), - attributes); - + function_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -490,17 +501,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // If the operation is static, then the property exists on the interface - // object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, staticFunctionStaticMethod); method_template->RemovePrototype(); method_template->SetLength(0); - function_template->Set( - NewInternalString(isolate, "staticFunction"), - method_template); + // The location of the property is determined as follows: + // If the operation is static, then the property exists on the interface + // object. + function_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -521,6 +531,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.cc index 2afe72f..0191e97 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_anonymous_operation_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 46; +const int kInterfaceUniqueId = 47; @@ -93,17 +94,23 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "StringifierAnonymousOperationInterface is not constructible."); +} -void Stringifier(v8::Local<v8::String> property, - const v8::PropertyCallbackInfo<v8::Value>& info) { + + +void Stringifier(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state(isolate); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<StringifierAnonymousOperationInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cStringifierAnonymousOperationInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -160,7 +167,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -205,6 +212,15 @@ NewInternalString(isolate, "StringifierAnonymousOperationInterface"), static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + { + v8::Local<v8::String> name = NewInternalString(isolate, "toString"); + v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, Stringifier); + prototype_template->Set( + NewInternalString(isolate, "toString"), + method_template); + } + + }
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.cc index bdfb6ec..2f85599 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_attribute_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 47; +const int kInterfaceUniqueId = 48; @@ -93,17 +94,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "StringifierAttributeInterface is not constructible."); +} + + void theStringifierAttributeAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<StringifierAttributeInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cStringifierAttributeInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -120,27 +128,28 @@ StringifierAttributeInterface* impl = wrapper_private->wrappable<StringifierAttributeInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->the_stringifier_attribute(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void theStringifierAttributeAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<StringifierAttributeInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cStringifierAttributeInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -169,16 +178,15 @@ } -void Stringifier(v8::Local<v8::String> property, - const v8::PropertyCallbackInfo<v8::Value>& info) { +void Stringifier(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state(isolate); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<StringifierAttributeInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cStringifierAttributeInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -235,7 +243,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -282,18 +290,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, theStringifierAttributeAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, theStringifierAttributeAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - theStringifierAttributeAttributeGetter, - theStringifierAttributeAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -314,6 +324,15 @@ NewInternalString(isolate, "StringifierAttributeInterface"), static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + { + v8::Local<v8::String> name = NewInternalString(isolate, "toString"); + v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, Stringifier); + prototype_template->Set( + NewInternalString(isolate, "toString"), + method_template); + } + + }
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.cc index 3954c44..3e88169 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_stringifier_operation_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 48; +const int kInterfaceUniqueId = 49; @@ -93,15 +94,22 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "StringifierOperationInterface is not constructible."); +} + + void theStringifierOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<StringifierOperationInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cStringifierOperationInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -130,16 +138,15 @@ } -void Stringifier(v8::Local<v8::String> property, - const v8::PropertyCallbackInfo<v8::Value>& info) { +void Stringifier(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state(isolate); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<StringifierOperationInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cStringifierOperationInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -196,7 +203,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -246,16 +253,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, theStringifierOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "theStringifierOperation"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -274,6 +281,15 @@ NewInternalString(isolate, "StringifierOperationInterface"), static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); + { + v8::Local<v8::String> name = NewInternalString(isolate, "toString"); + v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, Stringifier); + prototype_template->Set( + NewInternalString(isolate, "toString"), + method_template); + } + + }
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.cc index 5e0cdbf..8b2a210 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_target_interface.cc
@@ -38,6 +38,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -84,7 +85,7 @@ namespace { -const int kInterfaceUniqueId = 49; +const int kInterfaceUniqueId = 50; @@ -93,15 +94,22 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "TargetInterface is not constructible."); +} + + void implementedInterfaceFunctionMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<TargetInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cTargetInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -127,11 +135,11 @@ void partialInterfaceFunctionMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<TargetInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cTargetInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -181,7 +189,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -231,16 +239,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, implementedInterfaceFunctionMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "implementedInterfaceFunction"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -264,16 +272,16 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, the property exists solely on the interface's interface - // prototype object. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, partialInterfaceFunctionMethod); method_template->RemovePrototype(); method_template->SetLength(0); - prototype_template->Set( - NewInternalString(isolate, "partialInterfaceFunction"), - method_template); + + // The location of the property is determined as follows: + // Otherwise, the property exists solely on the interface's interface + // prototype object. + prototype_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -294,6 +302,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_dictionary.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_dictionary.cc index 4ed11e4..41aaf2c 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_dictionary.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_dictionary.cc
@@ -1,18 +1,16 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_enum.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_enum.cc index 2d077b1..703f664 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_enum.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_test_enum.cc
@@ -1,19 +1,17 @@ -/* - * Copyright 2018 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.cc index 4700703..48455d9 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_union_types_interface.cc
@@ -42,6 +42,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -92,7 +93,7 @@ namespace { -const int kInterfaceUniqueId = 51; +const int kInterfaceUniqueId = 52; @@ -101,17 +102,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "UnionTypesInterface is not constructible."); +} + + void unionPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<UnionTypesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cUnionTypesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -128,27 +136,28 @@ UnionTypesInterface* impl = wrapper_private->wrappable<UnionTypesInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->union_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void unionPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<UnionTypesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cUnionTypesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -178,15 +187,15 @@ void unionWithNullableMemberPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<UnionTypesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cUnionTypesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -203,27 +212,28 @@ UnionTypesInterface* impl = wrapper_private->wrappable<UnionTypesInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->union_with_nullable_member_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void unionWithNullableMemberPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<UnionTypesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cUnionTypesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -253,15 +263,15 @@ void nullableUnionPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<UnionTypesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cUnionTypesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -278,27 +288,28 @@ UnionTypesInterface* impl = wrapper_private->wrappable<UnionTypesInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->nullable_union_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void nullableUnionPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<UnionTypesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cUnionTypesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -328,15 +339,15 @@ void unionBasePropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<UnionTypesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cUnionTypesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -353,27 +364,28 @@ UnionTypesInterface* impl = wrapper_private->wrappable<UnionTypesInterface>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->union_base_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void unionBasePropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<UnionTypesInterface>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cUnionTypesInterface::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -429,7 +441,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -476,18 +488,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, unionPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, unionPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - unionPropertyAttributeGetter, - unionPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -510,18 +524,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, unionWithNullableMemberPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, unionWithNullableMemberPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - unionWithNullableMemberPropertyAttributeGetter, - unionWithNullableMemberPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -544,18 +560,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, nullableUnionPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, nullableUnionPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - nullableUnionPropertyAttributeGetter, - nullableUnionPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -578,18 +596,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, unionBasePropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, unionBasePropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - unionBasePropertyAttributeGetter, - unionBasePropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); + prototype_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -612,6 +632,8 @@ + + } } // namespace
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc index f50864a..07237ba 100644 --- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc +++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc
@@ -38,6 +38,7 @@ #include "cobalt/bindings/testing/constants_interface.h" #include "cobalt/bindings/testing/constructor_interface.h" #include "cobalt/bindings/testing/constructor_with_arguments_interface.h" +#include "cobalt/bindings/testing/convert_simple_object_interface.h" #include "cobalt/bindings/testing/derived_getter_setter_interface.h" #include "cobalt/bindings/testing/derived_interface.h" #include "cobalt/bindings/testing/dictionary_interface.h" @@ -87,6 +88,7 @@ #include "cobalt/bindings/testing/v8c_constants_interface.h" #include "cobalt/bindings/testing/v8c_constructor_interface.h" #include "cobalt/bindings/testing/v8c_constructor_with_arguments_interface.h" +#include "cobalt/bindings/testing/v8c_convert_simple_object_interface.h" #include "cobalt/bindings/testing/v8c_derived_getter_setter_interface.h" #include "cobalt/bindings/testing/v8c_derived_interface.h" #include "cobalt/bindings/testing/v8c_dictionary_interface.h" @@ -138,6 +140,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -163,6 +166,7 @@ using cobalt::bindings::testing::ConstantsInterface; using cobalt::bindings::testing::ConstructorInterface; using cobalt::bindings::testing::ConstructorWithArgumentsInterface; +using cobalt::bindings::testing::ConvertSimpleObjectInterface; using cobalt::bindings::testing::DOMStringTestInterface; using cobalt::bindings::testing::DerivedGetterSetterInterface; using cobalt::bindings::testing::DerivedInterface; @@ -216,6 +220,7 @@ using cobalt::bindings::testing::V8cConstantsInterface; using cobalt::bindings::testing::V8cConstructorInterface; using cobalt::bindings::testing::V8cConstructorWithArgumentsInterface; +using cobalt::bindings::testing::V8cConvertSimpleObjectInterface; using cobalt::bindings::testing::V8cDOMStringTestInterface; using cobalt::bindings::testing::V8cDerivedGetterSetterInterface; using cobalt::bindings::testing::V8cDerivedInterface; @@ -292,7 +297,7 @@ namespace { -const int kInterfaceUniqueId = 53; +const int kInterfaceUniqueId = 54; @@ -301,17 +306,24 @@ +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "Window is not constructible."); +} + + void windowPropertyAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<Window>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cWindow::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -328,27 +340,28 @@ Window* impl = wrapper_private->wrappable<Window>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->window_property(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void windowPropertyAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<Window>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cWindow::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -378,15 +391,15 @@ void windowAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<Window>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cWindow::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -403,28 +416,30 @@ Window* impl = wrapper_private->wrappable<Window>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->window(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void onEventAttributeGetter( - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<Window>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cWindow::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -441,27 +456,28 @@ Window* impl = wrapper_private->wrappable<Window>().get(); + if (!exception_state.is_exception_set()) { ToJSValue(isolate, impl->on_event(), &result_value); } - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } void onEventAttributeSetter( - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<Window>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cWindow::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -493,11 +509,11 @@ void getStackTraceMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<Window>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cWindow::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -530,11 +546,11 @@ void setTimeoutMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<Window>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cWindow::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -616,11 +632,11 @@ void windowOperationMethod(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<Window>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !V8cWindow::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -670,7 +686,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -728,20 +744,21 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, windowPropertyAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, windowPropertyAttributeSetter); // The location of the property is determined as follows: // Otherwise, if the attribute is unforgeable on the interface or if the // interface was declared with the [Global] extended attribute, then the // property exists on every object that implements the interface. - instance_template->SetAccessor( - name, - windowPropertyAttributeGetter, - windowPropertyAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); - + instance_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -764,20 +781,20 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, windowAttributeGetter); + v8::Local<v8::FunctionTemplate> setter; // The location of the property is determined as follows: // Otherwise, if the attribute is unforgeable on the interface or if the // interface was declared with the [Global] extended attribute, then the // property exists on every object that implements the interface. - instance_template->SetAccessor( - name, - windowAttributeGetter, - 0, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); - + instance_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } { @@ -800,20 +817,21 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, onEventAttributeGetter); + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, onEventAttributeSetter); // The location of the property is determined as follows: // Otherwise, if the attribute is unforgeable on the interface or if the // interface was declared with the [Global] extended attribute, then the // property exists on every object that implements the interface. - instance_template->SetAccessor( - name, - onEventAttributeGetter, - onEventAttributeSetter, - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); - + instance_template-> + SetAccessorProperty( + name, + getter, + setter, + attributes); } @@ -839,18 +857,17 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, if the operation is unforgeable on the interface or if the - // interface was declared with the [Global] extended attribute, then the - // property exists on every object that implements the interface. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, getStackTraceMethod); method_template->RemovePrototype(); method_template->SetLength(0); - instance_template->Set( - NewInternalString(isolate, "getStackTrace"), - method_template); + // The location of the property is determined as follows: + // Otherwise, if the operation is unforgeable on the interface or if the + // interface was declared with the [Global] extended attribute, then the + // property exists on every object that implements the interface. + instance_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -874,18 +891,17 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, if the operation is unforgeable on the interface or if the - // interface was declared with the [Global] extended attribute, then the - // property exists on every object that implements the interface. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, setTimeoutMethod); method_template->RemovePrototype(); method_template->SetLength(1); - instance_template->Set( - NewInternalString(isolate, "setTimeout"), - method_template); + // The location of the property is determined as follows: + // Otherwise, if the operation is unforgeable on the interface or if the + // interface was declared with the [Global] extended attribute, then the + // property exists on every object that implements the interface. + instance_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -909,18 +925,17 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); - // The location of the property is determined as follows: - // Otherwise, if the operation is unforgeable on the interface or if the - // interface was declared with the [Global] extended attribute, then the - // property exists on every object that implements the interface. v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, windowOperationMethod); method_template->RemovePrototype(); method_template->SetLength(0); - instance_template->Set( - NewInternalString(isolate, "windowOperation"), - method_template); + // The location of the property is determined as follows: + // Otherwise, if the operation is unforgeable on the interface or if the + // interface was declared with the [Global] extended attribute, then the + // property exists on every object that implements the interface. + instance_template-> + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -941,6 +956,8 @@ + + } } // namespace @@ -986,6 +1003,8 @@ context_.Reset(isolate_, context); v8::Context::Scope context_scope(context); + global_wrappable_ = global_interface; + DCHECK(!environment_settings_); DCHECK(environment_settings); environment_settings_ = environment_settings; @@ -1048,6 +1067,10 @@ base::Bind(V8cConstructorWithArgumentsInterface::CreateWrapper), base::Bind(V8cConstructorWithArgumentsInterface::GetTemplate)); wrapper_factory_->RegisterWrappableType( + ConvertSimpleObjectInterface::ConvertSimpleObjectInterfaceWrappableType(), + base::Bind(V8cConvertSimpleObjectInterface::CreateWrapper), + base::Bind(V8cConvertSimpleObjectInterface::GetTemplate)); + wrapper_factory_->RegisterWrappableType( DOMStringTestInterface::DOMStringTestInterfaceWrappableType(), base::Bind(V8cDOMStringTestInterface::CreateWrapper), base::Bind(V8cDOMStringTestInterface::GetTemplate)); @@ -1197,6 +1220,7 @@ Window::WindowWrappableType(), base::Bind(V8cWindow::CreateWrapper), base::Bind(V8cWindow::GetTemplate)); + } } // namespace v8c
diff --git a/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template b/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template index 6c46b81..cae17a7 100644 --- a/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template +++ b/src/cobalt/bindings/mozjs45/templates/dictionary-conversion.cc.template
@@ -13,21 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. #} -/* - * Copyright {{today.year}} Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright {{today.year}} Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template b/src/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template index 0c708c5..974876a 100644 --- a/src/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template +++ b/src/cobalt/bindings/mozjs45/templates/enumeration-conversion.cc.template
@@ -13,21 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. #} -/* - * Copyright {{today.year}} Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright {{today.year}} Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/mozjs45/templates/generated-types.h.template b/src/cobalt/bindings/mozjs45/templates/generated-types.h.template index 96d2650..5c7ef02 100644 --- a/src/cobalt/bindings/mozjs45/templates/generated-types.h.template +++ b/src/cobalt/bindings/mozjs45/templates/generated-types.h.template
@@ -13,21 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. #} -/* - * Copyright {{today.year}} Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright {{today.year}} Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/templates/dictionary.h.template b/src/cobalt/bindings/templates/dictionary.h.template index 3a8bc12..7587c81 100644 --- a/src/cobalt/bindings/templates/dictionary.h.template +++ b/src/cobalt/bindings/templates/dictionary.h.template
@@ -13,21 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. #} -/* - * Copyright {{today.year}} Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright {{today.year}} Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off @@ -101,8 +99,8 @@ {% else %} if (other.{{member.name}}_) { {{member.name}}_.reset( - new script::ScriptValue<{{member.type}}>::StrongReference( - other.{{member.name}}_->referenced_value())); + new script::Handle<{{member.type}}>( + *other.{{member.name}}_)); } {% endif %} {% endfor %} @@ -121,8 +119,8 @@ {% else %} if (other.{{member.name}}_) { {{member.name}}_.reset( - new script::ScriptValue<{{member.type}}>::StrongReference( - other.{{member.name}}_->referenced_value())); + new script::Handle<{{member.type}}>( + *other.{{member.name}}_)); } else { {{member.name}}_.reset(); } @@ -147,7 +145,7 @@ if (!{{member.name}}_) { return NULL; } - return &({{member.name}}_->referenced_value()); + return ({{member.name}}_->GetScriptValue()); {% else %} return {{member.name}}_; {% endif %} @@ -159,7 +157,7 @@ {% if member.is_script_value %} if (value) { {{member.name}}_.reset( - new script::ScriptValue<{{member.type}}>::StrongReference(*value)); + new script::Handle<{{member.type}}>(*value)); } else { {{member.name}}_.reset(); } @@ -169,13 +167,14 @@ } {% endfor %} + private: {% for member in members %} {% if not member.default_value %} bool has_{{member.name}}_; {% endif %} {% if member.is_script_value %} - scoped_ptr<script::ScriptValue<{{member.type}}>::StrongReference> {{member.name}}_; + scoped_ptr<script::Handle<{{member.type}}>> {{member.name}}_; {% else %} {{member.type}} {{member.name}}_; {% endif %}
diff --git a/src/cobalt/bindings/templates/enumeration.h.template b/src/cobalt/bindings/templates/enumeration.h.template index d161069..6d8a5b1 100644 --- a/src/cobalt/bindings/templates/enumeration.h.template +++ b/src/cobalt/bindings/templates/enumeration.h.template
@@ -13,21 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. #} -/* - * Copyright {{today.year}} Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright {{today.year}} Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/testing/callback_interface_interface.idl b/src/cobalt/bindings/testing/callback_interface_interface.idl index 1ee588a..b297054 100644 --- a/src/cobalt/bindings/testing/callback_interface_interface.idl +++ b/src/cobalt/bindings/testing/callback_interface_interface.idl
@@ -13,7 +13,7 @@ // limitations under the License. interface CallbackInterfaceInterface { - void registerCallback(SingleOperationInterface callback_interface); + void registerCallback(SingleOperationInterface callbackInterface); attribute SingleOperationInterface? callbackAttribute; void someOperation();
diff --git a/src/cobalt/bindings/testing/constructor_bindings_test.cc b/src/cobalt/bindings/testing/constructor_bindings_test.cc index 02c09b3..4f67481 100644 --- a/src/cobalt/bindings/testing/constructor_bindings_test.cc +++ b/src/cobalt/bindings/testing/constructor_bindings_test.cc
@@ -103,6 +103,24 @@ EXPECT_STREQ("true", result.c_str()); } +TEST_F(ConstructorBindingsTest, IllegalConstructorThrows) { + const char script[] = R"( + let result = null; + try { + const ngi = new NamedGetterInterface(); + // Poke at the object a little bit in order to ensure it is actually + // backed by a native object. + ngi.property; + } catch (ex) { + result = ex.name; + } + result; + )"; + std::string result; + EXPECT_TRUE(EvaluateScript(script, &result)); + EXPECT_STREQ("TypeError", result.c_str()); +} + } // namespace testing } // namespace bindings } // namespace cobalt
diff --git a/src/cobalt/bindings/testing/constructor_with_arguments_interface.idl b/src/cobalt/bindings/testing/constructor_with_arguments_interface.idl index 780b7e4..629fedc 100644 --- a/src/cobalt/bindings/testing/constructor_with_arguments_interface.idl +++ b/src/cobalt/bindings/testing/constructor_with_arguments_interface.idl
@@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -[Constructor(long arg1, boolean arg2, optional DOMString default_arg = "default")] +[Constructor(long arg1, boolean arg2, optional DOMString defaultArg = "default")] interface ConstructorWithArgumentsInterface { readonly attribute long longArg; readonly attribute boolean booleanArg;
diff --git a/src/cobalt/bindings/testing/convert_simple_object_interface.h b/src/cobalt/bindings/testing/convert_simple_object_interface.h new file mode 100644 index 0000000..486abc9 --- /dev/null +++ b/src/cobalt/bindings/testing/convert_simple_object_interface.h
@@ -0,0 +1,50 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COBALT_BINDINGS_TESTING_CONVERT_SIMPLE_OBJECT_INTERFACE_H_ +#define COBALT_BINDINGS_TESTING_CONVERT_SIMPLE_OBJECT_INTERFACE_H_ + +#include <string> +#include <unordered_map> + +#include "cobalt/script/exception_state.h" +#include "cobalt/script/value_handle.h" +#include "cobalt/script/wrappable.h" + +namespace cobalt { +namespace bindings { +namespace testing { + +class ConvertSimpleObjectInterface : public script::Wrappable { + public: + ConvertSimpleObjectInterface() = default; + + std::unordered_map<std::string, std::string> result() { return result_; } + + void ConvertSimpleObjectToMapTest(const script::ValueHandleHolder& value, + script::ExceptionState* exception_state) { + result_ = script::ConvertSimpleObjectToMap(value, exception_state); + } + + DEFINE_WRAPPABLE_TYPE(ConvertSimpleObjectInterface); + + private: + std::unordered_map<std::string, std::string> result_; +}; + +} // namespace testing +} // namespace bindings +} // namespace cobalt + +#endif // COBALT_BINDINGS_TESTING_CONVERT_SIMPLE_OBJECT_INTERFACE_H_
diff --git a/src/cobalt/bindings/testing/convert_simple_object_interface.idl b/src/cobalt/bindings/testing/convert_simple_object_interface.idl new file mode 100644 index 0000000..1240dec --- /dev/null +++ b/src/cobalt/bindings/testing/convert_simple_object_interface.idl
@@ -0,0 +1,18 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +[ Constructor ] +interface ConvertSimpleObjectInterface { + [RaisesException] void convertSimpleObjectToMapTest(any value); +};
diff --git a/src/cobalt/bindings/testing/convert_simple_object_test.cc b/src/cobalt/bindings/testing/convert_simple_object_test.cc new file mode 100644 index 0000000..1e16e5e --- /dev/null +++ b/src/cobalt/bindings/testing/convert_simple_object_test.cc
@@ -0,0 +1,114 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cobalt/bindings/testing/bindings_test_base.h" +#include "cobalt/bindings/testing/convert_simple_object_interface.h" + +using ::testing::ContainsRegex; + +namespace cobalt { +namespace bindings { +namespace testing { + +class ConvertSimpleObjectTest + : public InterfaceBindingsTest<ConvertSimpleObjectInterface> {}; + +TEST_F(ConvertSimpleObjectTest, ValidObjectAllStrings) { + std::unordered_map<std::string, std::string> expected_result = { + {"countryCode", "US"}, {"language", "english"}}; + + const char script[] = R"( + const productSpecificData = {countryCode: 'US', language: 'english'}; + test.convertSimpleObjectToMapTest(productSpecificData); + )"; + + EXPECT_TRUE(EvaluateScript(script)); + EXPECT_EQ(test_mock().result(), expected_result); +} + +TEST_F(ConvertSimpleObjectTest, ValidObjectWithNumber) { + std::unordered_map<std::string, std::string> expected_result = { + {"countryCode", "5"}, {"language", "english"}}; + + const char script[] = R"( + const productSpecificData = {countryCode: 5, language: 'english'}; + test.convertSimpleObjectToMapTest(productSpecificData); + )"; + + EXPECT_TRUE(EvaluateScript(script)); + EXPECT_EQ(test_mock().result(), expected_result); +} + +TEST_F(ConvertSimpleObjectTest, ValidObjectWithBool) { + std::unordered_map<std::string, std::string> expected_result = { + {"countryCode", "US"}, {"language", "true"}}; + + const char script[] = R"( + const productSpecificData = {countryCode: 'US', language: true}; + test.convertSimpleObjectToMapTest(productSpecificData); + )"; + + EXPECT_TRUE(EvaluateScript(script)); + EXPECT_EQ(test_mock().result(), expected_result); +} + +TEST_F(ConvertSimpleObjectTest, InvalidValue) { + const char script[] = R"( + const productSpecificData = 'data'; + let error = null; + try { + test.convertSimpleObjectToMapTest(productSpecificData); + } catch(ex) { + error = ex; + } + )"; + + EXPECT_TRUE(EvaluateScript(script)); + std::string result; + EXPECT_TRUE(EvaluateScript("error instanceof Error;", &result)); + EXPECT_STREQ("true", result.c_str()); + EXPECT_TRUE(EvaluateScript("error.toString();", &result)); + EXPECT_STREQ("TypeError: The value must be an object", result.c_str()); +} + +TEST_F(ConvertSimpleObjectTest, InvalidObjectValues) { + const std::string invalid_values[] = {"null", "undefined", "{}", + "function(){}", "Symbol('US')"}; + + std::string result; + for (const auto& value : invalid_values) { + const std::string script = R"( + var productSpecificData = {countryCode: )" + + value + R"(, language: 'english'}; + var error = null; + try { + test.convertSimpleObjectToMapTest(productSpecificData); + } catch(ex) { + error = ex; + } + )"; + + EXPECT_TRUE(EvaluateScript(script)); + EXPECT_TRUE(EvaluateScript("error instanceof Error;", &result)); + EXPECT_STREQ("true", result.c_str()); + EXPECT_TRUE(EvaluateScript("error.toString();", &result)); + EXPECT_STREQ( + "TypeError: Object property values must be a number, string or boolean", + result.c_str()); + } +} + +} // namespace testing +} // namespace bindings +} // namespace cobalt
diff --git a/src/cobalt/bindings/testing/date_bindings_test.cc b/src/cobalt/bindings/testing/date_bindings_test.cc index bcff234..56c3c37 100644 --- a/src/cobalt/bindings/testing/date_bindings_test.cc +++ b/src/cobalt/bindings/testing/date_bindings_test.cc
@@ -70,23 +70,6 @@ EXPECT_STREQ("Invalid Date", result.c_str()); } -TEST_F(DateBindingsTest, EpocDates) { - std::string result; - // Set date to POSIX EPOC Jan 1, 1970 00:00:00 (month is 0-based) - EXPECT_TRUE(EvaluateScript("test.setDate(new Date(1970, 0))", &result)) - << result; - EXPECT_TRUE(EvaluateScript("test.getDate()", &result)) << result; - EXPECT_PRED_FORMAT2(::testing::IsSubstring, "Thu Jan 01 1970 00:00:00", - result.c_str()); - - // Set date to WIN32 EPOC Jan 1, 1601 00:00:00 (month is 0-based) - EXPECT_TRUE(EvaluateScript("test.setDate(new Date(1601, 0))", &result)) - << result; - EXPECT_TRUE(EvaluateScript("test.getDate()", &result)) << result; - EXPECT_PRED_FORMAT2(::testing::IsSubstring, "Mon Jan 01 1601 00:00:00", - result.c_str()); -} - } // namespace } // namespace testing } // namespace bindings
diff --git a/src/cobalt/bindings/testing/derived_dictionary.idl b/src/cobalt/bindings/testing/derived_dictionary.idl index 0dfea67..a62b824 100644 --- a/src/cobalt/bindings/testing/derived_dictionary.idl +++ b/src/cobalt/bindings/testing/derived_dictionary.idl
@@ -1,18 +1,16 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. dictionary DerivedDictionary : TestDictionary { boolean additionalMember = false;
diff --git a/src/cobalt/bindings/testing/dictionary_interface.h b/src/cobalt/bindings/testing/dictionary_interface.h index b43cfc7..46c3d35 100644 --- a/src/cobalt/bindings/testing/dictionary_interface.h +++ b/src/cobalt/bindings/testing/dictionary_interface.h
@@ -1,18 +1,16 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef COBALT_BINDINGS_TESTING_DICTIONARY_INTERFACE_H_ #define COBALT_BINDINGS_TESTING_DICTIONARY_INTERFACE_H_
diff --git a/src/cobalt/bindings/testing/dictionary_interface.idl b/src/cobalt/bindings/testing/dictionary_interface.idl index 07982cd..281c44a 100644 --- a/src/cobalt/bindings/testing/dictionary_interface.idl +++ b/src/cobalt/bindings/testing/dictionary_interface.idl
@@ -1,18 +1,16 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. interface DictionaryInterface { void dictionaryOperation(TestDictionary dictionary);
diff --git a/src/cobalt/bindings/testing/dictionary_test.cc b/src/cobalt/bindings/testing/dictionary_test.cc index 0f97d89..19d0d35 100644 --- a/src/cobalt/bindings/testing/dictionary_test.cc +++ b/src/cobalt/bindings/testing/dictionary_test.cc
@@ -1,18 +1,16 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include <limits>
diff --git a/src/cobalt/bindings/testing/dictionary_with_dictionary_member.idl b/src/cobalt/bindings/testing/dictionary_with_dictionary_member.idl index 60f967a..1b5aeec 100644 --- a/src/cobalt/bindings/testing/dictionary_with_dictionary_member.idl +++ b/src/cobalt/bindings/testing/dictionary_with_dictionary_member.idl
@@ -1,18 +1,16 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. dictionary DictionaryWithDictionaryMember { TestDictionary nestedDictionary;
diff --git a/src/cobalt/bindings/testing/exception_object_interface.idl b/src/cobalt/bindings/testing/exception_object_interface.idl index 6759636..2decb75 100644 --- a/src/cobalt/bindings/testing/exception_object_interface.idl +++ b/src/cobalt/bindings/testing/exception_object_interface.idl
@@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + exception ExceptionObjectInterface { readonly attribute DOMString error; readonly attribute DOMString message;
diff --git a/src/cobalt/bindings/testing/garbage_collection_test.cc b/src/cobalt/bindings/testing/garbage_collection_test.cc index 4fab7d5..e8a8736 100644 --- a/src/cobalt/bindings/testing/garbage_collection_test.cc +++ b/src/cobalt/bindings/testing/garbage_collection_test.cc
@@ -14,6 +14,7 @@ #include "cobalt/bindings/testing/bindings_test_base.h" #include "cobalt/bindings/testing/garbage_collection_test_interface.h" +#include "cobalt/bindings/testing/interface_with_any.h" #include "testing/gtest/include/gtest/gtest.h" namespace cobalt { @@ -21,7 +22,12 @@ namespace testing { namespace { + typedef BindingsTestBase GarbageCollectionTest; + +class ScriptValueGarbageCollectionTest + : public InterfaceBindingsTest<InterfaceWithAny> {}; + } // namespace TEST_F(GarbageCollectionTest, JSObjectHoldsReferenceToPlatformObject) { @@ -145,6 +151,48 @@ EXPECT_STREQ("7", result.c_str()); } +TEST_F(ScriptValueGarbageCollectionTest, ScriptHandleConstructors) { + EXPECT_TRUE(EvaluateScript("test.setAny({});")); + script::Handle<script::ValueHandle> object = test_mock().GetAny(); + EXPECT_TRUE(EvaluateScript("test.setAny({})")); + CollectGarbage(); + EXPECT_FALSE(object.GetScriptValue()->IsNull()); + + auto weak_ref = object.GetScriptValue()->MakeWeakCopy(); + object = test_mock().GetAny(); + CollectGarbage(); +#if !defined(ENGINE_USES_CONSERVATIVE_ROOTING) + EXPECT_TRUE(weak_ref->IsNull()); +#endif + + EvaluateScript("test.setAny({})"); + { + script::Handle<script::ValueHandle> object = test_mock().GetAny(); + script::Handle<script::ValueHandle> object2(object); + EXPECT_TRUE(object.GetScriptValue()->EqualTo(*object2.GetScriptValue())); + CollectGarbage(); + EXPECT_FALSE(object.GetScriptValue()->IsNull()); + EXPECT_FALSE(object2.GetScriptValue()->IsNull()); + EXPECT_TRUE(object.GetScriptValue()->EqualTo(*object2.GetScriptValue())); + + weak_ref = object.GetScriptValue()->MakeWeakCopy(); + } + + EvaluateScript("test.setAny({})"); + CollectGarbage(); +#if !defined(ENGINE_USES_CONSERVATIVE_ROOTING) + EXPECT_TRUE(weak_ref->IsNull()); +#endif + + { + script::Handle<script::ValueHandle> object = test_mock().GetAny(); + script::Handle<script::ValueHandle> object2 = std::move(object); + EXPECT_FALSE(object2.GetScriptValue()->IsNull()); + script::Handle<script::ValueHandle> object3(std::move(object2)); + EXPECT_FALSE(object3.GetScriptValue()->IsNull()); + } +} + } // namespace testing } // namespace bindings } // namespace cobalt
diff --git a/src/cobalt/bindings/testing/get_own_property_descriptor.cc b/src/cobalt/bindings/testing/get_own_property_descriptor.cc index 41da7fa..10c25d1 100644 --- a/src/cobalt/bindings/testing/get_own_property_descriptor.cc +++ b/src/cobalt/bindings/testing/get_own_property_descriptor.cc
@@ -25,11 +25,6 @@ typedef InterfaceBindingsTest<ArbitraryInterface> GetOwnPropertyDescriptorTest; } // namespace -// TODO: This test is expected to succeed on SpiderMonkey 24 and fail on -// SpiderMonkey 45 due to an improper proxy handler implementation. See -// "cobalt/script/mozjs-45/proxy_handler.h" for more details. The override of -// |getOwnPropertyDescriptor| should not call |getPropertyDescriptor|, however -// currently does as a temporary fix for other tests. TEST_F(GetOwnPropertyDescriptorTest, DoesNotHaveArbitraryPropertyPropertyDescriptor) { std::string result; @@ -43,12 +38,12 @@ TEST_F(GetOwnPropertyDescriptorTest, GetPropertyDescriptorFromPrototype) { std::string result; - EvaluateScript( - "var descriptor = " - "Object.getOwnPropertyDescriptor(ArbitraryInterface.prototype, " - "\"arbitraryProperty\")", - &result); - EXPECT_EQ(result, "undefined"); + const char script[] = R"( + var descriptor = + Object.getOwnPropertyDescriptor(ArbitraryInterface.prototype, + "arbitraryProperty"); + )"; + EXPECT_TRUE(EvaluateScript(script, &result)); EvaluateScript("descriptor.configurable", &result); EXPECT_EQ(result, "true"); EvaluateScript("descriptor.enumerable", &result);
diff --git a/src/cobalt/bindings/testing/getter_setter_test.cc b/src/cobalt/bindings/testing/getter_setter_test.cc index 137643d..7ed305a 100644 --- a/src/cobalt/bindings/testing/getter_setter_test.cc +++ b/src/cobalt/bindings/testing/getter_setter_test.cc
@@ -16,6 +16,7 @@ #include "cobalt/bindings/testing/anonymous_indexed_getter_interface.h" #include "cobalt/bindings/testing/anonymous_named_getter_interface.h" #include "cobalt/bindings/testing/anonymous_named_indexed_getter_interface.h" +#include "cobalt/bindings/testing/arbitrary_interface.h" #include "cobalt/bindings/testing/bindings_test_base.h" #include "cobalt/bindings/testing/derived_getter_setter_interface.h" #include "cobalt/bindings/testing/indexed_getter_interface.h" @@ -35,6 +36,7 @@ namespace testing { namespace { + // Use this fixture to create a new MockT object with a BaseClass wrapper, and // bind the wrapper to the javascript variable "test". template <class MockT> @@ -50,6 +52,8 @@ const scoped_refptr<MockT> test_mock_; }; +typedef GetterSetterBindingsTestBase<ArbitraryInterface> + GetterSetterBindingsTest; typedef GetterSetterBindingsTestBase<AnonymousIndexedGetterInterface> AnonymousIndexedGetterBindingsTest; typedef GetterSetterBindingsTestBase<AnonymousNamedIndexedGetterInterface> @@ -79,8 +83,43 @@ private: int num_properties_; }; + } // namespace +TEST_F(GetterSetterBindingsTest, GetterCanHandleAllValueTypes) { + const char* script = R"EOF( + const getter = Object.getOwnPropertyDescriptor( + ArbitraryInterface.prototype, "arbitraryProperty").get; + [null, undefined, false, 0, "", {}, Symbol("")] + .map(value => { + try { getter.call(value); } + catch (ex) { return ex.toString().startsWith("TypeError"); } + return false; + }) + .every(result => result); + )EOF"; + std::string result; + EXPECT_TRUE(EvaluateScript(script, &result)); + EXPECT_STREQ("true", result.c_str()); +} + +TEST_F(GetterSetterBindingsTest, SetterCanHandleAllValueTypes) { + const char* script = R"EOF( + const setter = Object.getOwnPropertyDescriptor( + ArbitraryInterface.prototype, "arbitraryProperty").set; + [null, undefined, false, 0, "", {}, Symbol("")] + .map(value => { + try { setter.call(value); } + catch (ex) { return ex.toString().startsWith("TypeError"); } + return false; + }) + .every(result => result); + )EOF"; + std::string result; + EXPECT_TRUE(EvaluateScript(script, &result)); + EXPECT_STREQ("true", result.c_str()); +} + TEST_F(IndexedGetterBindingsTest, IndexedGetter) { ON_CALL(test_mock(), length()).WillByDefault(Return(10)); ON_CALL(test_mock(), IndexedGetter(_)).WillByDefault(ReturnArg<0>()); @@ -450,42 +489,6 @@ EXPECT_STREQ("true", result.c_str()); } -TEST_F(DerivedGetterSetterBindingsTest, - GetterCanHandleAllJavaScriptValueTypes) { - const char* script = R"EOF( - const getter = Object.getOwnPropertyDescriptor( - ArbitraryInterface.prototype, "arbitraryProperty").get; - [null, undefined, false, 0, "", {}, Symbol("")] - .map(value => { - try { getter.call(value); } - catch (ex) { return ex.toString().startsWith("TypeError"); } - return false; - }) - .every(result => result); - )EOF"; - std::string result; - EXPECT_TRUE(EvaluateScript(script, &result)); - EXPECT_STREQ("true", result.c_str()); -} - -TEST_F(DerivedGetterSetterBindingsTest, - SetterCanHandleAllJavaScriptValueTypes) { - const char* script = R"EOF( - const setter = Object.getOwnPropertyDescriptor( - ArbitraryInterface.prototype, "arbitraryProperty").set; - [null, undefined, false, 0, "", {}, Symbol("")] - .map(value => { - try { setter.call(value); } - catch (ex) { return ex.toString().startsWith("TypeError"); } - return false; - }) - .every(result => result); - )EOF"; - std::string result; - EXPECT_TRUE(EvaluateScript(script, &result)); - EXPECT_STREQ("true", result.c_str()); -} - } // namespace testing } // namespace bindings } // namespace cobalt
diff --git a/src/cobalt/bindings/testing/global_interface_parent.idl b/src/cobalt/bindings/testing/global_interface_parent.idl index 4196b2b..67020fe 100644 --- a/src/cobalt/bindings/testing/global_interface_parent.idl +++ b/src/cobalt/bindings/testing/global_interface_parent.idl
@@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + interface GlobalInterfaceParent { void parentOperation(); };
diff --git a/src/cobalt/bindings/testing/interface_with_any.h b/src/cobalt/bindings/testing/interface_with_any.h index 0109870..9f126c7 100644 --- a/src/cobalt/bindings/testing/interface_with_any.h +++ b/src/cobalt/bindings/testing/interface_with_any.h
@@ -37,14 +37,13 @@ } void SetAny(const script::ValueHandleHolder& value) { - value_.reset(new script::ValueHandleHolder::TracedReference(value)); + value_.reset(new script::ValueHandleHolder::Reference(this, value)); } DEFINE_WRAPPABLE_TYPE(InterfaceWithAny); - void TraceMembers(script::Tracer* tracer) override { tracer->Trace(value_); } private: - scoped_ptr<script::ValueHandleHolder::TracedReference> value_; + scoped_ptr<script::ValueHandleHolder::Reference> value_; }; } // namespace testing
diff --git a/src/cobalt/bindings/testing/nested_put_forwards_interface.idl b/src/cobalt/bindings/testing/nested_put_forwards_interface.idl index bf09d18..a97bbca 100644 --- a/src/cobalt/bindings/testing/nested_put_forwards_interface.idl +++ b/src/cobalt/bindings/testing/nested_put_forwards_interface.idl
@@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + interface NestedPutForwardsInterface { [PutForwards=forwardingAttribute] readonly attribute PutForwardsInterface nestedForwardingAttribute;
diff --git a/src/cobalt/bindings/testing/no_interface_object_interface.idl b/src/cobalt/bindings/testing/no_interface_object_interface.idl index d27b1ed..432ce6a 100644 --- a/src/cobalt/bindings/testing/no_interface_object_interface.idl +++ b/src/cobalt/bindings/testing/no_interface_object_interface.idl
@@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + [NoInterfaceObject] interface NoInterfaceObjectInterface { };
diff --git a/src/cobalt/bindings/testing/object_type_bindings_test.cc b/src/cobalt/bindings/testing/object_type_bindings_test.cc index dc46b12..bc7c2a7 100644 --- a/src/cobalt/bindings/testing/object_type_bindings_test.cc +++ b/src/cobalt/bindings/testing/object_type_bindings_test.cc
@@ -86,16 +86,6 @@ << result; } -#if defined(ENGINE_DEFINES_ATTRIBUTES_ON_OBJECT) -TEST_F(PlatformObjectBindingsTest, PropertyIsOwnProperty) { - EXPECT_CALL(test_mock(), arbitrary_object()); - - std::string result; - EXPECT_TRUE(EvaluateScript( - "test.arbitraryObject.hasOwnProperty(\"arbitraryProperty\");", &result)); - EXPECT_STREQ("true", result.c_str()); -} -#else TEST_F(PlatformObjectBindingsTest, PropertyIsDefinedOnPrototype) { EXPECT_CALL(test_mock(), arbitrary_object()); @@ -106,7 +96,6 @@ &result)); EXPECT_STREQ("true", result.c_str()); } -#endif // defined(ENGINE_DEFINES_ATTRIBUTES_ON_OBJECT) TEST_F(PlatformObjectBindingsTest, MemberFunctionIsPrototypeProperty) { EXPECT_CALL(test_mock(), arbitrary_object()).Times(3);
diff --git a/src/cobalt/bindings/testing/operations_test_interface.idl b/src/cobalt/bindings/testing/operations_test_interface.idl index 48b5b8a..9a870a3 100644 --- a/src/cobalt/bindings/testing/operations_test_interface.idl +++ b/src/cobalt/bindings/testing/operations_test_interface.idl
@@ -28,7 +28,7 @@ void variadicPrimitiveArguments(long... bools); void variadicStringArgumentsAfterOptionalArgument( - optional boolean optional_arg, DOMString... strings); + optional boolean optionalArg, DOMString... strings); void overloadedFunction(); void overloadedFunction(long arg);
diff --git a/src/cobalt/bindings/testing/promise_interface.h b/src/cobalt/bindings/testing/promise_interface.h index 5942a58..87806af 100644 --- a/src/cobalt/bindings/testing/promise_interface.h +++ b/src/cobalt/bindings/testing/promise_interface.h
@@ -30,16 +30,12 @@ class PromiseInterface : public script::Wrappable { public: - typedef script::ScriptValue<script::Promise<void> > VoidPromiseValue; - typedef script::ScriptValue<script::Promise<bool> > BooleanPromiseValue; - typedef script::ScriptValue<script::Promise<std::string> > StringPromiseValue; - typedef script::ScriptValue<script::Promise< - scoped_refptr<script::Wrappable> > > InterfacePromiseValue; - - MOCK_METHOD0(ReturnVoidPromise, const VoidPromiseValue*()); - MOCK_METHOD0(ReturnBooleanPromise, const BooleanPromiseValue*()); - MOCK_METHOD0(ReturnStringPromise, const StringPromiseValue*()); - MOCK_METHOD0(ReturnInterfacePromise, const InterfacePromiseValue*()); + MOCK_METHOD0(ReturnVoidPromise, script::Handle<script::Promise<void>>()); + MOCK_METHOD0(ReturnBooleanPromise, script::Handle<script::Promise<bool>>()); + MOCK_METHOD0(ReturnStringPromise, + script::Handle<script::Promise<std::string>>()); + MOCK_METHOD0(ReturnInterfacePromise, + script::Handle<script::Promise<scoped_refptr<Wrappable>>>()); MOCK_METHOD0(OnSuccess, void());
diff --git a/src/cobalt/bindings/testing/promise_test.cc b/src/cobalt/bindings/testing/promise_test.cc index 71cc0b1..f40bcd7 100644 --- a/src/cobalt/bindings/testing/promise_test.cc +++ b/src/cobalt/bindings/testing/promise_test.cc
@@ -32,60 +32,73 @@ namespace testing { namespace { + +template <typename T> +using Handle = script::Handle<T>; +template <typename T> +using Promise = script::Promise<T>; + // Simple implementation of window.setTimeout. window.setTimeout is needed for // the Promise polyfill used in SpiderMonkey 24. int32_t SetTimeoutFunction(const Window::TimerCallbackArg& timer_callback, int32_t /* timeout */) { // Just execute immediately. This is sufficient for the tests below. - Window::TimerCallbackArg::StrongReference reference(timer_callback); - reference.value().Run(); + Handle<Window::TimerCallback> handle(timer_callback); + handle->Run(); return 1; } class PromiseTest : public InterfaceBindingsTest<PromiseInterface> { + public: + ~PromiseTest() { + // This is required so that gmock doesn't hold onto the mock handles we + // gave it to return. + ::testing::Mock::VerifyAndClearExpectations(&test_mock()); + } + protected: void SetUp() { window()->SetSetTimeoutHandler(base::Bind(&SetTimeoutFunction)); } }; + } // namespace TEST_F(PromiseTest, ResolveVoidPromise) { - typedef PromiseInterface::VoidPromiseValue VoidPromiseValue; - scoped_ptr<VoidPromiseValue> promise = + Handle<Promise<void>> promise = global_environment_->script_value_factory()->CreateBasicPromise<void>(); - VoidPromiseValue::StrongReference reference(*promise); - EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise.get())); + EXPECT_EQ(promise->State(), script::PromiseState::kPending); + EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise)); EXPECT_TRUE( EvaluateScript("var promise = test.returnVoidPromise();\n" "promise.then(function() { test.onSuccess(); })\n")); EXPECT_CALL(test_mock(), OnSuccess()); - reference.value().Resolve(); + promise->Resolve(); + EXPECT_EQ(promise->State(), script::PromiseState::kFulfilled); } TEST_F(PromiseTest, RejectVoidPromise) { - typedef PromiseInterface::VoidPromiseValue VoidPromiseValue; - scoped_ptr<VoidPromiseValue> promise = + Handle<Promise<void>> promise = global_environment_->script_value_factory()->CreateBasicPromise<void>(); - VoidPromiseValue::StrongReference reference(*promise); - EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise.get())); + EXPECT_EQ(promise->State(), script::PromiseState::kPending); + EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise)); EXPECT_TRUE( EvaluateScript("var promise = test.returnVoidPromise();\n" "promise.catch(function() { test.onSuccess(); })\n")); EXPECT_CALL(test_mock(), OnSuccess()); - reference.value().Reject(); + promise->Reject(); + EXPECT_EQ(promise->State(), script::PromiseState::kRejected); } TEST_F(PromiseTest, RejectWithExceptionObject) { - typedef PromiseInterface::VoidPromiseValue VoidPromiseValue; - scoped_ptr<VoidPromiseValue> promise = + Handle<Promise<void>> promise = global_environment_->script_value_factory()->CreateBasicPromise<void>(); - VoidPromiseValue::StrongReference reference(*promise); - EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise.get())); + EXPECT_EQ(promise->State(), script::PromiseState::kPending); + EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise)); EXPECT_TRUE( EvaluateScript("var promise = test.returnVoidPromise();\n" @@ -99,15 +112,15 @@ scoped_refptr<ExceptionObjectInterface> exception_object( new ExceptionObjectInterface()); EXPECT_CALL(*exception_object, message()).WillOnce(Return("apple")); - reference.value().Reject(exception_object); + promise->Reject(exception_object); + EXPECT_EQ(promise->State(), script::PromiseState::kRejected); } TEST_F(PromiseTest, RejectWithSimpleException) { - typedef PromiseInterface::VoidPromiseValue VoidPromiseValue; - scoped_ptr<VoidPromiseValue> promise = + Handle<Promise<void>> promise = global_environment_->script_value_factory()->CreateBasicPromise<void>(); - VoidPromiseValue::StrongReference reference(*promise); - EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise.get())); + EXPECT_EQ(promise->State(), script::PromiseState::kPending); + EXPECT_CALL(test_mock(), ReturnVoidPromise()).WillOnce(Return(promise)); EXPECT_TRUE( EvaluateScript("var promise = test.returnVoidPromise();\n" @@ -118,16 +131,15 @@ "};\n" "promise.catch(onReject)\n")); EXPECT_CALL(test_mock(), OnSuccess()); - reference.value().Reject(script::kTypeError); + promise->Reject(script::kTypeError); + EXPECT_EQ(promise->State(), script::PromiseState::kRejected); } TEST_F(PromiseTest, BooleanPromise) { - typedef PromiseInterface::BooleanPromiseValue BooleanPromiseValue; - scoped_ptr<BooleanPromiseValue> promise = + Handle<Promise<bool>> promise = global_environment_->script_value_factory()->CreateBasicPromise<bool>(); - BooleanPromiseValue::StrongReference reference(*promise); - EXPECT_CALL(test_mock(), ReturnBooleanPromise()) - .WillOnce(Return(promise.get())); + EXPECT_EQ(promise->State(), script::PromiseState::kPending); + EXPECT_CALL(test_mock(), ReturnBooleanPromise()).WillOnce(Return(promise)); EXPECT_TRUE( EvaluateScript("var promise = test.returnBooleanPromise();\n" @@ -138,17 +150,16 @@ "};\n" "promise.then(onFulfill)\n")); EXPECT_CALL(test_mock(), OnSuccess()); - reference.value().Resolve(true); + promise->Resolve(true); + EXPECT_EQ(promise->State(), script::PromiseState::kFulfilled); } TEST_F(PromiseTest, StringPromise) { - typedef PromiseInterface::StringPromiseValue StringPromiseValue; - scoped_ptr<StringPromiseValue> promise = + Handle<Promise<std::string>> promise = global_environment_->script_value_factory() ->CreateBasicPromise<std::string>(); - StringPromiseValue::StrongReference reference(*promise); - EXPECT_CALL(test_mock(), ReturnStringPromise()) - .WillOnce(Return(promise.get())); + EXPECT_EQ(promise->State(), script::PromiseState::kPending); + EXPECT_CALL(test_mock(), ReturnStringPromise()).WillOnce(Return(promise)); EXPECT_TRUE( EvaluateScript("var promise = test.returnStringPromise();\n" @@ -159,17 +170,16 @@ "};\n" "promise.then(onFulfill)\n")); EXPECT_CALL(test_mock(), OnSuccess()); - reference.value().Resolve("banana"); + promise->Resolve("banana"); + EXPECT_EQ(promise->State(), script::PromiseState::kFulfilled); } TEST_F(PromiseTest, InterfacePromise) { - typedef PromiseInterface::InterfacePromiseValue InterfacePromiseValue; - scoped_ptr<InterfacePromiseValue> promise = + Handle<Promise<scoped_refptr<script::Wrappable>>> promise = global_environment_->script_value_factory() - ->CreateInterfacePromise<scoped_refptr<ArbitraryInterface> >(); - InterfacePromiseValue::StrongReference reference(*promise); - EXPECT_CALL(test_mock(), ReturnInterfacePromise()) - .WillOnce(Return(promise.get())); + ->CreateInterfacePromise<scoped_refptr<ArbitraryInterface>>(); + EXPECT_EQ(promise->State(), script::PromiseState::kPending); + EXPECT_CALL(test_mock(), ReturnInterfacePromise()).WillOnce(Return(promise)); EXPECT_TRUE( EvaluateScript("var promise = test.returnInterfacePromise();\n" @@ -182,7 +192,8 @@ scoped_refptr<ArbitraryInterface> result = new ArbitraryInterface(); EXPECT_CALL(*result.get(), ArbitraryFunction()); EXPECT_CALL(test_mock(), OnSuccess()); - reference.value().Resolve(result); + promise->Resolve(result); + EXPECT_EQ(promise->State(), script::PromiseState::kFulfilled); } } // namespace testing
diff --git a/src/cobalt/bindings/testing/put_forwards_interface.idl b/src/cobalt/bindings/testing/put_forwards_interface.idl index fefacd9..e9b0dc8 100644 --- a/src/cobalt/bindings/testing/put_forwards_interface.idl +++ b/src/cobalt/bindings/testing/put_forwards_interface.idl
@@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + interface PutForwardsInterface { [PutForwards=arbitraryProperty] readonly attribute ArbitraryInterface forwardingAttribute;
diff --git a/src/cobalt/bindings/testing/static_properties_interface.idl b/src/cobalt/bindings/testing/static_properties_interface.idl index 1e6c21f..76bb1d0 100644 --- a/src/cobalt/bindings/testing/static_properties_interface.idl +++ b/src/cobalt/bindings/testing/static_properties_interface.idl
@@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + interface StaticPropertiesInterface { static void staticFunction(); static void staticFunction(long arg);
diff --git a/src/cobalt/bindings/testing/test_dictionary.idl b/src/cobalt/bindings/testing/test_dictionary.idl index 5a0078c..885eb61 100644 --- a/src/cobalt/bindings/testing/test_dictionary.idl +++ b/src/cobalt/bindings/testing/test_dictionary.idl
@@ -1,18 +1,16 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. dictionary TestDictionary { boolean booleanMember;
diff --git a/src/cobalt/bindings/testing/testing.gyp b/src/cobalt/bindings/testing/testing.gyp index 36a470e..b7808dc 100644 --- a/src/cobalt/bindings/testing/testing.gyp +++ b/src/cobalt/bindings/testing/testing.gyp
@@ -37,6 +37,7 @@ 'constants_interface.idl', 'constructor_interface.idl', 'constructor_with_arguments_interface.idl', + 'convert_simple_object_interface.idl', 'derived_getter_setter_interface.idl', 'derived_interface.idl', 'dictionary_interface.idl', @@ -152,6 +153,7 @@ 'conditional_attribute_test.cc', 'constants_bindings_test.cc', 'constructor_bindings_test.cc', + 'convert_simple_object_test.cc', 'date_bindings_test.cc', 'dependent_interface_test.cc', 'dictionary_test.cc', @@ -178,13 +180,11 @@ 'stringifier_bindings_test.cc', 'union_type_bindings_test.cc', 'unsupported_test.cc', - 'user_agent_test.cc', 'variadic_arguments_bindings_test.cc', ], 'defines': [ '<@(bindings_defines)'], 'dependencies': [ '<(DEPTH)/cobalt/base/base.gyp:base', - '<(DEPTH)/cobalt/network/network.gyp:network', '<(DEPTH)/cobalt/script/engine.gyp:engine', '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests', '<(DEPTH)/testing/gmock.gyp:gmock',
diff --git a/src/cobalt/bindings/testing/user_agent_test.cc b/src/cobalt/bindings/testing/user_agent_test.cc deleted file mode 100644 index c977ede..0000000 --- a/src/cobalt/bindings/testing/user_agent_test.cc +++ /dev/null
@@ -1,112 +0,0 @@ -// Copyright 2018 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "cobalt/bindings/testing/bindings_test_base.h" -#include "cobalt/network/user_agent_string_factory.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cobalt { -namespace bindings { -namespace testing { - -class UserAgentBindingsTest : public BindingsTestBase { - public: - bool EvaluateRegex(const std::string& regex, const std::string& str, - std::string* result) { - std::stringstream ss; - ss << "/" << regex << "/g.exec('" << str << "')"; - bool ok = EvaluateScript(ss.str(), result); - return ok && (result->compare("null") != 0); - } - - bool RegexMatches(const std::string& regex, const std::string& str) { - std::string result; - return EvaluateRegex(regex, str, &result); - } - - // This matches a device.py file in Google's internal code repository. - std::vector<std::string> GenUserAgentRegexes() { - std::vector<std::string> output; - output.reserve(14); - const char ua_field_chars[] = "-.A-Za-z0-9\\\\,_/()"; - - std::stringstream ss; - // Note that ?P<NAME> is used by python for named capture groups. Javascript - // does not support named capture groups, so they are commented out. - // Additionally, all model names have been replaced with more general regexes. - // YTTV 2016: Operator_DeviceType_Chipset/Firmware (Brand, Model, Connection) - ss << "("/*?P<device>*/"("/*?P<operator>*/"[" << ua_field_chars << "'/]*)_" - << "("/*?P<type>*/"[" << ua_field_chars << "/]+)_" - << "("/*?P<chipset>*/"[" << ua_field_chars << "/_]+)) ?/ ?" - << "[" << ua_field_chars << "_]* " - << "\\(("/*?P<brand>*/"[" << ua_field_chars << "/_ ]+), ?("/*?P<model>*/"[^,]*), " - << "?[WIREDLSwiredls\\\\/]*\\)"; - output.push_back(ss.str()); - // TTV 2013: Device/Firmware (Brand, Model, Connection) - output.push_back("("/*?P<device>*/"[-_.A-Za-z0-9\\/]+) ?/ ?[-_.A-Za-z0-9\\]* "); - output.push_back("\\(("/*?P<brand>*/"[-_.A-Za-z0-9\\\\/ ]+), ?("/*?P<model>*/"[^,]*), "); - output.push_back("?[WIREDLSwiredls\\\\/]*\\)"); - // YTTV 2012: Vendor Device/Firmware (Model, SKU, Lang, Country) - output.push_back("("/*?P<brand>*/"[-_.A-Za-z0-9\\\\/]+) "); - output.push_back("("/*?P<device>*/"[-_.A-Za-z0-9\\\\]+)/[-_.A-Za-z0-9\\\\/]* \\(("/*?P<model>*/"[^,]*), "); - output.push_back("?[-_.A-Za-z0-9\\\\/]*,[^,]+,[^)]+\\)"); - // (REMOVED) Spec: - output.push_back("("/*?P<device>*/"[a-zA-Z]+)/[0-9.]+ \\([^;]*; ?("/*?P<brand>*/"[^;]*); "); - output.push_back("?("/*?P<model>*/"[^;]*);[^;]*;[^;]*;[^)]*\\)"); - // (REMOVED) 2012: - output.push_back("("/*?P<brand>*/"[G-L]+) Browser/[0-9.]+\\([^;]*; [E-L]+; "); - output.push_back("?("/*?P<model>*/"[^;]*);[^;]*;[^;]*;\\); [G-L]+ ("/*?P<device>*/"[-_.A-Za-z0-9\\\\/]+) "); - // (REMOVED) - // Note: device is more of a firmware version, but model should feasibly always match. - output.push_back("("/*?P<brand>*/"[a-zA-Z]+)/("/*?P<model>*/"\\S+) \\(("/*?P<device>*/"[^)]+)\\)"); - // (REMOVED) - output.push_back("[.A-Za-z0-9\\\\/]+ [a-zA-Z]+/[.0-9]+ ("/*?P<brand>*/"[a-zA-Z]+)[T-V]+/[.0-9]+ "); - output.push_back("model/("/*?P<model>*/"\\S+) ?[-_.A-Za-z0-9\\\\/]* build/[A-Za-z0-9]+"); - return output; - } -}; - -// Simple test to make sure that simple regex's work on this platform. -TEST_F(UserAgentBindingsTest, RegexSanity) { - ASSERT_TRUE(RegexMatches("b+", "b")); - ASSERT_TRUE(RegexMatches("b+", "bb")); - ASSERT_TRUE(RegexMatches("b+", "bbb")); - ASSERT_TRUE(RegexMatches("[ab]+", "ab")); - ASSERT_FALSE(RegexMatches("[ab]+", "c")); - ASSERT_FALSE(RegexMatches("abc", "c")); -} - -// Tests that the platform user agent string will be accepted by one of the -// user agent regexes. -TEST_F(UserAgentBindingsTest, UserAgent) { - using cobalt::network::UserAgentStringFactory; - - std::string user_agent_string = - UserAgentStringFactory::ForCurrentPlatform()->CreateUserAgentString(); - - std::vector<std::string> regexes = GenUserAgentRegexes(); - - bool passes = false; - for (const std::string& regex : regexes) { - passes = RegexMatches(regex, user_agent_string); - if (passes) { - break; - } - } - EXPECT_TRUE(passes); -} - -} // namespace testing -} // namespace bindings -} // namespace cobalt
diff --git a/src/cobalt/bindings/testing/window.idl b/src/cobalt/bindings/testing/window.idl index 2cbfe51..72deede 100644 --- a/src/cobalt/bindings/testing/window.idl +++ b/src/cobalt/bindings/testing/window.idl
@@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + [PrimaryGlobal] interface Window : GlobalInterfaceParent { void windowOperation();
diff --git a/src/cobalt/bindings/v8c/templates/callback-interface.cc.template b/src/cobalt/bindings/v8c/templates/callback-interface.cc.template index eeebf75..32e20d5 100644 --- a/src/cobalt/bindings/v8c/templates/callback-interface.cc.template +++ b/src/cobalt/bindings/v8c/templates/callback-interface.cc.template
@@ -57,9 +57,7 @@ {% endif %} DCHECK(isolate_); - if (this->IsEmpty()) { - goto done; - } + DCHECK(!this->IsEmpty()); { EntryScope entry_scope(isolate_); v8::Local<v8::Context> context = isolate_->GetCurrentContext();
diff --git a/src/cobalt/bindings/v8c/templates/dictionary-conversion.cc.template b/src/cobalt/bindings/v8c/templates/dictionary-conversion.cc.template index c2b9ca1..79a6222 100644 --- a/src/cobalt/bindings/v8c/templates/dictionary-conversion.cc.template +++ b/src/cobalt/bindings/v8c/templates/dictionary-conversion.cc.template
@@ -13,21 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. #} -/* - * Copyright {{today.year}} Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright {{today.year}} Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template b/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template index 2d0da59..cecbd18 100644 --- a/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template +++ b/src/cobalt/bindings/v8c/templates/enumeration-conversion.cc.template
@@ -14,21 +14,19 @@ # limitations under the License. #} -/* - * Copyright {{today.year}} Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright {{today.year}} Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/v8c/templates/generated-types.h.template b/src/cobalt/bindings/v8c/templates/generated-types.h.template index 502e00c..a5cd49e 100644 --- a/src/cobalt/bindings/v8c/templates/generated-types.h.template +++ b/src/cobalt/bindings/v8c/templates/generated-types.h.template
@@ -14,21 +14,19 @@ # limitations under the License. #} -/* - * Copyright {{today.year}} Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright {{today.year}} Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // clang-format off
diff --git a/src/cobalt/bindings/v8c/templates/interface.cc.template b/src/cobalt/bindings/v8c/templates/interface.cc.template index 24464b7..6f3a9de 100644 --- a/src/cobalt/bindings/v8c/templates/interface.cc.template +++ b/src/cobalt/bindings/v8c/templates/interface.cc.template
@@ -40,6 +40,7 @@ #include "cobalt/script/v8c/type_traits.h" #include "cobalt/script/v8c/v8c_callback_function.h" #include "cobalt/script/v8c/v8c_callback_interface_holder.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_exception_state.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/v8c_property_enumerator.h" @@ -102,7 +103,7 @@ v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {{ nonstatic_function_prologue(impl_class) }} std::string property_name = *v8::String::Utf8Value(isolate, property); if (!impl->CanQueryNamedProperty(property_name)) { @@ -119,30 +120,60 @@ DCHECK(!exception_state.is_exception_set()); } +{% if not indexed_property_getter %} +void IndexedPropertyGetterCallback( + uint32_t index, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); + NamedPropertyGetterCallback(as_string, info); +} +{% endif %} + void NamedPropertyQueryCallback( v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {{ get_impl_class_instance(impl_class) }} std::string property_name = *v8::String::Utf8Value(isolate, property); bool result = impl->CanQueryNamedProperty(property_name); if (!result) { return; } + // https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty + int properties = v8::None; // 2.7. If |O| implements an interface with a named property setter, then set // desc.[[Writable]] to true, otherwise set it to false. +{% if named_property_setter %} +{% else %} + properties |= v8::ReadOnly; +{% endif %} // 2.8. If |O| implements an interface with the // [LegacyUnenumerableNamedProperties] extended attribute, then set // desc.[[Enumerable]] to false, otherwise set it to true. - info.GetReturnValue().Set(v8::DontEnum | v8::ReadOnly); +{% if has_legacy_unenumerable_named_properties %} + // TODO: Note that this is never true at the moment, as Cobalt's IDLs and + // IDL compiler do not support this. + properties |= v8::DontEnum; +{% endif %} + + info.GetReturnValue().Set(properties); } +{% if not indexed_property_getter %} +void IndexedPropertyDescriptorCallback( + uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { + // TODO: Figure out under what conditions this gets called. It's not + // getting called in our tests. + NOTIMPLEMENTED(); +} +{% endif %} + void NamedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {{ get_impl_class_instance(impl_class) }} v8::Local<v8::Array> array = v8::Array::New(isolate); V8cPropertyEnumerator property_enumerator(isolate, &array); @@ -158,7 +189,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {{ nonstatic_function_prologue(impl_class) }} std::string property_name = *v8::String::Utf8Value(isolate, property); TypeTraits<{{named_property_setter.type}}>::ConversionType native_value; @@ -171,8 +202,20 @@ named_property_setter.name, ["property_name", "native_value"], named_property_setter.raises_exception, named_property_setter.call_with) }} + info.GetReturnValue().Set(value); DCHECK(!exception_state.is_exception_set()); } + +{% if not indexed_property_setter %} +void IndexedPropertySetterCallback( + uint32_t index, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); + NamedPropertySetterCallback(as_string, value, info); +} +{% endif %} + {% endif %} {% if named_property_deleter %} @@ -180,7 +223,7 @@ v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {{ nonstatic_function_prologue(impl_class) }} std::string property_name = *v8::String::Utf8Value(isolate, property); if (!impl->CanQueryNamedProperty(property_name)) { @@ -190,8 +233,22 @@ named_property_deleter.name, ["property_name"], named_property_deleter.raises_exception, named_property_deleter.call_with) }} - DCHECK(!exception_state.is_exception_set()); + if (exception_state.is_exception_set()) { + return; + } + info.GetReturnValue().Set(true); } + +{% if not indexed_property_deleter %} +void IndexedPropertyDeleterCallback( + uint32_t index, + const v8::PropertyCallbackInfo<v8::Boolean>& info) { + v8::Isolate* isolate = info.GetIsolate(); + v8::Local<v8::String> as_string = v8::Integer::New(info.GetIsolate(), index)->ToString(); + NamedPropertyDeleterCallback(as_string, info); +} +{% endif %} + {% endif %} {% if indexed_property_getter %} @@ -200,7 +257,7 @@ uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {{ nonstatic_function_prologue(impl_class) }} if (index >= impl->length()) { // |index| is out of bounds, so return undefined. @@ -223,7 +280,7 @@ void IndexedPropertyEnumeratorCallback( const v8::PropertyCallbackInfo<v8::Array>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {{ get_impl_class_instance(impl_class) }} const uint32_t length = impl->length(); v8::Local<v8::Array> array = v8::Array::New(isolate, length); @@ -249,7 +306,7 @@ v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {{ nonstatic_function_prologue(impl_class) }} if (index >= impl->length()) { return; @@ -276,7 +333,7 @@ uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {{ nonstatic_function_prologue(impl_class) }} if (index >= impl->length()) { return; @@ -310,6 +367,15 @@ {{ overload_resolution_implementation(constructor, "Constructor")}} {% endif %} } + +{% else %} + +void DummyConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) { + V8cExceptionState exception(info.GetIsolate()); + exception.SetSimpleException( + script::kTypeError, "{{interface_name}} is not constructible."); +} + {% endif %} {% for attribute in attributes + static_attributes %} @@ -320,14 +386,10 @@ {% if attribute.is_constructor_attribute %} // Nothing for {{attribute}}. We will just give them the v8::FunctionTemplate. {% else %} -{% if attribute.is_static %} -void {{attribute.idl_name}}StaticAttributeGetter( -{% else %} void {{attribute.idl_name}}AttributeGetter( -{% endif %} - v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {% if attribute.is_static %} {{ static_function_prologue() -}} @@ -337,26 +399,23 @@ {{ check_if_object_implements_interface() }} {{ nonstatic_function_prologue(impl_class) }} {% endif %} + {{ call_cobalt_function(impl_class, attribute.type, attribute.getter_function_name, [], attribute.raises_exception, attribute.call_with, attribute.is_static) }} - if (!exception_state.is_exception_set()) { - info.GetReturnValue().Set(result_value); + if (exception_state.is_exception_set()) { + return; } + info.GetReturnValue().Set(result_value); } {% if attribute.has_setter %} -{% if attribute.is_static %} -void {{attribute.idl_name}}StaticAttributeSetter( -{% else %} void {{attribute.idl_name}}AttributeSetter( -{% endif %} - v8::Local<v8::String> property, - v8::Local<v8::Value> v8_value, - const v8::PropertyCallbackInfo<void>& info) { + const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); + v8::Local<v8::Value> v8_value = info[0]; {% if attribute.is_static %} {{ static_function_prologue() }} @@ -401,10 +460,9 @@ {% endfor %} {% if stringifier %} -void Stringifier(v8::Local<v8::String> property, - const v8::PropertyCallbackInfo<v8::Value>& info) { +void Stringifier(const v8::FunctionCallbackInfo<v8::Value>& info) { v8::Isolate* isolate = info.GetIsolate(); - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); V8cExceptionState exception_state(isolate); {{ check_if_object_implements_interface() }} @@ -471,7 +529,7 @@ v8::Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New( isolate, - nullptr, + DummyConstructor, v8::Local<v8::Value>(), v8::Local<v8::Signature>(), 0); @@ -583,8 +641,15 @@ // // S is the attribute setter created given the attribute, the interface, and // the relevant Realm of the object that is the location of the property. - {% if not attribute.is_constructor_attribute %} + v8::Local<v8::FunctionTemplate> getter = + v8::FunctionTemplate::New(isolate, {{attribute.idl_name}}AttributeGetter); +{% if attribute.has_setter %} + v8::Local<v8::FunctionTemplate> setter = + v8::FunctionTemplate::New(isolate, {{attribute.idl_name}}AttributeSetter); +{% else %} + v8::Local<v8::FunctionTemplate> setter; +{% endif %} // The location of the property is determined as follows: {% if attribute.is_static %} @@ -594,48 +659,22 @@ // If the attribute is a static attribute, then there is a single // corresponding property and it exists on the interface's interface object. - function_template->SetNativeDataProperty( - name, - {{attribute.idl_name}}StaticAttributeGetter, -{% if attribute.has_setter %} - {{attribute.idl_name}}StaticAttributeSetter, -{% else %} - 0 -{% endif %} - v8::Local<v8::Value>(), - attributes); - + function_template-> {% elif attribute.is_unforgeable or is_global_interface %} // Otherwise, if the attribute is unforgeable on the interface or if the // interface was declared with the [Global] extended attribute, then the // property exists on every object that implements the interface. - instance_template->SetAccessor( - name, - {{attribute.idl_name}}AttributeGetter, -{% if attribute.has_setter %} - {{attribute.idl_name}}AttributeSetter, -{% else %} - 0, -{% endif %} - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); - + instance_template-> {% else %} // Otherwise, the property exists solely on the interface's interface // prototype object. - prototype_template->SetAccessor( - name, - {{attribute.idl_name}}AttributeGetter, -{% if attribute.has_setter %} - {{attribute.idl_name}}AttributeSetter, -{% else %} - 0, + prototype_template-> {% endif %} - v8::Local<v8::Value>(), - v8::DEFAULT, - attributes); -{% endif %} + SetAccessorProperty( + name, + getter, + setter, + attributes); {% else %} {#- not attribute.is_constructor_attribute #} { @@ -687,41 +726,27 @@ v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>( B ? v8::None : (v8::ReadOnly | v8::DontDelete)); + v8::Local<v8::FunctionTemplate> method_template = + v8::FunctionTemplate::New(isolate, {{operation.idl_name}}{{"Static" if operation.is_static else ""}}Method); + method_template->RemovePrototype(); + method_template->SetLength({{operation.length}}); + // The location of the property is determined as follows: {% if operation.is_static %} // If the operation is static, then the property exists on the interface // object. - v8::Local<v8::FunctionTemplate> method_template = - v8::FunctionTemplate::New(isolate, {{operation.idl_name}}StaticMethod); - method_template->RemovePrototype(); - method_template->SetLength({{operation.length}}); - function_template->Set( - NewInternalString(isolate, "{{operation.idl_name}}"), - method_template); - + function_template-> {% elif operation.is_unforgeable or is_global_interface %} // Otherwise, if the operation is unforgeable on the interface or if the // interface was declared with the [Global] extended attribute, then the // property exists on every object that implements the interface. - v8::Local<v8::FunctionTemplate> method_template = - v8::FunctionTemplate::New(isolate, {{operation.idl_name}}Method); - method_template->RemovePrototype(); - method_template->SetLength({{operation.length}}); - instance_template->Set( - NewInternalString(isolate, "{{operation.idl_name}}"), - method_template); - + instance_template-> {% else %} // Otherwise, the property exists solely on the interface's interface // prototype object. - v8::Local<v8::FunctionTemplate> method_template = - v8::FunctionTemplate::New(isolate, {{operation.idl_name}}Method); - method_template->RemovePrototype(); - method_template->SetLength({{operation.length}}); - prototype_template->Set( - NewInternalString(isolate, "{{operation.idl_name}}"), - method_template); + prototype_template-> {% endif %} + Set(name, method_template); // The value of the property is the result of creating an operation function // given the operation, the interface, and the relevant Realm of the object @@ -744,6 +769,16 @@ NewInternalString(isolate, "{{interface_name}}"), static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)); +{% if stringifier %} + { + v8::Local<v8::String> name = NewInternalString(isolate, "toString"); + v8::Local<v8::FunctionTemplate> method_template = v8::FunctionTemplate::New(isolate, Stringifier); + prototype_template->Set( + NewInternalString(isolate, "toString"), + method_template); + } +{% endif %} + {% if named_property_getter %} { v8::NamedPropertyHandlerConfiguration named_property_handler_configuration = { @@ -759,6 +794,20 @@ } {% endif %} +{% if named_property_getter and not indexed_property_getter %} + { + v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { + IndexedPropertyGetterCallback, + {{ "IndexedPropertySetterCallback" if named_property_setter else "nullptr" }}, + IndexedPropertyDescriptorCallback, + {{ "IndexedPropertyDeleterCallback" if named_property_deleter else "nullptr" }}, + nullptr, + nullptr + }; + instance_template->SetHandler(indexed_property_handler_configuration); + } +{% endif %} + {% if indexed_property_getter %} { v8::IndexedPropertyHandlerConfiguration indexed_property_handler_configuration = { @@ -840,6 +889,8 @@ context_.Reset(isolate_, context); v8::Context::Scope context_scope(context); + global_wrappable_ = global_interface; + DCHECK(!environment_settings_); DCHECK(environment_settings); environment_settings_ = environment_settings; @@ -863,6 +914,7 @@ #endif // defined({{interface.conditional}}) {% endif %} {% endfor %} + } } // namespace v8c
diff --git a/src/cobalt/bindings/v8c/templates/macros.cc.template b/src/cobalt/bindings/v8c/templates/macros.cc.template index f7f435b..cfd19e5 100644 --- a/src/cobalt/bindings/v8c/templates/macros.cc.template +++ b/src/cobalt/bindings/v8c/templates/macros.cc.template
@@ -20,8 +20,8 @@ {% macro check_if_object_implements_interface() %} V8cGlobalEnvironment* global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate); WrapperFactory* wrapper_factory = global_environment->wrapper_factory(); - if (!wrapper_factory->DoesObjectImplementInterface( - object, base::GetTypeId<{{impl_class}}>())) { + if (!WrapperPrivate::HasWrapperPrivate(object) || + !{{binding_class}}::GetTemplate(isolate)->HasInstance(object)) { V8cExceptionState exception(isolate); exception.SetSimpleException(script::kDoesNotImplementInterface); return; @@ -38,7 +38,7 @@ {% if operation.is_static %} {{ static_function_prologue() }} {% else %} - v8::Local<v8::Object> object = info.This(); + v8::Local<v8::Object> object = info.Holder(); {{ check_if_object_implements_interface() }} {{ nonstatic_function_prologue(impl_class) }} {% endif %}
diff --git a/src/cobalt/black_box_tests/README.md b/src/cobalt/black_box_tests/README.md new file mode 100644 index 0000000..335028f --- /dev/null +++ b/src/cobalt/black_box_tests/README.md
@@ -0,0 +1,80 @@ +# Cobalt Black Box Test Framework + +## Overview + +Black Box Test is a place to put End-To-End tests of the Cobalt process. Black +Box Tests can launch, send signals to, and terminate Cobalt using platforms' +app launcher. Black Box Tests can also control Cobalt using JavaScript or +webdriver and can be used to test multiple restarts of the Cobalt process and +data that persists between them. They can also be used to test Cobalt's +interaction with different web server behavior. + +### Some examples of things you can test using the Black Box Tests: + 1. Persistent Cookies between consecutive Cobalt sessions. + 2. Basic functionalities after resuming from suspend state. + 3. Re-fetch network requests after network shortage. + + +## Running Tests: + + 1. To run all tests: + + $ python path/to/black_box_tests.py --platform PLATFORM --config CONFIG + + e.g. + $ python path/to/black_box_tests.py --platform linux-x64x11 --config + devel + + + 2. To run an individual test: + + $ python path/to/black_box_tests.py --platform PLATFORM --config CONFIG + --test_name TEST_NAME + + e.g. + $ python path/to/black_box_tests.py --platform linux-x64x11 --config devel + --test_name preload_font + + +## Tests + +Each script in tests/ includes one python unittest test case. +In a test script, you can perform any webdriver operations on Cobalt through +BlackBoxCobaltRunner class. + + +## BlackBoxCobaltRunner + +A wrapper around the app launcher. BlackBoxCobaltRunner includes a webdriver +module attached to the app launcher's Cobalt instance after it starts running. +Includes a method(HTMLTestsSucceeded()) to check test result on the JavaScript +side. Call this method to wait for JavaScript test result. +black_box_test_js_util.js provides some utility functions that are meant to +work with runner.HTMLTestsSucceeded() in the python test scripts. Together, +they allow for test logic to exist in either the python test scripts or +JavaScript test data. +e.g. Call OnEndTest() to signal test completion in the JavaScripts, +HTMLTestsSucceeded() will react to the signal and return the test status of +JavaScript test logic. + + +## Test Data + +A default local test server will be launcher before any unit test starts to +serve the test data in black_box_tests/testdata/. The server's port will be +passed to the app launcher to fetch test data from. +Test data can include target web page for Cobalt to open and any additional +resource(font file, JavaScripts...). +Tests are free to start their own HTTP servers if the default test server is +inadequate(e.g. The test is testing that Cobalt handles receipt of a specific +HTTP server generated error code properly). + + +## Adding a New Test + + 1. Add a python test script in tests/. + 2. Add target web page(s) and associated resources(if any) to testdata/. + 3. Add the test name(name of the python test script) to black_box_tests.py + to automate new test. Add the name to either the list of tests requiring + app launcher support for system signals(e.g. suspend/resume), or the list + of tests that don't.
diff --git a/src/v8/tools/release/testdata/v8/base/trace_event/common/common b/src/cobalt/black_box_tests/__init__.py similarity index 100% copy from src/v8/tools/release/testdata/v8/base/trace_event/common/common copy to src/cobalt/black_box_tests/__init__.py
diff --git a/src/cobalt/black_box_tests/_env.py b/src/cobalt/black_box_tests/_env.py new file mode 100644 index 0000000..6188bb7 --- /dev/null +++ b/src/cobalt/black_box_tests/_env.py
@@ -0,0 +1,26 @@ +# +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""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/black_box_tests/black_box_cobalt_runner.py b/src/cobalt/black_box_tests/black_box_cobalt_runner.py new file mode 100644 index 0000000..97f838f --- /dev/null +++ b/src/cobalt/black_box_tests/black_box_cobalt_runner.py
@@ -0,0 +1,30 @@ +"""The base class for black box tests.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import _env # pylint: disable=unused-import +from cobalt.tools.automated_testing import cobalt_runner + +# The following constants and logics are shared between this module and +# the JavaScript test environment. Anyone making changes here should also +# ensure necessary changes are made to testdata/black_box_js_test_utils.js +_TEST_STATUS_ELEMENT_NAME = 'black_box_test_status' +_JS_TEST_SUCCESS_MESSAGE = 'JavaScript_test_succeeded' +_JS_TEST_SETUP_DONE_MESSAGE = 'JavaScript_setup_done' + + +class BlackBoxCobaltRunner(cobalt_runner.CobaltRunner): + + def JSTestsSucceeded(self): + """Check test assertions in HTML page.""" + + self.PollUntilFound('[' + _TEST_STATUS_ELEMENT_NAME + ']') + body_element = self.UniqueFind('body') + return body_element.get_attribute( + _TEST_STATUS_ELEMENT_NAME) == _JS_TEST_SUCCESS_MESSAGE + + def WaitForJSTestsSetup(self): + """Poll setup status until JavaScript gives green light.""" + self.PollUntilFound('#{}'.format(_JS_TEST_SETUP_DONE_MESSAGE))
diff --git a/src/cobalt/black_box_tests/black_box_tests.py b/src/cobalt/black_box_tests/black_box_tests.py new file mode 100644 index 0000000..90171b9 --- /dev/null +++ b/src/cobalt/black_box_tests/black_box_tests.py
@@ -0,0 +1,168 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse +import importlib +import logging +import os +import socket +import subprocess +import sys +import unittest + +import _env # pylint: disable=unused-import +from cobalt.black_box_tests import black_box_cobalt_runner +from cobalt.tools.automated_testing import cobalt_runner +from starboard.tools import abstract_launcher + +_SERVER_EXIT_TIMEOUT_SECONDS = 30 +# These tests can only be run on platforms whose app launcher can send suspend/ +# resume signals. +_TESTS_NEEDING_SYSTEM_SIGNAL = [ + 'preload_font', + 'timer_hit_in_preload', + 'timer_hit_after_preload', + 'preload_visibility', + 'suspend_visibility', +] +# These tests only need app launchers with webdriver. +_TESTS_NO_SIGNAL = [ + # TODO: Re-enable cookie test after its flakiness is resovled. + # 'persistent_cookie', + 'allow_eval', + 'disable_eval_with_csp', +] +# Port number of the HTTP server serving test data. +_DEFAULT_TEST_DATA_SERVER_PORT = 8000 +# Location of test files. +_TEST_DIR_PATH = 'cobalt.black_box_tests.tests.' +# Platform dependent device parameters. +_device_params = None + + +def GetDeviceParams(): + + global _device_params + _device_params = cobalt_runner.GetDeviceParamsFromCommandLine() + # Keep other modules from seeing these args + sys.argv = sys.argv[:1] + + +def GetDefaultBlackBoxTestDataAddress(): + """Gets the ip address with port for the server hosting test data.""" + # We are careful to choose this method that allows external device to connect + # to the host running the web server hosting test data. + address_pack_list = socket.getaddrinfo(socket.gethostname(), + _DEFAULT_TEST_DATA_SERVER_PORT) + first_address_pack = address_pack_list[0] + ip_address, port = first_address_pack[4] + return 'http://{}:{}/'.format(ip_address, port) + + +class BlackBoxTestCase(unittest.TestCase): + + def __init__(self, *args, **kwargs): + super(BlackBoxTestCase, self).__init__(*args, **kwargs) + + @classmethod + def setUpClass(cls): + print('Running ' + cls.__name__) + + @classmethod + def tearDownClass(cls): + print('Done ' + cls.__name__) + + def GetURL(self, file_name): + return GetDefaultBlackBoxTestDataAddress() + file_name + + def CreateCobaltRunner(self, url, target_params=None): + new_runner = black_box_cobalt_runner.BlackBoxCobaltRunner( + device_params=_device_params, url=url, target_params=target_params) + return new_runner + + +def LoadTests(platform, config): + + launcher = abstract_launcher.LauncherFactory( + platform, + 'cobalt', + config, + device_id=None, + target_params=None, + output_file=None, + out_directory=None) + + if launcher.SupportsSuspendResume(): + test_targets = _TESTS_NEEDING_SYSTEM_SIGNAL + _TESTS_NO_SIGNAL + else: + test_targets = _TESTS_NO_SIGNAL + + test_suite = unittest.TestSuite() + for test in test_targets: + test_suite.addTest(unittest.TestLoader().loadTestsFromModule( + importlib.import_module(_TEST_DIR_PATH + test))) + return test_suite + + +class BlackBoxTests(object): + """Helper class to run all black box tests and return results.""" + + def __init__(self, test_name=None): + + self.test_name = test_name + + def Run(self): + + self._StartTestdataServer() + logging.basicConfig(level=logging.DEBUG) + GetDeviceParams() + if self.test_name: + suite = unittest.TestLoader().loadTestsFromModule( + importlib.import_module(_TEST_DIR_PATH + self.test_name)) + else: + suite = LoadTests(_device_params.platform, _device_params.config) + return_code = not unittest.TextTestRunner( + verbosity=0, stream=sys.stdout).run(suite).wasSuccessful() + self._KillTestdataServer() + return return_code + + def _StartTestdataServer(self): + """Start a local server to serve test data.""" + # Some tests like preload_font requires server feature support. + # Using HTTP URL instead of file URL also saves the trouble to + # deploy test data to device. + self.default_test_data_server_process = subprocess.Popen( + [ + 'python', '-m', 'SimpleHTTPServer', + '{}'.format(_DEFAULT_TEST_DATA_SERVER_PORT) + ], + cwd=os.path.join( + os.path.dirname(os.path.realpath(__file__)), 'testdata'), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + print('Starting HTTP server on port: {}'.format( + _DEFAULT_TEST_DATA_SERVER_PORT)) + + def _KillTestdataServer(self): + """Exit black_box_test_runner with test result.""" + self.default_test_data_server_process.kill() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--test_name') + args, _ = parser.parse_known_args() + + test_object = BlackBoxTests(args.test_name) + sys.exit(test_object.Run()) + + +if __name__ == '__main__': + # Running this script on the command line and importing this file are + # different and create two modules. + # Import this module to ensure we are using the same module as the tests to + # make module-owned variables like device_param accessible to the tests. + main_module = importlib.import_module( + 'cobalt.black_box_tests.black_box_tests') + main_module.main()
diff --git a/src/cobalt/black_box_tests/testdata/allow_eval.html b/src/cobalt/black_box_tests/testdata/allow_eval.html new file mode 100644 index 0000000..f0eeab3 --- /dev/null +++ b/src/cobalt/black_box_tests/testdata/allow_eval.html
@@ -0,0 +1,18 @@ +<!DOCTYPE html> + +<head> + <title>Cobalt eval() allowed when missing csp test</title> + <script src='black_box_js_test_utils.js'></script> +</head> + +<body> + <h1> + <span id="unique_id">ID element</span> + </h1> + <script> + // When Content Security Policy is missing, JavaScript eval() should be + // allowed. + assertEqual(4, eval('1+3')); + onEndTest(); + </script> +</body> \ No newline at end of file
diff --git a/src/cobalt/black_box_tests/testdata/black_box_js_test_utils.js b/src/cobalt/black_box_tests/testdata/black_box_js_test_utils.js new file mode 100644 index 0000000..a68f498 --- /dev/null +++ b/src/cobalt/black_box_tests/testdata/black_box_js_test_utils.js
@@ -0,0 +1,83 @@ +// This file provides JavaScript-side environment and test utility functions. +// The foolowing constants and logics are used in communication with the python +// tests. Anyone making changes here should also ensure corresponding changes +// are made in black_box_cobalt_runner.py. + +const TEST_STATUS_ELEMENT_NAME = 'black_box_test_status'; +const SUCCESS_MESSAGE = 'JavaScript_test_succeeded'; +const FAILURE_MESSAGE = 'JavaScript_test_failed'; +const SETUP_DONE_MESSAGE = 'JavaScript_setup_done'; +const EFFECT_AFTER_VISIBILITY_CHANGE_TIMEOUT_SECONDS = 5; + +function notReached() { + document.body.setAttribute(TEST_STATUS_ELEMENT_NAME, FAILURE_MESSAGE); +} + +function assertTrue(result) { + if (!result){ + notReached(); + } +} + +function assertFalse(result) { + if (result){ + notReached(); + } +} + +function assertEqual(expected, result) { + if (expected !== result) { + console.log('\n' + + 'Black Box Test Equal Test Assertion failed: \n' + + 'expected: ' + expected + '\n' + + 'but got: ' + result); + notReached(); + } +} + +function assertNotEqual(expected, result) { + if (expected === result) { + console.log('\n' + + 'Black Box Test Unequal Assertion failed: \n' + + 'both are: ' + expected); + notReached(); + } +} + +function onEndTest() { + if (document.body.getAttribute(TEST_STATUS_ELEMENT_NAME) === FAILURE_MESSAGE) { + return; + } + document.body.setAttribute(TEST_STATUS_ELEMENT_NAME, SUCCESS_MESSAGE); +} + + +class TimerTestCase { + constructor(name, ExpectedCallTimes) { + this.name = name; + this.ExpectedCallTimes = ExpectedCallTimes; + this.times = 0; + } + called() { + this.times++; + } + verify(InputExpectedTimes = -1) { + let ExpectedCallTimes = this.ExpectedCallTimes + if (InputExpectedTimes >= 0) { + ExpectedCallTimes = InputExpectedTimes + } + if (this.times !== ExpectedCallTimes) { + console.log("Test Error: " + this.name + " is called " + this.times + + " times, expected: " + ExpectedCallTimes); + assertEqual(ExpectedCallTimes, this.times) + } + } +}; + +function setupFinished() { + // Append an element to tell python test scripts that setup steps are done + // on the JavaScript side. + let span_ele = document.createElement('span'); + span_ele.id = SETUP_DONE_MESSAGE; + document.appendChild(span_ele); +} \ No newline at end of file
diff --git a/src/cobalt/black_box_tests/testdata/disable_eval_with_csp.html b/src/cobalt/black_box_tests/testdata/disable_eval_with_csp.html new file mode 100644 index 0000000..402e2c3 --- /dev/null +++ b/src/cobalt/black_box_tests/testdata/disable_eval_with_csp.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> + +<head> + <title>Disable eval when serving CSP test</title> + <script src='black_box_js_test_utils.js'></script> + <meta http-equiv="Content-Security-Policy" content="default-src 'self'; + script-src 'unsafe-inline';" /> +</head> + +<body> + <h1> + <span id="unique_id">ID element</span> + </h1> + <script> + document.addEventListener("securitypolicyviolation", (e) => { + onEndTest(); + }); + eval('1+3') + + </script> +</body> \ No newline at end of file
diff --git a/src/cobalt/black_box_tests/testdata/persistent_cookie.html b/src/cobalt/black_box_tests/testdata/persistent_cookie.html new file mode 100644 index 0000000..25cec14 --- /dev/null +++ b/src/cobalt/black_box_tests/testdata/persistent_cookie.html
@@ -0,0 +1,76 @@ +<!DOCTYPE html> + +<head> + <title>Test cookies between sessions.</title> + <style> + h1 { + color: #c50000; + font-size: 6em; + } + </style> + <script src='black_box_js_test_utils.js'></script> +</head> + +<body> + <h1> + <span id="unique_id">Test cookies between sessions.</span> + </h1> + <script> + document.body.style.backgroundColor = "green"; + const persistent_cookie = "persistent_cookie_status=exists"; + const persistent_cookie_expire_time = "; expires=Fri, 18 Dec 2023 12:00:00 UTC"; + const very_old_date = "; expires=Fri, 18 Dec 2003 12:00:00 UTC"; + + function firstRun() { + console.log( + "First run, verifying basic functionalities and write persistent cookie"); + // If the previous run failed, persistent cookie might not be deleted. + // Clear it to avoid affecting this test run. + document.cookie = persistent_cookie + very_old_date; + // Verify basic cookie functionalities work before testing persistent cookie. + const cookie_1 = "test_content=exist"; + const cookie_2 = "second_test_content=still_exist"; + const cookie_1_change = "test_content=changed"; + + document.cookie = cookie_1; + assertEqual(cookie_1, document.cookie); + document.cookie = cookie_2; + assertEqual(cookie_1 + '; ' + cookie_2, document.cookie); + document.cookie = cookie_1_change; + assertEqual(cookie_2 + '; ' + cookie_1_change, document.cookie); + document.cookie = cookie_1 + very_old_date; + assertEqual(cookie_2, document.cookie); + // End of basic functionality tests. + + // Add persistent cookie that's supposed to last till next opening. + document.cookie = persistent_cookie + persistent_cookie_expire_time; + assertEqual(cookie_2 + '; ' + persistent_cookie, document.cookie); + } + + function secondRun() { + console.log("Second run: Verifying persistent cookie is written."); + assertEqual(persistent_cookie, document.cookie); + document.cookie = persistent_cookie + very_old_date; + assertEqual('', document.cookie); + } + + function thirdRun() { + console.log("Third run: verifying persistent cookie is wiped."); + assertEqual('', document.cookie); + } + + window.onkeydown = function(event) { + if (event.keyCode === 97) { + firstRun(); + } else if (event.keyCode === 98) { + secondRun(); + } else if (event.keyCode === 99) { + thirdRun(); + } + // Flush local storage to ensure cookie is written before closing. + h5vcc.storage.flush(); + onEndTest(); + } + setupFinished(); + </script> +</body> \ No newline at end of file
diff --git a/src/cobalt/black_box_tests/testdata/preload_font.html b/src/cobalt/black_box_tests/testdata/preload_font.html new file mode 100644 index 0000000..180af0e --- /dev/null +++ b/src/cobalt/black_box_tests/testdata/preload_font.html
@@ -0,0 +1,47 @@ +<!DOCTYPE html> + +<head> + <title>Font Loading After Preload Test</title> + <style> + @font-face { + font-family: networkfont; + src: url('test_font.ttf'); + } + + h1 { + font-family: networkfont; + color: #c50000; + } + </style> + <script src='black_box_js_test_utils.js'></script> +</head> + +<body> + <h1> + <span id="unique_id">ID element</span> + </h1> + <script> + // 80.34375 is the size of "unique_id" in the designated web font. + assertNotEqual(80.34375, + document.getElementsByTagName('span').item(0).offsetWidth); + + function handleVisibilityChange() { + // Visibility Change happens when Cobalt receives resume signal, check font every .1 sec. + const font_loading_time_maximum_in_milliseconds = 1000; + let time_elapsed = 0; + let set_interval_id = setInterval(() => { + if (80.34375 === document.getElementsByTagName('span').item(0).offsetWidth) { + onEndTest(); + clearInterval(set_interval_id); + } else if (time_elapsed >= font_loading_time_maximum_in_milliseconds) { + notReached(); + clearInterval(set_interval_id); + } else { + time_elapsed += 100; + } + }, 100); + } + document.addEventListener("visibilitychange", handleVisibilityChange); + setupFinished(); + </script> +</body> \ No newline at end of file
diff --git a/src/cobalt/black_box_tests/testdata/preload_visibility.html b/src/cobalt/black_box_tests/testdata/preload_visibility.html new file mode 100644 index 0000000..19f2ef2 --- /dev/null +++ b/src/cobalt/black_box_tests/testdata/preload_visibility.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> + +<head> + <title>Cobalt preload state visibility test</title> + <script src='black_box_js_test_utils.js'></script> +</head> + +<body> + <h1> + <span id="unique_id">ID element</span> + </h1> + <script> + // In preload mode, visibility should be "prerender" and window/document + // should not have focus. + assertEqual("prerender", document.visibilityState); + assertFalse(document.hasFocus()); + + // Wait for visibility change to verify visibilityState and having focus. + function handleVisibilityChange() { + assertEqual("visible", document.visibilityState); + assertTrue(document.hasFocus()); + onEndTest(); + } + document.addEventListener("visibilitychange", handleVisibilityChange); + setupFinished(); + </script> +</body> \ No newline at end of file
diff --git a/src/cobalt/black_box_tests/testdata/suspend_visibility.html b/src/cobalt/black_box_tests/testdata/suspend_visibility.html new file mode 100644 index 0000000..50c9197 --- /dev/null +++ b/src/cobalt/black_box_tests/testdata/suspend_visibility.html
@@ -0,0 +1,77 @@ +<!DOCTYPE html> + +<head> + <title>Cobalt suspend state visibility test</title> + <script src='black_box_js_test_utils.js'></script> +</head> + +<body> + <h1> + <span id="unique_id">ID element</span> + </h1> + <script> + // In started mode, visibility should be "visible" and window/document + // should have focus. + assertEqual("visible", document.visibilityState); + assertTrue(document.hasFocus()); + + let suspendStateVerified = false; + // Success callback for verifying visibility and focus in suspend. + function intoSuspendCallback() { + suspendStateVerified = true; + } + + // Success callback for verifying visibility and focus after suspend in + // started. + function outOfSuspendCallback() { + if (suspendStateVerified){ + onEndTest(); + } else { + // By now, it is in Started state and Suspend state behavior + // verification will never secceed. + notReached(); + } + } + + function verifyVisibilityAndFocusInterval(shouldBeVisible, shouldHaveFocus) { + visibility_str = shouldBeVisible ? "visible" : "hidden"; + if (document.hasFocus() === shouldHaveFocus && document.visibilityState === visibility_str) { + return true; + } + } + + // Post periodic timer to check desired visibility and focus settings. + function verifyVisibilityAndFocus(shouldBeVisible, shouldHaveFocus, successCallback) { + if (verifyVisibilityAndFocusInterval(shouldBeVisible, shouldHaveFocus)) { + successCallback(); + return true; + } + time_elapsed = 0; + set_interval_id = setInterval(() => { + if (verifyVisibilityAndFocusInterval(shouldBeVisible, shouldHaveFocus)){ + successCallback() + clearInterval(set_interval_id); + }else { + if (time_elapsed > EFFECT_AFTER_VISIBILITY_CHANGE_TIMEOUT_SECONDS){ + console.log("Waiting for visibilityState and focus change timed out") + notReached(); + } + time_elapsed += 0.1; + } + }, 50); + } + + // Wait for visibility change to verify visibilityState and focus status. + let visibilityChangeCalledBefore = false; + function handleVisibilityChange() { + if (!visibilityChangeCalledBefore) { + verifyVisibilityAndFocus(false, false, intoSuspendCallback); + visibilityChangeCalledBefore = true; + } else { + verifyVisibilityAndFocus(true, true, outOfSuspendCallback); + } + } + document.addEventListener("visibilitychange", handleVisibilityChange); + setupFinished(); + </script> +</body> \ No newline at end of file
diff --git a/src/cobalt/black_box_tests/testdata/test_font.ttf b/src/cobalt/black_box_tests/testdata/test_font.ttf new file mode 100644 index 0000000..eac54e7 --- /dev/null +++ b/src/cobalt/black_box_tests/testdata/test_font.ttf Binary files differ
diff --git a/src/cobalt/black_box_tests/testdata/timer_hit_after_preload.html b/src/cobalt/black_box_tests/testdata/timer_hit_after_preload.html new file mode 100644 index 0000000..f43854c --- /dev/null +++ b/src/cobalt/black_box_tests/testdata/timer_hit_after_preload.html
@@ -0,0 +1,45 @@ +<!DOCTYPE html> + +<head> + <title>timer callback after preload mode test</title> + <style> + h1 { + color: #c50000; + } + </style> + <script src='black_box_js_test_utils.js'></script> +</head> + +<body> + <h1> + <span id="unique_id">To test timer behavior after preload and continue</span> + </h1> + <script> + + let setTimeoutMethod = new TimerTestCase('setTimeout', 1); + let setIntervalMethod = new TimerTestCase('setInterval', 4); + + function verifyAllMethods() { + setTimeoutMethod.verify(); + setIntervalMethod.verify(); + onEndTest(); + } + + let set_interval_id = setInterval(() => { + setIntervalMethod.called(); + }, 500); + + setTimeout(() => { + setTimeoutMethod.called(); + clearInterval(set_interval_id); + }, 2250); + + setTimeout(() => { + verifyAllMethods(); + }, 2500) + + let NewElement = document.createElement('div'); + NewElement.setAttribute('id', 'script_executed'); + document.appendChild(NewElement); + </script> +</body> \ No newline at end of file
diff --git a/src/cobalt/black_box_tests/testdata/timer_hit_in_preload.html b/src/cobalt/black_box_tests/testdata/timer_hit_in_preload.html new file mode 100644 index 0000000..a54cc6b --- /dev/null +++ b/src/cobalt/black_box_tests/testdata/timer_hit_in_preload.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> + +<head> + <title>timer callback in preload mode test</title> + <style> + h1 { + color: #c50000; + } + </style> + <script src='black_box_js_test_utils.js'></script> +</head> + +<body> + <h1> + <span id="unique_id">This test is for timer callback while preloading</span> + </h1> + <script> + let setTimeoutMethod = new TimerTestCase('setTimeout', 1); + let setIntervalMethod = new TimerTestCase('setInterval', 3); + + function verifyAllMethods() { + setTimeoutMethod.verify(); + setIntervalMethod.verify(); + onEndTest(); + } + + let set_interval_id = setInterval(() => { + setIntervalMethod.called(); + }, 300); + + setTimeout(() => { + setTimeoutMethod.called(); + clearInterval(set_interval_id); + }, 1150); + + setTimeout(() => { + verifyAllMethods(); + }, 1500); + </script> +</body> \ No newline at end of file
diff --git a/src/v8/tools/release/testdata/v8/base/trace_event/common/common b/src/cobalt/black_box_tests/tests/__init__.py similarity index 100% copy from src/v8/tools/release/testdata/v8/base/trace_event/common/common copy to src/cobalt/black_box_tests/tests/__init__.py
diff --git a/src/cobalt/black_box_tests/tests/_env.py b/src/cobalt/black_box_tests/tests/_env.py new file mode 100644 index 0000000..6188bb7 --- /dev/null +++ b/src/cobalt/black_box_tests/tests/_env.py
@@ -0,0 +1,26 @@ +# +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""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/black_box_tests/tests/allow_eval.py b/src/cobalt/black_box_tests/tests/allow_eval.py new file mode 100644 index 0000000..ca6b47d --- /dev/null +++ b/src/cobalt/black_box_tests/tests/allow_eval.py
@@ -0,0 +1,19 @@ +"""Set a JS timer that expires after exiting preload mode.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import _env # pylint: disable=unused-import + +from cobalt.black_box_tests import black_box_tests + + +class AllowEvalTest(black_box_tests.BlackBoxTestCase): + + def test_simple(self): + + url = self.GetURL(file_name='allow_eval.html') + + with self.CreateCobaltRunner(url=url) as runner: + self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/disable_eval_with_csp.py b/src/cobalt/black_box_tests/tests/disable_eval_with_csp.py new file mode 100644 index 0000000..936c57b --- /dev/null +++ b/src/cobalt/black_box_tests/tests/disable_eval_with_csp.py
@@ -0,0 +1,19 @@ +"""Set a JS timer that expires after exiting preload mode.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import _env # pylint: disable=unused-import + +from cobalt.black_box_tests import black_box_tests + + +class DisableEvalWithCSPTest(black_box_tests.BlackBoxTestCase): + + def test_simple(self): + + url = self.GetURL(file_name='disable_eval_with_csp.html') + + with self.CreateCobaltRunner(url=url) as runner: + self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/persistent_cookie.py b/src/cobalt/black_box_tests/tests/persistent_cookie.py new file mode 100644 index 0000000..4bbefc5 --- /dev/null +++ b/src/cobalt/black_box_tests/tests/persistent_cookie.py
@@ -0,0 +1,46 @@ +"""Open Cobalt in preload mode and find a basic element.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import _env # pylint: disable=unused-import + +from cobalt.black_box_tests import black_box_tests +from cobalt.tools.automated_testing import webdriver_utils + +keys = webdriver_utils.import_selenium_module('webdriver.common.keys') + +_MAX_RESUME_WAIT_SECONDS = 30 + + +class PersistentCookieTest(black_box_tests.BlackBoxTestCase): + + # The same page has to be used since cookie are stored per URL. + + def test_simple(self): + + url = self.GetURL(file_name='persistent_cookie.html') + + # The webpage listens for NUMPAD1, NUMPAD2 and NUMPAD3 at opening. + with self.CreateCobaltRunner(url=url) as runner: + # Press NUMPAD1 to verify basic cookie functionality and set + # a persistent cookie. + runner.WaitForJSTestsSetup() + runner.SendKeys(keys.Keys.NUMPAD1) + self.assertTrue(runner.JSTestsSucceeded()) + + with self.CreateCobaltRunner(url=url) as runner: + runner.WaitForJSTestsSetup() + # Press NUMPAD2 to indicate this is the second time we opened + # the webpage and verify a persistent cookie is on device. Then + # clear this persistent cookie. + runner.SendKeys(keys.Keys.NUMPAD2) + self.assertTrue(runner.JSTestsSucceeded()) + + with self.CreateCobaltRunner(url=url) as runner: + runner.WaitForJSTestsSetup() + # Press NUMPAD3 to verify the persistent cookie we cleared is + # not on the device for this URL any more. + runner.SendKeys(keys.Keys.NUMPAD3) + self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/preload_font.py b/src/cobalt/black_box_tests/tests/preload_font.py new file mode 100644 index 0000000..b50e779 --- /dev/null +++ b/src/cobalt/black_box_tests/tests/preload_font.py
@@ -0,0 +1,33 @@ +"""Open Cobalt in preload mode and find a basic element.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import time + +import _env # pylint: disable=unused-import + +from cobalt.black_box_tests import black_box_tests + +_MAX_RESUME_WAIT_SECONDS = 30 + + +class PreloadFontTest(black_box_tests.BlackBoxTestCase): + + def test_simple(self): + + url = self.GetURL(file_name='preload_font.html') + + with self.CreateCobaltRunner( + url=url, target_params=['--preload']) as runner: + runner.WaitForJSTestsSetup() + runner.SendResume() + start_time = time.time() + while runner.IsInPreload(): + if time.time() - start_time > _MAX_RESUME_WAIT_SECONDS: + raise Exception('Cobalt can not exit preload mode after receiving' + 'resume signal') + time.sleep(.1) + # At this point, Cobalt is in started mode. + self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/preload_visibility.py b/src/cobalt/black_box_tests/tests/preload_visibility.py new file mode 100644 index 0000000..cd23614 --- /dev/null +++ b/src/cobalt/black_box_tests/tests/preload_visibility.py
@@ -0,0 +1,23 @@ +"""Set a JS timer that expires after exiting preload mode.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import _env # pylint: disable=unused-import + +from cobalt.black_box_tests import black_box_tests + + +class PreloadVisibilityTest(black_box_tests.BlackBoxTestCase): + + def test_simple(self): + + url = self.GetURL(file_name='preload_visibility.html') + + with self.CreateCobaltRunner( + url=url, target_params=['--preload']) as runner: + runner.WaitForJSTestsSetup() + self.assertTrue(runner.IsInPreload()) + runner.SendResume() + self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/suspend_visibility.py b/src/cobalt/black_box_tests/tests/suspend_visibility.py new file mode 100644 index 0000000..66b8c0c --- /dev/null +++ b/src/cobalt/black_box_tests/tests/suspend_visibility.py
@@ -0,0 +1,22 @@ +"""Set a JS timer that expires after exiting preload mode.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import _env # pylint: disable=unused-import + +from cobalt.black_box_tests import black_box_tests + + +class SuspendVisibilityTest(black_box_tests.BlackBoxTestCase): + + def test_simple(self): + + url = self.GetURL(file_name='suspend_visibility.html') + + with self.CreateCobaltRunner(url=url) as runner: + runner.WaitForJSTestsSetup() + runner.SendSuspend() + runner.SendResume() + self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/timer_hit_after_preload.py b/src/cobalt/black_box_tests/tests/timer_hit_after_preload.py new file mode 100644 index 0000000..d0d803e --- /dev/null +++ b/src/cobalt/black_box_tests/tests/timer_hit_after_preload.py
@@ -0,0 +1,24 @@ +"""Set a JS timer that expires after exiting preload mode.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import _env # pylint: disable=unused-import + +from cobalt.black_box_tests import black_box_tests + + +class TimerAfterPreloadTest(black_box_tests.BlackBoxTestCase): + + def test_simple(self): + + url = self.GetURL(file_name='timer_hit_after_preload.html') + + with self.CreateCobaltRunner( + url=url, target_params=['--preload']) as runner: + self.assertTrue(runner.IsInPreload()) + # setInterval will hit once during the .5 seconds. + runner.PollUntilFound('#script_executed') + runner.SendResume() + self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/timer_hit_in_preload.py b/src/cobalt/black_box_tests/tests/timer_hit_in_preload.py new file mode 100644 index 0000000..320f0a5 --- /dev/null +++ b/src/cobalt/black_box_tests/tests/timer_hit_in_preload.py
@@ -0,0 +1,21 @@ +"""Set a JS timer that expires during preload mode.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import _env # pylint: disable=unused-import + +from cobalt.black_box_tests import black_box_tests + + +class TimerInPreloadTest(black_box_tests.BlackBoxTestCase): + + def test_simple(self): + + url = self.GetURL(file_name='timer_hit_in_preload.html') + + with self.CreateCobaltRunner( + url=url, target_params=['--preload']) as runner: + self.assertTrue(runner.JSTestsSucceeded()) + self.assertTrue(runner.IsInPreload())
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc index 006245e..c454683 100644 --- a/src/cobalt/browser/application.cc +++ b/src/cobalt/browser/application.cc
@@ -87,11 +87,11 @@ #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) CommandLine* command_line = CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kRemoteDebuggingPort)) { - const std::string switchValue = + std::string switch_value = command_line->GetSwitchValueASCII(switches::kRemoteDebuggingPort); - if (!base::StringToInt(switchValue, &remote_debugging_port)) { + if (!base::StringToInt(switch_value, &remote_debugging_port)) { DLOG(ERROR) << "Invalid port specified for remote debug server: " - << switchValue + << switch_value << ". Using default port: " << kDefaultRemoteDebuggingPort; remote_debugging_port = kDefaultRemoteDebuggingPort; } @@ -285,17 +285,17 @@ return base::nullopt; } - const std::string switchValue = + std::string switch_value = command_line->GetSwitchValueASCII(browser::switches::kViewport); - std::vector<ParsedIntValue> parsed_ints = ParseDimensions(switchValue); + std::vector<ParsedIntValue> parsed_ints = ParseDimensions(switch_value); if (parsed_ints.size() < 1) { return base::nullopt; } const ParsedIntValue parsed_width = parsed_ints[0]; if (parsed_width.error_) { - DLOG(ERROR) << "Invalid value specified for viewport width: " << switchValue - << ". Using default viewport size."; + DLOG(ERROR) << "Invalid value specified for viewport width: " + << switch_value << ". Using default viewport size."; return base::nullopt; } @@ -317,7 +317,7 @@ if (parsed_height_ptr->error_) { DLOG(ERROR) << "Invalid value specified for viewport height: " - << switchValue << ". Using default viewport size."; + << switch_value << ". Using default viewport size."; return base::nullopt; } @@ -841,7 +841,17 @@ new base::AccessibilityCaptionSettingsChangedEvent()); break; #endif // SB_HAS(CAPTIONS) - default: + // Explicitly list unhandled cases here so that the compiler can give a + // warning when a value is added, but not handled. + case kSbEventTypeInput: +#if SB_API_VERSION >= 6 + case kSbEventTypePreload: +#endif // SB_API_VERSION >= 6 + case kSbEventTypeScheduled: + case kSbEventTypeStart: + case kSbEventTypeStop: + case kSbEventTypeUser: + case kSbEventTypeVerticalSync: DLOG(WARNING) << "Unhandled Starboard event of type: " << starboard_event->type; } @@ -918,8 +928,29 @@ browser_module_->ReduceMemory(); DLOG(INFO) << "Finished reducing memory usage."; break; + // All of the remaining event types are unexpected: + case kSbEventTypePreload: #endif // SB_API_VERSION >= 6 - default: +#if SB_API_VERSION >= 8 + case kSbEventTypeWindowSizeChanged: +#endif +#if SB_HAS(CAPTIONS) + case kSbEventTypeAccessibilityCaptionSettingsChanged: +#endif // SB_HAS(CAPTIONS) +#if SB_HAS(ON_SCREEN_KEYBOARD) + case kSbEventTypeOnScreenKeyboardBlurred: + case kSbEventTypeOnScreenKeyboardFocused: + case kSbEventTypeOnScreenKeyboardHidden: + case kSbEventTypeOnScreenKeyboardShown: +#endif // SB_HAS(ON_SCREEN_KEYBOARD) + case kSbEventTypeAccessiblitySettingsChanged: + case kSbEventTypeInput: + case kSbEventTypeLink: + case kSbEventTypeNetworkConnect: + case kSbEventTypeNetworkDisconnect: + case kSbEventTypeScheduled: + case kSbEventTypeUser: + case kSbEventTypeVerticalSync: NOTREACHED() << "Unexpected event type: " << event_type; return; } @@ -1002,10 +1033,6 @@ "Total free application CPU memory remaining."), used_cpu_memory("Memory.CPU.Used", 0, "Total CPU memory allocated via the app's allocators."), - js_reserved_memory("Memory.JS", 0, - "The total memory that is reserved by the engine, " - "including the part that is actually occupied by " - "JS objects, and the part that is not yet."), app_start_time("Time.Cobalt.Start", base::StartupTimer::StartTime().ToInternalValue(), "Start time of the application in microseconds."), @@ -1087,8 +1114,7 @@ *c_val_stats_.used_gpu_memory = *used_gpu_memory; } - c_val_stats_.js_reserved_memory = - script::JavaScriptEngine::UpdateMemoryStatsAndReturnReserved(); + browser_module_->UpdateJavaScriptHeapStatistics(); browser_module_->CheckMemory(used_cpu_memory, used_gpu_memory); }
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h index 1344ac3..2797340 100644 --- a/src/cobalt/browser/application.h +++ b/src/cobalt/browser/application.h
@@ -160,11 +160,7 @@ base::optional<base::CVal<base::cval::SizeInBytes, base::CValPublic> > used_gpu_memory; - // The total memory that is reserved by the engine, including the part that - // is actually occupied by JS objects, and the part that is not yet. - base::CVal<base::cval::SizeInBytes, base::CValPublic> js_reserved_memory; - - base::CVal<int64> app_start_time; + base::CVal<int64, base::CValPublic> app_start_time; base::CVal<base::TimeDelta, base::CValPublic> app_lifetime; };
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp index aef3083..9869893 100644 --- a/src/cobalt/browser/browser.gyp +++ b/src/cobalt/browser/browser.gyp
@@ -86,8 +86,6 @@ 'on_screen_keyboard_starboard_bridge.h', 'render_tree_combiner.cc', 'render_tree_combiner.h', - 'resource_provider_array_buffer_allocator.cc', - 'resource_provider_array_buffer_allocator.h', 'splash_screen.cc', 'splash_screen.h', 'splash_screen_cache.cc', @@ -278,37 +276,15 @@ }, { - 'target_name': 'browser_copy_test_data', - 'type': 'none', - 'actions': [ - { - 'action_name': 'browser_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/browser/testdata/', - ], - 'output_dir': 'cobalt/browser/testdata/', - }, - 'includes': [ '../build/copy_test_data.gypi' ], - }, - ], - }, - - { 'target_name': 'browser_copy_debug_console', 'type': 'none', - 'actions': [ - { - 'action_name': 'browser_copy_debug_console', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/browser/debug_console/', - ], - 'output_dir': 'cobalt/browser/debug_console/', - }, - 'includes': [ '../build/copy_web_data.gypi' ], - }, - ], + 'variables': { + 'content_web_input_files': [ + '<(DEPTH)/cobalt/browser/debug_console/', + ], + 'content_web_output_subdir': 'cobalt/browser/debug_console/', + }, + 'includes': [ '<(DEPTH)/cobalt/build/copy_web_data.gypi' ], }, ], }
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc index d4ed23e..2115273 100644 --- a/src/cobalt/browser/browser_module.cc +++ b/src/cobalt/browser/browser_module.cc
@@ -33,7 +33,6 @@ #include "cobalt/base/source_location.h" #include "cobalt/base/tokens.h" #include "cobalt/browser/on_screen_keyboard_starboard_bridge.h" -#include "cobalt/browser/resource_provider_array_buffer_allocator.h" #include "cobalt/browser/screen_shot_writer.h" #include "cobalt/browser/storage_upgrade_handler.h" #include "cobalt/browser/switches.h" @@ -232,11 +231,6 @@ .PassAs<storage::StorageManager::UpgradeHandler>(), options_.storage_manager_options), is_rendered_(false), -#if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) - array_buffer_allocator_( - new ResourceProviderArrayBufferAllocator(GetResourceProvider())), - array_buffer_cache_(new dom::ArrayBuffer::Cache(3 * 1024 * 1024)), -#endif // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) can_play_type_handler_(media::MediaModule::CreateCanPlayTypeHandler()), network_module_(&storage_manager_, event_dispatcher_, options_.network_module_options), @@ -253,6 +247,11 @@ "The last time a navigation occurred."), on_load_event_time_("Time.Browser.OnLoadEvent", 0, "The last time the window.OnLoad event fired."), + javascript_reserved_memory_( + "Memory.JS", 0, + "The total memory that is reserved by the JavaScript engine, which " + "includes both parts that have live JavaScript values, as well as " + "preallocated space for future values."), #if defined(ENABLE_DEBUG_CONSOLE) ALLOW_THIS_IN_INITIALIZER_LIST(fuzzer_toggle_command_handler_( kFuzzerToggleCommand, @@ -347,11 +346,6 @@ application_state_ == base::kApplicationStatePaused) { InitializeSystemWindow(); } else if (application_state_ == base::kApplicationStatePreloading) { -#if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) - // Preloading is not supported on platforms that allocate ArrayBuffers on - // GPU memory. - NOTREACHED(); -#endif // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) resource_provider_stub_.emplace(true /*allocate_image_data*/); } @@ -362,8 +356,7 @@ base::Unretained(this)), &network_module_, GetViewportSize(), GetResourceProvider(), kLayoutMaxRefreshFrequencyInHz, - base::Bind(&BrowserModule::GetDebugServer, base::Unretained(this)), - options_.web_module_options.javascript_engine_options)); + base::Bind(&BrowserModule::GetDebugServer, base::Unretained(this)))); lifecycle_observers_.AddObserver(debug_console_.get()); #endif // defined(ENABLE_DEBUG_CONSOLE) @@ -494,11 +487,6 @@ base::Bind(&BrowserModule::Navigate, base::Unretained(this)); options.loaded_callbacks.push_back( base::Bind(&BrowserModule::OnLoad, base::Unretained(this))); -#if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) - options.dom_settings_options.array_buffer_allocator = - array_buffer_allocator_.get(); - options.dom_settings_options.array_buffer_cache = array_buffer_cache_.get(); -#endif // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) #if defined(ENABLE_FAKE_MICROPHONE) if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kFakeMicrophone) || CommandLine::ForCurrentProcess()->HasSwitch(switches::kInputFuzzer)) { @@ -782,6 +770,9 @@ DCHECK_EQ(MessageLoop::current(), self_message_loop_); // Only inject shown events to the main WebModule. on_screen_keyboard_show_called_ = true; + if (splash_screen_ && splash_screen_->ShutdownSignaled()) { + DestroySplashScreen(base::TimeDelta()); + } if (web_module_) { web_module_->InjectOnScreenKeyboardShownEvent(event->ticket()); } @@ -1309,6 +1300,11 @@ used_gpu_memory); } +void BrowserModule::UpdateJavaScriptHeapStatistics() { + web_module_->RequestJavaScriptHeapStatistics(base::Bind( + &BrowserModule::GetHeapStatisticsCallback, base::Unretained(this))); +} + void BrowserModule::OnRendererSubmissionRasterized() { TRACE_EVENT0("cobalt::browser", "BrowserModule::OnRendererSubmissionRasterized()"); @@ -1488,18 +1484,6 @@ debug_console_layer_->Reset(); #endif // defined(ENABLE_DEBUG_CONSOLE) -#if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) - // Note that the following function call will leak the GPU memory allocated. - // This is because after the renderer_module_ is destroyed it is no longer - // safe to release the GPU memory allocated. - // - // The following code can call reset() to release the allocated memory but the - // memory may still be used by XHR and ArrayBuffer. As this feature is only - // used on platform without Resume() support, it is safer to leak the memory - // then to release it. - dom::ArrayBuffer::Allocator* allocator = array_buffer_allocator_.release(); -#endif // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) - if (media_module_) { media_module_->Suspend(); } @@ -1525,12 +1509,6 @@ // Propagate the current screen size. UpdateScreenSize(); -#if defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) - // Start() and Resume() are not supported on platforms that allocate - // ArrayBuffers in GPU memory. - NOTREACHED(); -#endif // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR) - if (is_start) { FOR_EACH_OBSERVER(LifecycleObserver, lifecycle_observers_, Start(GetResourceProvider())); @@ -1617,6 +1595,17 @@ } } +void BrowserModule::GetHeapStatisticsCallback( + const script::HeapStatistics& heap_statistics) { + if (MessageLoop::current() != self_message_loop_) { + self_message_loop_->PostTask( + FROM_HERE, base::Bind(&BrowserModule::GetHeapStatisticsCallback, + base::Unretained(this), heap_statistics)); + return; + } + javascript_reserved_memory_ = heap_statistics.total_heap_size; +} + void BrowserModule::SubmitCurrentRenderTreeToRenderer() { if (!renderer_module_) { return;
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h index 4b3a05c..1086759 100644 --- a/src/cobalt/browser/browser_module.h +++ b/src/cobalt/browser/browser_module.h
@@ -168,6 +168,10 @@ void CheckMemory(const int64_t& used_cpu_memory, const base::optional<int64_t>& used_gpu_memory); + // Post a task to the main web module to update + // |javascript_reserved_memory_|. + void UpdateJavaScriptHeapStatistics(); + #if SB_API_VERSION >= 8 // Called when a kSbEventTypeWindowSizeChange event is fired. void OnWindowSizeChanged(const SbWindowSize& size); @@ -366,6 +370,10 @@ // Applies the current AutoMem settings to all applicable submodules. void ApplyAutoMemSettings(); + // The callback posted to the main web module in for + // |UpdateJavaScriptHeapStatistics|. + void GetHeapStatisticsCallback(const script::HeapStatistics& heap_statistics); + // If it exists, takes the current combined render tree from // |render_tree_combiner_| and submits it to the pipeline in the renderer // module. @@ -432,12 +440,6 @@ // ResourceProvider is created. Only valid in the Preloading state. base::optional<render_tree::ResourceProviderStub> resource_provider_stub_; - // Optional memory allocator used by ArrayBuffer. - scoped_ptr<dom::ArrayBuffer::Allocator> array_buffer_allocator_; - - // Optional cache used by ArrayBuffer. - scoped_ptr<dom::ArrayBuffer::Cache> array_buffer_cache_; - // Controls all media playback related objects/resources. scoped_ptr<media::MediaModule> media_module_; @@ -491,10 +493,16 @@ // The time when a URL navigation starts. This is recorded after the previous // WebModule is destroyed. - base::CVal<int64> navigate_time_; + base::CVal<int64, base::CValPublic> navigate_time_; // The time when the WebModule's Window.onload event is fired. - base::CVal<int64> on_load_event_time_; + base::CVal<int64, base::CValPublic> on_load_event_time_; + + // The total memory that is reserved by the JavaScript engine, which + // includes both parts that have live JavaScript values, as well as + // preallocated space for future values. + base::CVal<base::cval::SizeInBytes, base::CValPublic> + javascript_reserved_memory_; #if defined(ENABLE_DEBUG_CONSOLE) // Possibly null, but if not, will contain a reference to an instance of
diff --git a/src/cobalt/browser/cobalt.gyp b/src/cobalt/browser/cobalt.gyp index 9c1f00b..fdaca10 100644 --- a/src/cobalt/browser/cobalt.gyp +++ b/src/cobalt/browser/cobalt.gyp
@@ -34,17 +34,16 @@ 'main.cc', ], }], - ['cobalt_copy_test_data == 1', { - 'dependencies': [ - '<(DEPTH)/cobalt/browser/browser.gyp:browser_copy_test_data', - '<(DEPTH)/cobalt/xhr/xhr.gyp:xhr_copy_test_data', - ], - }], ['cobalt_copy_debug_console == 1', { 'dependencies': [ '<(DEPTH)/cobalt/browser/browser.gyp:browser_copy_debug_console', ], }], + ['cobalt_splash_screen_file != ""', { + 'dependencies': [ + '<(DEPTH)/cobalt/browser/splash_screen/splash_screen.gyp:copy_splash_screen', + ], + }], ], }, @@ -59,31 +58,45 @@ }, 'includes': [ '../../starboard/build/deploy.gypi' ], }, + { - 'target_name': 'snapshot_app_stats', - 'type': '<(final_executable_type)', - 'sources': [ - 'snapshot_app_stats.cc', - ], - 'dependencies': [ - 'cobalt', - '<(DEPTH)/cobalt/browser/browser.gyp:browser', - ], - }, - { - 'target_name': 'snapshot_app_stats_deploy', + # Convenience target to build cobalt and copy the demos into + # content/data/test/cobalt/demos + 'target_name': 'cobalt_with_demos', 'type': 'none', 'dependencies': [ - 'snapshot_app_stats', + 'cobalt', + '<(DEPTH)/cobalt/demos/demos.gyp:copy_demos', ], - 'variables': { - 'executable_name': 'snapshot_app_stats', - }, - 'includes': [ '../../starboard/build/deploy.gypi' ], }, - ], 'conditions': [ + ['build_snapshot_app_stats', { + 'targets': [ + { + 'target_name': 'snapshot_app_stats', + 'type': '<(final_executable_type)', + 'sources': [ + 'snapshot_app_stats.cc', + ], + 'dependencies': [ + 'cobalt', + '<(DEPTH)/cobalt/browser/browser.gyp:browser', + ], + }, + { + 'target_name': 'snapshot_app_stats_deploy', + 'type': 'none', + 'dependencies': [ + 'snapshot_app_stats', + ], + 'variables': { + 'executable_name': 'snapshot_app_stats', + }, + 'includes': [ '../../starboard/build/deploy.gypi' ], + }, + ] + }], ['final_executable_type == "shared_library"', { 'targets': [ {
diff --git a/src/cobalt/browser/debug_console.cc b/src/cobalt/browser/debug_console.cc index 0f76e19..9409f44 100644 --- a/src/cobalt/browser/debug_console.cc +++ b/src/cobalt/browser/debug_console.cc
@@ -166,12 +166,10 @@ render_tree_produced_callback, network::NetworkModule* network_module, const math::Size& window_dimensions, render_tree::ResourceProvider* resource_provider, float layout_refresh_rate, - const debug::Debugger::GetDebugServerCallback& get_debug_server_callback, - const script::JavaScriptEngine::Options& javascript_engine_options) { + const debug::Debugger::GetDebugServerCallback& get_debug_server_callback) { mode_ = GetInitialMode(); WebModule::Options web_module_options; - web_module_options.javascript_engine_options = javascript_engine_options; web_module_options.name = "DebugConsoleWebModule"; // The debug console does not load any image assets. web_module_options.image_cache_capacity = 0;
diff --git a/src/cobalt/browser/debug_console.h b/src/cobalt/browser/debug_console.h index 1a5f314..60162fa 100644 --- a/src/cobalt/browser/debug_console.h +++ b/src/cobalt/browser/debug_console.h
@@ -45,8 +45,7 @@ const math::Size& window_dimensions, render_tree::ResourceProvider* resource_provider, float layout_refresh_rate, - const debug::Debugger::GetDebugServerCallback& get_debug_server_callback, - const script::JavaScriptEngine::Options& javascript_engine_options); + const debug::Debugger::GetDebugServerCallback& get_debug_server_callback); ~DebugConsole(); // Filters a key event.
diff --git a/src/cobalt/browser/debug_console/console_values.js b/src/cobalt/browser/debug_console/console_values.js index 531e90e..c9191ae 100644 --- a/src/cobalt/browser/debug_console/console_values.js +++ b/src/cobalt/browser/debug_console/console_values.js
@@ -23,7 +23,7 @@ 'Cobalt Memory.CPU Memory.MainWebModule Memory.JS Memory.Font ' + 'Count.MainWebModule.ImageCache.Resource ' + 'Count.MainWebModule.DOM.HtmlElement Count.MainWebModule.Layout.Box ' + - 'Event.Count.MainWebModule.KeyDown.DOM.HtmlElement.Added ' + + 'Event.Count.MainWebModule.KeyDown.DOM.HtmlElement.Document.Added ' + 'Event.Count.MainWebModule.KeyDown.Layout.Box.Created ' + 'Event.Count.MainWebModule.KeyDown.Layout.Box.Destroyed ' + 'Event.Duration.MainWebModule.DOM.VideoStartDelay ' +
diff --git a/src/cobalt/browser/lib/cobalt.def b/src/cobalt/browser/lib/cobalt.def index 4aa31cc..f94b88a 100644 --- a/src/cobalt/browser/lib/cobalt.def +++ b/src/cobalt/browser/lib/cobalt.def
@@ -22,10 +22,12 @@ ; From cobalt/render/rasterizer/lib/exported/graphics.h: CbLibGraphicsSetContextCreatedCallback - CbLibGraphicsSetBeginRenderFrameCallback - CbLibGraphicsSetEndRenderFrameCallback + CbLibGraphicsSetRenderFrameCallback CbLibGrapicsGetMainTextureHandle CbLibGraphicsSetTargetMainTextureSize + CbLibGraphicsRenderCobalt + CbLibGraphicsCopyBackbuffer + CbLibGraphicsSwapBackbuffer ; From cobalt/render/rasterizer/lib/exported/video.h: CbLibVideoSetOnUpdateProjectionTypeAndStereoMode
diff --git a/src/cobalt/browser/memory_settings/auto_mem.cc b/src/cobalt/browser/memory_settings/auto_mem.cc index 855e8ad..889315d 100644 --- a/src/cobalt/browser/memory_settings/auto_mem.cc +++ b/src/cobalt/browser/memory_settings/auto_mem.cc
@@ -35,7 +35,6 @@ #include "cobalt/browser/memory_settings/scaling_function.h" #include "cobalt/browser/switches.h" #include "cobalt/math/clamp.h" -#include "nb/lexical_cast.h" namespace cobalt { namespace browser {
diff --git a/src/cobalt/browser/memory_settings/auto_mem_settings.cc b/src/cobalt/browser/memory_settings/auto_mem_settings.cc index c5ecee4..1c398f3 100644 --- a/src/cobalt/browser/memory_settings/auto_mem_settings.cc +++ b/src/cobalt/browser/memory_settings/auto_mem_settings.cc
@@ -23,9 +23,9 @@ #include "base/optional.h" #include "base/string_number_conversions.h" #include "base/string_split.h" +#include "base/string_util.h" #include "cobalt/browser/memory_settings/constants.h" #include "cobalt/browser/switches.h" -#include "nb/lexical_cast.h" namespace cobalt { namespace browser { @@ -56,20 +56,10 @@ return output; } -char ToLowerCharTypesafe(int c) { return static_cast<char>(::tolower(c)); } - -std::string ToLower(const std::string& input) { - std::string value_str = input; - std::transform(value_str.begin(), value_str.end(), value_str.begin(), - ToLowerCharTypesafe); - - return value_str; -} - bool StringValueSignalsAutoset(const std::string& value) { - std::string value_lower_case = ToLower(value); - return ((value_lower_case == "auto") || (value_lower_case == "autoset") || - (value_lower_case == "-1")); + return LowerCaseEqualsASCII(value, "auto") || + LowerCaseEqualsASCII(value, "autoset") || + value == "-1"; } struct ParsedIntValue { @@ -83,7 +73,7 @@ // Parses a string like "1234x5678" to vector of parsed int values. std::vector<ParsedIntValue> ParseDimensions(const std::string& input) { - std::string value_str = ToLower(input); + std::string value_str = StringToLowerASCII(input); std::vector<ParsedIntValue> output; std::vector<std::string> lengths; @@ -97,38 +87,36 @@ return output; } -bool StringEndsWith(const std::string& value, const std::string& ending) { - if (ending.size() > value.size()) { - return false; - } - // Reverse search through the back of the string. - return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); -} - -// Handles bytes: "12435" +// Handles bytes: "12435", "1234B" // Handles kilobytes: "128KB" // Handles megabytes: "64MB" // Handles gigabytes: "1GB" // Handles fractional units for kilo/mega/gigabytes int64_t ParseMemoryValue(const std::string& value, bool* parse_ok) { - // nb::lexical_cast<> will parse out the number but it will ignore the - // unit part, such as "kb" or "mb". - double numerical_value = nb::lexical_cast<double>(value, parse_ok); - if (!(*parse_ok)) { - return static_cast<int64_t>(numerical_value); + // Use case-insensitive string comparisons. + const bool kIgnoreCase = false; + + // Filter out the decimal portion from any unit designation in the string. + std::string number_string; + double units = 1.0; + if (EndsWith(value, "kb", kIgnoreCase)) { + units = 1024.0; + number_string = value.substr(0, value.size() - 2); + } else if (EndsWith(value, "mb", kIgnoreCase)) { + units = 1024.0 * 1024.0; + number_string = value.substr(0, value.size() - 2); + } else if (EndsWith(value, "gb", kIgnoreCase)) { + units = 1024.0 * 1024.0 * 1024.0; + number_string = value.substr(0, value.size() - 2); + } else if (EndsWith(value, "b", kIgnoreCase)) { + number_string = value.substr(0, value.size() - 1); + } else { + number_string = value; } - // Lowercasing the string makes the units easier to detect. - std::string value_lower_case = ToLower(value); - - if (StringEndsWith(value_lower_case, "kb")) { - numerical_value *= 1024; // convert kb -> bytes. - } else if (StringEndsWith(value_lower_case, "mb")) { - numerical_value *= 1024 * 1024; // convert mb -> bytes. - } else if (StringEndsWith(value_lower_case, "gb")) { - numerical_value *= 1024 * 1024 * 1024; // convert gb -> bytes. - } - return static_cast<int64_t>(numerical_value); + double numerical_value = 0.0; + *parse_ok = base::StringToDouble(number_string, &numerical_value); + return static_cast<int64_t>(numerical_value * units); } template <typename ValueType>
diff --git a/src/cobalt/browser/memory_settings/table_printer.cc b/src/cobalt/browser/memory_settings/table_printer.cc index 69b9612..483f1a6 100644 --- a/src/cobalt/browser/memory_settings/table_printer.cc +++ b/src/cobalt/browser/memory_settings/table_printer.cc
@@ -94,12 +94,12 @@ // ex: "|________|________|" std::string MakeRowDelimiter() const; - // Follows a data row to provide verticle space before a TopSeperatorRow(). + // Follows a data row to provide verticle space before a TopSeparatorRow(). // ex: "| | |" - std::string MakeTopSeperatorRowAbove() const; + std::string MakeTopSeparatorRowAbove() const; // ex: " _________________ " - std::string MakeTopSeperatorRow() const; + std::string MakeTopSeparatorRow() const; const std::vector<size_t>& column_sizes_; const TablePrinter::Color text_color_; @@ -120,9 +120,9 @@ std::stringstream output_ss; output_ss << printer.MakeHeaderRow(rows[0]) << "\n"; - output_ss << printer.MakeTopSeperatorRow() << "\n"; + output_ss << printer.MakeTopSeparatorRow() << "\n"; - std::string seperator_row_above = printer.MakeTopSeperatorRowAbove(); + std::string separator_row_above = printer.MakeTopSeparatorRowAbove(); std::string row_delimiter = printer.MakeRowDelimiter(); // Print body. @@ -131,7 +131,7 @@ const std::string row_string = printer.MakeDataRow(row); - output_ss << seperator_row_above << "\n"; + output_ss << separator_row_above << "\n"; output_ss << row_string << "\n"; output_ss << row_delimiter << "\n"; } @@ -265,7 +265,7 @@ return output; } -std::string TablePrinterImpl::MakeTopSeperatorRow() const { +std::string TablePrinterImpl::MakeTopSeparatorRow() const { std::stringstream ss; for (size_t i = 0; i < column_sizes_.size(); ++i) { if (i == 0) { @@ -283,7 +283,7 @@ return output; } -std::string TablePrinterImpl::MakeTopSeperatorRowAbove() const { +std::string TablePrinterImpl::MakeTopSeparatorRowAbove() const { std::stringstream ss; for (size_t i = 0; i < column_sizes_.size(); ++i) { ss << "|";
diff --git a/src/cobalt/browser/memory_tracker/tool.cc b/src/cobalt/browser/memory_tracker/tool.cc index a94cb1e..6789df7 100644 --- a/src/cobalt/browser/memory_tracker/tool.cc +++ b/src/cobalt/browser/memory_tracker/tool.cc
@@ -20,6 +20,7 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/string_number_conversions.h" #include "cobalt/browser/memory_tracker/tool/compressed_time_series_tool.h" #include "cobalt/browser/memory_tracker/tool/leak_finder_tool.h" #include "cobalt/browser/memory_tracker/tool/log_writer_tool.h" @@ -32,7 +33,7 @@ #include "cobalt/browser/memory_tracker/tool/tool_thread.h" #include "nb/analytics/memory_tracker_helpers.h" -#include "nb/lexical_cast.h" +#include "starboard/double.h" #include "starboard/log.h" namespace cobalt { @@ -299,8 +300,9 @@ case kStartup: { double num_mins = 1.0; if (!tool_arg.empty()) { - num_mins = nb::lexical_cast<double>(tool_arg.c_str()); - if ((num_mins > 0) == false) { // Accounts for NaN. + if (!base::StringToDouble(tool_arg, &num_mins) || + SbDoubleIsNan(num_mins) || + num_mins <= 0) { num_mins = 1.0; } }
diff --git a/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h b/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h index 0c8ef3e..f901ff6 100644 --- a/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h +++ b/src/cobalt/browser/memory_tracker/tool/histogram_table_csv_base.h
@@ -98,16 +98,16 @@ } std::string ToString() const { - const char kSeperator[] = "//////////////////////////////////////////////"; + const char kSeparator[] = "//////////////////////////////////////////////"; std::stringstream ss; - ss << kSeperator << kNewLine; + ss << kSeparator << kNewLine; if (title_.size()) { ss << "// CSV of " << title_ << kNewLine; } for (size_t i = 0; i < NumberOfRows(); ++i) { ss << StringifyRow(i); } - ss << kSeperator; + ss << kSeparator; return ss.str(); }
diff --git a/src/cobalt/browser/memory_tracker/tool/print_tool.cc b/src/cobalt/browser/memory_tracker/tool/print_tool.cc index f896f2f..427ca8b 100644 --- a/src/cobalt/browser/memory_tracker/tool/print_tool.cc +++ b/src/cobalt/browser/memory_tracker/tool/print_tool.cc
@@ -74,7 +74,7 @@ PrintTool::~PrintTool() {} void PrintTool::Run(Params* params) { - const std::string kSeperator = + const std::string kSeparator = "--------------------------------------------------"; while (!params->finished()) { @@ -117,12 +117,12 @@ ss << "TimeNow " << params->TimeInMinutesString() << " (minutes):" << kNewLine << kNewLine; - ss << kSeperator << kNewLine; + ss << kSeparator << kNewLine; nb::analytics::MemoryStats memstats = nb::analytics::GetProcessMemoryStats(); F::PrintRow(&ss, "MALLOC STAT", "IN USE BYTES", ""); - ss << kSeperator << kNewLine; + ss << kSeparator << kNewLine; F::PrintRow(&ss, "Total CPU Reserved", NumberFormatWithCommas(memstats.total_cpu_memory), ""); @@ -135,11 +135,11 @@ F::PrintRow(&ss, "Total GPU Used", NumberFormatWithCommas(memstats.used_gpu_memory), ""); - ss << kSeperator << kNewLine << kNewLine; + ss << kSeparator << kNewLine << kNewLine; - ss << kSeperator << kNewLine; + ss << kSeparator << kNewLine; F::PrintRow(&ss, "MEMORY REGION", "IN USE BYTES", "NUM ALLOCS"); - ss << kSeperator << kNewLine; + ss << kSeparator << kNewLine; for (MapIt it = output.begin(); it != output.end(); ++it) { const AllocationGroup* group = it->second; @@ -171,7 +171,7 @@ NumberFormatWithCommas(total_bytes), NumberFormatWithCommas(num_allocs)); - ss << kSeperator << kNewLine; + ss << kSeparator << kNewLine; ss << kNewLine << kNewLine; params->logger()->Output(ss.str().c_str());
diff --git a/src/cobalt/browser/on_screen_keyboard_starboard_bridge.cc b/src/cobalt/browser/on_screen_keyboard_starboard_bridge.cc index ea1bba0..83016b2 100644 --- a/src/cobalt/browser/on_screen_keyboard_starboard_bridge.cc +++ b/src/cobalt/browser/on_screen_keyboard_starboard_bridge.cc
@@ -47,6 +47,20 @@ return SbWindowIsOnScreenKeyboardShown(sb_window_provider_.Run()); } +scoped_refptr<dom::DOMRect> +OnScreenKeyboardStarboardBridge::BoundingRect() const { + // Delay providing the SbWindow until as late as possible. + SbWindowRect sb_window_rect = SbWindowRect(); + if (!SbWindowGetOnScreenKeyboardBoundingRect(sb_window_provider_.Run(), + &sb_window_rect)) { + return nullptr; + } + scoped_refptr<dom::DOMRect> bounding_rect = + new dom::DOMRect(sb_window_rect.x, sb_window_rect.y, sb_window_rect.width, + sb_window_rect.height); + return bounding_rect; +} + bool OnScreenKeyboardStarboardBridge::IsValidTicket(int ticket) const { return ticket != kSbEventOnScreenKeyboardInvalidTicket; }
diff --git a/src/cobalt/browser/on_screen_keyboard_starboard_bridge.h b/src/cobalt/browser/on_screen_keyboard_starboard_bridge.h index 49b3e9c..8e88a9b 100644 --- a/src/cobalt/browser/on_screen_keyboard_starboard_bridge.h +++ b/src/cobalt/browser/on_screen_keyboard_starboard_bridge.h
@@ -44,6 +44,8 @@ bool IsShown() const override; + scoped_refptr<dom::DOMRect> BoundingRect() const override; + bool IsValidTicket(int ticket) const override; void SetKeepFocus(bool keep_focus) override;
diff --git a/src/cobalt/browser/resource_provider_array_buffer_allocator.cc b/src/cobalt/browser/resource_provider_array_buffer_allocator.cc deleted file mode 100644 index ef9020e..0000000 --- a/src/cobalt/browser/resource_provider_array_buffer_allocator.cc +++ /dev/null
@@ -1,43 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "cobalt/browser/resource_provider_array_buffer_allocator.h" - -#include "starboard/common/scoped_ptr.h" - -namespace cobalt { -namespace browser { - -ResourceProviderArrayBufferAllocator::ResourceProviderArrayBufferAllocator( - render_tree::ResourceProvider* resource_provider) { - gpu_memory_buffer_space_ = - resource_provider->AllocateRawImageMemory(kPoolSize, kAlignment); - DCHECK(gpu_memory_buffer_space_); - DCHECK(gpu_memory_buffer_space_->GetMemory()); - - gpu_memory_pool_.set(starboard::make_scoped_ptr( - new nb::FirstFitMemoryPool(gpu_memory_buffer_space_->GetMemory(), - gpu_memory_buffer_space_->GetSizeInBytes()))); -} - -void* ResourceProviderArrayBufferAllocator::Allocate(size_t size) { - return gpu_memory_pool_->Allocate(size, kAlignment); -} - -void ResourceProviderArrayBufferAllocator::Free(void* p) { - gpu_memory_pool_->Free(p); -} - -} // namespace browser -} // namespace cobalt
diff --git a/src/cobalt/browser/resource_provider_array_buffer_allocator.h b/src/cobalt/browser/resource_provider_array_buffer_allocator.h deleted file mode 100644 index 00ee048..0000000 --- a/src/cobalt/browser/resource_provider_array_buffer_allocator.h +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef COBALT_BROWSER_RESOURCE_PROVIDER_ARRAY_BUFFER_ALLOCATOR_H_ -#define COBALT_BROWSER_RESOURCE_PROVIDER_ARRAY_BUFFER_ALLOCATOR_H_ - -#include "cobalt/dom/array_buffer.h" -#include "cobalt/render_tree/resource_provider.h" -#include "nb/memory_pool.h" -#include "starboard/common/locked_ptr.h" - -namespace cobalt { -namespace browser { - -class ResourceProviderArrayBufferAllocator - : public dom::ArrayBuffer::Allocator { - public: - static const size_t kPoolSize = 32 * 1024 * 1024; - static const size_t kAlignment = 128; - - explicit ResourceProviderArrayBufferAllocator( - render_tree::ResourceProvider* resource_provider); - - private: - void* Allocate(size_t size) override; - void Free(void* p) override; - - scoped_ptr<render_tree::RawImageMemory> gpu_memory_buffer_space_; - starboard::LockedPtr<nb::FirstFitMemoryPool> gpu_memory_pool_; -}; - -} // namespace browser -} // namespace cobalt - -#endif // COBALT_BROWSER_RESOURCE_PROVIDER_ARRAY_BUFFER_ALLOCATOR_H_
diff --git a/src/cobalt/browser/storage_upgrade_handler_test.cc b/src/cobalt/browser/storage_upgrade_handler_test.cc index e2a92eb..303e0f8 100644 --- a/src/cobalt/browser/storage_upgrade_handler_test.cc +++ b/src/cobalt/browser/storage_upgrade_handler_test.cc
@@ -109,7 +109,7 @@ EXPECT_TRUE(pathname); EXPECT_TRUE(string_out); FilePath file_path; - EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); + EXPECT_TRUE(PathService::Get(base::DIR_TEST_DATA, &file_path)); file_path = file_path.Append(pathname); EXPECT_TRUE(file_util::ReadFileToString(file_path, string_out)); const char* data = string_out->c_str();
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc index eb8db8f..92ec183 100644 --- a/src/cobalt/browser/switches.cc +++ b/src/cobalt/browser/switches.cc
@@ -40,6 +40,10 @@ "Disables caching of rasterized render tree nodes; caching improves " "performance but may result in sub-pixel differences."; +const char kDisableSignIn[] = "disable_sign_in"; +const char kDisableSignInHelp[] = + "Disables sign-in on platforms that use H5VCC Account Manager."; + const char kDisableSplashScreenOnReloads[] = "disable_splash_screen_on_reloads"; const char kDisableSplashScreenOnReloadsHelp[] = "Disables the splash screen on reloads; instead it will only appear on the " @@ -317,6 +321,7 @@ {kDebugConsoleMode, kDebugConsoleModeHelp}, {kDisableImageAnimations, kDisableImageAnimationsHelp}, {kDisableRasterizerCaching, kDisableRasterizerCachingHelp}, + {kDisableSignIn, kDisableSignInHelp}, {kDisableSplashScreenOnReloads, kDisableSplashScreenOnReloadsHelp}, {kDisableWebDriver, kDisableWebDriverHelp}, {kDisableWebmVp9, kDisableWebmVp9Help},
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h index a46e11b..b347192 100644 --- a/src/cobalt/browser/switches.h +++ b/src/cobalt/browser/switches.h
@@ -29,6 +29,8 @@ extern const char kDisableImageAnimations[]; extern const char kDisableImageAnimationsHelp[]; extern const char kDisableRasterizerCaching[]; +extern const char kDisableSignIn[]; +extern const char kDisableSignInHelp[]; extern const char kDisableSplashScreenOnReloads[]; extern const char kDisableSplashScreenOnReloadsHelp[]; extern const char kDisableWebDriver[];
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-85.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-85.mp4 new file mode 100644 index 0000000..831859e --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-85.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-86.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-86.mp4 new file mode 100644 index 0000000..fe5f3ca --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-86.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8b.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8b.mp4 new file mode 100644 index 0000000..703415a --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8b.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8c.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8c.mp4 new file mode 100644 index 0000000..381ffa9 --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8c.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8d.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8d.mp4 new file mode 100644 index 0000000..3e9abdd --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8d.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-audio-1MB-trunc.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-audio-1MB-trunc.mp4 new file mode 100644 index 0000000..5a8e3aa --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-audio-1MB-trunc.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_20130125_18.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_20130125_18.mp4 new file mode 100644 index 0000000..cb88666 --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_20130125_18.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-85.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-85.mp4 new file mode 100644 index 0000000..2abd243 --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-85.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8b.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8b.mp4 new file mode 100644 index 0000000..4f89212 --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8b.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8c.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8c.mp4 new file mode 100644 index 0000000..899fa90 --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8c.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8d.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8d.mp4 new file mode 100644 index 0000000..60237b0 --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8d.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/nq-frames23-tfdt24.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/nq-frames23-tfdt24.mp4 new file mode 100644 index 0000000..ebf0935 --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/nq-frames23-tfdt24.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/nq-frames24-tfdt23.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/nq-frames24-tfdt23.mp4 new file mode 100644 index 0000000..75b6afb --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/nq-frames24-tfdt23.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/oops_cenc-20121114-145-143.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/oops_cenc-20121114-145-143.mp4 new file mode 100644 index 0000000..6007fd8 --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/oops_cenc-20121114-145-143.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/sintel-trunc.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/sintel-trunc.mp4 new file mode 100644 index 0000000..e9692fa --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/sintel-trunc.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/test-video-1MB.mp4 b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/test-video-1MB.mp4 new file mode 100644 index 0000000..217a71a --- /dev/null +++ b/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/test-video-1MB.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/splash_screen/redirect_server.py b/src/cobalt/browser/testdata/splash_screen/redirect_server.py deleted file mode 100644 index 1720c31..0000000 --- a/src/cobalt/browser/testdata/splash_screen/redirect_server.py +++ /dev/null
@@ -1,86 +0,0 @@ -#!/usr/bin/env python -# Copyright 2017 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Tests the effect that HTTP redirects have on the Cobalt splash screen. - -Run this script from the src directory, i.e. - -src$ python cobalt/browser/testdata/splash_screen/redirect_server.py - -Let $(hostname) be the local machine's hostname, and let ${port} be -the port printed out by the python script. Then in a separate tab, run -Cobalt and pass the initial URL - -http://"$(hostname)":${port}/cobalt/browser/testdata/splash_screen/link_splash_screen.html?redirect=yes - -(note the HTTP not HTTPS) which will be redirected to - -http://"$(hostname)":${port}/cobalt/browser/testdata/splash_screen/redirected.html - -The expected behavior is that the splash screen served by -redirected.html (beforeunload.html, a blue page with an animated rectangle in -the top left) will be cached separately from the splash screen served -by link_splash_screen.html (the Cobalt logo splash screen). - -Specifically, starting with an empty cache, if we navigate to -http://"$(hostname)":${port}...link_splash_screen.html?redirect=yes , -a new splash screen should be cached to the cache directory -(beforeunload.html). If we then navigate to -http://"$(hostname)":${port}...link_splash_screen.html (without the -query parameter), we should see no splash screen displayed at first -run, and cobalt_splash_screen.html should be cached to the cache -directory. On a second navigation to -http://"$(hostname)":${port}...link_splash_screen.html (without the -query parameter), we should see the cached Cobalt logo splash screen -displayed. - -In short, although redirected.html serves a splash screen it should -not be seen when we redirect there via -link_splash_screen.html?redirect=yes and certainly not when we -navigate to link_splash_screen.html without query parameters. The -splash screen cached by redirected.html should only be seen when we -navigate directly to redirected.html. - -It is OK if navigating to link_splash_screen.html first, then shows -the Cobalt logo splash screen on the next navigation to -link_splash_screen.html?redirect=yes. -""" -import SimpleHTTPServer -import SocketServer - - -class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler): - - def do_GET(self): - path = self.path - print 'path = ' + path - redirect_from = 'link_splash_screen.html?redirect=yes' - redirect_to = 'redirected.html' - if redirect_from in path: - self.send_response(302) - self.send_header('Location', path.replace(redirect_from, redirect_to)) - self.end_headers() - else: - return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) - - -port = 8000 -while True: - try: - handler = SocketServer.TCPServer(('', port), Handler) - print 'seving port ' + str(port) - handler.serve_forever() - except SocketServer.socket.error as exc: - port += 1 - print 'trying port ' + str(port)
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc index dbb2ba4..180ba6a 100644 --- a/src/cobalt/browser/web_module.cc +++ b/src/cobalt/browser/web_module.cc
@@ -242,6 +242,8 @@ void Resume(render_tree::ResourceProvider* resource_provider); void ReduceMemory(); + void GetJavaScriptHeapStatistics( + const JavaScriptHeapStatisticsCallback& callback); void LogScriptError(const base::SourceLocation& source_location, const std::string& error_message); @@ -269,13 +271,17 @@ void OnRenderTreeProduced(const LayoutResults& layout_results); // Called by the Renderer on the Renderer thread when it rasterizes a render - // tree with this callback attached. + // tree with this callback attached. It includes the time the render tree was + // produced. void OnRenderTreeRasterized( - scoped_refptr<base::MessageLoopProxy> web_module_message_loop); + scoped_refptr<base::MessageLoopProxy> web_module_message_loop, + const base::TimeTicks& produced_time); // WebModule thread handling of the OnRenderTreeRasterized() callback. It - // includes the time that the rasterization callback was initially received. - void ProcessOnRenderTreeRasterized(const base::TimeTicks& on_rasterize_time); + // includes the time that the render tree was produced and the time that the + // render tree was rasterized. + void ProcessOnRenderTreeRasterized(const base::TimeTicks& produced_time, + const base::TimeTicks& rasterized_time); void OnCspPolicyChanged(); @@ -302,6 +308,9 @@ // Initializes the ResourceProvider and dependent resources. void SetResourceProvider(render_tree::ResourceProvider* resource_provider); + void OnStartDispatchEvent(const scoped_refptr<dom::Event>& event); + void OnStopDispatchEvent(const scoped_refptr<dom::Event>& event); + // Thread checker ensures all calls to the WebModule are made from the same // thread that it is created in. base::ThreadChecker thread_checker_; @@ -311,6 +320,9 @@ // Simple flag used for basic error checking. bool is_running_; + // The most recent time that a new render tree was produced. + base::TimeTicks last_render_tree_produced_time_; + // Whether or not a render tree has been produced but not yet rasterized. base::CVal<bool, base::CValPublic> is_render_tree_rasterization_pending_; @@ -444,8 +456,8 @@ } } - void OnMutation() override{}; - void OnFocusChanged() override{}; + void OnMutation() override {} + void OnFocusChanged() override {} private: ClosureVector loaded_callbacks_; @@ -611,6 +623,9 @@ data.window_close_callback, data.window_minimize_callback, data.options.on_screen_keyboard_bridge, data.options.camera_3d, media_session_client_->GetMediaSession(), + base::Bind(&WebModule::Impl::OnStartDispatchEvent, + base::Unretained(this)), + base::Bind(&WebModule::Impl::OnStopDispatchEvent, base::Unretained(this)), data.options.csp_insecure_allowed_token, data.dom_max_element_depth, data.options.video_playback_rate_multiplier, #if defined(ENABLE_TEST_RUNNER) @@ -620,8 +635,7 @@ #else dom::Window::kClockTypeSystemTime, #endif - splash_screen_cache_callback, - system_caption_settings_); + splash_screen_cache_callback, system_caption_settings_); DCHECK(window_); window_weak_ = base::AsWeakPtr(window_.get()); @@ -735,17 +749,11 @@ DCHECK(is_running_); DCHECK(window_); - web_module_stat_tracker_->OnStartInjectEvent(event); - if (element) { element->DispatchEvent(event); } else { window_->InjectEvent(event); } - - web_module_stat_tracker_->OnEndInjectEvent( - window_->HasPendingAnimationFrameCallbacks(), - layout_manager_->IsRenderTreePending()); } #if SB_HAS(ON_SCREEN_KEYBOARD) @@ -860,13 +868,17 @@ DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(is_running_); + last_render_tree_produced_time_ = base::TimeTicks::Now(); is_render_tree_rasterization_pending_ = true; - web_module_stat_tracker_->OnRenderTreeProduced(); + + web_module_stat_tracker_->OnRenderTreeProduced( + last_render_tree_produced_time_); LayoutResults layout_results_with_callback( layout_results.render_tree, layout_results.layout_time, base::Bind(&WebModule::Impl::OnRenderTreeRasterized, - base::Unretained(this), base::MessageLoopProxy::current())); + base::Unretained(this), base::MessageLoopProxy::current(), + last_render_tree_produced_time_)); #if defined(ENABLE_DEBUG_CONSOLE) debug_overlay_->OnRenderTreeProduced(layout_results_with_callback); @@ -876,17 +888,23 @@ } void WebModule::Impl::OnRenderTreeRasterized( - scoped_refptr<base::MessageLoopProxy> web_module_message_loop) { + scoped_refptr<base::MessageLoopProxy> web_module_message_loop, + const base::TimeTicks& produced_time) { web_module_message_loop->PostTask( FROM_HERE, base::Bind(&WebModule::Impl::ProcessOnRenderTreeRasterized, - base::Unretained(this), base::TimeTicks::Now())); + base::Unretained(this), produced_time, + base::TimeTicks::Now())); } void WebModule::Impl::ProcessOnRenderTreeRasterized( - const base::TimeTicks& on_rasterize_time) { + const base::TimeTicks& produced_time, + const base::TimeTicks& rasterized_time) { DCHECK(thread_checker_.CalledOnValidThread()); - web_module_stat_tracker_->OnRenderTreeRasterized(on_rasterize_time); - is_render_tree_rasterization_pending_ = false; + web_module_stat_tracker_->OnRenderTreeRasterized(produced_time, + rasterized_time); + if (produced_time >= last_render_tree_produced_time_) { + is_render_tree_rasterization_pending_ = false; + } } #if defined(ENABLE_PARTIAL_LAYOUT_CONTROL) @@ -1030,6 +1048,18 @@ } } +void WebModule::Impl::OnStartDispatchEvent( + const scoped_refptr<dom::Event>& event) { + web_module_stat_tracker_->OnStartDispatchEvent(event); +} + +void WebModule::Impl::OnStopDispatchEvent( + const scoped_refptr<dom::Event>& event) { + web_module_stat_tracker_->OnStopDispatchEvent( + event, window_->HasPendingAnimationFrameCallbacks(), + layout_manager_->IsRenderTreePending()); +} + void WebModule::Impl::Start(render_tree::ResourceProvider* resource_provider) { TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Start()"); SetResourceProvider(resource_provider); @@ -1124,6 +1154,16 @@ } } +void WebModule::Impl::GetJavaScriptHeapStatistics( + const JavaScriptHeapStatisticsCallback& callback) { + TRACE_EVENT0("cobalt::browser", + "WebModule::Impl::GetJavaScriptHeapStatistics()"); + DCHECK(thread_checker_.CalledOnValidThread()); + script::HeapStatistics heap_statistics = + javascript_engine_->GetHeapStatistics(); + callback.Run(heap_statistics); +} + void WebModule::Impl::LogScriptError( const base::SourceLocation& source_location, const std::string& error_message) { @@ -1619,5 +1659,15 @@ base::Unretained(impl_.get()))); } +void WebModule::RequestJavaScriptHeapStatistics( + const JavaScriptHeapStatisticsCallback& callback) { + // Must only be called by a thread external from the WebModule thread. + DCHECK_NE(MessageLoop::current(), message_loop()); + + message_loop()->PostTask( + FROM_HERE, base::Bind(&WebModule::Impl::GetJavaScriptHeapStatistics, + base::Unretained(impl_.get()), callback)); +} + } // namespace browser } // namespace cobalt
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h index 8b712db..cc70eab 100644 --- a/src/cobalt/browser/web_module.h +++ b/src/cobalt/browser/web_module.h
@@ -218,6 +218,8 @@ OnRenderTreeProducedCallback; typedef base::Callback<void(const GURL&, const std::string&)> OnErrorCallback; typedef dom::Window::CloseCallback CloseCallback; + typedef base::Callback<void(const script::HeapStatistics&)> + JavaScriptHeapStatisticsCallback; WebModule(const GURL& initial_url, base::ApplicationState initial_application_state, @@ -313,6 +315,14 @@ // system indication that memory usage is nearing a critical level. void ReduceMemory(); + // Post a task that gets the current |script::HeapStatistics| for our + // |JavaScriptEngine| to the web module thread, and then passes that to + // |callback|. Note that |callback| will be called on the main web module + // thread. It is the responsibility of |callback| to get back to its + // intended thread should it want to. + void RequestJavaScriptHeapStatistics( + const JavaScriptHeapStatisticsCallback& callback); + private: // Data required to construct a WebModule, initialized in the constructor and // passed to |Initialize|.
diff --git a/src/cobalt/browser/web_module_stat_tracker.cc b/src/cobalt/browser/web_module_stat_tracker.cc index 601cc5b..0e93bfc 100644 --- a/src/cobalt/browser/web_module_stat_tracker.cc +++ b/src/cobalt/browser/web_module_stat_tracker.cc
@@ -30,13 +30,14 @@ WebModuleStatTracker::WebModuleStatTracker(const std::string& name, bool should_track_event_stats) - : dom_stat_tracker_(new dom::DomStatTracker(name)), - layout_stat_tracker_(new layout::LayoutStatTracker(name)), + : name_(name), should_track_event_stats_(should_track_event_stats), - current_event_type_(kEventTypeInvalid), - name_(name), + dom_stat_tracker_(new dom::DomStatTracker(name)), + layout_stat_tracker_(new layout::LayoutStatTracker(name)), event_is_processing_(StringPrintf("Event.%s.IsProcessing", name.c_str()), - false, "Nonzero when an event is being processed.") { + false, "Nonzero when an event is being processed."), + current_event_type_(kEventTypeInvalid), + current_event_dispatched_event_(nullptr) { if (should_track_event_stats_) { event_stats_list_.reserve(kNumEventTypes); for (int i = 0; i < kNumEventTypes; ++i) { @@ -54,9 +55,7 @@ stop_watch_durations_.resize(kNumStopWatchTypes, base::TimeDelta()); } -WebModuleStatTracker::~WebModuleStatTracker() { EndCurrentEvent(false); } - -void WebModuleStatTracker::OnStartInjectEvent( +void WebModuleStatTracker::OnStartDispatchEvent( const scoped_refptr<dom::Event>& event) { if (!should_track_event_stats_) { return; @@ -83,72 +82,114 @@ // If this is a valid event type, then start tracking it. if (current_event_type_ != kEventTypeInvalid) { + DCHECK(!event_is_processing_); + event_is_processing_ = true; - event_start_time_ = base::TimeTicks::Now(); + current_event_dispatched_event_ = event; + current_event_start_time_ = base::TimeTicks::Now(); + current_event_render_tree_produced_time_ = base::TimeTicks(); - dom_stat_tracker_->OnStartEvent(); - layout_stat_tracker_->OnStartEvent(); + dom_stat_tracker_->StartTrackingEvent(); + layout_stat_tracker_->StartTrackingEvent(); - stop_watch_durations_[kStopWatchTypeEvent] = base::TimeDelta(); - stop_watch_durations_[kStopWatchTypeInjectEvent] = base::TimeDelta(); - - stop_watches_[kStopWatchTypeEvent].Start(); - stop_watches_[kStopWatchTypeInjectEvent].Start(); + stop_watch_durations_[kStopWatchTypeDispatchEvent] = base::TimeDelta(); + stop_watches_[kStopWatchTypeDispatchEvent].Start(); } } -void WebModuleStatTracker::OnEndInjectEvent( +void WebModuleStatTracker::OnStopDispatchEvent( + const scoped_refptr<dom::Event>& event, bool are_animation_frame_callbacks_pending, bool is_new_render_tree_pending) { - // If the injection isn't currently being timed, then this event injection - // isn't being tracked. Simply return. - if (!stop_watches_[kStopWatchTypeInjectEvent].IsCounting()) { + // Verify that this dispatched event is the one currently being tracked. + if (event != current_event_dispatched_event_) { return; } - stop_watches_[kStopWatchTypeInjectEvent].Stop(); + current_event_dispatched_event_ = nullptr; + stop_watches_[kStopWatchTypeDispatchEvent].Stop(); - if (!are_animation_frame_callbacks_pending && !is_new_render_tree_pending) { - EndCurrentEvent(false); + if (!are_animation_frame_callbacks_pending && !is_new_render_tree_pending && + current_event_render_tree_produced_time_.is_null()) { + EndCurrentEvent(base::TimeTicks::Now()); } } void WebModuleStatTracker::OnRanAnimationFrameCallbacks( bool is_new_render_tree_pending) { - if (!is_new_render_tree_pending) { - EndCurrentEvent(false); + if (current_event_type_ == kEventTypeInvalid) { + return; + } + + if (!is_new_render_tree_pending && + current_event_render_tree_produced_time_.is_null()) { + EndCurrentEvent(base::TimeTicks::Now()); } } -void WebModuleStatTracker::OnRenderTreeProduced() { EndCurrentEvent(true); } +void WebModuleStatTracker::OnRenderTreeProduced( + const base::TimeTicks& produced_time) { + // Flush the periodic tracking regardless of whether or not there is a current + // event. Periodic tracking is not tied to events. + dom_stat_tracker_->FlushPeriodicTracking(); + layout_stat_tracker_->FlushPeriodicTracking(); + + if (current_event_type_ == kEventTypeInvalid) { + return; + } + + // Event tracking stops when the first render tree being produced. At that + // point, processing switches to the rasterizer thread and any subsequent + // dom/layout work that occurs will not be associated with the event's first + // render tree. + if (current_event_render_tree_produced_time_.is_null()) { + current_event_render_tree_produced_time_ = produced_time; + dom_stat_tracker_->StopTrackingEvent(); + layout_stat_tracker_->StopTrackingEvent(); + } +} void WebModuleStatTracker::OnRenderTreeRasterized( - const base::TimeTicks& on_rasterize_time) { - for (const auto& event_stats : event_stats_list_) { - if (event_stats->is_render_tree_rasterization_pending) { - event_stats->is_render_tree_rasterization_pending = false; - event_stats->duration_renderer_rasterize_render_tree_delay = - on_rasterize_time - event_stats->start_time; - } + const base::TimeTicks& produced_time, + const base::TimeTicks& rasterized_time) { + if (current_event_type_ == kEventTypeInvalid) { + return; + } + + // End the event if the event's render tree has already been produced and + // the rasterized render tree is not older than the event's render tree. + if (!current_event_render_tree_produced_time_.is_null() && + produced_time >= current_event_render_tree_produced_time_) { + EndCurrentEvent(rasterized_time); } } WebModuleStatTracker::EventStats::EventStats(const std::string& name) - : produced_render_tree( + : start_time(StringPrintf("Event.Time.%s.Start", name.c_str()), 0, + "The time that the event started."), + produced_render_tree( StringPrintf("Event.%s.ProducedRenderTree", name.c_str()), false, "Nonzero when the event produced a render tree."), - count_dom_html_elements_created( + count_dom_html_element( + StringPrintf("Event.Count.%s.DOM.HtmlElement", name.c_str()), 0, + "Total number of HTML elements."), + count_dom_html_element_created( StringPrintf("Event.Count.%s.DOM.HtmlElement.Created", name.c_str()), - 0, "Number of HTML elements created."), - count_dom_html_elements_destroyed( + 0, "Total number of HTML elements created."), + count_dom_html_element_destroyed( StringPrintf("Event.Count.%s.DOM.HtmlElement.Destroyed", name.c_str()), - 0, "Number of HTML elements destroyed."), - count_dom_html_elements_added( - StringPrintf("Event.Count.%s.DOM.HtmlElement.Added", name.c_str()), 0, - "Number of HTML elements added to document."), - count_dom_html_elements_removed( - StringPrintf("Event.Count.%s.DOM.HtmlElement.Removed", name.c_str()), + 0, "Total number of HTML elements destroyed."), + count_dom_html_element_document( + StringPrintf("Event.Count.%s.DOM.HtmlElement.Document", name.c_str()), + 0, "Number of HTML elements in document."), + count_dom_html_element_document_added( + StringPrintf("Event.Count.%s.DOM.HtmlElement.Document.Added", + name.c_str()), + 0, "Number of HTML elements added to document."), + count_dom_html_element_document_removed( + StringPrintf("Event.Count.%s.DOM.HtmlElement.Document.Removed", + name.c_str()), 0, "Number of HTML elements removed from document."), count_dom_update_matching_rules( StringPrintf("Event.Count.%s.DOM.HtmlElement.UpdateMatchingRules", @@ -170,32 +211,34 @@ name.c_str()), 0, "Number of pseudo elements that had their computed style generated."), - count_layout_boxes_created( + count_layout_box(StringPrintf("Event.Count.%s.Layout.Box", name.c_str()), + 0, "Number of layout boxes."), + count_layout_box_created( StringPrintf("Event.Count.%s.Layout.Box.Created", name.c_str()), 0, - "Number of boxes created."), - count_layout_boxes_destroyed( + "Number of layout boxes created."), + count_layout_box_destroyed( StringPrintf("Event.Count.%s.Layout.Box.Destroyed", name.c_str()), 0, - "Number of boxes destroyed."), + "Number of layout boxes destroyed."), count_layout_update_size( StringPrintf("Event.Count.%s.Layout.Box.UpdateSize", name.c_str()), 0, - "Number of boxes that had their size updated."), + "Number of layout boxes that had their size updated."), count_layout_render_and_animate( StringPrintf("Event.Count.%s.Layout.Box.RenderAndAnimate", name.c_str()), - 0, "Number of boxes that had their render tree node updated."), + 0, "Number of layout boxes that had their render tree node updated."), count_layout_update_cross_references( StringPrintf("Event.Count.%s.Layout.Box.UpdateCrossReferences", name.c_str()), - 0, "Number of boxes that had their cross references updated."), + 0, "Number of layout boxes that had their cross references updated."), duration_total(StringPrintf("Event.Duration.%s", name.c_str()), base::TimeDelta(), "Total duration of the event (in microseconds). This is " - "the time elapsed from the event injection until the " + "the time elapsed from the event dispatch until the " "render tree is produced."), - duration_dom_inject_event( - StringPrintf("Event.Duration.%s.DOM.InjectEvent", name.c_str()), + duration_dom_dispatch_event( + StringPrintf("Event.Duration.%s.DOM.DispatchEvent", name.c_str()), base::TimeDelta(), - "Injection duration, which includes JS, for event (in " + "Dispatch duration, which includes JS, for event (in " "microseconds). This does not include subsequent DOM and Layout " "processing."), duration_dom_run_animation_frame_callbacks( @@ -228,19 +271,16 @@ name.c_str()), base::TimeDelta(), "RenderAndAnimate duration for event (in microseconds)."), + duration_renderer_rasterize( + StringPrintf("Event.Duration.%s.Renderer.Rasterize", name.c_str()), + base::TimeDelta(), "Rasterize duration for event (in microseconds).") #if defined(ENABLE_WEBDRIVER) + , value_dictionary( StringPrintf("Event.%s.ValueDictionary", name.c_str()), "{}", - "All event values represented as a dictionary in a string."), + "All event values represented as a dictionary in a string.") #endif // ENABLE_WEBDRIVER - // Post-event delays that are not included in the value dictionary. - duration_renderer_rasterize_render_tree_delay( - StringPrintf("Event.Duration.%s.Renderer.Rasterize.RenderTreeDelay", - name.c_str()), - base::TimeDelta(), - "The delay from the event starting until its render tree is first " - "rasterized (in microseconds)."), - is_render_tree_rasterization_pending(false) { +{ } bool WebModuleStatTracker::IsStopWatchEnabled(int /*id*/) const { return true; } @@ -250,53 +290,71 @@ stop_watch_durations_[static_cast<size_t>(id)] += time_elapsed; } -void WebModuleStatTracker::EndCurrentEvent(bool was_render_tree_produced) { +void WebModuleStatTracker::EndCurrentEvent(base::TimeTicks event_end_time) { if (current_event_type_ == kEventTypeInvalid) { - dom_stat_tracker_->OnEndEvent(); - layout_stat_tracker_->OnEndEvent(); return; } - event_is_processing_ = false; + DCHECK(event_is_processing_); + DCHECK(!current_event_start_time_.is_null()); - stop_watches_[kStopWatchTypeEvent].Stop(); + // If no render tree was produced by this event, then tracking stops at the + // end of the event; otherwise, it already stopped when the render tree was + // produced. + if (current_event_render_tree_produced_time_.is_null()) { + dom_stat_tracker_->StopTrackingEvent(); + layout_stat_tracker_->StopTrackingEvent(); + } + + // If a render tree was produced by this event, then the event is ending with + // the render tree's rasterization; otherwise, there was no rasterization. + base::TimeDelta renderer_rasterize_duration = + !current_event_render_tree_produced_time_.is_null() + ? event_end_time - current_event_render_tree_produced_time_ + : base::TimeDelta(); EventStats* event_stats = event_stats_list_[current_event_type_]; - event_stats->start_time = event_start_time_; - event_stats->produced_render_tree = was_render_tree_produced; + event_stats->start_time = current_event_start_time_.ToInternalValue(); + event_stats->produced_render_tree = + !current_event_render_tree_produced_time_.is_null(); // Update event counts - event_stats->count_dom_html_elements_created = - dom_stat_tracker_->html_elements_created_count(); - event_stats->count_dom_html_elements_destroyed = - dom_stat_tracker_->html_elements_destroyed_count(); - event_stats->count_dom_html_elements_added = - dom_stat_tracker_->html_elements_added_to_document_count(); - event_stats->count_dom_html_elements_removed = - dom_stat_tracker_->html_elements_removed_from_document_count(); + event_stats->count_dom_html_element = + dom_stat_tracker_->EventCountHtmlElement(); + event_stats->count_dom_html_element_created = + dom_stat_tracker_->event_count_html_element_created(); + event_stats->count_dom_html_element_destroyed = + dom_stat_tracker_->event_count_html_element_destroyed(); + event_stats->count_dom_html_element_document = + dom_stat_tracker_->EventCountHtmlElementDocument(); + event_stats->count_dom_html_element_document_added = + dom_stat_tracker_->event_count_html_element_document_added(); + event_stats->count_dom_html_element_document_removed = + dom_stat_tracker_->event_count_html_element_document_removed(); event_stats->count_dom_update_matching_rules = - dom_stat_tracker_->update_matching_rules_count(); + dom_stat_tracker_->event_count_update_matching_rules(); event_stats->count_dom_update_computed_style = - dom_stat_tracker_->update_computed_style_count(); + dom_stat_tracker_->event_count_update_computed_style(); event_stats->count_dom_generate_html_element_computed_style = - dom_stat_tracker_->generate_html_element_computed_style_count(); + dom_stat_tracker_->event_count_generate_html_element_computed_style(); event_stats->count_dom_generate_pseudo_element_computed_style = - dom_stat_tracker_->generate_pseudo_element_computed_style_count(); - event_stats->count_layout_boxes_created = - layout_stat_tracker_->boxes_created_count(); - event_stats->count_layout_boxes_destroyed = - layout_stat_tracker_->boxes_destroyed_count(); + dom_stat_tracker_->event_count_generate_pseudo_element_computed_style(); + event_stats->count_layout_box = layout_stat_tracker_->EventCountBox(); + event_stats->count_layout_box_created = + layout_stat_tracker_->event_count_box_created(); + event_stats->count_layout_box_destroyed = + layout_stat_tracker_->event_count_box_destroyed(); event_stats->count_layout_update_size = - layout_stat_tracker_->update_size_count(); + layout_stat_tracker_->event_count_update_size(); event_stats->count_layout_render_and_animate = - layout_stat_tracker_->render_and_animate_count(); + layout_stat_tracker_->event_count_render_and_animate(); event_stats->count_layout_update_cross_references = - layout_stat_tracker_->update_cross_references_count(); + layout_stat_tracker_->event_count_update_cross_references(); // Update event durations - event_stats->duration_total = stop_watch_durations_[kStopWatchTypeEvent]; - event_stats->duration_dom_inject_event = - stop_watch_durations_[kStopWatchTypeInjectEvent]; + event_stats->duration_total = event_end_time - current_event_start_time_; + event_stats->duration_dom_dispatch_event = + stop_watch_durations_[kStopWatchTypeDispatchEvent]; event_stats->duration_dom_run_animation_frame_callbacks = dom_stat_tracker_->GetStopWatchTypeDuration( dom::DomStatTracker::kStopWatchTypeRunAnimationFrameCallbacks); @@ -315,58 +373,51 @@ event_stats->duration_layout_render_and_animate = layout_stat_tracker_->GetStopWatchTypeDuration( layout::LayoutStatTracker::kStopWatchTypeRenderAndAnimate); + event_stats->duration_renderer_rasterize = renderer_rasterize_duration; #if defined(ENABLE_WEBDRIVER) - // Include the event's numbers in the total counts. - int html_elements_count = dom_stat_tracker_->html_elements_count() + - dom_stat_tracker_->html_elements_created_count() - - dom_stat_tracker_->html_elements_destroyed_count(); - int document_html_elements_count = - dom_stat_tracker_->document_html_elements_count() + - dom_stat_tracker_->html_elements_added_to_document_count() - - dom_stat_tracker_->html_elements_removed_from_document_count(); - int layout_boxes_count = layout_stat_tracker_->total_boxes() + - layout_stat_tracker_->boxes_created_count() - - layout_stat_tracker_->boxes_destroyed_count(); - - // When the Webdriver is enabled, all of the event's values are stored within - // a single string representing a dictionary of key-value pairs. This allows - // the Webdriver to query a single CVal to retrieve all of the event's values. + // When the Webdriver is enabled, all of the event's values are stored + // within a single string representing a dictionary of key-value pairs. + // This allows the Webdriver to query a single CVal to retrieve all of the + // event's values. std::ostringstream oss; oss << "{" - << "\"StartTime\":" << event_start_time_.ToInternalValue() << ", " - << "\"ProducedRenderTree\":" << was_render_tree_produced << ", " + << "\"StartTime\":" << current_event_start_time_.ToInternalValue() << ", " + << "\"ProducedRenderTree\":" + << !current_event_render_tree_produced_time_.is_null() << ", " << "\"CntDomEventListeners\":" << dom::GlobalStats::GetInstance()->GetNumEventListeners() << ", " << "\"CntDomNodes\":" << dom::GlobalStats::GetInstance()->GetNumNodes() << ", " - << "\"CntDomHtmlElements\":" << html_elements_count << ", " - << "\"CntDomDocumentHtmlElements\":" << document_html_elements_count + << "\"CntDomHtmlElements\":" << dom_stat_tracker_->EventCountHtmlElement() << ", " + << "\"CntDomDocumentHtmlElements\":" + << dom_stat_tracker_->EventCountHtmlElementDocument() << ", " << "\"CntDomHtmlElementsCreated\":" - << dom_stat_tracker_->html_elements_created_count() << ", " + << dom_stat_tracker_->event_count_html_element_created() << ", " << "\"CntDomUpdateMatchingRules\":" - << dom_stat_tracker_->update_matching_rules_count() << ", " + << dom_stat_tracker_->event_count_update_matching_rules() << ", " << "\"CntDomUpdateComputedStyle\":" - << dom_stat_tracker_->update_computed_style_count() << ", " + << dom_stat_tracker_->event_count_update_computed_style() << ", " << "\"CntDomGenerateHtmlComputedStyle\":" - << dom_stat_tracker_->generate_html_element_computed_style_count() << ", " + << dom_stat_tracker_->event_count_generate_html_element_computed_style() + << ", " << "\"CntDomGeneratePseudoComputedStyle\":" - << dom_stat_tracker_->generate_pseudo_element_computed_style_count() + << dom_stat_tracker_->event_count_generate_pseudo_element_computed_style() << ", " - << "\"CntLayoutBoxes\":" << layout_boxes_count << ", " + << "\"CntLayoutBoxes\":" << layout_stat_tracker_->EventCountBox() << ", " << "\"CntLayoutBoxesCreated\":" - << layout_stat_tracker_->boxes_created_count() << ", " - << "\"CntLayoutUpdateSize\":" << layout_stat_tracker_->update_size_count() - << ", " + << layout_stat_tracker_->event_count_box_created() << ", " + << "\"CntLayoutUpdateSize\":" + << layout_stat_tracker_->event_count_update_size() << ", " << "\"CntLayoutRenderAndAnimate\":" - << layout_stat_tracker_->render_and_animate_count() << ", " + << layout_stat_tracker_->event_count_render_and_animate() << ", " << "\"CntLayoutUpdateCrossReferences\":" - << layout_stat_tracker_->update_cross_references_count() << ", " + << layout_stat_tracker_->event_count_update_cross_references() << ", " << "\"DurTotalUs\":" - << stop_watch_durations_[kStopWatchTypeEvent].InMicroseconds() << ", " + << (event_end_time - current_event_start_time_).InMicroseconds() << ", " << "\"DurDomInjectEventUs\":" - << stop_watch_durations_[kStopWatchTypeInjectEvent].InMicroseconds() + << stop_watch_durations_[kStopWatchTypeDispatchEvent].InMicroseconds() << ", " << "\"DurDomRunAnimationFrameCallbacksUs\":" << dom_stat_tracker_ @@ -403,22 +454,14 @@ ->GetStopWatchTypeDuration( layout::LayoutStatTracker::kStopWatchTypeRenderAndAnimate) .InMicroseconds() - << "}"; + << ", " + << "\"DurRendererRasterizeUs\":" + << renderer_rasterize_duration.InMicroseconds() << "}"; event_stats->value_dictionary = oss.str(); #endif // ENABLE_WEBDRIVER - // Reset the rasterize delay. It'll be set when the rasterize callback occurs - // (if a render tree was produced). - event_stats->duration_renderer_rasterize_render_tree_delay = - base::TimeDelta(); - if (was_render_tree_produced) { - event_stats->is_render_tree_rasterization_pending = true; - } - + event_is_processing_ = false; current_event_type_ = kEventTypeInvalid; - - dom_stat_tracker_->OnEndEvent(); - layout_stat_tracker_->OnEndEvent(); } std::string WebModuleStatTracker::GetEventTypeName( @@ -434,10 +477,10 @@ return "PointerUp"; case WebModuleStatTracker::kEventTypeInvalid: case WebModuleStatTracker::kNumEventTypes: - default: - NOTREACHED(); - return "Invalid"; + break; } + NOTREACHED(); + return "Invalid"; } } // namespace browser
diff --git a/src/cobalt/browser/web_module_stat_tracker.h b/src/cobalt/browser/web_module_stat_tracker.h index 20105c1..14abbc3 100644 --- a/src/cobalt/browser/web_module_stat_tracker.h +++ b/src/cobalt/browser/web_module_stat_tracker.h
@@ -33,9 +33,8 @@ // thread use. It also owns the |DomStatTracker| and |LayoutStatTracker|. class WebModuleStatTracker : public base::StopWatchOwner { public: - WebModuleStatTracker(const std::string& name, - bool should_track_injected_events); - ~WebModuleStatTracker(); + WebModuleStatTracker(const std::string& web_module_name, + bool should_track_dispatched_events); dom::DomStatTracker* dom_stat_tracker() const { return dom_stat_tracker_.get(); @@ -45,25 +44,27 @@ return layout_stat_tracker_.get(); } - // |OnStartInjectEvent| starts event stat tracking if - // |should_track_injected_events_| is true. Otherwise, it does nothing. - void OnStartInjectEvent(const scoped_refptr<dom::Event>& event); + // |OnStartDispatchEvent| starts event stat tracking if + // |should_track_dispatched_events_| is true. Otherwise, it does nothing. + void OnStartDispatchEvent(const scoped_refptr<dom::Event>& event); - // |OnEndInjectEvent| notifies the event stat tracking that the event has - // finished being injected. If no animation frame callbacks and also no render - // tree is pending, it also ends tracking of the event. - void OnEndInjectEvent(bool are_animation_frame_callbacks_pending, - bool is_new_render_tree_pending); + // |OnStopDispatchEvent| notifies the event stat tracking that |event| has + // finished being dispatched. If this is the event currently being tracked + // and nothing is pending, then it also ends tracking of the event. + void OnStopDispatchEvent(const scoped_refptr<dom::Event>& event, + bool are_animation_frame_callbacks_pending, + bool is_new_render_tree_pending); // |OnRanAnimationFrameCallbacks| ends stat tracking for the current event // if no render tree is pending. void OnRanAnimationFrameCallbacks(bool is_new_render_tree_pending); // |OnRenderTreeProduced| ends stat tracking for the current event. - void OnRenderTreeProduced(); + void OnRenderTreeProduced(const base::TimeTicks& produced_time); // |OnRenderTreeRasterized| ends stat tracking for the current event. - void OnRenderTreeRasterized(const base::TimeTicks& on_rasterize_time); + void OnRenderTreeRasterized(const base::TimeTicks& produced_time, + const base::TimeTicks& rasterized_time); // Returns whether or not an event-based render tree has been produced but not // yet rasterized. @@ -80,37 +81,39 @@ }; enum StopWatchType { - kStopWatchTypeEvent, - kStopWatchTypeInjectEvent, + kStopWatchTypeDispatchEvent, kNumStopWatchTypes, }; struct EventStats { explicit EventStats(const std::string& name); - base::TimeTicks start_time; + base::CVal<int64, base::CValPublic> start_time; base::CVal<bool, base::CValPublic> produced_render_tree; // Count-related - base::CVal<int, base::CValPublic> count_dom_html_elements_created; - base::CVal<int, base::CValPublic> count_dom_html_elements_destroyed; - base::CVal<int, base::CValPublic> count_dom_html_elements_added; - base::CVal<int, base::CValPublic> count_dom_html_elements_removed; + base::CVal<int, base::CValPublic> count_dom_html_element; + base::CVal<int, base::CValPublic> count_dom_html_element_created; + base::CVal<int, base::CValPublic> count_dom_html_element_destroyed; + base::CVal<int, base::CValPublic> count_dom_html_element_document; + base::CVal<int, base::CValPublic> count_dom_html_element_document_added; + base::CVal<int, base::CValPublic> count_dom_html_element_document_removed; base::CVal<int, base::CValPublic> count_dom_update_matching_rules; base::CVal<int, base::CValPublic> count_dom_update_computed_style; base::CVal<int, base::CValPublic> count_dom_generate_html_element_computed_style; base::CVal<int, base::CValPublic> count_dom_generate_pseudo_element_computed_style; - base::CVal<int, base::CValPublic> count_layout_boxes_created; - base::CVal<int, base::CValPublic> count_layout_boxes_destroyed; + base::CVal<int, base::CValPublic> count_layout_box; + base::CVal<int, base::CValPublic> count_layout_box_created; + base::CVal<int, base::CValPublic> count_layout_box_destroyed; base::CVal<int, base::CValPublic> count_layout_update_size; base::CVal<int, base::CValPublic> count_layout_render_and_animate; base::CVal<int, base::CValPublic> count_layout_update_cross_references; // Duration-related base::CVal<base::TimeDelta, base::CValPublic> duration_total; - base::CVal<base::TimeDelta, base::CValPublic> duration_dom_inject_event; + base::CVal<base::TimeDelta, base::CValPublic> duration_dom_dispatch_event; base::CVal<base::TimeDelta, base::CValPublic> duration_dom_run_animation_frame_callbacks; base::CVal<base::TimeDelta, base::CValPublic> @@ -122,6 +125,7 @@ duration_layout_update_used_sizes; base::CVal<base::TimeDelta, base::CValPublic> duration_layout_render_and_animate; + base::CVal<base::TimeDelta, base::CValPublic> duration_renderer_rasterize; #if defined(ENABLE_WEBDRIVER) // A string containing all of the event's values, excluding post-event @@ -129,14 +133,6 @@ // and is only enabled with it. base::CVal<std::string> value_dictionary; #endif // ENABLE_WEBDRIVER - - // Post-event delay-related - base::CVal<base::TimeDelta, base::CValPublic> - duration_renderer_rasterize_render_tree_delay; - - // Whether or not the first rasterization for the render tree produced by - // the event is pending. - bool is_render_tree_rasterization_pending; }; // From base::StopWatchOwner @@ -145,28 +141,32 @@ // End the current event if one is active. This triggers an update of all // |EventStats| for the event. - void EndCurrentEvent(bool was_render_tree_produced); + void EndCurrentEvent(base::TimeTicks end_time); static std::string GetEventTypeName(EventType event_type); + const std::string name_; + const bool should_track_event_stats_; + // Web module owns the dom and layout stat trackers. scoped_ptr<dom::DomStatTracker> dom_stat_tracker_; scoped_ptr<layout::LayoutStatTracker> layout_stat_tracker_; // Event-related - const bool should_track_event_stats_; + base::CVal<bool> event_is_processing_; EventType current_event_type_; + // Raw pointer to the current event. This is used to verify that the event in + // |OnStopDispatchEvent| is the dispatched event being tracked. + dom::Event* current_event_dispatched_event_; + base::TimeTicks current_event_start_time_; + base::TimeTicks current_event_render_tree_produced_time_; + // Each individual |EventType| has its own entry in the vector. ScopedVector<EventStats> event_stats_list_; // Stop watch-related std::vector<base::StopWatch> stop_watches_; std::vector<base::TimeDelta> stop_watch_durations_; - - std::string name_; - - base::CVal<bool> event_is_processing_; - base::TimeTicks event_start_time_; }; } // namespace browser
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id index 04ded3e..f31b753 100644 --- a/src/cobalt/build/build.id +++ b/src/cobalt/build/build.id
@@ -1 +1 @@ -140175 \ No newline at end of file +154703 \ No newline at end of file
diff --git a/src/cobalt/build/build_config.h b/src/cobalt/build/build_config.h index 573de6e..c50057b 100644 --- a/src/cobalt/build/build_config.h +++ b/src/cobalt/build/build_config.h
@@ -60,6 +60,12 @@ #endif // COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P < // (COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P + // COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) +#if COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P < \ + COBALT_MEDIA_BUFFER_INITIAL_CAPACITY +#error cobalt_media_buffer_max_capacity_1080p has to be greater than \ + cobalt_media_buffer_initial_capacity +#endif // COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P < + // COBALT_MEDIA_BUFFER_INITIAL_CAPACITY #endif // COBALT_MEDIA_BUFFER_MAX_CAPACITY_1080P != 0 #if COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K != 0 @@ -72,6 +78,11 @@ #endif // COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K < // (COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K + // COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET) +#if COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K < COBALT_MEDIA_BUFFER_INITIAL_CAPACITY +#error cobalt_media_buffer_max_capacity_4k has to be greater than \ + cobalt_media_buffer_initial_capacity +#endif // COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K < + // COBALT_MEDIA_BUFFER_INITIAL_CAPACITY #endif // COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K != 0 #endif // COBALT_BUILD_BUILD_CONFIG_H_
diff --git a/src/cobalt/build/cobalt_configuration.gypi b/src/cobalt/build/cobalt_configuration.gypi index 950aad0..4a08129 100644 --- a/src/cobalt/build/cobalt_configuration.gypi +++ b/src/cobalt/build/cobalt_configuration.gypi
@@ -220,6 +220,13 @@ # The URL of default build time splash screen - see # cobalt/doc/splash_screen.md for information about this. 'fallback_splash_screen_url%': 'none', + # The path to a splash screen to copy into content/data/web which can be + # accessed via a file URL starting with + # "file:///cobalt/browser/splash_screen/". If '', no file is copied. + 'cobalt_splash_screen_file%': '', + + # Some platforms have difficulty linking snapshot_app_stats + 'build_snapshot_app_stats%': 1, # Cache parameters @@ -400,12 +407,11 @@ # video resolution is no larger than 1080p. If 0, then memory can grow # without bound. This must be larger than sum of 1080p video budget and non- # video budget. - 'cobalt_media_buffer_max_capacity_1080p%': 36 * 1024 * 1024, + 'cobalt_media_buffer_max_capacity_1080p%': 0, # The maximum amount of memory that will be used to store media buffers when - # video resolution is no larger than 4k. If 0, then memory can grow - # without bound. This must be larger than sum of 4k video budget and non- - # video budget. - 'cobalt_media_buffer_max_capacity_4k%': 65 * 1024 * 1024, + # video resolution is 4k. If 0, then memory can grow without bound. This + # must be larger than sum of 4k video budget and non- video budget. + 'cobalt_media_buffer_max_capacity_4k%': 0, # When the media stack needs more memory to store media buffers, it will # allocate extra memory in units of |cobalt_media_buffer_allocation_unit|. @@ -453,6 +459,17 @@ # significant difficulty if this value is too low. 'cobalt_media_buffer_video_budget_4k%': 60 * 1024 * 1024, + # Specifies the duration threshold of media source garbage collection. When + # the accumulated duration in a source buffer exceeds this value, the media + # source implementation will try to eject existing buffers from the cache. + # This is usually triggered when the video being played has a simple content + # and the encoded data is small. In such case this can limit how much is + # allocated for the book keeping data of the media buffers and avoid OOM of + # system heap. + # This should be set to 170 for most of the platforms. But it can be + # further reduced on systems with extremely low memory. + 'cobalt_media_source_garbage_collection_duration_threshold_in_seconds%': 170, + 'compiler_flags_host': [ '-D__LB_HOST__', # TODO: Is this still needed? ], @@ -467,9 +484,9 @@ 'ENABLE_DEBUG_COMMAND_LINE_SWITCHES', 'ENABLE_DEBUG_C_VAL', 'ENABLE_DEBUG_CONSOLE', - 'ENABLE_DIR_SOURCE_ROOT_ACCESS', 'ENABLE_IGNORE_CERTIFICATE_ERRORS', 'ENABLE_PARTIAL_LAYOUT_CONTROL', + 'ENABLE_TEST_DATA', 'ENABLE_TEST_RUNNER', '__LB_SHELL__ENABLE_SCREENSHOT__', @@ -487,9 +504,9 @@ 'ENABLE_DEBUG_COMMAND_LINE_SWITCHES', 'ENABLE_DEBUG_C_VAL', 'ENABLE_DEBUG_CONSOLE', - 'ENABLE_DIR_SOURCE_ROOT_ACCESS', 'ENABLE_IGNORE_CERTIFICATE_ERRORS', 'ENABLE_PARTIAL_LAYOUT_CONTROL', + 'ENABLE_TEST_DATA', 'ENABLE_TEST_RUNNER', '__LB_SHELL__ENABLE_SCREENSHOT__', '__LB_SHELL__FORCE_LOGGING__', @@ -503,9 +520,9 @@ 'ENABLE_DEBUG_COMMAND_LINE_SWITCHES', 'ENABLE_DEBUG_C_VAL', 'ENABLE_DEBUG_CONSOLE', - 'ENABLE_DIR_SOURCE_ROOT_ACCESS', 'ENABLE_IGNORE_CERTIFICATE_ERRORS', 'ENABLE_PARTIAL_LAYOUT_CONTROL', + 'ENABLE_TEST_DATA', 'ENABLE_TEST_RUNNER', '__LB_SHELL__ENABLE_SCREENSHOT__', ], @@ -532,6 +549,7 @@ 'COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET=<(cobalt_media_buffer_non_video_budget)', 'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P=<(cobalt_media_buffer_video_budget_1080p)', 'COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K=<(cobalt_media_buffer_video_budget_4k)', + 'COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS=<(cobalt_media_source_garbage_collection_duration_threshold_in_seconds)', ], 'conditions': [ ['cobalt_media_source_2016 == 1', { @@ -563,11 +581,6 @@ 'DIAL_SERVER', ], }], - ['enable_file_scheme == 1', { - 'defines': [ - 'COBALT_ENABLE_FILE_SCHEME', - ], - }], ['enable_spdy == 0', { 'defines': [ 'COBALT_DISABLE_SPDY', @@ -586,7 +599,6 @@ 'cobalt_copy_debug_console': 1, 'enable_about_scheme': 1, 'enable_fake_microphone': 1, - 'enable_file_scheme': 1, 'enable_network_logging': 1, 'enable_remote_debugging%': 1, 'enable_screenshot': 1, @@ -598,7 +610,6 @@ 'cobalt_copy_debug_console': 0, 'enable_about_scheme': 0, 'enable_fake_microphone': 0, - 'enable_file_scheme': 0, 'enable_network_logging': 0, 'enable_remote_debugging%': 0, 'enable_screenshot': 0,
diff --git a/src/cobalt/build/config/BUILD.gn b/src/cobalt/build/config/BUILD.gn index 09ff32f..b54d544 100644 --- a/src/cobalt/build/config/BUILD.gn +++ b/src/cobalt/build/config/BUILD.gn
@@ -39,6 +39,7 @@ "COBALT_MEDIA_BUFFER_NON_VIDEO_BUDGET=$cobalt_media_buffer_non_video_budget", "COBALT_MEDIA_BUFFER_VIDEO_BUDGET_1080P=$cobalt_media_buffer_video_budget_1080p", "COBALT_MEDIA_BUFFER_VIDEO_BUDGET_4K=$cobalt_media_buffer_video_budget_4k", + "COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS=$cobalt_media_source_garbage_collection_duration_threshold_in_seconds", # From common.gypi "USE_OPENSSL=1", @@ -62,10 +63,6 @@ defines += [ "DIAL_SERVER" ] } - if (enable_file_scheme) { - defines += [ "COBALT_ENABLE_FILE_SCHEME" ] - } - if (!enable_spdy) { defines += [ "COBALT_DISABLE_SPDY" ] } @@ -107,9 +104,9 @@ "ENABLE_DEBUG_COMMAND_LINE_SWITCHES", "ENABLE_DEBUG_C_VAL", "ENABLE_DEBUG_CONSOLE", - "ENABLE_DIR_SOURCE_ROOT_ACCESS", "ENABLE_IGNORE_CERTIFICATE_ERRORS", "ENABLE_PARTIAL_LAYOUT_CONTROL", + "ENABLE_TEST_DATA", "ENABLE_TEST_RUNNER", "__LB_SHELL__ENABLE_SCREENSHOT__", "__LB_SHELL__FORCE_LOGGING__", # TODO: Rename to COBALT_LOGGING_ENABLED. @@ -127,9 +124,9 @@ "ENABLE_DEBUG_COMMAND_LINE_SWITCHES", "ENABLE_DEBUG_C_VAL", "ENABLE_DEBUG_CONSOLE", - "ENABLE_DIR_SOURCE_ROOT_ACCESS", "ENABLE_IGNORE_CERTIFICATE_ERRORS", "ENABLE_PARTIAL_LAYOUT_CONTROL", + "ENABLE_TEST_DATA", "ENABLE_TEST_RUNNER", "__LB_SHELL__ENABLE_SCREENSHOT__", "__LB_SHELL__FORCE_LOGGING__", @@ -146,9 +143,9 @@ "ENABLE_DEBUG_COMMAND_LINE_SWITCHES", "ENABLE_DEBUG_C_VAL", "ENABLE_DEBUG_CONSOLE", - "ENABLE_DIR_SOURCE_ROOT_ACCESS", "ENABLE_IGNORE_CERTIFICATE_ERRORS", "ENABLE_PARTIAL_LAYOUT_CONTROL", + "ENABLE_TEST_DATA", "ENABLE_TEST_RUNNER", "__LB_SHELL__ENABLE_SCREENSHOT__", "NDEBUG",
diff --git a/src/cobalt/build/config/base.gni b/src/cobalt/build/config/base.gni index d695cc9..93b2d4c 100644 --- a/src/cobalt/build/config/base.gni +++ b/src/cobalt/build/config/base.gni
@@ -229,6 +229,13 @@ fallback_splash_screen_url = "none" } +# The path to a splash screen to copy into content/data/web which can be +# accessed via a file URL starting with +# "file:///cobalt/browser/splash_screen/". If "", no file is copied. +if (!defined(cobalt_splash_screen_file)) { + cobalt_splash_screen_file = "" +} + # Cache parameters # The following set of parameters define how much memory is reserved for @@ -525,6 +532,18 @@ cobalt_media_buffer_video_budget_4k = "(60 * 1024 * 1024)" } +# Specifies the duration threshold of media source garbage collection. When the +# accumulated duration in a source buffer exceeds this value, the media source +# implementation will try to eject existing buffers from the cache. +# This is usually triggered when the video being played has a simple content and +# the encoded data is small. In such case this can limit how much is allocated +# for the book keeping data of the media buffers and avoid OOM of system heap. +# This should be set to 170 for most of the platforms. But it can be further +# reduced on systems with extremely low memory. +if (!defined(cobalt_media_source_garbage_collection_duration_threshold_in_seconds)) { + cobalt_media_source_garbage_collection_duration_threshold_in_seconds = "(170)" +} + # Enables embedding Cobalt as a shared library within another app. This # requires a 'lib' starboard implementation for the corresponding platform. if (!defined(cobalt_enable_lib)) { @@ -548,9 +567,6 @@ if (!defined(enable_fake_microphone)) { enable_fake_microphone = _copy_test_data } -if (!defined(enable_file_scheme)) { - enable_file_scheme = _copy_test_data -} if (!defined(enable_network_logging)) { enable_network_logging = _copy_test_data }
diff --git a/src/cobalt/build/copy_icu_data.gypi b/src/cobalt/build/copy_icu_data.gypi index abbff85..adbeb06 100644 --- a/src/cobalt/build/copy_icu_data.gypi +++ b/src/cobalt/build/copy_icu_data.gypi
@@ -57,4 +57,10 @@ 'files': [ '<(inputs_icu)' ], }, ], + + 'all_dependent_settings': { + 'variables': { + 'content_deploy_subdirs': [ 'icu' ] + } + }, }
diff --git a/src/cobalt/build/copy_test_data.gypi b/src/cobalt/build/copy_test_data.gypi deleted file mode 100644 index e5d8dad..0000000 --- a/src/cobalt/build/copy_test_data.gypi +++ /dev/null
@@ -1,65 +0,0 @@ -# Copyright 2014 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This file is meant to be included into an action to copy test data files into -# the DIR_SOURCE_ROOT directory, e.g. out/PS3_Debug/content/dir_source_root/. -# -# To use this, create a gyp target with the following form: -# { -# 'target_name': 'copy_data_target_name', -# 'type': 'none', -# 'actions': [ -# { -# 'action_name': 'copy_data_target_name', -# 'variables': { -# 'input_files': [ -# 'path/to/datafile.txt', # The file will be copied. -# 'path/to/data/directory', # The directory and its content will be copied. -# 'path/to/data/directory/', # The directory's content will be copied. -# ], -# 'output_dir' : 'path/to/output/directory', -# }, -# 'includes': [ 'path/to/this/gypi/file' ], -# }, -# ], -# }, -# -# Meaning of the variables: -# input_files: list: paths to data files or directories. When an item is a -# directory, without the final "/", the directory (along -# with the content) will be copied, otherwise only the -# content will be copied. -# output_dir: string: The directory that all input files will be copied to. -# Generally, this should be the directory of the gypi file -# containing the target (e.g. it should be "base" for the -# target in base/base.gyp). -# It is recommended that input_files and output_dir have similar path, so the -# directory structure in dir_source_root/ will reflect that in the source -# folder. - -{ - 'includes': [ 'contents_dir.gypi' ], - 'inputs': [ - '<!@pymod_do_main(starboard.build.copy_data --inputs <(input_files))', - ], - 'outputs': [ - '<!@pymod_do_main(starboard.build.copy_data -o <(sb_static_contents_output_base_dir)/dir_source_root/<(output_dir) --outputs <(input_files))', - ], - 'action': [ - 'python', - '<(DEPTH)/starboard/build/copy_data.py', - '-o', '<(sb_static_contents_output_base_dir)/dir_source_root/<(output_dir)', - '<@(input_files)', - ], -}
diff --git a/src/cobalt/build/copy_web_data.gypi b/src/cobalt/build/copy_web_data.gypi index 672ee45..cb91751 100644 --- a/src/cobalt/build/copy_web_data.gypi +++ b/src/cobalt/build/copy_web_data.gypi
@@ -17,49 +17,57 @@ # # To use this, create a gyp target with the following form: # { -# 'target_name': 'copy_data_target_name', +# 'target_name': 'target_name_copy_web_files', # 'type': 'none', -# 'actions': [ -# { -# 'action_name': 'copy_data_target_name', -# 'variables': { -# 'input_files': [ -# 'path/to/datafile.txt', # The file will be copied. -# 'path/to/data/directory', # The directory and its content will be copied. -# 'path/to/data/directory/', # The directory's content will be copied. -# ] -# 'output_dir' : 'path/to/output/directory', -# }, -# 'includes': [ 'path/to/this/gypi/file' ], -# }, -# ], +# 'variables': { +# 'content_web_input_files': [ +# 'path/to/datafile.txt', # The file will be copied. +# 'path/to/data/directory', # The directory and its content will be copied. +# 'path/to/data/directory/', # The directory's content will be copied. +# ] +# 'content_web_output_subdir' : 'path/to/output/directory', +# }, +# 'includes': [ '<(DEPTH)/cobalt/build/copy_web_data.gypi' ], # }, # # Meaning of the variables: -# input_files: list: paths to data files or directories. When an item is a -# directory, without the final "/", the directory (along -# with the content) will be copied, otherwise only the -# content will be copied. -# output_dir: string: The directory that all input files will be copied to. -# Generally, this should be the directory of the gypi file -# containing the target (e.g. it should be "base" for the -# target in base/base.gyp). -# It is recommended that input_files and output_dir have similar path, so the -# directory structure in dir_source_root/ will reflect that in the source -# folder. +# content_web_input_files: list: +# Paths to data files or directories. When an item is a directory, +# without the final "/", the directory (along with the content) will be +# copied, otherwise only the content will be copied. +# content_web_output_subdir: string: +# Directory within the 'web' directory that all input files will be +# copied to. Generally, this should be the directory of the gypi file +# containing the target (e.g. it should be "base" for the target in +# base/base.gyp). +# It is recommended that content_web_input_files and content_web_output_subdir +# have similar paths, so the directory structure in web/ will reflect that in +# the source folder. { 'includes': [ 'contents_dir.gypi' ], - 'inputs': [ - '<!@pymod_do_main(starboard.build.copy_data --inputs <(input_files))', + + 'actions': [ + { + 'action_name': 'copy_web_files', + 'inputs': [ + '<!@pymod_do_main(starboard.build.copy_data --inputs <(content_web_input_files))', + ], + 'outputs': [ + '<!@pymod_do_main(starboard.build.copy_data -o <(sb_static_contents_output_data_dir)/web/<(content_web_output_subdir) --outputs <(content_web_input_files))', + ], + 'action': [ + 'python', + '<(DEPTH)/starboard/build/copy_data.py', + '-o', '<(sb_static_contents_output_data_dir)/web/<(content_web_output_subdir)', + '<@(content_web_input_files)', + ], + }, ], - 'outputs': [ - '<!@pymod_do_main(starboard.build.copy_data -o <(sb_static_contents_output_data_dir)/web/<(output_dir) --outputs <(input_files))', - ], - 'action': [ - 'python', - '<(DEPTH)/starboard/build/copy_data.py', - '-o', '<(sb_static_contents_output_data_dir)/web/<(output_dir)', - '<@(input_files)', - ], + + 'all_dependent_settings': { + 'variables': { + 'content_deploy_subdirs': [ 'web/<(content_web_output_subdir)' ] + } + }, }
diff --git a/src/cobalt/csp/csp.gyp b/src/cobalt/csp/csp.gyp index e8e9ac7..b10984d 100644 --- a/src/cobalt/csp/csp.gyp +++ b/src/cobalt/csp/csp.gyp
@@ -80,18 +80,13 @@ { 'target_name': 'csp_copy_test_data', 'type': 'none', - 'actions': [ - { - 'action_name': 'csp_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/csp/testdata/', - ], - 'output_dir': 'cobalt/csp/testdata/', - }, - 'includes': [ '../build/copy_test_data.gypi' ], - }, - ], + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/cobalt/csp/testdata/', + ], + 'content_test_output_subdir': 'cobalt/csp/testdata/', + }, + 'includes': [ '<(DEPTH)/starboard/build/copy_test_data.gypi' ], }, ], }
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y index f1d197e..d8fa199 100644 --- a/src/cobalt/css_parser/grammar.y +++ b/src/cobalt/css_parser/grammar.y
@@ -4430,7 +4430,7 @@ } ; -// This property accepts a comma-seperated list of shadow effects to be applied +// This property accepts a comma-separated list of shadow effects to be applied // to the text of the element. // https://www.w3.org/TR/css-text-decor-3/#text-shadow-property text_shadow_property_value:
diff --git a/src/cobalt/css_parser/scanner.cc b/src/cobalt/css_parser/scanner.cc index 9ad950b..aa318b4 100644 --- a/src/cobalt/css_parser/scanner.cc +++ b/src/cobalt/css_parser/scanner.cc
@@ -491,9 +491,6 @@ return ScanFromVerticalBar(); case kTildeCharacter: return ScanFromTilde(); - default: - NOTREACHED(); - break; } } }
diff --git a/src/cobalt/cssom/cascade_precedence.h b/src/cobalt/cssom/cascade_precedence.h index c8fc022..5e4d948 100644 --- a/src/cobalt/cssom/cascade_precedence.h +++ b/src/cobalt/cssom/cascade_precedence.h
@@ -103,7 +103,6 @@ case kImportantAuthor: case kImportantOverride: case kImportantUserAgent: - default: NOTREACHED(); } }
diff --git a/src/cobalt/cssom/computed_style.cc b/src/cobalt/cssom/computed_style.cc index 41de8b2..534f5c9 100644 --- a/src/cobalt/cssom/computed_style.cc +++ b/src/cobalt/cssom/computed_style.cc
@@ -92,11 +92,9 @@ viewport_size.height() * specified_length->value() / 100.0f, kPixelsUnit); } - - default: - NOTREACHED(); - return NULL; } + NOTREACHED(); + return NULL; } // For values that can be either lengths or percentages, this function will @@ -384,7 +382,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -484,7 +481,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -591,7 +587,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -721,7 +716,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -846,7 +840,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -969,7 +962,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -1086,7 +1078,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -1197,7 +1188,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -1215,8 +1205,6 @@ case kViewportHeightPercentsAkaVhUnit: computed_length_is_negative_ = length_value->value() < 0; break; - default: - NOTREACHED(); } } @@ -1337,9 +1325,6 @@ ComputeThreeOrFourValuesPosition(input_position_builder, output_position_builder); break; - default: - NOTREACHED(); - break; } } @@ -1515,7 +1500,6 @@ break; } case kNone: // fall-through - default: NOTREACHED(); break; } @@ -1615,7 +1599,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); break; } @@ -1893,7 +1876,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -2143,7 +2125,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -2250,7 +2231,6 @@ root_computed_font_size_, viewport_size_), calc_value->percentage_value()))); } break; - default: { NOTREACHED(); } } } @@ -2353,8 +2333,6 @@ property_list_value->value()[2].get()), computed_font_size_, root_computed_font_size_, viewport_size_); break; - default: - NOTREACHED(); } computed_transform_origin_ = @@ -2531,7 +2509,6 @@ case KeywordValue::kTop: case KeywordValue::kUppercase: case KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -3042,7 +3019,6 @@ case kTextDecorationProperty: case kTransitionProperty: case kUnicodeRangeProperty: - default: NOTREACHED(); break; }
diff --git a/src/cobalt/cssom/css_declared_style_data.h b/src/cobalt/cssom/css_declared_style_data.h index 37dd38a..4f2613c 100644 --- a/src/cobalt/cssom/css_declared_style_data.h +++ b/src/cobalt/cssom/css_declared_style_data.h
@@ -17,10 +17,11 @@ #include <bitset> #include <functional> +#include <map> #include <string> #include "base/compiler_specific.h" -#include "base/hash_tables.h" +#include "base/containers/small_map.h" #include "base/memory/ref_counted.h" #include "cobalt/base/unused.h" #include "cobalt/cssom/css_declaration_data.h" @@ -36,7 +37,10 @@ public: CSSDeclaredStyleData(); - typedef base::hash_map<PropertyKey, scoped_refptr<PropertyValue> > + // NOTE: The array size of base::SmallMap is based on extensive testing. Do + // not change it unless additional profiling data justifies it. + typedef base::SmallMap<std::map<PropertyKey, scoped_refptr<PropertyValue> >, + 8, std::equal_to<PropertyKey> > PropertyValues; // The length attribute must return the number of CSS declarations in the
diff --git a/src/cobalt/cssom/keyword_value.cc b/src/cobalt/cssom/keyword_value.cc index 302b671..5dac126 100644 --- a/src/cobalt/cssom/keyword_value.cc +++ b/src/cobalt/cssom/keyword_value.cc
@@ -477,10 +477,9 @@ return kUppercaseKeywordName; case kVisible: return kVisibleKeywordName; - default: - NOTREACHED(); - return ""; } + NOTREACHED(); + return ""; } } // namespace cssom
diff --git a/src/cobalt/debug/debug.gyp b/src/cobalt/debug/debug.gyp index 3242c1d..69c3f0d 100644 --- a/src/cobalt/debug/debug.gyp +++ b/src/cobalt/debug/debug.gyp
@@ -83,18 +83,13 @@ { 'target_name': 'debug_copy_web_files', 'type': 'none', - 'actions': [ - { - 'action_name': 'debug_copy_web_files', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/debug/content/', - ], - 'output_dir': 'cobalt/debug', - }, - 'includes': [ '../build/copy_web_data.gypi' ], - }, - ], + 'variables': { + 'content_web_input_files': [ + '<(DEPTH)/cobalt/debug/content/', + ], + 'content_web_output_subdir': 'cobalt/debug', + }, + 'includes': [ '<(DEPTH)/cobalt/build/copy_web_data.gypi' ], }, ], }
diff --git a/src/cobalt/debug/debug_hub.idl b/src/cobalt/debug/debug_hub.idl index 1785d8a..0056773 100644 --- a/src/cobalt/debug/debug_hub.idl +++ b/src/cobalt/debug/debug_hub.idl
@@ -44,4 +44,4 @@ }; callback LogMessageCallback = void(long severity, DOMString file, long line, - long message_start, DOMString message); + long messageStart, DOMString message);
diff --git a/src/cobalt/debug/debug_script_runner.idl b/src/cobalt/debug/debug_script_runner.idl index 4224241..5a4d7f7 100644 --- a/src/cobalt/debug/debug_script_runner.idl +++ b/src/cobalt/debug/debug_script_runner.idl
@@ -21,4 +21,4 @@ }; callback CreateRemoteObjectCallback = - DOMString(object obj, DOMString json_params); + DOMString(object obj, DOMString jsonParams);
diff --git a/src/cobalt/browser/testdata/animations-demo/index.html b/src/cobalt/demos/content/animations-demo/index.html similarity index 100% rename from src/cobalt/browser/testdata/animations-demo/index.html rename to src/cobalt/demos/content/animations-demo/index.html
diff --git a/src/cobalt/browser/testdata/animations-demo/layer_fern.css b/src/cobalt/demos/content/animations-demo/layer_fern.css similarity index 100% rename from src/cobalt/browser/testdata/animations-demo/layer_fern.css rename to src/cobalt/demos/content/animations-demo/layer_fern.css
diff --git a/src/cobalt/browser/testdata/animations-demo/layer_fern.js b/src/cobalt/demos/content/animations-demo/layer_fern.js similarity index 100% rename from src/cobalt/browser/testdata/animations-demo/layer_fern.js rename to src/cobalt/demos/content/animations-demo/layer_fern.js
diff --git a/src/cobalt/browser/testdata/animations-demo/layer_intro.css b/src/cobalt/demos/content/animations-demo/layer_intro.css similarity index 100% rename from src/cobalt/browser/testdata/animations-demo/layer_intro.css rename to src/cobalt/demos/content/animations-demo/layer_intro.css
diff --git a/src/cobalt/browser/testdata/animations-demo/layer_intro.js b/src/cobalt/demos/content/animations-demo/layer_intro.js similarity index 100% rename from src/cobalt/browser/testdata/animations-demo/layer_intro.js rename to src/cobalt/demos/content/animations-demo/layer_intro.js
diff --git a/src/cobalt/browser/testdata/animations-demo/layer_sun.css b/src/cobalt/demos/content/animations-demo/layer_sun.css similarity index 100% rename from src/cobalt/browser/testdata/animations-demo/layer_sun.css rename to src/cobalt/demos/content/animations-demo/layer_sun.css
diff --git a/src/cobalt/browser/testdata/animations-demo/layer_sun.js b/src/cobalt/demos/content/animations-demo/layer_sun.js similarity index 100% rename from src/cobalt/browser/testdata/animations-demo/layer_sun.js rename to src/cobalt/demos/content/animations-demo/layer_sun.js
diff --git a/src/cobalt/browser/testdata/cobalt-oxide/cobalt-oxide.css b/src/cobalt/demos/content/cobalt-oxide/cobalt-oxide.css similarity index 100% rename from src/cobalt/browser/testdata/cobalt-oxide/cobalt-oxide.css rename to src/cobalt/demos/content/cobalt-oxide/cobalt-oxide.css
diff --git a/src/cobalt/browser/testdata/cobalt-oxide/cobalt-oxide.html b/src/cobalt/demos/content/cobalt-oxide/cobalt-oxide.html similarity index 100% rename from src/cobalt/browser/testdata/cobalt-oxide/cobalt-oxide.html rename to src/cobalt/demos/content/cobalt-oxide/cobalt-oxide.html
diff --git a/src/cobalt/browser/testdata/cobalt-oxide/cobalt-oxide.js b/src/cobalt/demos/content/cobalt-oxide/cobalt-oxide.js similarity index 100% rename from src/cobalt/browser/testdata/cobalt-oxide/cobalt-oxide.js rename to src/cobalt/demos/content/cobalt-oxide/cobalt-oxide.js
diff --git a/src/cobalt/browser/testdata/color-transitions-demo/color-transitions-demo.html b/src/cobalt/demos/content/color-transitions-demo/color-transitions-demo.html similarity index 100% rename from src/cobalt/browser/testdata/color-transitions-demo/color-transitions-demo.html rename to src/cobalt/demos/content/color-transitions-demo/color-transitions-demo.html
diff --git a/src/cobalt/browser/testdata/deep-link-demo/deep-link-demo.html b/src/cobalt/demos/content/deep-link-demo/deep-link-demo.html similarity index 100% rename from src/cobalt/browser/testdata/deep-link-demo/deep-link-demo.html rename to src/cobalt/demos/content/deep-link-demo/deep-link-demo.html
diff --git a/src/cobalt/browser/testdata/deviceorientation-demo/deviceorientation-demo.html b/src/cobalt/demos/content/deviceorientation-demo/deviceorientation-demo.html similarity index 100% rename from src/cobalt/browser/testdata/deviceorientation-demo/deviceorientation-demo.html rename to src/cobalt/demos/content/deviceorientation-demo/deviceorientation-demo.html
diff --git a/src/cobalt/browser/testdata/disable-jit/index.html b/src/cobalt/demos/content/disable-jit/index.html similarity index 100% rename from src/cobalt/browser/testdata/disable-jit/index.html rename to src/cobalt/demos/content/disable-jit/index.html
diff --git a/src/cobalt/browser/testdata/dom-gc-demo/dom-gc-demo.html b/src/cobalt/demos/content/dom-gc-demo/dom-gc-demo.html similarity index 100% rename from src/cobalt/browser/testdata/dom-gc-demo/dom-gc-demo.html rename to src/cobalt/demos/content/dom-gc-demo/dom-gc-demo.html
diff --git a/src/cobalt/browser/testdata/dual-playback-demo/bear.mp4 b/src/cobalt/demos/content/dual-playback-demo/bear.mp4 similarity index 100% rename from src/cobalt/browser/testdata/dual-playback-demo/bear.mp4 rename to src/cobalt/demos/content/dual-playback-demo/bear.mp4 Binary files differ
diff --git a/src/cobalt/browser/testdata/dual-playback-demo/dual-playback-demo.html b/src/cobalt/demos/content/dual-playback-demo/dual-playback-demo.html similarity index 100% rename from src/cobalt/browser/testdata/dual-playback-demo/dual-playback-demo.html rename to src/cobalt/demos/content/dual-playback-demo/dual-playback-demo.html
diff --git a/src/cobalt/browser/testdata/eme-demo/eme-demo.html b/src/cobalt/demos/content/eme-demo/eme-demo.html similarity index 100% rename from src/cobalt/browser/testdata/eme-demo/eme-demo.html rename to src/cobalt/demos/content/eme-demo/eme-demo.html
diff --git a/src/cobalt/browser/testdata/eme-demo/eme-demo.js b/src/cobalt/demos/content/eme-demo/eme-demo.js similarity index 100% rename from src/cobalt/browser/testdata/eme-demo/eme-demo.js rename to src/cobalt/demos/content/eme-demo/eme-demo.js
diff --git a/src/cobalt/browser/testdata/focus-demo/focus-demo.html b/src/cobalt/demos/content/focus-demo/focus-demo.html similarity index 100% rename from src/cobalt/browser/testdata/focus-demo/focus-demo.html rename to src/cobalt/demos/content/focus-demo/focus-demo.html
diff --git a/src/cobalt/browser/testdata/media-capture/media-devices-test.html b/src/cobalt/demos/content/media-capture/media-devices-test.html similarity index 100% rename from src/cobalt/browser/testdata/media-capture/media-devices-test.html rename to src/cobalt/demos/content/media-capture/media-devices-test.html
diff --git a/src/cobalt/browser/testdata/media-element-demo/README.txt b/src/cobalt/demos/content/media-element-demo/README.txt similarity index 100% rename from src/cobalt/browser/testdata/media-element-demo/README.txt rename to src/cobalt/demos/content/media-element-demo/README.txt
diff --git a/src/cobalt/browser/testdata/media-element-demo/media-element-demo.html b/src/cobalt/demos/content/media-element-demo/media-element-demo.html similarity index 100% rename from src/cobalt/browser/testdata/media-element-demo/media-element-demo.html rename to src/cobalt/demos/content/media-element-demo/media-element-demo.html
diff --git a/src/cobalt/browser/testdata/media-element-demo/media-element-demo.js b/src/cobalt/demos/content/media-element-demo/media-element-demo.js similarity index 100% rename from src/cobalt/browser/testdata/media-element-demo/media-element-demo.js rename to src/cobalt/demos/content/media-element-demo/media-element-demo.js
diff --git a/src/cobalt/demos/content/media-element-demo/multi-video-demo.html b/src/cobalt/demos/content/media-element-demo/multi-video-demo.html new file mode 100644 index 0000000..e79f36b --- /dev/null +++ b/src/cobalt/demos/content/media-element-demo/multi-video-demo.html
@@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> + <title>Multi Video Demo</title> + <style> + body { + background-color: rgb(255, 255, 255); + color: #0047ab; + font-size: 10px; + } + video { + transform: translateX(100px) rotate(3deg); + } + </style> +</head> +<body> + <script type="text/javascript" src="multi-video-demo.js"></script> +</body> +</html>
diff --git a/src/cobalt/demos/content/media-element-demo/multi-video-demo.js b/src/cobalt/demos/content/media-element-demo/multi-video-demo.js new file mode 100644 index 0000000..f14a95f --- /dev/null +++ b/src/cobalt/demos/content/media-element-demo/multi-video-demo.js
@@ -0,0 +1,197 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 demo simply plays a video using get parameters in the form of: +// .../multi-video-demo.html?audio=filename_in_same_folder&video=filename_in_same_folder&instances=2 + +var kAudioChunkSize = 512 * 1024; +var kVideoChunkSize = 2 * 1024 * 1024; + +var kEndOfStreamOffset = -1; + +class Player { + constructor(audio_url, video_url) { + this.audio_offset = 0; + this.audio_url = audio_url; + this.video_offset = 0; + this.video_url = video_url; + this.video_tag = this.createVideoElement(); + this.status = this.createStatusElement(this.video_tag); + + this.video_tag.src = ''; + this.video_tag.load(); + this.mediasource = new MediaSource; + this.mediasource.addEventListener('sourceopen', this.onsourceopen.bind(this)); + this.video_tag.src = window.URL.createObjectURL(this.mediasource); + } + + onsourceopen() { + if (this.video_url.endsWith('.mp4')) { + this.video_source_buffer = this.mediasource.addSourceBuffer( + 'video/mp4; codecs="avc1.640028"'); + } else { + this.video_source_buffer = this.mediasource.addSourceBuffer( + 'video/webm; codecs="vp9"'); + } + + this.audio_source_buffer = this.mediasource.addSourceBuffer( + 'audio/mp4; codecs="mp4a.40.2"'); + this.tryToDownloadAudioData(); + } + + downloadAndAppend(url, begin, end, source_buffer, callback) { + var xhr = new XMLHttpRequest; + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.addEventListener('load', function(e) { + var data = new Uint8Array(e.target.response); + var onupdateend = function() { + source_buffer.removeEventListener('updateend', onupdateend); + callback(data.length); + }; + source_buffer.addEventListener('updateend', onupdateend); + source_buffer.appendBuffer(data); + console.log('append ' + data.length + ' bytes from ' + + url); + }); + xhr.setRequestHeader('Range', ('bytes=' + begin +'-' + end)); + xhr.send(); + } + + isTrackNeedAudioData() { + if (this.audio_offset == kEndOfStreamOffset) { + return false; + } + var buffer_range_size = this.audio_source_buffer.buffered.length; + if (buffer_range_size == 0) { + return true; + } + var start = this.audio_source_buffer.buffered.start(buffer_range_size - 1); + var end = this.audio_source_buffer.buffered.end(buffer_range_size - 1); + console.log('audio ' + start + '/' + end + ' ' + this.video_tag.currentTime) + return end - this.video_tag.currentTime <= 20; + } + + isTrackNeedVideoData() { + if (this.video_offset == kEndOfStreamOffset) { + return false; + } + var buffer_range_size = this.video_source_buffer.buffered.length; + if (buffer_range_size == 0) { + return true; + } + var start = this.video_source_buffer.buffered.start(buffer_range_size - 1); + var end = this.video_source_buffer.buffered.end(buffer_range_size - 1); + console.log('video ' + start + '/' + end + ' ' + this.video_tag.currentTime) + return end - this.video_tag.currentTime <= 20; + } + + tryToDownloadAudioData() { + if (!this.isTrackNeedAudioData()) { + if (this.isTrackNeedVideoData()) { + this.tryToDownloadVideoData(); + } else { + window.setTimeout(this.tryToDownloadAudioData.bind(this), 1000); + } + return; + } + + this.downloadAndAppend( + this.audio_url, this.audio_offset, + this.audio_offset + kAudioChunkSize - 1, this.audio_source_buffer, + this.onAudioDataDownloaded.bind(this)); + } + + onAudioDataDownloaded(length) { + if (length != kAudioChunkSize) { + this.audio_offset = kEndOfStreamOffset; + } + if (this.audio_offset != kEndOfStreamOffset) { + this.audio_offset += kAudioChunkSize; + } + this.tryToDownloadVideoData(); + } + + tryToDownloadVideoData() { + if (!this.isTrackNeedVideoData()) { + if (this.isTrackNeedAudioData()) { + this.tryToDownloadAudioData(); + } else { + window.setTimeout(this.tryToDownloadVideoData.bind(this), 1000); + } + return; + } + + this.downloadAndAppend( + this.video_url, this.video_offset, + this.video_offset + kVideoChunkSize - 1, this.video_source_buffer, + this.onVideoDataDownloaded.bind(this)); + } + + onVideoDataDownloaded(length) { + if (length != kVideoChunkSize) { + this.video_offset = kEndOfStreamOffset; + } + if (this.video_offset != kEndOfStreamOffset) { + this.video_offset += kVideoChunkSize; + } + this.tryToDownloadAudioData(); + } + + createVideoElement() { + var video = document.createElement('video'); + video.autoplay = true; + video.style.width = '320px'; + video.style.height = '240px'; + document.body.appendChild(video); + + return video; + } + + createStatusElement(video) { + var status = document.createElement('div'); + document.body.appendChild(status); + video.addEventListener('timeupdate', function () { + status.textContent = 'time: ' + video.currentTime.toFixed(2); + }); + + return status; + } +} + +function main() { + var get_parameters = window.location.search.substr(1).split('&'); + var audio_url, video_url, instances = 2; + for (var param of get_parameters) { + splitted = param.split('='); + if (splitted[0] == 'audio') { + audio_url = splitted[1]; + } else if (splitted[0] == 'video') { + video_url = splitted[1]; + } else if (splitted[0] == 'instances') { + instances = splitted[1]; + } + } + + if (audio_url && video_url) { + for (var i = 0; i < instances; ++i) { + new Player(audio_url, video_url); + } + } else { + status.textContent = "invalid get parameters " + + window.location.search.substr(1); + } +} + +main();
diff --git a/src/cobalt/browser/testdata/media-element-demo/progressive-demo.html b/src/cobalt/demos/content/media-element-demo/progressive-demo.html similarity index 100% rename from src/cobalt/browser/testdata/media-element-demo/progressive-demo.html rename to src/cobalt/demos/content/media-element-demo/progressive-demo.html
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/0.5.html b/src/cobalt/demos/content/mse-eme-conformance-tests/0.5.html similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/0.5.html rename to src/cobalt/demos/content/mse-eme-conformance-tests/0.5.html
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/README.txt b/src/cobalt/demos/content/mse-eme-conformance-tests/README.txt similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/README.txt rename to src/cobalt/demos/content/mse-eme-conformance-tests/README.txt
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-85.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-85.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-85.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-85.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-86.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-86.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-86.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-86.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8b.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8b.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8b.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8b.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8c.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8c.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8c.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8c.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8d.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8d.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-20120827-8d.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8d.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-audio-1MB-trunc.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car-audio-1MB-trunc.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car-audio-1MB-trunc.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car-audio-1MB-trunc.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_20130125_18.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car_20130125_18.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_20130125_18.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car_20130125_18.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-85.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-85.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-85.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-85.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8b.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8b.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8b.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8b.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8c.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8c.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8c.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8c.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8d.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8d.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/car_cenc-20120827-8d.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8d.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/nq-frames23-tfdt24.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/nq-frames23-tfdt24.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/nq-frames23-tfdt24.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/nq-frames23-tfdt24.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/nq-frames24-tfdt23.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/nq-frames24-tfdt23.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/nq-frames24-tfdt23.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/nq-frames24-tfdt23.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/oops_cenc-20121114-145-143.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/oops_cenc-20121114-145-143.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/oops_cenc-20121114-145-143.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/oops_cenc-20121114-145-143.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/sintel-trunc.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/sintel-trunc.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/sintel-trunc.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/sintel-trunc.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/media/test-video-1MB.mp4.sha1 b/src/cobalt/demos/content/mse-eme-conformance-tests/media/test-video-1MB.mp4.sha1 similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/media/test-video-1MB.mp4.sha1 rename to src/cobalt/demos/content/mse-eme-conformance-tests/media/test-video-1MB.mp4.sha1
diff --git a/src/cobalt/browser/testdata/mse-eme-conformance-tests/style-20150612143746.css b/src/cobalt/demos/content/mse-eme-conformance-tests/style-20150612143746.css similarity index 100% rename from src/cobalt/browser/testdata/mse-eme-conformance-tests/style-20150612143746.css rename to src/cobalt/demos/content/mse-eme-conformance-tests/style-20150612143746.css
diff --git a/src/cobalt/browser/testdata/mtm-demo/mtm.html b/src/cobalt/demos/content/mtm-demo/mtm.html similarity index 100% rename from src/cobalt/browser/testdata/mtm-demo/mtm.html rename to src/cobalt/demos/content/mtm-demo/mtm.html
diff --git a/src/cobalt/browser/testdata/mtm-demo/normal.html b/src/cobalt/demos/content/mtm-demo/normal.html similarity index 100% rename from src/cobalt/browser/testdata/mtm-demo/normal.html rename to src/cobalt/demos/content/mtm-demo/normal.html
diff --git a/src/cobalt/browser/testdata/opacity-transitions-demo/opacity-transitions-demo.html b/src/cobalt/demos/content/opacity-transitions-demo/opacity-transitions-demo.html similarity index 100% rename from src/cobalt/browser/testdata/opacity-transitions-demo/opacity-transitions-demo.html rename to src/cobalt/demos/content/opacity-transitions-demo/opacity-transitions-demo.html
diff --git a/src/cobalt/browser/testdata/page-visibility-demo/page-visibility-demo.html b/src/cobalt/demos/content/page-visibility-demo/page-visibility-demo.html similarity index 100% rename from src/cobalt/browser/testdata/page-visibility-demo/page-visibility-demo.html rename to src/cobalt/demos/content/page-visibility-demo/page-visibility-demo.html
diff --git a/src/cobalt/browser/testdata/pointer-events-demo/pointer-events-demo.html b/src/cobalt/demos/content/pointer-events-demo/pointer-events-demo.html similarity index 100% rename from src/cobalt/browser/testdata/pointer-events-demo/pointer-events-demo.html rename to src/cobalt/demos/content/pointer-events-demo/pointer-events-demo.html
diff --git a/src/cobalt/browser/testdata/script-debugger-test/script-debugger-test.html b/src/cobalt/demos/content/script-debugger-test/script-debugger-test.html similarity index 100% rename from src/cobalt/browser/testdata/script-debugger-test/script-debugger-test.html rename to src/cobalt/demos/content/script-debugger-test/script-debugger-test.html
diff --git a/src/cobalt/browser/testdata/script-tag-demo/increment-and-print-i.js b/src/cobalt/demos/content/script-tag-demo/increment-and-print-i.js similarity index 100% rename from src/cobalt/browser/testdata/script-tag-demo/increment-and-print-i.js rename to src/cobalt/demos/content/script-tag-demo/increment-and-print-i.js
diff --git a/src/cobalt/browser/testdata/script-tag-demo/script-tag-demo.html b/src/cobalt/demos/content/script-tag-demo/script-tag-demo.html similarity index 100% rename from src/cobalt/browser/testdata/script-tag-demo/script-tag-demo.html rename to src/cobalt/demos/content/script-tag-demo/script-tag-demo.html
diff --git a/src/cobalt/browser/testdata/selector-tester/selector-tester.html b/src/cobalt/demos/content/selector-tester/selector-tester.html similarity index 100% rename from src/cobalt/browser/testdata/selector-tester/selector-tester.html rename to src/cobalt/demos/content/selector-tester/selector-tester.html
diff --git a/src/cobalt/xhr/testdata/simple-xhr/simple-xhr.html b/src/cobalt/demos/content/simple-xhr/simple-xhr.html similarity index 100% rename from src/cobalt/xhr/testdata/simple-xhr/simple-xhr.html rename to src/cobalt/demos/content/simple-xhr/simple-xhr.html
diff --git a/src/cobalt/xhr/testdata/simple-xhr/simple-xhr.js b/src/cobalt/demos/content/simple-xhr/simple-xhr.js similarity index 100% rename from src/cobalt/xhr/testdata/simple-xhr/simple-xhr.js rename to src/cobalt/demos/content/simple-xhr/simple-xhr.js
diff --git a/src/cobalt/browser/testdata/smooth-animations-demo/index.html b/src/cobalt/demos/content/smooth-animations-demo/index.html similarity index 100% rename from src/cobalt/browser/testdata/smooth-animations-demo/index.html rename to src/cobalt/demos/content/smooth-animations-demo/index.html
diff --git a/src/cobalt/browser/testdata/specificity-demo/specificity-demo.html b/src/cobalt/demos/content/specificity-demo/specificity-demo.html similarity index 100% rename from src/cobalt/browser/testdata/specificity-demo/specificity-demo.html rename to src/cobalt/demos/content/specificity-demo/specificity-demo.html
diff --git a/src/cobalt/browser/testdata/speech-synthesis-demo/index.html b/src/cobalt/demos/content/speech-synthesis-demo/index.html similarity index 100% rename from src/cobalt/browser/testdata/speech-synthesis-demo/index.html rename to src/cobalt/demos/content/speech-synthesis-demo/index.html
diff --git a/src/cobalt/browser/testdata/splash_screen/beforeunload.html b/src/cobalt/demos/content/splash_screen/beforeunload.html similarity index 100% rename from src/cobalt/browser/testdata/splash_screen/beforeunload.html rename to src/cobalt/demos/content/splash_screen/beforeunload.html
diff --git a/src/cobalt/browser/testdata/splash_screen/block_render_tree_html_display_none.html b/src/cobalt/demos/content/splash_screen/block_render_tree_html_display_none.html similarity index 100% rename from src/cobalt/browser/testdata/splash_screen/block_render_tree_html_display_none.html rename to src/cobalt/demos/content/splash_screen/block_render_tree_html_display_none.html
diff --git a/src/cobalt/browser/testdata/splash_screen/link_splash_screen.html b/src/cobalt/demos/content/splash_screen/link_splash_screen.html similarity index 100% rename from src/cobalt/browser/testdata/splash_screen/link_splash_screen.html rename to src/cobalt/demos/content/splash_screen/link_splash_screen.html
diff --git a/src/cobalt/browser/testdata/splash_screen/link_splash_screen_network.html b/src/cobalt/demos/content/splash_screen/link_splash_screen_network.html similarity index 100% rename from src/cobalt/browser/testdata/splash_screen/link_splash_screen_network.html rename to src/cobalt/demos/content/splash_screen/link_splash_screen_network.html
diff --git a/src/cobalt/demos/content/splash_screen/redirect_server.py b/src/cobalt/demos/content/splash_screen/redirect_server.py new file mode 100644 index 0000000..92f3818 --- /dev/null +++ b/src/cobalt/demos/content/splash_screen/redirect_server.py
@@ -0,0 +1,86 @@ +#!/usr/bin/env python +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests the effect that HTTP redirects have on the Cobalt splash screen. + +Run this script from the src directory, i.e. + +src$ python cobalt/demos/content/splash_screen/redirect_server.py + +Let $(hostname) be the local machine's hostname, and let ${port} be +the port printed out by the python script. Then in a separate tab, run +Cobalt and pass the initial URL + +http://"$(hostname)":${port}/cobalt/demos/splash_screen/link_splash_screen.html?redirect=yes + +(note the HTTP not HTTPS) which will be redirected to + +http://"$(hostname)":${port}/cobalt/demos/splash_screen/redirected.html + +The expected behavior is that the splash screen served by +redirected.html (beforeunload.html, a blue page with an animated rectangle in +the top left) will be cached separately from the splash screen served +by link_splash_screen.html (the Cobalt logo splash screen). + +Specifically, starting with an empty cache, if we navigate to +http://"$(hostname)":${port}...link_splash_screen.html?redirect=yes , +a new splash screen should be cached to the cache directory +(beforeunload.html). If we then navigate to +http://"$(hostname)":${port}...link_splash_screen.html (without the +query parameter), we should see no splash screen displayed at first +run, and cobalt_splash_screen.html should be cached to the cache +directory. On a second navigation to +http://"$(hostname)":${port}...link_splash_screen.html (without the +query parameter), we should see the cached Cobalt logo splash screen +displayed. + +In short, although redirected.html serves a splash screen it should +not be seen when we redirect there via +link_splash_screen.html?redirect=yes and certainly not when we +navigate to link_splash_screen.html without query parameters. The +splash screen cached by redirected.html should only be seen when we +navigate directly to redirected.html. + +It is OK if navigating to link_splash_screen.html first, then shows +the Cobalt logo splash screen on the next navigation to +link_splash_screen.html?redirect=yes. +""" +import SimpleHTTPServer +import SocketServer + + +class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler): + + def do_GET(self): + path = self.path + print 'path = ' + path + redirect_from = 'link_splash_screen.html?redirect=yes' + redirect_to = 'redirected.html' + if redirect_from in path: + self.send_response(302) + self.send_header('Location', path.replace(redirect_from, redirect_to)) + self.end_headers() + else: + return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) + + +port = 8000 +while True: + try: + handler = SocketServer.TCPServer(('', port), Handler) + print 'seving port ' + str(port) + handler.serve_forever() + except SocketServer.socket.error as exc: + port += 1 + print 'trying port ' + str(port)
diff --git a/src/cobalt/browser/testdata/splash_screen/redirected.html b/src/cobalt/demos/content/splash_screen/redirected.html similarity index 100% rename from src/cobalt/browser/testdata/splash_screen/redirected.html rename to src/cobalt/demos/content/splash_screen/redirected.html
diff --git a/src/cobalt/browser/testdata/splash_screen/render_postponed.html b/src/cobalt/demos/content/splash_screen/render_postponed.html similarity index 100% rename from src/cobalt/browser/testdata/splash_screen/render_postponed.html rename to src/cobalt/demos/content/splash_screen/render_postponed.html
diff --git a/src/cobalt/browser/testdata/timer-demo/timer-demo.html b/src/cobalt/demos/content/timer-demo/timer-demo.html similarity index 100% rename from src/cobalt/browser/testdata/timer-demo/timer-demo.html rename to src/cobalt/demos/content/timer-demo/timer-demo.html
diff --git a/src/cobalt/browser/testdata/transitions-demo/transitions-demo.html b/src/cobalt/demos/content/transitions-demo/transitions-demo.html similarity index 100% rename from src/cobalt/browser/testdata/transitions-demo/transitions-demo.html rename to src/cobalt/demos/content/transitions-demo/transitions-demo.html
diff --git a/src/cobalt/browser/testdata/unload-demo/unload-demo.html b/src/cobalt/demos/content/unload-demo/unload-demo.html similarity index 100% rename from src/cobalt/browser/testdata/unload-demo/unload-demo.html rename to src/cobalt/demos/content/unload-demo/unload-demo.html
diff --git a/src/cobalt/demos/demos.gyp b/src/cobalt/demos/demos.gyp new file mode 100644 index 0000000..47a31be --- /dev/null +++ b/src/cobalt/demos/demos.gyp
@@ -0,0 +1,30 @@ +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +{ + 'variables': { + 'sb_pedantic_warnings': 1, + }, + 'targets': [ + { + 'target_name': 'copy_demos', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ '<(DEPTH)/cobalt/demos/content/' ], + 'content_test_output_subdir': 'cobalt/demos', + }, + 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], + }, + ], +}
diff --git a/src/cobalt/dom/array_buffer.cc b/src/cobalt/dom/array_buffer.cc index ac89aff..2b0211a 100644 --- a/src/cobalt/dom/array_buffer.cc +++ b/src/cobalt/dom/array_buffer.cc
@@ -37,179 +37,76 @@ } // namespace -ArrayBuffer::Data::Data(script::EnvironmentSettings* settings, size_t size) - : allocator_(NULL), cache_(NULL), offloaded_(false), data_(NULL), size_(0) { - Initialize(settings, size); +ArrayBuffer::Data::Data(size_t size) : data_(NULL), size_(0) { + Initialize(size); if (data_) { memset(data_, 0, size); } - if (cache_) { - cache_->Register(this); - } } -ArrayBuffer::Data::Data(script::EnvironmentSettings* settings, - const uint8* data, size_t size) - : allocator_(NULL), cache_(NULL), offloaded_(false), data_(NULL), size_(0) { - Initialize(settings, size); +ArrayBuffer::Data::Data(const uint8* data, size_t size) + : data_(NULL), size_(0) { + Initialize(size); DCHECK(data_); memcpy(data_, data, size); - // Register() has to be called after copying the data as Register() will call - // TryToOffload() which may delete the |data_| passed in that belongs to - // another ArrayBuffer. - if (cache_) { - cache_->Register(this); - } } ArrayBuffer::Data::Data(scoped_array<uint8> data, size_t size) - : allocator_(NULL), - cache_(NULL), - offloaded_(false), - data_(data.release()), - size_(size) { + : data_(data.release()), size_(size) { DCHECK(data_); } -ArrayBuffer::Data::~Data() { - if (offloaded_) { - allocator_->Free(data_); - } else { - delete[] data_; - } - if (cache_) { - cache_->Unregister(this); - } -} +ArrayBuffer::Data::~Data() { delete[] data_; } uint8* ArrayBuffer::Data::data() const { - if (cache_) { - cache_->ReportUsage(this); - } return data_; } -bool ArrayBuffer::Data::Offload() { - if (offloaded_) { - return true; - } - if (!allocator_) { - return false; - } - uint8* data = reinterpret_cast<uint8*>(allocator_->Allocate(size())); - if (data) { - memcpy(data, data_, size()); - delete[] data_; - data_ = data; - offloaded_ = true; - } - return offloaded_; -} - -void ArrayBuffer::Data::Initialize(script::EnvironmentSettings* settings, - size_t size) { +void ArrayBuffer::Data::Initialize(size_t size) { TRACK_MEMORY_SCOPE("DOM"); - if (settings) { - DOMSettings* dom_settings = - base::polymorphic_downcast<dom::DOMSettings*>(settings); - allocator_ = dom_settings->array_buffer_allocator(); - cache_ = dom_settings->array_buffer_cache(); - if (allocator_) { - DCHECK(cache_); - } else { - DCHECK(!cache_); - } - } data_ = new uint8[size]; size_ = size; } -ArrayBuffer::Cache::Cache(size_t maximum_size_in_main_memory) - : total_size_in_main_memory_(0), - maximum_size_in_main_memory_(maximum_size_in_main_memory) {} - -void ArrayBuffer::Cache::Register(Data* data) { - total_size_in_main_memory_ += data->size(); - // Offload before push_back to ensure that the last one is always not - // offloaded immediately. - TryToOffload(); - datas_.push_back(data); -} - -void ArrayBuffer::Cache::Unregister(Data* data) { - DCHECK(std::find(datas_.begin(), datas_.end(), data) != datas_.end()); - datas_.erase(std::find(datas_.begin(), datas_.end(), data)); - if (!data->offloaded()) { - DCHECK_GE(total_size_in_main_memory_, data->size()); - total_size_in_main_memory_ -= data->size(); - } -} - -void ArrayBuffer::Cache::ReportUsage(const Data* data) { - DCHECK(data); - DCHECK(std::find(datas_.begin(), datas_.end(), data) != datas_.end()); - if (data->offloaded() || datas_.back() == data) { - return; - } - // Move |data| to the end. - datas_.erase(std::find(datas_.begin(), datas_.end(), data)); - datas_.push_back(const_cast<Data*>(data)); -} - -void ArrayBuffer::Cache::TryToOffload() { - TRACK_MEMORY_SCOPE("DOM"); - if (total_size_in_main_memory_ <= maximum_size_in_main_memory_) { - return; - } - std::vector<Data*>::iterator iter = datas_.begin(); - while (iter != datas_.end() && - total_size_in_main_memory_ > maximum_size_in_main_memory_) { - if (!(*iter)->offloaded() && (*iter)->Offload()) { - total_size_in_main_memory_ -= (*iter)->size(); - } - ++iter; - } - if (total_size_in_main_memory_ > maximum_size_in_main_memory_) { - LOG(WARNING) << "ArrayBuffer takes " << total_size_in_main_memory_ - << " of main memory and cannot be offloaded"; - } -} - ArrayBuffer::ArrayBuffer(script::EnvironmentSettings* settings, uint32 length) - : data_(settings, length) { + : data_(length) { // TODO: Once we can have a reliable way to pass the // EnvironmentSettings to HTMLMediaElement, we should make EnvironmentSettings // mandatory for creating ArrayBuffer in non-testing code. if (settings) { DOMSettings* dom_settings = base::polymorphic_downcast<dom::DOMSettings*>(settings); - dom_settings->javascript_engine()->ReportExtraMemoryCost(data_.size()); + javascript_engine_ = dom_settings->javascript_engine(); + javascript_engine_->AdjustAmountOfExternalAllocatedMemory( + static_cast<int64_t>(data_.size())); } } ArrayBuffer::ArrayBuffer(script::EnvironmentSettings* settings, const uint8* data, uint32 length) - : data_(settings, data, length) { + : data_(data, length) { // TODO: Make EnvironmentSettings mandatory for creating // ArrayBuffer in non-testing code. if (settings) { DOMSettings* dom_settings = base::polymorphic_downcast<dom::DOMSettings*>(settings); - dom_settings->javascript_engine()->ReportExtraMemoryCost(data_.size()); + javascript_engine_ = dom_settings->javascript_engine(); + javascript_engine_->AdjustAmountOfExternalAllocatedMemory( + static_cast<int64_t>(data_.size())); } } ArrayBuffer::ArrayBuffer(script::EnvironmentSettings* settings, - AllocationType allocation_type, scoped_array<uint8> data, uint32 length) : data_(data.Pass(), length) { - DCHECK_EQ(allocation_type, kFromHeap); // TODO: Make EnvironmentSettings mandatory for creating // ArrayBuffer in non-testing code. if (settings) { DOMSettings* dom_settings = base::polymorphic_downcast<dom::DOMSettings*>(settings); - dom_settings->javascript_engine()->ReportExtraMemoryCost(data_.size()); + javascript_engine_ = dom_settings->javascript_engine(); + javascript_engine_->AdjustAmountOfExternalAllocatedMemory( + static_cast<int64_t>(data_.size())); } } @@ -239,7 +136,12 @@ *clamped_end = end; } -ArrayBuffer::~ArrayBuffer() {} +ArrayBuffer::~ArrayBuffer() { + if (javascript_engine_) { + javascript_engine_->AdjustAmountOfExternalAllocatedMemory( + -static_cast<int64_t>(data_.size())); + } +} } // namespace dom } // namespace cobalt
diff --git a/src/cobalt/dom/array_buffer.h b/src/cobalt/dom/array_buffer.h index 64678aa..00db1f1 100644 --- a/src/cobalt/dom/array_buffer.h +++ b/src/cobalt/dom/array_buffer.h
@@ -19,6 +19,7 @@ #include "base/memory/scoped_ptr.h" // For scoped_array #include "cobalt/script/environment_settings.h" +#include "cobalt/script/javascript_engine.h" #include "cobalt/script/wrappable.h" namespace cobalt { @@ -26,79 +27,32 @@ class ArrayBuffer : public script::Wrappable { public: - // To explicitly express that a specific ArrayBuffer should be allocated from - // the heap. - enum AllocationType { kFromHeap }; - - class Cache; - - // Optional Allocator to be used to allocate/free memory for ArrayBuffer. - // ArrayBuffer will allocate from the heap if an Allocator is not provided. - // This is because ArrayBuffer allocates its memory on the heap by default and - // ArrayBuffers may occupy a lot of memory. It is possible to provide an - // allocator on some platforms so ArrayBuffer can possibly use memory that is - // not part of the heap. - class Allocator { - public: - virtual ~Allocator() {} - virtual void* Allocate(size_t size) = 0; - virtual void Free(void* p) = 0; - }; - // This class manages the internal buffer of an ArrayBuffer. It deals the // fact that the buffer can be allocated from an Allocator or from the heap. class Data { public: - Data(script::EnvironmentSettings* settings, size_t size); - Data(script::EnvironmentSettings* settings, const uint8* data, size_t size); + explicit Data(size_t size); + Data(const uint8* data, size_t size); Data(scoped_array<uint8> data, size_t size); ~Data(); uint8* data() const; size_t size() const { return size_; } - // Move the ArrayBuffer allocated on the heap to memory allocated using the - // provided allocator. It returns true if such move is successful or if the - // ArrayBuffer has already been offloaded. - bool Offload(); - bool offloaded() const { return offloaded_; } private: - void Initialize(script::EnvironmentSettings* settings, size_t size); + void Initialize(size_t size); - Allocator* allocator_; - Cache* cache_; - // True only if |data_| is allocated by |allocator_|. - bool offloaded_; uint8* data_; size_t size_; DISALLOW_COPY_AND_ASSIGN(Data); }; - class Cache { - public: - explicit Cache(size_t maximum_size_in_main_memory); - - void Register(Data* data); - void Unregister(Data* data); - void ReportUsage(const Data* data); - - private: - void TryToOffload(); - - size_t total_size_in_main_memory_; - size_t maximum_size_in_main_memory_; - std::vector<Data*> datas_; - }; - ArrayBuffer(script::EnvironmentSettings* settings, uint32 length); ArrayBuffer(script::EnvironmentSettings* settings, const uint8* data, uint32 length); - // This is for use by AudioBuffer as we do want to ensure decoded audio data - // stay in main memory. - ArrayBuffer(script::EnvironmentSettings* settings, - AllocationType allocation_type, scoped_array<uint8> data, + ArrayBuffer(script::EnvironmentSettings* settings, scoped_array<uint8> data, uint32 length); uint32 byte_length() const { return static_cast<uint32>(data_.size()); } @@ -126,6 +80,7 @@ ~ArrayBuffer(); Data data_; + script::JavaScriptEngine* javascript_engine_ = nullptr; DISALLOW_COPY_AND_ASSIGN(ArrayBuffer); };
diff --git a/src/cobalt/dom/blob.h b/src/cobalt/dom/blob.h index a4c4fa4..c38e0a3 100644 --- a/src/cobalt/dom/blob.h +++ b/src/cobalt/dom/blob.h
@@ -52,7 +52,7 @@ const scoped_refptr<ArrayBuffer>& buffer = NULL); Blob(script::EnvironmentSettings* settings, - script::Sequence<BlobPart> blobParts, + script::Sequence<BlobPart> blob_parts, const BlobPropertyBag& options = EmptyBlobPropertyBag()); const uint8* data() { return buffer_->data(); }
diff --git a/src/cobalt/dom/camera_3d.idl b/src/cobalt/dom/camera_3d.idl index 51b40a1..b2715c5 100644 --- a/src/cobalt/dom/camera_3d.idl +++ b/src/cobalt/dom/camera_3d.idl
@@ -23,7 +23,7 @@ const unsigned long DOM_CAMERA_YAW = 0x02; void createKeyMapping(long keyCode, unsigned long cameraAxis, - float degrees_per_second); + float degreesPerSecond); void clearKeyMapping(long keyCode); void clearAllKeyMappings();
diff --git a/src/cobalt/dom/console.cc b/src/cobalt/dom/console.cc index 6e13712..9473247 100644 --- a/src/cobalt/dom/console.cc +++ b/src/cobalt/dom/console.cc
@@ -83,10 +83,9 @@ return kLogString; case kWarning: return kWarningString; - default: - NOTREACHED(); - return ""; } + NOTREACHED(); + return ""; } void Console::AddListener(Listener* listener) { listeners_.insert(listener); }
diff --git a/src/cobalt/dom/csp_delegate.cc b/src/cobalt/dom/csp_delegate.cc index 743b0ae..c9a9b49 100644 --- a/src/cobalt/dom/csp_delegate.cc +++ b/src/cobalt/dom/csp_delegate.cc
@@ -90,9 +90,6 @@ case kWebSocket: can_load = csp_->AllowConnectToSource(url, redirect_status); break; - default: - NOTREACHED() << "Invalid resource type " << type; - break; } return can_load; }
diff --git a/src/cobalt/dom/csp_violation_reporter.cc b/src/cobalt/dom/csp_violation_reporter.cc index 6fd8c4b..4c5543a 100644 --- a/src/cobalt/dom/csp_violation_reporter.cc +++ b/src/cobalt/dom/csp_violation_reporter.cc
@@ -126,7 +126,7 @@ return; } - DLOG(INFO) << violation_info.console_message; + LOG(INFO) << violation_info.console_message; ViolationEvent violation_data; GatherSecurityPolicyViolationEventData(document_, violation_info, &violation_data);
diff --git a/src/cobalt/dom/custom_event_test.cc b/src/cobalt/dom/custom_event_test.cc index 3cb9127..2d5d184 100644 --- a/src/cobalt/dom/custom_event_test.cc +++ b/src/cobalt/dom/custom_event_test.cc
@@ -71,7 +71,9 @@ kCspEnforcementEnable, base::Closure() /* csp_policy_changed */, base::Closure() /* ran_animation_frame_callbacks */, dom::Window::CloseCallback() /* window_close */, - base::Closure() /* window_minimize */, NULL, NULL, NULL)) { + base::Closure() /* window_minimize */, NULL, NULL, NULL, + dom::Window::OnStartDispatchEventCallback(), + dom::Window::OnStopDispatchEventCallback())) { engine_ = script::JavaScriptEngine::CreateEngine(); global_environment_ = engine_->CreateGlobalEnvironment(); global_environment_->CreateGlobalObject(window_,
diff --git a/src/cobalt/dom/dom_settings.cc b/src/cobalt/dom/dom_settings.cc index bbd39d4..24de247 100644 --- a/src/cobalt/dom/dom_settings.cc +++ b/src/cobalt/dom/dom_settings.cc
@@ -35,19 +35,12 @@ fetcher_factory_(fetcher_factory), network_module_(network_module), window_(window), - array_buffer_allocator_(options.array_buffer_allocator), - array_buffer_cache_(options.array_buffer_cache), media_source_registry_(media_source_registry), blob_registry_(blob_registry), can_play_type_handler_(can_play_type_handler), javascript_engine_(engine), global_environment_(global_environment), mutation_observer_task_manager_(mutation_observer_task_manager) { - if (array_buffer_allocator_) { - DCHECK(options.array_buffer_cache); - } else { - DCHECK(!options.array_buffer_cache); - } } DOMSettings::~DOMSettings() {}
diff --git a/src/cobalt/dom/dom_settings.h b/src/cobalt/dom/dom_settings.h index 3437d3a..e2e0932 100644 --- a/src/cobalt/dom/dom_settings.h +++ b/src/cobalt/dom/dom_settings.h
@@ -49,17 +49,6 @@ typedef UrlRegistry<MediaSource> MediaSourceRegistry; // Hold optional settings for DOMSettings. struct Options { - Options() : array_buffer_allocator(NULL), array_buffer_cache(NULL) {} - - // ArrayBuffer allocates its memory on the heap by default and ArrayBuffers - // may occupy a lot of memory. It is possible to provide an allocator via - // the following member on some platforms so ArrayBuffer can possibly use - // memory that is not part of the heap. - ArrayBuffer::Allocator* array_buffer_allocator; - // When array_buffer_allocator is provided, we still need to hold certain - // amount of ArrayBuffer inside main memory. So we have provide the - // following cache to manage ArrayBuffer in main memory. - ArrayBuffer::Cache* array_buffer_cache; // Microphone options. speech::Microphone::Options microphone_options; }; @@ -85,12 +74,6 @@ void set_window(const scoped_refptr<Window>& window); scoped_refptr<Window> window() const; - ArrayBuffer::Allocator* array_buffer_allocator() const { - return array_buffer_allocator_; - } - - ArrayBuffer::Cache* array_buffer_cache() const { return array_buffer_cache_; } - void set_fetcher_factory(loader::FetcherFactory* fetcher_factory) { fetcher_factory_ = fetcher_factory; } @@ -128,8 +111,6 @@ loader::FetcherFactory* fetcher_factory_; network::NetworkModule* network_module_; scoped_refptr<Window> window_; - ArrayBuffer::Allocator* array_buffer_allocator_; - ArrayBuffer::Cache* array_buffer_cache_; MediaSourceRegistry* media_source_registry_; Blob::Registry* blob_registry_; media::CanPlayTypeHandler* can_play_type_handler_;
diff --git a/src/cobalt/dom/dom_stat_tracker.cc b/src/cobalt/dom/dom_stat_tracker.cc index e2e1219..2f5861e 100644 --- a/src/cobalt/dom/dom_stat_tracker.cc +++ b/src/cobalt/dom/dom_stat_tracker.cc
@@ -20,32 +20,41 @@ namespace dom { DomStatTracker::DomStatTracker(const std::string& name) - : html_elements_count_( - StringPrintf("Count.%s.DOM.HtmlElement.Total", name.c_str()), 0, + : count_html_element_( + StringPrintf("Count.%s.DOM.HtmlElement", name.c_str()), 0, "Total number of HTML elements."), - document_html_elements_count_( + count_html_element_document_( StringPrintf("Count.%s.DOM.HtmlElement.Document", name.c_str()), 0, "Number of HTML elements in the document."), - is_event_active_(false), + count_html_element_created_(0), + count_html_element_destroyed_(0), + count_html_element_document_added_(0), + count_html_element_document_removed_(0), + script_element_execute_count_( + StringPrintf("Count.%s.DOM.HtmlScriptElement.Execute", name.c_str()), + 0, "Count of HTML script element execute calls."), + script_element_execute_total_size_( + StringPrintf("Memory.%s.DOM.HtmlScriptElement.Execute", name.c_str()), + 0, "Total size in bytes of HTML script elements executed."), + script_element_execute_time_( + StringPrintf("Time.%s.DOM.HtmlScriptElement.Execute", name.c_str()), + 0, "Time of the last HTML script element execute."), + is_tracking_event_(false), + event_initial_count_html_element_(0), + event_initial_count_html_element_document_(0), + event_count_html_element_created_(0), + event_count_html_element_destroyed_(0), + event_count_html_element_document_added_(0), + event_count_html_element_document_removed_(0), + event_count_update_matching_rules_(0), + event_count_update_computed_style_(0), + event_count_generate_html_element_computed_style_(0), + event_count_generate_pseudo_element_computed_style_(0), event_video_start_delay_stop_watch_(kStopWatchTypeEventVideoStartDelay, base::StopWatch::kAutoStartOff, this), event_video_start_delay_( StringPrintf("Event.Duration.%s.DOM.VideoStartDelay", name.c_str()), - base::TimeDelta(), "Total delay between event and video starting."), - script_element_execute_count_( - StringPrintf("Count.%s.DOM.HtmlScriptElement.Execute", name.c_str()), - 0, "Count of HTML script element execute calls."), - script_element_execute_time_( - StringPrintf("Time.%s.DOM.HtmlScriptElement.Execute", name.c_str()), - 0, "Time of the last HTML script element execute."), - html_elements_created_count_(0), - html_elements_destroyed_count_(0), - html_elements_inserted_into_document_count_(0), - html_elements_removed_from_document_count_(0), - update_matching_rules_count_(0), - update_computed_style_count_(0), - generate_html_element_computed_style_count_(0), - generate_pseudo_element_computed_style_count_(0) { + base::TimeDelta(), "Total delay between event and video starting.") { stop_watch_durations_.resize(kNumStopWatchTypes, base::TimeDelta()); } @@ -54,18 +63,111 @@ // Verify that all of the elements were removed from the document and // destroyed. - DCHECK_EQ(html_elements_count_, 0); - DCHECK_EQ(document_html_elements_count_, 0); + DCHECK_EQ(count_html_element_, 0); + DCHECK_EQ(count_html_element_document_, 0); event_video_start_delay_stop_watch_.Stop(); } -void DomStatTracker::OnStartEvent() { - is_event_active_ = true; +void DomStatTracker::OnHtmlElementCreated() { + ++count_html_element_created_; + if (is_tracking_event_) { + ++event_count_html_element_created_; + } +} - // Flush the periodic tracking prior to starting the event. This ensures that - // an accurate count of the periodic counts is produced during the event. - FlushPeriodicTracking(); +void DomStatTracker::OnHtmlElementDestroyed() { + ++count_html_element_destroyed_; + if (is_tracking_event_) { + ++event_count_html_element_destroyed_; + } +} + +void DomStatTracker::OnHtmlElementInsertedIntoDocument() { + ++count_html_element_document_added_; + if (is_tracking_event_) { + ++event_count_html_element_document_added_; + } +} + +void DomStatTracker::OnHtmlElementRemovedFromDocument() { + ++count_html_element_document_removed_; + if (is_tracking_event_) { + ++event_count_html_element_document_removed_; + } +} + +void DomStatTracker::OnUpdateMatchingRules() { + if (is_tracking_event_) { + ++event_count_update_matching_rules_; + } +} + +void DomStatTracker::OnUpdateComputedStyle() { + if (is_tracking_event_) { + ++event_count_update_computed_style_; + } +} + +void DomStatTracker::OnGenerateHtmlElementComputedStyle() { + if (is_tracking_event_) { + ++event_count_generate_html_element_computed_style_; + } +} + +void DomStatTracker::OnGeneratePseudoElementComputedStyle() { + if (is_tracking_event_) { + ++event_count_generate_pseudo_element_computed_style_; + } +} + +void DomStatTracker::OnHtmlScriptElementExecuted(size_t script_size) { + ++script_element_execute_count_; + script_element_execute_total_size_ += script_size; + script_element_execute_time_ = base::TimeTicks::Now().ToInternalValue(); +} + +void DomStatTracker::OnHtmlVideoElementPlaying() { + if (event_video_start_delay_stop_watch_.IsCounting()) { + event_video_start_delay_stop_watch_.Stop(); + event_video_start_delay_ = + stop_watch_durations_[kStopWatchTypeEventVideoStartDelay]; + } +} + +void DomStatTracker::FlushPeriodicTracking() { + // Update the CVals before clearing the periodic values. + count_html_element_ += + count_html_element_created_ - count_html_element_destroyed_; + count_html_element_document_ += + count_html_element_document_added_ - count_html_element_document_removed_; + + // Now clear the values. + count_html_element_created_ = 0; + count_html_element_destroyed_ = 0; + count_html_element_document_added_ = 0; + count_html_element_document_removed_ = 0; +} + +void DomStatTracker::StartTrackingEvent() { + DCHECK(!is_tracking_event_); + is_tracking_event_ = true; + + event_initial_count_html_element_ = count_html_element_ + + count_html_element_created_ - + count_html_element_destroyed_; + event_initial_count_html_element_document_ = + count_html_element_document_ + count_html_element_document_added_ - + count_html_element_document_removed_; + + event_count_html_element_created_ = 0; + event_count_html_element_destroyed_ = 0; + event_count_html_element_document_added_ = 0; + event_count_html_element_document_removed_ = 0; + event_count_update_matching_rules_ = 0; + event_count_update_computed_style_ = 0; + event_count_generate_html_element_computed_style_ = 0; + event_count_generate_pseudo_element_computed_style_ = 0; // Stop the watch before re-starting it. This ensures that the count is // zeroed out if it was still running from the prior event (this is a common @@ -81,51 +183,20 @@ } } -void DomStatTracker::OnEndEvent() { - is_event_active_ = false; - - // Flush the periodic tracking after the event. This updates the cval totals, - // providing an accurate picture of them at the moment the event ends. - FlushPeriodicTracking(); +void DomStatTracker::StopTrackingEvent() { + DCHECK(is_tracking_event_); + is_tracking_event_ = false; } -void DomStatTracker::OnHtmlVideoElementPlaying() { - if (event_video_start_delay_stop_watch_.IsCounting()) { - event_video_start_delay_stop_watch_.Stop(); - event_video_start_delay_ = - stop_watch_durations_[kStopWatchTypeEventVideoStartDelay]; - } +int DomStatTracker::EventCountHtmlElement() const { + return event_initial_count_html_element_ + event_count_html_element_created_ - + event_count_html_element_destroyed_; } -void DomStatTracker::OnHtmlScriptElementExecuted() { - ++script_element_execute_count_; - script_element_execute_time_ = base::TimeTicks::Now().ToInternalValue(); -} - -void DomStatTracker::OnHtmlElementCreated() { ++html_elements_created_count_; } - -void DomStatTracker::OnHtmlElementDestroyed() { - ++html_elements_destroyed_count_; -} - -void DomStatTracker::OnHtmlElementInsertedIntoDocument() { - ++html_elements_inserted_into_document_count_; -} - -void DomStatTracker::OnHtmlElementRemovedFromDocument() { - ++html_elements_removed_from_document_count_; -} - -void DomStatTracker::OnUpdateMatchingRules() { ++update_matching_rules_count_; } - -void DomStatTracker::OnUpdateComputedStyle() { ++update_computed_style_count_; } - -void DomStatTracker::OnGenerateHtmlElementComputedStyle() { - ++generate_html_element_computed_style_count_; -} - -void DomStatTracker::OnGeneratePseudoElementComputedStyle() { - ++generate_pseudo_element_computed_style_count_; +int DomStatTracker::EventCountHtmlElementDocument() const { + return event_initial_count_html_element_document_ + + event_count_html_element_document_added_ - + event_count_html_element_document_removed_; } base::TimeDelta DomStatTracker::GetStopWatchTypeDuration( @@ -134,30 +205,12 @@ } bool DomStatTracker::IsStopWatchEnabled(int id) const { - return is_event_active_ || id == kStopWatchTypeEventVideoStartDelay; + return is_tracking_event_ || id == kStopWatchTypeEventVideoStartDelay; } void DomStatTracker::OnStopWatchStopped(int id, base::TimeDelta time_elapsed) { stop_watch_durations_[static_cast<size_t>(id)] += time_elapsed; } -void DomStatTracker::FlushPeriodicTracking() { - // Update the CVals before clearing the periodic values. - html_elements_count_ += - html_elements_created_count_ - html_elements_destroyed_count_; - document_html_elements_count_ += html_elements_inserted_into_document_count_ - - html_elements_removed_from_document_count_; - - // Now clear the values. - html_elements_created_count_ = 0; - html_elements_destroyed_count_ = 0; - html_elements_inserted_into_document_count_ = 0; - html_elements_removed_from_document_count_ = 0; - update_matching_rules_count_ = 0; - update_computed_style_count_ = 0; - generate_html_element_computed_style_count_ = 0; - generate_pseudo_element_computed_style_count_ = 0; -} - } // namespace dom } // namespace cobalt
diff --git a/src/cobalt/dom/dom_stat_tracker.h b/src/cobalt/dom/dom_stat_tracker.h index 50e6ebd..266ccae 100644 --- a/src/cobalt/dom/dom_stat_tracker.h +++ b/src/cobalt/dom/dom_stat_tracker.h
@@ -37,14 +37,6 @@ explicit DomStatTracker(const std::string& name); ~DomStatTracker(); - // Event-related - void OnStartEvent(); - void OnEndEvent(); - - void OnHtmlVideoElementPlaying(); - void OnHtmlScriptElementExecuted(); - - // Periodic count-related void OnHtmlElementCreated(); void OnHtmlElementDestroyed(); void OnHtmlElementInsertedIntoDocument(); @@ -53,35 +45,42 @@ void OnUpdateComputedStyle(); void OnGenerateHtmlElementComputedStyle(); void OnGeneratePseudoElementComputedStyle(); + void OnHtmlScriptElementExecuted(size_t script_size); + void OnHtmlVideoElementPlaying(); - int html_elements_count() const { return html_elements_count_; } - int document_html_elements_count() const { - return document_html_elements_count_; - } + // This function updates the CVals from the periodic values and then clears + // those values. + void FlushPeriodicTracking(); - int html_elements_created_count() const { - return html_elements_created_count_; + // Event-related + void StartTrackingEvent(); + void StopTrackingEvent(); + + int EventCountHtmlElement() const; + int EventCountHtmlElementDocument() const; + int event_count_html_element_created() const { + return event_count_html_element_created_; } - int html_elements_destroyed_count() const { - return html_elements_destroyed_count_; + int event_count_html_element_destroyed() const { + return event_count_html_element_destroyed_; } - int html_elements_added_to_document_count() const { - return html_elements_inserted_into_document_count_; + int event_count_html_element_document_added() const { + return event_count_html_element_document_added_; } - int html_elements_removed_from_document_count() const { - return html_elements_removed_from_document_count_; + int event_count_html_element_document_removed() const { + return event_count_html_element_document_removed_; } - int update_matching_rules_count() const { - return update_matching_rules_count_; + int event_count_update_matching_rules() const { + return event_count_update_matching_rules_; } - int update_computed_style_count() const { - return update_computed_style_count_; + int event_count_update_computed_style() const { + return event_count_update_computed_style_; } - int generate_html_element_computed_style_count() const { - return generate_html_element_computed_style_count_; + int event_count_generate_html_element_computed_style() const { + return event_count_generate_html_element_computed_style_; } - int generate_pseudo_element_computed_style_count() const { - return generate_pseudo_element_computed_style_count_; + int event_count_generate_pseudo_element_computed_style() const { + return event_count_generate_pseudo_element_computed_style_; } base::TimeDelta GetStopWatchTypeDuration(StopWatchType type) const; @@ -91,36 +90,41 @@ bool IsStopWatchEnabled(int id) const override; void OnStopWatchStopped(int id, base::TimeDelta time_elapsed) override; - // This function updates the CVals from the periodic values and then clears - // those values. - void FlushPeriodicTracking(); - // Count cvals that are updated when the periodic tracking is flushed. - base::CVal<int, base::CValPublic> html_elements_count_; - base::CVal<int, base::CValPublic> document_html_elements_count_; + base::CVal<int, base::CValPublic> count_html_element_; + base::CVal<int, base::CValPublic> count_html_element_document_; + + // Periodic counts. The counts are cleared after the CVals are updated in + // |FlushPeriodicTracking|. + int count_html_element_created_; + int count_html_element_destroyed_; + int count_html_element_document_added_; + int count_html_element_document_removed_; + + // Count of HtmlScriptElement::Execute() calls, their total size in bytes, and + // the time of last call. + base::CVal<int, base::CValPublic> script_element_execute_count_; + base::CVal<base::cval::SizeInBytes, base::CValPublic> + script_element_execute_total_size_; + base::CVal<int64, base::CValPublic> script_element_execute_time_; // Event-related - bool is_event_active_; + bool is_tracking_event_; + int event_initial_count_html_element_; + int event_initial_count_html_element_document_; + int event_count_html_element_created_; + int event_count_html_element_destroyed_; + int event_count_html_element_document_added_; + int event_count_html_element_document_removed_; + int event_count_update_matching_rules_; + int event_count_update_computed_style_; + int event_count_generate_html_element_computed_style_; + int event_count_generate_pseudo_element_computed_style_; // Tracking of videos produced by an event. base::StopWatch event_video_start_delay_stop_watch_; base::CVal<base::TimeDelta, base::CValPublic> event_video_start_delay_; - // Count of HtmlScriptElement::Execute() calls and time of last call. - base::CVal<int> script_element_execute_count_; - base::CVal<int64> script_element_execute_time_; - - // Periodic counts. The counts are cleared after the CVals are updated in - // |FlushPeriodicTracking|. - int html_elements_created_count_; - int html_elements_destroyed_count_; - int html_elements_inserted_into_document_count_; - int html_elements_removed_from_document_count_; - int update_matching_rules_count_; - int update_computed_style_count_; - int generate_html_element_computed_style_count_; - int generate_pseudo_element_computed_style_count_; - // Stop watch-related. std::vector<base::TimeDelta> stop_watch_durations_; };
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp index 8cf266a..14a615a 100644 --- a/src/cobalt/dom/dom_test.gyp +++ b/src/cobalt/dom/dom_test.gyp
@@ -57,6 +57,7 @@ 'node_list_live_test.cc', 'node_list_test.cc', 'node_test.cc', + 'on_screen_keyboard_test.cc', 'performance_test.cc', 'rule_matching_test.cc', 'screen_test.cc',
diff --git a/src/cobalt/dom/element_test.cc b/src/cobalt/dom/element_test.cc index 229fd5c..1932267 100644 --- a/src/cobalt/dom/element_test.cc +++ b/src/cobalt/dom/element_test.cc
@@ -181,13 +181,17 @@ EXPECT_EQ("2", attributes->GetNamedItem("a")->value()); // Make sure that adding another attribute through the element affects - // the NamedNodeMap. + // the NamedNodeMap. Note that NamedNodeMap does not guarantee order of items. element->SetAttribute("b", "2"); EXPECT_EQ(2, attributes->length()); EXPECT_EQ("b", attributes->GetNamedItem("b")->name()); EXPECT_EQ("2", attributes->GetNamedItem("b")->value()); - EXPECT_EQ("b", attributes->Item(1)->name()); - EXPECT_EQ("2", attributes->Item(1)->value()); + if ("b" == attributes->Item(1)->name()) { + EXPECT_EQ("2", attributes->Item(1)->value()); + } else { + EXPECT_EQ("b", attributes->Item(0)->name()); + EXPECT_EQ("2", attributes->Item(0)->value()); + } // Make sure that removing an attribute through the element affects // the NamedNodeMap.
diff --git a/src/cobalt/dom/eme/media_key_session.cc b/src/cobalt/dom/eme/media_key_session.cc index 5af2533..8db9c20 100644 --- a/src/cobalt/dom/eme/media_key_session.cc +++ b/src/cobalt/dom/eme/media_key_session.cc
@@ -41,6 +41,11 @@ base::Bind(&MediaKeySession::OnSessionUpdateKeyStatuses, base::AsWeakPtr(this)) #endif // SB_HAS(DRM_KEY_STATUSES) +#if SB_HAS(DRM_SESSION_CLOSED) + , + base::Bind(&MediaKeySession::OnSessionClosed, + base::AsWeakPtr(this)) +#endif // SB_HAS(DRM_SESSION_CLOSED) )), // NOLINT(whitespace/parens) script_value_factory_(script_value_factory), uninitialized_(true), @@ -91,20 +96,18 @@ // See // https://www.w3.org/TR/encrypted-media/#dom-mediakeysession-generaterequest. -scoped_ptr<MediaKeySession::VoidPromiseValue> MediaKeySession::GenerateRequest( +script::Handle<script::Promise<void>> MediaKeySession::GenerateRequest( const std::string& init_data_type, const BufferSource& init_data) { - scoped_ptr<VoidPromiseValue> promise = + script::Handle<script::Promise<void>> promise = script_value_factory_->CreateBasicPromise<void>(); - VoidPromiseValue::StrongReference promise_reference(*promise); // 1. If this object is closed, return a promise rejected with // an InvalidStateError. // 2. If this object's uninitialized value is false, return a promise rejected // with an InvalidStateError. if (drm_system_session_->is_closed() || !uninitialized_) { - promise_reference.value().Reject( - new DOMException(DOMException::kInvalidStateErr)); - return promise.Pass(); + promise->Reject(new DOMException(DOMException::kInvalidStateErr)); + return promise; } // 3. Let this object's uninitialized value be false. @@ -119,8 +122,8 @@ // 5. If initData is an empty array, return a promise rejected with a newly // created TypeError. if (init_data_type.empty() || init_data_buffer_size == 0) { - promise_reference.value().Reject(script::kTypeError); - return promise.Pass(); + promise->Reject(script::kTypeError); + return promise; } // 10.2. The user agent must thoroughly validate the initialization data @@ -134,30 +137,28 @@ init_data_type, init_data_buffer, init_data_buffer_size, base::Bind(&MediaKeySession::OnSessionUpdateRequestGenerated, base::AsWeakPtr(this), - base::Owned(new VoidPromiseValue::Reference(this, *promise))), + base::Owned(new VoidPromiseValue::Reference(this, promise))), base::Bind(&MediaKeySession::OnSessionUpdateRequestDidNotGenerate, base::AsWeakPtr(this), - base::Owned(new VoidPromiseValue::Reference(this, *promise)))); + base::Owned(new VoidPromiseValue::Reference(this, promise)))); // 11. Return promise. - return promise.Pass(); + return promise; } // See https://www.w3.org/TR/encrypted-media/#dom-mediakeysession-update. -scoped_ptr<MediaKeySession::VoidPromiseValue> MediaKeySession::Update( +script::Handle<script::Promise<void>> MediaKeySession::Update( const BufferSource& response) { - scoped_ptr<VoidPromiseValue> promise = + script::Handle<script::Promise<void>> promise = script_value_factory_->CreateBasicPromise<void>(); - VoidPromiseValue::StrongReference promise_reference(*promise); // 1. If this object is closed, return a promise rejected with // an InvalidStateError. // 2. If this object's callable value is false, return a promise rejected // with an InvalidStateError. if (drm_system_session_->is_closed() || !callable_) { - promise_reference.value().Reject( - new DOMException(DOMException::kInvalidStateErr)); - return promise.Pass(); + promise->Reject(new DOMException(DOMException::kInvalidStateErr)); + return promise; } const uint8* response_buffer; @@ -167,8 +168,8 @@ // 3. If response is an empty array, return a promise rejected with a newly // created TypeError. if (response_buffer_size == 0) { - promise_reference.value().Reject(script::kTypeError); - return promise.Pass(); + promise->Reject(script::kTypeError); + return promise; } // 6.1. Let sanitized response be a validated and/or sanitized version of @@ -180,32 +181,30 @@ drm_system_session_->Update( response_buffer, response_buffer_size, base::Bind(&MediaKeySession::OnSessionUpdated, base::AsWeakPtr(this), - base::Owned(new VoidPromiseValue::Reference(this, *promise))), + base::Owned(new VoidPromiseValue::Reference(this, promise))), base::Bind(&MediaKeySession::OnSessionDidNotUpdate, base::AsWeakPtr(this), - base::Owned(new VoidPromiseValue::Reference(this, *promise)))); + base::Owned(new VoidPromiseValue::Reference(this, promise)))); // 7. Return promise. - return promise.Pass(); + return promise; } // See https://www.w3.org/TR/encrypted-media/#dom-mediakeysession-close. -scoped_ptr<MediaKeySession::VoidPromiseValue> MediaKeySession::Close() { - scoped_ptr<VoidPromiseValue> promise = +script::Handle<script::Promise<void>> MediaKeySession::Close() { + script::Handle<script::Promise<void>> promise = script_value_factory_->CreateBasicPromise<void>(); - VoidPromiseValue::StrongReference promise_reference(*promise); // 2. If session is closed, return a resolved promise. if (drm_system_session_->is_closed()) { - promise_reference.value().Resolve(); - return promise.Pass(); + promise->Resolve(); + return promise; } // 3. If session's callable value is false, return a promise rejected with // an InvalidStateError. if (!callable_) { - promise_reference.value().Reject( - new DOMException(DOMException::kInvalidStateErr)); - return promise.Pass(); + promise->Reject(new DOMException(DOMException::kInvalidStateErr)); + return promise; } // 5.2. Use CDM to close the key session associated with session. @@ -216,18 +215,18 @@ closed_callback_.Run(this); // 5.3.1. Run the Session Closed algorithm on the session. - OnClosed(); + OnSessionClosed(); // 5.3.2. Resolve promise. - promise_reference.value().Resolve(); - return promise.Pass(); + promise->Resolve(); + return promise; } void MediaKeySession::TraceMembers(script::Tracer* tracer) { EventTarget::TraceMembers(tracer); + tracer->Trace(event_queue_); tracer->Trace(key_status_map_); - event_queue_.TraceMembers(tracer); } // See @@ -245,8 +244,8 @@ // 10.9.4.1. Let message be the request that needs to be processed before // a license request request for the requested license type can be // generated based on the sanitized init data. - media_key_message_event_init.set_message(new ArrayBuffer( - NULL, ArrayBuffer::kFromHeap, message.Pass(), message_size)); + media_key_message_event_init.set_message( + new ArrayBuffer(NULL, message.Pass(), message_size)); // 10.9.4.2. Let message type reflect the type of message, either // "license-request" or "individualization-request". // @@ -373,7 +372,7 @@ } // See https://www.w3.org/TR/encrypted-media/#session-closed. -void MediaKeySession::OnClosed() { +void MediaKeySession::OnSessionClosed() { // 2. Run the Update Key Statuses algorithm on the session, providing an empty // sequence. //
diff --git a/src/cobalt/dom/eme/media_key_session.h b/src/cobalt/dom/eme/media_key_session.h index 3f59984..cdd39ab 100644 --- a/src/cobalt/dom/eme/media_key_session.h +++ b/src/cobalt/dom/eme/media_key_session.h
@@ -57,10 +57,10 @@ void set_onkeystatuseschange(const EventListenerScriptValue& event_listener); const EventListenerScriptValue* onmessage() const; void set_onmessage(const EventListenerScriptValue& event_listener); - scoped_ptr<VoidPromiseValue> GenerateRequest( + script::Handle<script::Promise<void>> GenerateRequest( const std::string& init_data_type, const BufferSource& init_data); - scoped_ptr<VoidPromiseValue> Update(const BufferSource& response); - scoped_ptr<VoidPromiseValue> Close(); + script::Handle<script::Promise<void>> Update(const BufferSource& response); + script::Handle<script::Promise<void>> Close(); DEFINE_WRAPPABLE_TYPE(MediaKeySession); void TraceMembers(script::Tracer* tracer) override; @@ -78,7 +78,7 @@ void OnSessionUpdateKeyStatuses( const std::vector<std::string>& key_ids, const std::vector<SbDrmKeyStatus>& key_statuses); - void OnClosed(); + void OnSessionClosed(); EventQueue event_queue_;
diff --git a/src/cobalt/dom/eme/media_key_system_access.cc b/src/cobalt/dom/eme/media_key_system_access.cc index 14c8f2c..f054390 100644 --- a/src/cobalt/dom/eme/media_key_system_access.cc +++ b/src/cobalt/dom/eme/media_key_system_access.cc
@@ -31,21 +31,19 @@ // See // https://www.w3.org/TR/encrypted-media/#dom-mediakeysystemaccess-createmediakeys. -scoped_ptr<MediaKeySystemAccess::InterfacePromiseValue> +script::Handle<MediaKeySystemAccess::InterfacePromise> MediaKeySystemAccess::CreateMediaKeys() const { // 1. Let promise be a new promise. - scoped_ptr<InterfacePromiseValue> promise = - script_value_factory_ - ->CreateInterfacePromise<scoped_refptr<MediaKeys> >(); - InterfacePromiseValue::StrongReference promise_reference(*promise); + script::Handle<MediaKeySystemAccess::InterfacePromise> promise = + script_value_factory_->CreateInterfacePromise<scoped_refptr<MediaKeys>>(); // 2.10. Let media keys be a new MediaKeys object. scoped_refptr<MediaKeys> media_keys( new MediaKeys(key_system_, script_value_factory_)); // 2.11. Resolve promise with media keys. - promise_reference.value().Resolve(media_keys); - return promise.Pass(); + promise->Resolve(media_keys); + return promise; } } // namespace eme
diff --git a/src/cobalt/dom/eme/media_key_system_access.h b/src/cobalt/dom/eme/media_key_system_access.h index f25a1c0..3e72e82 100644 --- a/src/cobalt/dom/eme/media_key_system_access.h +++ b/src/cobalt/dom/eme/media_key_system_access.h
@@ -30,8 +30,7 @@ // https://www.w3.org/TR/encrypted-media/#mediakeysystemaccess-interface class MediaKeySystemAccess : public script::Wrappable { public: - typedef script::ScriptValue<script::Promise< - scoped_refptr<script::Wrappable> > > InterfacePromiseValue; + using InterfacePromise = script::Promise<scoped_refptr<script::Wrappable>>; // Custom, not in any spec. MediaKeySystemAccess(const std::string& key_system, @@ -43,7 +42,7 @@ const MediaKeySystemConfiguration& GetConfiguration() const { return configuration_; } - scoped_ptr<InterfacePromiseValue> CreateMediaKeys() const; + script::Handle<InterfacePromise> CreateMediaKeys() const; DEFINE_WRAPPABLE_TYPE(MediaKeySystemAccess);
diff --git a/src/cobalt/dom/error_event_test.cc b/src/cobalt/dom/error_event_test.cc index 988ab56..6b30fb1 100644 --- a/src/cobalt/dom/error_event_test.cc +++ b/src/cobalt/dom/error_event_test.cc
@@ -71,7 +71,9 @@ kCspEnforcementEnable, base::Closure() /* csp_policy_changed */, base::Closure() /* ran_animation_frame_callbacks */, dom::Window::CloseCallback() /* window_close */, - base::Closure() /* window_minimize */, NULL, NULL, NULL)) { + base::Closure() /* window_minimize */, NULL, NULL, NULL, + dom::Window::OnStartDispatchEventCallback(), + dom::Window::OnStopDispatchEventCallback())) { engine_ = script::JavaScriptEngine::CreateEngine(); global_environment_ = engine_->CreateGlobalEnvironment(); global_environment_->CreateGlobalObject(window_,
diff --git a/src/cobalt/dom/font_face_updater.cc b/src/cobalt/dom/font_face_updater.cc index 91c392d..3e1918e 100644 --- a/src/cobalt/dom/font_face_updater.cc +++ b/src/cobalt/dom/font_face_updater.cc
@@ -147,6 +147,7 @@ case cssom::KeywordValue::kCurrentColor: case cssom::KeywordValue::kEllipsis: case cssom::KeywordValue::kEnd: + case cssom::KeywordValue::kEquirectangular: case cssom::KeywordValue::kFixed: case cssom::KeywordValue::kForwards: case cssom::KeywordValue::kHidden: @@ -176,7 +177,6 @@ case cssom::KeywordValue::kTop: case cssom::KeywordValue::kUppercase: case cssom::KeywordValue::kVisible: - default: NOTREACHED(); } }
diff --git a/src/cobalt/dom/html_link_element.cc b/src/cobalt/dom/html_link_element.cc index 2f55a12..d8dbd04 100644 --- a/src/cobalt/dom/html_link_element.cc +++ b/src/cobalt/dom/html_link_element.cc
@@ -185,10 +185,11 @@ base::Bind(&HTMLLinkElement::OnLoadingError, base::Unretained(this)))); } -void HTMLLinkElement::OnLoadingDone(const std::string& content, - const loader::Origin& last_url_origin) { - TRACK_MEMORY_SCOPE("DOM"); +void HTMLLinkElement::OnLoadingDone(const loader::Origin& last_url_origin, + scoped_ptr<std::string> content) { DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(content); + TRACK_MEMORY_SCOPE("DOM"); TRACE_EVENT0("cobalt::dom", "HTMLLinkElement::OnLoadingDone()"); // Get resource's final destination url from loader. @@ -196,9 +197,9 @@ Document* document = node_document(); if (rel() == "stylesheet") { - OnStylesheetLoaded(document, content); + OnStylesheetLoaded(document, *content); } else if (rel() == "splashscreen") { - OnSplashscreenLoaded(document, content); + OnSplashscreenLoaded(document, *content); } else { NOTIMPLEMENTED(); return;
diff --git a/src/cobalt/dom/html_link_element.h b/src/cobalt/dom/html_link_element.h index d3755a3..ac64b61 100644 --- a/src/cobalt/dom/html_link_element.h +++ b/src/cobalt/dom/html_link_element.h
@@ -74,8 +74,8 @@ // From the spec: HTMLLinkElement. void Obtain(); - void OnLoadingDone(const std::string& content, - const loader::Origin& last_url_origin); + void OnLoadingDone(const loader::Origin& last_url_origin, + scoped_ptr<std::string> content); void OnLoadingError(const std::string& error); void OnSplashscreenLoaded(Document* document, const std::string& content); void OnStylesheetLoaded(Document* document, const std::string& content);
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc index ca51156..5aac100 100644 --- a/src/cobalt/dom/html_media_element.cc +++ b/src/cobalt/dom/html_media_element.cc
@@ -130,7 +130,7 @@ UNREFERENCED_PARAMETER(resource_url); UNREFERENCED_PARAMETER(origin); return true; -#else // SB_HAS(PLAYER_WITH_URL) +#else // SB_HAS(PLAYER_WITH_URL) if (resource_url.SchemeIs("blob")) { // Blob resources come from application and is same-origin. return true; @@ -171,7 +171,6 @@ paused_(true), seeking_(false), controls_(false), - last_time_update_event_wall_time_(0), last_time_update_event_movie_time_(std::numeric_limits<float>::max()), processing_media_player_callback_(0), media_source_url_(std::string(kMediaSourceUrlProtocol) + ':' + @@ -309,21 +308,21 @@ } // See https://www.w3.org/TR/encrypted-media/#dom-htmlmediaelement-setmediakeys. -scoped_ptr<HTMLMediaElement::VoidPromiseValue> HTMLMediaElement::SetMediaKeys( +script::Handle<script::Promise<void>> HTMLMediaElement::SetMediaKeys( const scoped_refptr<eme::MediaKeys>& media_keys) { TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::SetMediaKeys()"); - scoped_ptr<VoidPromiseValue> promise = node_document() - ->html_element_context() - ->script_value_factory() - ->CreateBasicPromise<void>(); - VoidPromiseValue::StrongReference promise_reference(*promise); + script::Handle<script::Promise<void>> promise = + node_document() + ->html_element_context() + ->script_value_factory() + ->CreateBasicPromise<void>(); // 1. If mediaKeys and the mediaKeys attribute are the same object, return // a resolved promise. if (media_keys_ == media_keys) { - promise_reference.value().Resolve(); - return promise.Pass(); + promise->Resolve(); + return promise; } // 5.2. If the mediaKeys attribute is not null: @@ -353,10 +352,10 @@ media_keys_ = media_keys; // 5.6. Resolve promise. - promise_reference.value().Resolve(); + promise->Resolve(); // 6. Return promise. - return promise.Pass(); + return promise; } #else // defined(COBALT_MEDIA_SOURCE_2016) @@ -374,7 +373,6 @@ DOMException::Raise(DOMException::kNotSupportedErr, exception_state); break; case WebMediaPlayer::kMediaKeyExceptionNoError: - default: NOTREACHED(); break; } @@ -962,7 +960,8 @@ media_url = node_document()->url_as_gurl().Resolve(src); } if (media_url.is_empty()) { - MediaLoadingFailed(WebMediaPlayer::kNetworkStateFormatError); + MediaLoadingFailed(WebMediaPlayer::kNetworkStateFormatError, + "Invalid source."); DLOG(WARNING) << "HTMLMediaElement::LoadInternal, invalid 'src' " << src; return; } @@ -991,7 +990,7 @@ if (!url.is_valid()) { // Try to filter out invalid urls as GURL::spec() DCHECKs if the url is // valid. - NoneSupported(); + NoneSupported("URL is invalid."); return; } @@ -1001,7 +1000,7 @@ if (!node_document()->csp_delegate()->CanLoad(CspDelegate::kMedia, url, false)) { DLOG(INFO) << "URL " << url << " is rejected by security policy."; - NoneSupported(); + NoneSupported("URL is rejected by security policy."); return; } @@ -1017,7 +1016,7 @@ #endif // defined(COBALT_MEDIA_SOURCE_2016) media_source_url_ = url; } else { - NoneSupported(); + NoneSupported("Media source is NULL."); return; } } @@ -1080,7 +1079,7 @@ } } -void HTMLMediaElement::NoneSupported() { +void HTMLMediaElement::NoneSupported(const std::string& message) { MLOG(); DLOG(WARNING) << "HTMLMediaElement::NoneSupported() error."; @@ -1095,8 +1094,8 @@ // 6.1 - Set the error attribute to a new MediaError object whose code // attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED. - error_ = new MediaError(MediaError::kMediaErrSrcNotSupported); - + error_ = new MediaError(MediaError::kMediaErrSrcNotSupported, + message.empty() ? "Source not supported." : message); // 6.2 - Forget the media element's media-resource-specific text tracks. // 6.3 - Set the element's networkState attribute to the kNetworkNoSource @@ -1109,18 +1108,25 @@ ClearMediaSource(); } -void HTMLMediaElement::MediaLoadingFailed(WebMediaPlayer::NetworkState error) { +void HTMLMediaElement::MediaLoadingFailed(WebMediaPlayer::NetworkState error, + const std::string& message) { StopPeriodicTimers(); if (error == WebMediaPlayer::kNetworkStateNetworkError && ready_state_ >= WebMediaPlayer::kReadyStateHaveMetadata) { - MediaEngineError(new MediaError(MediaError::kMediaErrNetwork)); + MediaEngineError(new MediaError( + MediaError::kMediaErrNetwork, + message.empty() ? "Media loading failed with network error." + : message)); } else if (error == WebMediaPlayer::kNetworkStateDecodeError) { - MediaEngineError(new MediaError(MediaError::kMediaErrDecode)); + MediaEngineError(new MediaError( + MediaError::kMediaErrDecode, + message.empty() ? "Media loading failed with decode error." : message)); } else if ((error == WebMediaPlayer::kNetworkStateFormatError || error == WebMediaPlayer::kNetworkStateNetworkError) && load_state_ == kLoadingFromSrcAttr) { - NoneSupported(); + NoneSupported(message.empty() ? "Media loading failed with none supported." + : message); } } @@ -1175,8 +1181,9 @@ previous_progress_time_ = base::Time::Now().ToDoubleT(); playback_progress_timer_.Start( - FROM_HERE, base::TimeDelta::FromMilliseconds( - static_cast<int64>(kMaxTimeupdateEventFrequency * 1000)), + FROM_HERE, + base::TimeDelta::FromMilliseconds( + static_cast<int64>(kMaxTimeupdateEventFrequency * 1000)), this, &HTMLMediaElement::OnPlaybackProgressTimer); } @@ -1197,20 +1204,14 @@ } void HTMLMediaElement::ScheduleTimeupdateEvent(bool periodic_event) { - double now = base::Time::Now().ToDoubleT(); - double time_delta = now - last_time_update_event_wall_time_; - - // throttle the periodic events - if (periodic_event && time_delta < kMaxTimeupdateEventFrequency) { - return; - } - // Some media engines make multiple "time changed" callbacks at the same time, // but we only want one event at a given time so filter here float movie_time = current_time(NULL); if (movie_time != last_time_update_event_movie_time_) { + if (!periodic_event && playback_progress_timer_.IsRunning()) { + playback_progress_timer_.Reset(); + } ScheduleOwnEvent(base::Tokens::timeupdate()); - last_time_update_event_wall_time_ = now; last_time_update_event_movie_time_ = movie_time; } } @@ -1324,39 +1325,52 @@ } void HTMLMediaElement::SetNetworkState(WebMediaPlayer::NetworkState state) { - if (state == WebMediaPlayer::kNetworkStateEmpty) { - // Just update the cached state and leave, we can't do anything. - network_state_ = kNetworkEmpty; - return; + switch (state) { + case WebMediaPlayer::kNetworkStateEmpty: + // Just update the cached state and leave, we can't do anything. + network_state_ = kNetworkEmpty; + break; + case WebMediaPlayer::kNetworkStateIdle: + if (network_state_ > kNetworkIdle) { + ChangeNetworkStateFromLoadingToIdle(); + } else { + network_state_ = kNetworkIdle; + } + break; + case WebMediaPlayer::kNetworkStateLoading: + if (network_state_ < kNetworkLoading || + network_state_ == kNetworkNoSource) { + StartProgressEventTimer(); + } + network_state_ = kNetworkLoading; + break; + case WebMediaPlayer::kNetworkStateLoaded: + if (network_state_ != kNetworkIdle) { + ChangeNetworkStateFromLoadingToIdle(); + } + break; + case WebMediaPlayer::kNetworkStateFormatError: + case WebMediaPlayer::kNetworkStateNetworkError: + case WebMediaPlayer::kNetworkStateDecodeError: + NOTREACHED() << "Passed SetNetworkState an error state"; + break; } +} - if (state == WebMediaPlayer::kNetworkStateFormatError || - state == WebMediaPlayer::kNetworkStateNetworkError || - state == WebMediaPlayer::kNetworkStateDecodeError) { - MediaLoadingFailed(state); - return; - } - - if (state == WebMediaPlayer::kNetworkStateIdle) { - if (network_state_ > kNetworkIdle) { - ChangeNetworkStateFromLoadingToIdle(); - } else { - network_state_ = kNetworkIdle; - } - } - - if (state == WebMediaPlayer::kNetworkStateLoading) { - if (network_state_ < kNetworkLoading || - network_state_ == kNetworkNoSource) { - StartProgressEventTimer(); - } - network_state_ = kNetworkLoading; - } - - if (state == WebMediaPlayer::kNetworkStateLoaded) { - if (network_state_ != kNetworkIdle) { - ChangeNetworkStateFromLoadingToIdle(); - } +void HTMLMediaElement::SetNetworkError(WebMediaPlayer::NetworkState state, + const std::string& message) { + switch (state) { + case WebMediaPlayer::kNetworkStateFormatError: + case WebMediaPlayer::kNetworkStateNetworkError: + case WebMediaPlayer::kNetworkStateDecodeError: + MediaLoadingFailed(state, message); + break; + case WebMediaPlayer::kNetworkStateEmpty: + case WebMediaPlayer::kNetworkStateIdle: + case WebMediaPlayer::kNetworkStateLoading: + case WebMediaPlayer::kNetworkStateLoaded: + NOTREACHED() << "Passed SetNetworkError a non-error state"; + break; } } @@ -1583,7 +1597,12 @@ void HTMLMediaElement::MediaEngineError(scoped_refptr<MediaError> error) { MLOG() << error->code(); - LOG(WARNING) << "HTMLMediaElement::MediaEngineError " << error->code(); + if (error->message().empty()) { + LOG(WARNING) << "HTMLMediaElement::MediaEngineError " << error->code(); + } else { + LOG(WARNING) << "HTMLMediaElement::MediaEngineError " << error->code() + << " message: " << error->message(); + } // 1 - The user agent should cancel the fetching process. StopPeriodicTimers(); @@ -1614,6 +1633,16 @@ EndProcessingMediaPlayerCallback(); } +void HTMLMediaElement::NetworkError(const std::string& message) { + DCHECK(player_); + if (!player_) { + return; + } + BeginProcessingMediaPlayerCallback(); + SetNetworkError(player_->GetNetworkState(), message); + EndProcessingMediaPlayerCallback(); +} + void HTMLMediaElement::ReadyStateChanged() { DCHECK(player_); if (!player_) { @@ -1689,10 +1718,15 @@ ScheduleOwnEvent(base::Tokens::durationchange()); - float now = current_time(NULL); - float dur = duration(); - if (now > dur) { - Seek(dur); + double now = current_time(NULL); + // Reset and update |duration_|. + duration_ = std::numeric_limits<double>::quiet_NaN(); + if (player_ && ready_state_ >= WebMediaPlayer::kReadyStateHaveMetadata) { + duration_ = player_->GetDuration(); + } + + if (now > duration_) { + Seek(static_cast<float>(duration_)); } EndProcessingMediaPlayerCallback(); @@ -1777,7 +1811,7 @@ cssom::MapToMeshFunction::ExtractFromFilterList(filter); return map_to_mesh_filter; -#else // defined(ENABLE_MAP_TO_MESH) +#else // defined(ENABLE_MAP_TO_MESH) // If map-to-mesh is disabled, we never prefer decode-to-texture. return false; #endif // defined(ENABLE_MAP_TO_MESH) @@ -1799,10 +1833,14 @@ return "keyids"; case media::kEmeInitDataTypeWebM: return "webm"; - default: + case media::kEmeInitDataTypeUnknown: LOG(WARNING) << "Unknown EME initialization data type."; return ""; } + // Some compilers error with "control reaches end of non-void function" + // without this. + NOTREACHED(); + return ""; } } // namespace @@ -1871,10 +1909,6 @@ case kDomainError: code = MediaKeyError::kMediaKeyerrDomain; break; - default: - NOTREACHED(); - code = MediaKeyError::kMediaKeyerrUnknown; - break; } event_queue_.Enqueue( new MediaKeyErrorEvent(key_system, session_id, code, system_code)); @@ -1909,7 +1943,7 @@ media_source_->Close(); media_source_ = NULL; } -#else // defined(COBALT_MEDIA_SOURCE_2016) +#else // defined(COBALT_MEDIA_SOURCE_2016) SetSourceState(kMediaSourceReadyStateClosed); #endif // defined(COBALT_MEDIA_SOURCE_2016) }
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h index 7c7f55f..feff279 100644 --- a/src/cobalt/dom/html_media_element.h +++ b/src/cobalt/dom/html_media_element.h
@@ -83,8 +83,8 @@ scoped_refptr<TimeRanges> buffered() const; void Load(); - std::string CanPlayType(const std::string& mimeType); - std::string CanPlayType(const std::string& mimeType, + std::string CanPlayType(const std::string& mime_type); + std::string CanPlayType(const std::string& mime_type, const std::string& key_system); #if defined(COBALT_MEDIA_SOURCE_2016) @@ -95,7 +95,7 @@ return media_keys_; } typedef script::ScriptValue<script::Promise<void> > VoidPromiseValue; - scoped_ptr<VoidPromiseValue> SetMediaKeys( + script::Handle<script::Promise<void>> SetMediaKeys( const scoped_refptr<eme::MediaKeys>& media_keys); #else // defined(COBALT_MEDIA_SOURCE_2016) void GenerateKeyRequest( @@ -185,8 +185,9 @@ void LoadResource(const GURL& initial_url, const std::string& content_type, const std::string& key_system); void ClearMediaPlayer(); - void NoneSupported(); - void MediaLoadingFailed(WebMediaPlayer::NetworkState error); + void NoneSupported(const std::string& message); + void MediaLoadingFailed(WebMediaPlayer::NetworkState error, + const std::string& message); // Timers void OnLoadTimer(); @@ -214,6 +215,8 @@ // States void SetReadyState(WebMediaPlayer::ReadyState state); void SetNetworkState(WebMediaPlayer::NetworkState state); + void SetNetworkError(WebMediaPlayer::NetworkState state, + const std::string& message); void ChangeNetworkStateFromLoadingToIdle(); // Playback @@ -236,6 +239,7 @@ // WebMediaPlayerClient methods void NetworkStateChanged() override; + void NetworkError(const std::string& message) override; void ReadyStateChanged() override; void TimeChanged(bool eos_played) override; void DurationChanged() override; @@ -302,8 +306,6 @@ bool seeking_; bool controls_; - // The last time a timeupdate event was sent (wall clock). - double last_time_update_event_wall_time_; // The last time a timeupdate event was sent in movie time. double last_time_update_event_movie_time_;
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc index 99e6736..50688c4 100644 --- a/src/cobalt/dom/html_script_element.cc +++ b/src/cobalt/dom/html_script_element.cc
@@ -15,6 +15,7 @@ #include "cobalt/dom/html_script_element.h" #include <deque> +#include <utility> #include "base/bind.h" #include "base/compiler_specific.h" @@ -309,6 +310,8 @@ PreventGarbageCollection(); ExecuteExternal(); AllowGarbageCollection(); + // Release the content string now that we're finished with it. + content_.reset(); } else { // Executing the script block must just consist of firing a simple event // named error at the element. @@ -396,12 +399,12 @@ } } -void HTMLScriptElement::OnSyncLoadingDone( - const std::string& content, const loader::Origin& last_url_origin) { +void HTMLScriptElement::OnSyncLoadingDone(const loader::Origin& last_url_origin, + scoped_ptr<std::string> content) { TRACE_EVENT0("cobalt::dom", "HTMLScriptElement::OnSyncLoadingDone()"); - content_ = content; - is_sync_load_successful_ = true; fetched_last_url_origin_ = last_url_origin; + content_ = content.Pass(); + is_sync_load_successful_ = true; } void HTMLScriptElement::OnSyncLoadingError(const std::string& error) { @@ -411,18 +414,20 @@ // Algorithm for OnLoadingDone: // https://www.w3.org/TR/html5/scripting-1.html#prepare-a-script -void HTMLScriptElement::OnLoadingDone(const std::string& content, - const loader::Origin& last_url_origin) { +void HTMLScriptElement::OnLoadingDone(const loader::Origin& last_url_origin, + scoped_ptr<std::string> content) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(load_option_ == 4 || load_option_ == 5); + DCHECK(content); TRACE_EVENT0("cobalt::dom", "HTMLScriptElement::OnLoadingDone()"); if (!document_) { AllowGarbageCollection(); return; } - fetched_last_url_origin_ = last_url_origin; - content_ = content; + fetched_last_url_origin_ = last_url_origin; + content_ = content.Pass(); + switch (load_option_) { case 4: { // If the element has a src attribute, does not have an async attribute, @@ -499,6 +504,13 @@ document_->DecreaseLoadingCounterAndMaybeDispatchLoadEvent(); } break; } + + // Release the content string now that we're finished with it. + content_.reset(); + + // Post a task to release the loader. + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&HTMLScriptElement::ReleaseLoader, this)); } // Algorithm for OnLoadingError: @@ -543,6 +555,19 @@ // document until the task that is queued by the networking task source // once the resource has been fetched (defined above) has been run. document_->DecreaseLoadingCounterAndMaybeDispatchLoadEvent(); + + // Post a task to release the loader. + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&HTMLScriptElement::ReleaseLoader, this)); +} + +void HTMLScriptElement::ExecuteExternal() { + DCHECK(content_); + Execute(*content_, base::SourceLocation(url_.spec(), 1, 1), true); +} + +void HTMLScriptElement::ExecuteInternal() { + Execute(text_content().value(), inline_script_location_, false); } // Algorithm for Execute: @@ -603,7 +628,7 @@ GlobalStats::GetInstance()->StopJavaScriptEvent(); // Notify the DomStatTracker of the execution. - dom_stat_tracker_->OnHtmlScriptElementExecuted(); + dom_stat_tracker_->OnHtmlScriptElementExecuted(content.size()); } void HTMLScriptElement::PreventGarbageCollectionAndPostToDispatchEvent( @@ -642,5 +667,11 @@ } } +void HTMLScriptElement::ReleaseLoader() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(loader_); + loader_.reset(); +} + } // namespace dom } // namespace cobalt
diff --git a/src/cobalt/dom/html_script_element.h b/src/cobalt/dom/html_script_element.h index 254d9be..d6c42ff 100644 --- a/src/cobalt/dom/html_script_element.h +++ b/src/cobalt/dom/html_script_element.h
@@ -93,20 +93,16 @@ // void Prepare(); - void OnSyncLoadingDone(const std::string& content, - const loader::Origin& last_url_origin); + void OnSyncLoadingDone(const loader::Origin& last_url_origin, + scoped_ptr<std::string> content); void OnSyncLoadingError(const std::string& error); - void OnLoadingDone(const std::string& content, - const loader::Origin& last_url_origin); + void OnLoadingDone(const loader::Origin& last_url_origin, + scoped_ptr<std::string> content); void OnLoadingError(const std::string& error); - void ExecuteExternal() { - Execute(content_, base::SourceLocation(url_.spec(), 1, 1), true); - } - void ExecuteInternal() { - Execute(text_content().value(), inline_script_location_, false); - } + void ExecuteExternal(); + void ExecuteInternal(); void Execute(const std::string& content, const base::SourceLocation& script_location, bool is_external); @@ -114,6 +110,7 @@ const tracked_objects::Location& location, const base::Token& token); void PreventGarbageCollection(); void AllowGarbageCollection(); + void ReleaseLoader(); // Whether the script has been started. bool is_already_started_; @@ -137,8 +134,8 @@ bool is_sync_load_successful_; // Resolved URL of the script. GURL url_; - // Content of the script. - std::string content_; + // Content of the script. Released after Execute is called. + scoped_ptr<std::string> content_; // Active requests disabling garbage collection. int prevent_garbage_collection_count_;
diff --git a/src/cobalt/dom/html_video_element.cc b/src/cobalt/dom/html_video_element.cc index dad9259..c366be7 100644 --- a/src/cobalt/dom/html_video_element.cc +++ b/src/cobalt/dom/html_video_element.cc
@@ -16,6 +16,9 @@ #include "base/logging.h" #include "base/string_number_conversions.h" +#include "cobalt/dom/dom_settings.h" +#include "cobalt/dom/performance.h" +#include "cobalt/dom/window.h" #include "cobalt/math/size_f.h" namespace cobalt { @@ -76,15 +79,18 @@ return static_cast<uint32>(player()->GetNaturalSize().height()); } -scoped_refptr<VideoPlaybackQuality> HTMLVideoElement::GetVideoPlaybackQuality() - const { - // TODO: Provide all attributes with valid values. +scoped_refptr<VideoPlaybackQuality> HTMLVideoElement::GetVideoPlaybackQuality( + script::EnvironmentSettings* environment_settings) const { + DOMSettings* dom_settings = + base::polymorphic_downcast<DOMSettings*>(environment_settings); + DCHECK(dom_settings); + DCHECK(dom_settings->window()); + DCHECK(dom_settings->window()->performance()); + return new VideoPlaybackQuality( - 0., // creation_time + dom_settings->window()->performance()->Now(), player() ? static_cast<uint32>(player()->GetDecodedFrameCount()) : 0, - player() ? static_cast<uint32>(player()->GetDroppedFrameCount()) : 0, - 0, // corrupted_video_frames - 0.); // total_frame_delay + player() ? static_cast<uint32>(player()->GetDroppedFrameCount()) : 0); } scoped_refptr<VideoFrameProvider> HTMLVideoElement::GetVideoFrameProvider() {
diff --git a/src/cobalt/dom/html_video_element.h b/src/cobalt/dom/html_video_element.h index fbfb27e..7857022 100644 --- a/src/cobalt/dom/html_video_element.h +++ b/src/cobalt/dom/html_video_element.h
@@ -21,6 +21,7 @@ #include "cobalt/dom/video_playback_quality.h" #include "cobalt/math/rect.h" #include "cobalt/math/size_f.h" +#include "cobalt/script/environment_settings.h" #if !defined(COBALT_MEDIA_SOURCE_2016) #include "media/base/shell_video_frame_provider.h" #endif // !defined(COBALT_MEDIA_SOURCE_2016) @@ -50,7 +51,8 @@ void set_height(uint32 height); uint32 video_width() const; uint32 video_height() const; - scoped_refptr<VideoPlaybackQuality> GetVideoPlaybackQuality() const; + scoped_refptr<VideoPlaybackQuality> GetVideoPlaybackQuality( + script::EnvironmentSettings* environment_settings) const; // Custom, not in any spec //
diff --git a/src/cobalt/dom/html_video_element.idl b/src/cobalt/dom/html_video_element.idl index 53259ab..d73e064 100644 --- a/src/cobalt/dom/html_video_element.idl +++ b/src/cobalt/dom/html_video_element.idl
@@ -21,5 +21,5 @@ readonly attribute unsigned long videoHeight; // https://www.w3.org/TR/media-source/#widl-HTMLVideoElement-getVideoPlaybackQuality-VideoPlaybackQuality - VideoPlaybackQuality getVideoPlaybackQuality(); + [CallWith=EnvironmentSettings] VideoPlaybackQuality getVideoPlaybackQuality(); };
diff --git a/src/cobalt/dom/media_error.h b/src/cobalt/dom/media_error.h index 38fa816..46b0869 100644 --- a/src/cobalt/dom/media_error.h +++ b/src/cobalt/dom/media_error.h
@@ -15,6 +15,8 @@ #ifndef COBALT_DOM_MEDIA_ERROR_H_ #define COBALT_DOM_MEDIA_ERROR_H_ +#include <string> + #include "cobalt/script/wrappable.h" namespace cobalt { @@ -38,16 +40,19 @@ // Custom, not in any spec. // - explicit MediaError(Code code) : code_(code) {} + explicit MediaError(Code code, const std::string& message = "") + : code_(code), message_(message) {} // Web API: MediaError // uint32 code() const { return code_; } + const std::string& message() const { return message_; } DEFINE_WRAPPABLE_TYPE(MediaError); private: Code code_; + std::string message_; }; } // namespace dom
diff --git a/src/cobalt/dom/media_error.idl b/src/cobalt/dom/media_error.idl index f35af7a..55eaa5b 100644 --- a/src/cobalt/dom/media_error.idl +++ b/src/cobalt/dom/media_error.idl
@@ -20,5 +20,7 @@ const unsigned short MEDIA_ERR_DECODE = 3; const unsigned short MEDIA_ERR_SRC_NOT_SUPPORTED = 4; const unsigned short MEDIA_ERR_ENCRYPTED = 5; + readonly attribute unsigned short code; + readonly attribute DOMString message; };
diff --git a/src/cobalt/dom/media_source/media_source.h b/src/cobalt/dom/media_source/media_source.h index 183c4bf..abf50ed 100644 --- a/src/cobalt/dom/media_source/media_source.h +++ b/src/cobalt/dom/media_source/media_source.h
@@ -128,7 +128,7 @@ private: void SetReadyState(MediaSourceReadyState ready_state); bool IsUpdating() const; - void ScheduleEvent(base::Token eventName); + void ScheduleEvent(base::Token event_name); media::ChunkDemuxer* chunk_demuxer_; MediaSourceReadyState ready_state_;
diff --git a/src/cobalt/dom/media_source/source_buffer.h b/src/cobalt/dom/media_source/source_buffer.h index 42f77ea..381fd1b 100644 --- a/src/cobalt/dom/media_source/source_buffer.h +++ b/src/cobalt/dom/media_source/source_buffer.h
@@ -157,11 +157,13 @@ void RemoveMediaTracks(); const TrackDefault* GetTrackDefault( - const std::string& trackType, const std::string& byteStreamTrackID) const; - std::string DefaultTrackLabel(const std::string& trackType, - const std::string& byteStreamTrackID) const; - std::string DefaultTrackLanguage(const std::string& trackType, - const std::string& byteStreamTrackID) const; + const std::string& track_type, + const std::string& byte_stream_track_id) const; + std::string DefaultTrackLabel(const std::string& track_type, + const std::string& byte_stream_track_id) const; + std::string DefaultTrackLanguage( + const std::string& track_type, + const std::string& byte_stream_track_id) const; const std::string id_; media::ChunkDemuxer* chunk_demuxer_;
diff --git a/src/cobalt/dom/memory_info.cc b/src/cobalt/dom/memory_info.cc index ceb427b..2a455a2 100644 --- a/src/cobalt/dom/memory_info.cc +++ b/src/cobalt/dom/memory_info.cc
@@ -21,14 +21,28 @@ namespace cobalt { namespace dom { -uint32 MemoryInfo::total_js_heap_size() const { +uint32 MemoryInfo::total_js_heap_size( + script::EnvironmentSettings* environment_settings) const { + if (!environment_settings) { + return 0u; + } return static_cast<uint32>( - script::JavaScriptEngine::UpdateMemoryStatsAndReturnReserved()); + base::polymorphic_downcast<DOMSettings*>(environment_settings) + ->javascript_engine() + ->GetHeapStatistics() + .total_heap_size); } -uint32 MemoryInfo::used_js_heap_size() const { +uint32 MemoryInfo::used_js_heap_size( + script::EnvironmentSettings* environment_settings) const { + if (!environment_settings) { + return 0u; + } return static_cast<uint32>( - script::JavaScriptEngine::UpdateMemoryStatsAndReturnReserved()); + base::polymorphic_downcast<DOMSettings*>(environment_settings) + ->javascript_engine() + ->GetHeapStatistics() + .used_heap_size); } } // namespace dom
diff --git a/src/cobalt/dom/memory_info.h b/src/cobalt/dom/memory_info.h index aca8bfd..842bca4 100644 --- a/src/cobalt/dom/memory_info.h +++ b/src/cobalt/dom/memory_info.h
@@ -26,17 +26,16 @@ // https://docs.webplatform.org/wiki/apis/timing/properties/memory class MemoryInfo : public script::Wrappable { public: - MemoryInfo() {} + MemoryInfo() = default; - uint32 total_js_heap_size() const; - - uint32 used_js_heap_size() const; + uint32 total_js_heap_size( + script::EnvironmentSettings* environment_settings) const; + uint32 used_js_heap_size( + script::EnvironmentSettings* environment_settings) const; DEFINE_WRAPPABLE_TYPE(MemoryInfo); private: - ~MemoryInfo() override {} - DISALLOW_COPY_AND_ASSIGN(MemoryInfo); };
diff --git a/src/cobalt/dom/memory_info.idl b/src/cobalt/dom/memory_info.idl index 6dbfd88..0f8b713 100644 --- a/src/cobalt/dom/memory_info.idl +++ b/src/cobalt/dom/memory_info.idl
@@ -18,6 +18,6 @@ [ NoInterfaceObject, ] interface MemoryInfo { - readonly attribute unsigned long totalJSHeapSize; - readonly attribute unsigned long usedJSHeapSize; + [CallWith=EnvironmentSettings] readonly attribute unsigned long totalJSHeapSize; + [CallWith=EnvironmentSettings] readonly attribute unsigned long usedJSHeapSize; };
diff --git a/src/cobalt/dom/navigator.cc b/src/cobalt/dom/navigator.cc index 49b810f..2188f58 100644 --- a/src/cobalt/dom/navigator.cc +++ b/src/cobalt/dom/navigator.cc
@@ -209,23 +209,22 @@ // See // https://www.w3.org/TR/encrypted-media/#dom-navigator-requestmediakeysystemaccess. -scoped_ptr<Navigator::InterfacePromiseValue> +script::Handle<Navigator::InterfacePromise> Navigator::RequestMediaKeySystemAccess( const std::string& key_system, const script::Sequence<eme::MediaKeySystemConfiguration>& supported_configurations) { - scoped_ptr<InterfacePromiseValue> promise = + script::Handle<InterfacePromise> promise = script_value_factory_ - ->CreateInterfacePromise<scoped_refptr<eme::MediaKeySystemAccess> >(); - InterfacePromiseValue::StrongReference promise_reference(*promise); + ->CreateInterfacePromise<scoped_refptr<eme::MediaKeySystemAccess>>(); // 1. If |keySystem| is the empty string, return a promise rejected // with a newly created TypeError. // 2. If |supportedConfigurations| is empty, return a promise rejected // with a newly created TypeError. if (key_system.empty() || supported_configurations.empty()) { - promise_reference.value().Reject(script::kTypeError); - return promise.Pass(); + promise->Reject(script::kTypeError); + return promise; } // 6.3. For each value in |supportedConfigurations|: @@ -243,15 +242,14 @@ *maybe_supported_configuration, script_value_factory_)); // 6.3.3.2. Resolve promise. - promise_reference.value().Resolve(media_key_system_access); - return promise.Pass(); + promise->Resolve(media_key_system_access); + return promise; } } // 6.4. Reject promise with a NotSupportedError. - promise_reference.value().Reject( - new DOMException(DOMException::kNotSupportedErr)); - return promise.Pass(); + promise->Reject(new DOMException(DOMException::kNotSupportedErr)); + return promise; } #endif // defined(COBALT_MEDIA_SOURCE_2016)
diff --git a/src/cobalt/dom/navigator.h b/src/cobalt/dom/navigator.h index f09f26f..f195bdb 100644 --- a/src/cobalt/dom/navigator.h +++ b/src/cobalt/dom/navigator.h
@@ -67,9 +67,8 @@ #if defined(COBALT_MEDIA_SOURCE_2016) // Web API: extension defined in Encrypted Media Extensions (16 March 2017). - typedef script::ScriptValue<script::Promise< - scoped_refptr<script::Wrappable> > > InterfacePromiseValue; - scoped_ptr<InterfacePromiseValue> RequestMediaKeySystemAccess( + using InterfacePromise = script::Promise<scoped_refptr<script::Wrappable>>; + script::Handle<InterfacePromise> RequestMediaKeySystemAccess( const std::string& key_system, const script::Sequence<eme::MediaKeySystemConfiguration>& supported_configurations);
diff --git a/src/cobalt/dom/node.cc b/src/cobalt/dom/node.cc index da6fa9b..29017a5 100644 --- a/src/cobalt/dom/node.cc +++ b/src/cobalt/dom/node.cc
@@ -109,6 +109,10 @@ window = node_document()->default_view(); } + if (window) { + window->OnStartDispatchEvent(event); + } + typedef std::vector<scoped_refptr<Node> > Ancestors; Ancestors ancestors; for (Node* current = this->parent_node(); current != NULL; @@ -159,6 +163,10 @@ event->set_event_phase(Event::kNone); + if (window) { + window->OnStopDispatchEvent(event); + } + // The event has completed being dispatched. Stop tracking it in the global // stats. GlobalStats::GetInstance()->StopJavaScriptEvent();
diff --git a/src/cobalt/dom/on_screen_keyboard.cc b/src/cobalt/dom/on_screen_keyboard.cc index aa20b06..4eb5de8 100644 --- a/src/cobalt/dom/on_screen_keyboard.cc +++ b/src/cobalt/dom/on_screen_keyboard.cc
@@ -31,60 +31,60 @@ DCHECK(bridge_) << "OnScreenKeyboardBridge must not be NULL"; } -scoped_ptr<OnScreenKeyboard::VoidPromiseValue> OnScreenKeyboard::Show() { - scoped_ptr<VoidPromiseValue> promise = +script::Handle<script::Promise<void>> OnScreenKeyboard::Show() { + script::Handle<script::Promise<void>> promise = script_value_factory_->CreateBasicPromise<void>(); int ticket = next_ticket_++; bool is_emplaced = ticket_to_show_promise_map_ - .emplace(ticket, std::unique_ptr<VoidPromiseValue::TracedReference>( - new VoidPromiseValue::TracedReference(*promise))) + .emplace(ticket, std::unique_ptr<VoidPromiseValue::Reference>( + new VoidPromiseValue::Reference(this, promise))) .second; DCHECK(is_emplaced); bridge_->Show(data_.c_str(), ticket); - return promise.Pass(); + return promise; } -scoped_ptr<OnScreenKeyboard::VoidPromiseValue> OnScreenKeyboard::Hide() { - scoped_ptr<VoidPromiseValue> promise = +script::Handle<script::Promise<void>> OnScreenKeyboard::Hide() { + script::Handle<script::Promise<void>> promise = script_value_factory_->CreateBasicPromise<void>(); int ticket = next_ticket_++; bool is_emplaced = ticket_to_hide_promise_map_ - .emplace(ticket, std::unique_ptr<VoidPromiseValue::TracedReference>( - new VoidPromiseValue::TracedReference(*promise))) + .emplace(ticket, std::unique_ptr<VoidPromiseValue::Reference>( + new VoidPromiseValue::Reference(this, promise))) .second; DCHECK(is_emplaced); bridge_->Hide(ticket); - return promise.Pass(); + return promise; } -scoped_ptr<OnScreenKeyboard::VoidPromiseValue> OnScreenKeyboard::Focus() { - scoped_ptr<VoidPromiseValue> promise = +script::Handle<script::Promise<void>> OnScreenKeyboard::Focus() { + script::Handle<script::Promise<void>> promise = script_value_factory_->CreateBasicPromise<void>(); int ticket = next_ticket_++; bool is_emplaced = ticket_to_focus_promise_map_ - .emplace(ticket, std::unique_ptr<VoidPromiseValue::TracedReference>( - new VoidPromiseValue::TracedReference(*promise))) + .emplace(ticket, std::unique_ptr<VoidPromiseValue::Reference>( + new VoidPromiseValue::Reference(this, promise))) .second; DCHECK(is_emplaced); bridge_->Focus(ticket); - return promise.Pass(); + return promise; } -scoped_ptr<OnScreenKeyboard::VoidPromiseValue> OnScreenKeyboard::Blur() { - scoped_ptr<VoidPromiseValue> promise = +script::Handle<script::Promise<void>> OnScreenKeyboard::Blur() { + script::Handle<script::Promise<void>> promise = script_value_factory_->CreateBasicPromise<void>(); int ticket = next_ticket_++; bool is_emplaced = ticket_to_blur_promise_map_ - .emplace(ticket, std::unique_ptr<VoidPromiseValue::TracedReference>( - new VoidPromiseValue::TracedReference(*promise))) + .emplace(ticket, std::unique_ptr<VoidPromiseValue::Reference>( + new VoidPromiseValue::Reference(this, promise))) .second; DCHECK(is_emplaced); bridge_->Blur(ticket); - return promise.Pass(); + return promise; } const EventTarget::EventListenerScriptValue* OnScreenKeyboard::onshow() const { @@ -130,6 +130,10 @@ bool OnScreenKeyboard::shown() const { return bridge_->IsShown(); } +scoped_refptr<DOMRect> OnScreenKeyboard::bounding_rect() const { + return bridge_->BoundingRect(); +} + void OnScreenKeyboard::set_keep_focus(bool keep_focus) { keep_focus_ = keep_focus; bridge_->SetKeepFocus(keep_focus); @@ -195,14 +199,5 @@ DispatchEvent(new dom::Event(base::Tokens::blur())); } -void OnScreenKeyboard::TraceMembers(script::Tracer* tracer) { - EventTarget::TraceMembers(tracer); - - tracer->TraceValues(ticket_to_hide_promise_map_); - tracer->TraceValues(ticket_to_show_promise_map_); - tracer->TraceValues(ticket_to_focus_promise_map_); - tracer->TraceValues(ticket_to_blur_promise_map_); -} - } // namespace dom } // namespace cobalt
diff --git a/src/cobalt/dom/on_screen_keyboard.h b/src/cobalt/dom/on_screen_keyboard.h index 4159a4a..c81d2c9 100644 --- a/src/cobalt/dom/on_screen_keyboard.h +++ b/src/cobalt/dom/on_screen_keyboard.h
@@ -21,6 +21,7 @@ #include "base/callback.h" #include "cobalt/base/tokens.h" +#include "cobalt/dom/dom_rect.h" #include "cobalt/dom/event_target.h" #include "cobalt/dom/on_screen_keyboard_bridge.h" #include "cobalt/dom/window.h" @@ -32,29 +33,29 @@ namespace dom { class Window; +class OnScreenKeyboardMockBridge; class OnScreenKeyboard : public EventTarget { public: typedef script::ScriptValue<script::Promise<void>> VoidPromiseValue; - typedef std::unordered_map<int, - std::unique_ptr<VoidPromiseValue::TracedReference>> + typedef std::unordered_map<int, std::unique_ptr<VoidPromiseValue::Reference>> TicketToPromiseMap; OnScreenKeyboard(OnScreenKeyboardBridge* bridge, script::ScriptValueFactory* script_value_factory); // Shows the on screen keyboard by calling a Starboard function. - scoped_ptr<VoidPromiseValue> Show(); + script::Handle<script::Promise<void>> Show(); // Hides the on screen keyboard by calling a Starboard function. - scoped_ptr<VoidPromiseValue> Hide(); + script::Handle<script::Promise<void>> Hide(); // Focuses the on screen keyboard by calling a Starboard function. - scoped_ptr<VoidPromiseValue> Focus(); + script::Handle<script::Promise<void>> Focus(); // Blurs the on screen keyboard by calling a Starboard function. - scoped_ptr<VoidPromiseValue> Blur(); + script::Handle<script::Promise<void>> Blur(); std::string data() const { return data_; } void set_data(const std::string& data) { data_ = data; } @@ -77,6 +78,9 @@ // If the keyboard is shown. bool shown() const; + // The rectangle of the keyboard in screen pixel coordinates. + scoped_refptr<DOMRect> bounding_rect() const; + void set_keep_focus(bool keep_focus); bool keep_focus() const { return keep_focus_; } @@ -87,9 +91,10 @@ void DispatchBlurEvent(int ticket); DEFINE_WRAPPABLE_TYPE(OnScreenKeyboard); - void TraceMembers(script::Tracer* tracer) override; private: + friend class OnScreenKeyboardMockBridge; + ~OnScreenKeyboard() override {} TicketToPromiseMap ticket_to_hide_promise_map_;
diff --git a/src/cobalt/dom/on_screen_keyboard.idl b/src/cobalt/dom/on_screen_keyboard.idl index 1b71f58..036b012 100644 --- a/src/cobalt/dom/on_screen_keyboard.idl +++ b/src/cobalt/dom/on_screen_keyboard.idl
@@ -21,6 +21,11 @@ Promise<void> focus(); Promise<void> blur(); readonly attribute boolean shown; + + // If the keyboard is shown, return bounding rectangle in screen pixel + // coordinates, otherwise return NULL. + readonly attribute DOMRect? boundingRect; + // If the keyboard should keep focus, preventing focus from moving away from // the keyboard due to user input. attribute boolean keepFocus;
diff --git a/src/cobalt/dom/on_screen_keyboard_bridge.h b/src/cobalt/dom/on_screen_keyboard_bridge.h index 682f4d2..9af0518 100644 --- a/src/cobalt/dom/on_screen_keyboard_bridge.h +++ b/src/cobalt/dom/on_screen_keyboard_bridge.h
@@ -15,6 +15,8 @@ #ifndef COBALT_DOM_ON_SCREEN_KEYBOARD_BRIDGE_H_ #define COBALT_DOM_ON_SCREEN_KEYBOARD_BRIDGE_H_ +#include "cobalt/dom/dom_rect.h" + namespace cobalt { namespace dom { @@ -29,6 +31,7 @@ virtual void Focus(int ticket) = 0; virtual void Blur(int ticket) = 0; virtual bool IsShown() const = 0; + virtual scoped_refptr<DOMRect> BoundingRect() const = 0; virtual void SetKeepFocus(bool keep_focus) = 0; virtual bool IsValidTicket(int ticket) const = 0; };
diff --git a/src/cobalt/dom/on_screen_keyboard_test.cc b/src/cobalt/dom/on_screen_keyboard_test.cc new file mode 100644 index 0000000..5f0d4a4 --- /dev/null +++ b/src/cobalt/dom/on_screen_keyboard_test.cc
@@ -0,0 +1,641 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <string> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "cobalt/bindings/testing/utils.h" +#include "cobalt/css_parser/parser.h" +#include "cobalt/dom/local_storage_database.h" +#include "cobalt/dom/testing/gtest_workarounds.h" +#include "cobalt/dom/window.h" +#include "cobalt/dom_parser/parser.h" +#include "cobalt/loader/fetcher_factory.h" +#include "cobalt/media_session/media_session.h" +#include "cobalt/network/network_module.h" +#include "cobalt/script/global_environment.h" +#include "cobalt/script/javascript_engine.h" +#include "cobalt/script/source_code.h" +#include "starboard/window.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cobalt { +namespace dom { + +class MockErrorCallback : public base::Callback<void(const std::string&)> { + public: + MOCK_METHOD1(Run, void(const std::string&)); +}; + +using ::testing::InSequence; +using ::testing::Mock; + +class OnScreenKeyboardMockBridge : public OnScreenKeyboardBridge { + public: + void Show(const char* input_text, int ticket) override { + ShowMock(input_text); + last_ticket_ = ticket; + shown_ = true; + script::Handle<script::Promise<void>> promise = + LookupPromiseForShowTicket(last_ticket_); + EXPECT_TRUE(promise->State() == cobalt::script::PromiseState::kPending); + DCHECK(window_); + window_->on_screen_keyboard()->DispatchShowEvent(last_ticket_); + EXPECT_TRUE(promise->State() == cobalt::script::PromiseState::kFulfilled); + last_ticket_ = -1; + } + + void Hide(int ticket) override { + HideMock(); + last_ticket_ = ticket; + shown_ = false; + + script::Handle<script::Promise<void>> promise = + LookupPromiseForHideTicket(last_ticket_); + EXPECT_TRUE(promise->State() == cobalt::script::PromiseState::kPending); + DCHECK(window_); + window_->on_screen_keyboard()->DispatchHideEvent(last_ticket_); + EXPECT_TRUE(promise->State() == cobalt::script::PromiseState::kFulfilled); + last_ticket_ = -1; + } + + void Focus(int ticket) override { + FocusMock(); + last_ticket_ = ticket; + script::Handle<script::Promise<void>> promise = + LookupPromiseForFocusTicket(last_ticket_); + EXPECT_TRUE(promise->State() == cobalt::script::PromiseState::kPending); + DCHECK(window_); + window_->on_screen_keyboard()->DispatchFocusEvent(last_ticket_); + EXPECT_TRUE(promise->State() == cobalt::script::PromiseState::kFulfilled); + last_ticket_ = -1; + } + + void Blur(int ticket) override { + BlurMock(); + last_ticket_ = ticket; + script::Handle<script::Promise<void>> promise = + LookupPromiseForBlurTicket(last_ticket_); + EXPECT_TRUE(promise->State() == cobalt::script::PromiseState::kPending); + DCHECK(window_); + window_->on_screen_keyboard()->DispatchBlurEvent(last_ticket_); + EXPECT_TRUE(promise->State() == cobalt::script::PromiseState::kFulfilled); + last_ticket_ = -1; + } + + bool IsShown() const override { return shown_; } + + scoped_refptr<DOMRect> BoundingRect() const override { + return BoundingRectMock(); + } + + bool IsValidTicket(int ticket) const override { + // The mock bridge will always dispatch events immediately once the + // show/hide/focus/blur function has been called, meaning we will never need + // to check the validity of a ticket that was not the last one generated. + // This method will always return false when called outside one of the + // show/hide/focus/blur methods. + return ticket != -1 && ticket == last_ticket_; + } + + void SetKeepFocus(bool keep_focus) override { SetKeepFocusMock(keep_focus); } + + MOCK_METHOD1(ShowMock, void(std::string)); + MOCK_METHOD0(HideMock, void()); + MOCK_METHOD0(BlurMock, void()); + MOCK_METHOD0(FocusMock, void()); + MOCK_CONST_METHOD0(BoundingRectMock, scoped_refptr<DOMRect>()); + MOCK_METHOD1(SetKeepFocusMock, void(bool)); + + // We shortcut the event dispatching and handling for tests. + dom::Window* window_; + + private: + // OnScreenKeyboardMockBridge needs to be friends with dom::OnScreenKeyboard + // to implement these functions. + script::Handle<script::Promise<void>> LookupPromiseForShowTicket(int ticket) { + DCHECK(window_); + const auto& map = + window_->on_screen_keyboard()->ticket_to_show_promise_map_; + auto it = map.find(ticket); + DCHECK(it != map.end()); + return script::Handle<script::Promise<void>>(*it->second); + } + + script::Handle<script::Promise<void>> LookupPromiseForHideTicket(int ticket) { + DCHECK(window_); + const auto& map = + window_->on_screen_keyboard()->ticket_to_hide_promise_map_; + auto it = map.find(ticket); + DCHECK(it != map.end()); + return script::Handle<script::Promise<void>>(*it->second); + } + + script::Handle<script::Promise<void>> LookupPromiseForFocusTicket( + int ticket) { + DCHECK(window_); + const auto& map = + window_->on_screen_keyboard()->ticket_to_focus_promise_map_; + auto it = map.find(ticket); + DCHECK(it != map.end()); + return script::Handle<script::Promise<void>>(*it->second); + } + + script::Handle<script::Promise<void>> LookupPromiseForBlurTicket(int ticket) { + DCHECK(window_); + const auto& map = + window_->on_screen_keyboard()->ticket_to_blur_promise_map_; + auto it = map.find(ticket); + DCHECK(it != map.end()); + return script::Handle<script::Promise<void>>(*it->second); + } + + bool shown_ = false; + int last_ticket_ = -1; +}; + +namespace { + +class OnScreenKeyboardTest : public ::testing::Test { + public: + OnScreenKeyboardTest() + : environment_settings_(new script::EnvironmentSettings), + message_loop_(MessageLoop::TYPE_DEFAULT), + css_parser_(css_parser::Parser::Create()), + dom_parser_(new dom_parser::Parser(mock_error_callback_)), + fetcher_factory_(new loader::FetcherFactory(&network_module_)), + local_storage_database_(NULL), + url_("about:blank"), + engine_(script::JavaScriptEngine::CreateEngine()), + global_environment_(engine_->CreateGlobalEnvironment()), + on_screen_keyboard_bridge_(new OnScreenKeyboardMockBridge()), + window_(new Window( + 1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(), + dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL, + NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL, + global_environment_ + ->script_value_factory() /* script_value_factory */, + NULL, NULL, url_, "", "en-US", "en", + base::Callback<void(const GURL&)>(), + base::Bind(&MockErrorCallback::Run, + base::Unretained(&mock_error_callback_)), + NULL, network_bridge::PostSender(), csp::kCSPRequired, + kCspEnforcementEnable, base::Closure() /* csp_policy_changed */, + base::Closure() /* ran_animation_frame_callbacks */, + dom::Window::CloseCallback() /* window_close */, + base::Closure() /* window_minimize */, + on_screen_keyboard_bridge_.get(), NULL, NULL, + dom::Window::OnStartDispatchEventCallback(), + dom::Window::OnStopDispatchEventCallback())) { + global_environment_->CreateGlobalObject(window_, + environment_settings_.get()); + on_screen_keyboard_bridge_->window_ = window_; + } + + ~OnScreenKeyboardTest() { + global_environment_->SetReportEvalCallback(base::Closure()); + global_environment_->SetReportErrorCallback( + script::GlobalEnvironment::ReportErrorCallback()); + window_->DispatchEvent(new dom::Event(base::Tokens::unload())); + + // TODO: figure out how to destruct OSK before global environment. + window_->ReleaseOnScreenKeyboard(); + + EXPECT_TRUE(Mock::VerifyAndClearExpectations(on_screen_keyboard_bridge())); + + on_screen_keyboard_bridge_.reset(); + window_ = nullptr; + global_environment_ = nullptr; + } + + bool EvaluateScript(const std::string& js_code, std::string* result); + + script::GlobalEnvironment* global_environment() const { + return global_environment_.get(); + } + + OnScreenKeyboardMockBridge* on_screen_keyboard_bridge() const { + return on_screen_keyboard_bridge_.get(); + } + + Window* window() const { return window_.get(); } + + private: + const scoped_ptr<script::EnvironmentSettings> environment_settings_; + MessageLoop message_loop_; + MockErrorCallback mock_error_callback_; + scoped_ptr<css_parser::Parser> css_parser_; + scoped_ptr<dom_parser::Parser> dom_parser_; + network::NetworkModule network_module_; + scoped_ptr<loader::FetcherFactory> fetcher_factory_; + dom::LocalStorageDatabase local_storage_database_; + GURL url_; + + scoped_ptr<script::JavaScriptEngine> engine_; + scoped_refptr<script::GlobalEnvironment> global_environment_; + scoped_ptr<OnScreenKeyboardMockBridge> on_screen_keyboard_bridge_; + scoped_refptr<Window> window_; +}; + +// TODO: refactor this into reusable test utility. +bool OnScreenKeyboardTest::EvaluateScript(const std::string& js_code, + std::string* result) { + DCHECK(global_environment_); + scoped_refptr<script::SourceCode> source_code = + script::SourceCode::CreateSourceCode( + js_code, base::SourceLocation(__FILE__, __LINE__, 1)); + + global_environment_->EnableEval(); + global_environment_->SetReportEvalCallback(base::Closure()); + bool succeeded = global_environment_->EvaluateScript( + source_code, false /*mute_errors*/, result); + return succeeded; +} + +} // namespace + +TEST_F(OnScreenKeyboardTest, ObjectExists) { + std::string result; + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard", &result)); + + EXPECT_TRUE(bindings::testing::IsAcceptablePrototypeString("OnScreenKeyboard", + result)); + + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.show", &result)); + EXPECT_PRED_FORMAT2(::testing::IsSubstring, "function show()", + result.c_str()); + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.hide", &result)); + EXPECT_PRED_FORMAT2(::testing::IsSubstring, "function hide()", + result.c_str()); + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.focus", &result)); + EXPECT_PRED_FORMAT2(::testing::IsSubstring, "function focus()", + result.c_str()); + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.blur", &result)); + EXPECT_PRED_FORMAT2(::testing::IsSubstring, "function blur()", + result.c_str()); + + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.keepFocus", &result)); + EXPECT_STREQ("false", result.c_str()); + + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.data", &result)); + EXPECT_STREQ("", result.c_str()); + + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onshow", &result)); + EXPECT_STREQ("null", result.c_str()); + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onhide", &result)); + EXPECT_STREQ("null", result.c_str()); + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onblur", &result)); + EXPECT_STREQ("null", result.c_str()); + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onfocus", &result)); + EXPECT_STREQ("null", result.c_str()); +} + +TEST_F(OnScreenKeyboardTest, ShowAndHide) { + // Not shown. + std::string result; + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.shown;", &result)); + EXPECT_EQ("false", result); + + { + InSequence seq; + EXPECT_CALL(*(on_screen_keyboard_bridge()), + ShowMock(window()->on_screen_keyboard()->data())); + EXPECT_CALL(*(on_screen_keyboard_bridge()), HideMock()); + } + // Show. + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.show();", &result)); + EXPECT_TRUE( + bindings::testing::IsAcceptablePrototypeString("Object", result) || + bindings::testing::IsAcceptablePrototypeString("Promise", result)); + + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.shown;", &result)); + EXPECT_EQ("true", result); + + // Hide. + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.hide();", &result)); + EXPECT_TRUE( + bindings::testing::IsAcceptablePrototypeString("Object", result) || + bindings::testing::IsAcceptablePrototypeString("Promise", result)); + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.shown;", &result)); + EXPECT_EQ("false", result); +} + +TEST_F(OnScreenKeyboardTest, ShowAndHideMultipleTimes) { + std::string result; + { + InSequence seq; + EXPECT_CALL(*(on_screen_keyboard_bridge()), + ShowMock(window()->on_screen_keyboard()->data())) + .Times(3); + EXPECT_CALL(*(on_screen_keyboard_bridge()), HideMock()).Times(3); + } + + // Show multiple times. + const char show_script[] = R"( + window.onScreenKeyboard.show(); + window.onScreenKeyboard.show(); + window.onScreenKeyboard.show(); + )"; + EXPECT_TRUE(EvaluateScript(show_script, &result)); + EXPECT_TRUE( + bindings::testing::IsAcceptablePrototypeString("Object", result) || + bindings::testing::IsAcceptablePrototypeString("Promise", result)); + + // Hide multiple times. + const char hide_script[] = R"( + window.onScreenKeyboard.hide(); + window.onScreenKeyboard.hide(); + window.onScreenKeyboard.hide(); + )"; + EXPECT_TRUE(EvaluateScript(hide_script, &result)); + EXPECT_TRUE( + bindings::testing::IsAcceptablePrototypeString("Object", result) || + bindings::testing::IsAcceptablePrototypeString("Promise", result)); +} + +TEST_F(OnScreenKeyboardTest, Data) { + std::string result = "(empty)"; + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.data;", &result)); + EXPECT_EQ("", result); + + std::string utf8_str = u8"z\u6c34\U0001d10b"; + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.data = '" + utf8_str + + "';" + "window.onScreenKeyboard.data", + &result)); + EXPECT_EQ(utf8_str, result); +} + +TEST_F(OnScreenKeyboardTest, FocusAndBlur) { + std::string result; + + { + InSequence seq; + EXPECT_CALL(*(on_screen_keyboard_bridge()), FocusMock()); + EXPECT_CALL(*(on_screen_keyboard_bridge()), BlurMock()); + } + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.focus();", &result)); + EXPECT_TRUE( + bindings::testing::IsAcceptablePrototypeString("Object", result) || + bindings::testing::IsAcceptablePrototypeString("Promise", result)); + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.blur();", &result)); + EXPECT_TRUE( + bindings::testing::IsAcceptablePrototypeString("Object", result) || + bindings::testing::IsAcceptablePrototypeString("Promise", result)); +} +TEST_F(OnScreenKeyboardTest, FocusAndBlurMultipleTimes) { + std::string result; + { + InSequence seq; + EXPECT_CALL(*(on_screen_keyboard_bridge()), FocusMock()).Times(3); + EXPECT_CALL(*(on_screen_keyboard_bridge()), BlurMock()).Times(3); + } + + const char focus_script[] = R"( + window.onScreenKeyboard.focus(); + window.onScreenKeyboard.focus(); + window.onScreenKeyboard.focus(); + )"; + EXPECT_TRUE(EvaluateScript(focus_script, &result)); + EXPECT_TRUE( + bindings::testing::IsAcceptablePrototypeString("Object", result) || + bindings::testing::IsAcceptablePrototypeString("Promise", result)); + const char blur_script[] = R"( + window.onScreenKeyboard.blur(); + window.onScreenKeyboard.blur(); + window.onScreenKeyboard.blur(); + )"; + EXPECT_TRUE(EvaluateScript(blur_script, &result)); + EXPECT_TRUE( + bindings::testing::IsAcceptablePrototypeString("Object", result) || + bindings::testing::IsAcceptablePrototypeString("Promise", result)); +} + +TEST_F(OnScreenKeyboardTest, ShowEventAttribute) { + EXPECT_CALL(*(on_screen_keyboard_bridge()), + ShowMock(window()->on_screen_keyboard()->data())) + .Times(3); + const char let_script[] = R"( + let promise; + let logString; + )"; + EXPECT_TRUE(EvaluateScript(let_script, NULL)); + const char event_script[] = R"( + logString = '(empty)'; + window.onScreenKeyboard.onshow = function() { + logString = 'show'; + }; + promise = window.onScreenKeyboard.show(); + logString + )"; + for (int i = 0; i < 3; ++i) { + std::string result; + EXPECT_TRUE(EvaluateScript(event_script, &result)); + EXPECT_EQ("show", result); + } +} + +TEST_F(OnScreenKeyboardTest, ShowEventListeners) { + std::string result; + EXPECT_CALL(*(on_screen_keyboard_bridge()), + ShowMock(window()->on_screen_keyboard()->data())); + const char script[] = R"( + let logString1 = '(empty)'; + let logString2 = '(empty)'; + window.onScreenKeyboard.addEventListener('show', + function() { + logString1 = 'show1'; + }); + window.onScreenKeyboard.addEventListener('show', + function() { + logString2 = 'show2'; + }); + let promise = window.onScreenKeyboard.show(); + logString1 + )"; + EXPECT_TRUE(EvaluateScript(script, &result)); + EXPECT_EQ("show1", result); + EXPECT_TRUE(EvaluateScript("logString2", &result)); + EXPECT_EQ(result, "show2"); +} + +TEST_F(OnScreenKeyboardTest, HideEventAttribute) { + EXPECT_CALL(*(on_screen_keyboard_bridge()), HideMock()).Times(3); + const char let_script[] = R"( + let promise; + let logString; + )"; + EXPECT_TRUE(EvaluateScript(let_script, NULL)); + const char event_script[] = R"( + logString = '(empty)'; + window.onScreenKeyboard.onhide = function() { + logString = 'hide'; + }; + promise = window.onScreenKeyboard.hide(); + logString + )"; + for (int i = 0; i < 3; ++i) { + std::string result; + EXPECT_TRUE(EvaluateScript(event_script, &result)); + EXPECT_EQ("hide", result); + } +} + +TEST_F(OnScreenKeyboardTest, HideEventListeners) { + std::string result; + EXPECT_CALL(*(on_screen_keyboard_bridge()), HideMock()); + const char script[] = R"( + let logString1 = '(empty)'; + let logString2 = '(empty)'; + window.onScreenKeyboard.addEventListener('hide', + function() { + logString1 = 'hide1'; + }); + window.onScreenKeyboard.addEventListener('hide', + function() { + logString2 = 'hide2'; + }); + let promise = window.onScreenKeyboard.hide(); + logString1 + )"; + EXPECT_TRUE(EvaluateScript(script, &result)); + EXPECT_EQ("hide1", result); + EXPECT_TRUE(EvaluateScript("logString2", &result)); + EXPECT_EQ(result, "hide2"); +} + +TEST_F(OnScreenKeyboardTest, FocusEventAttribute) { + EXPECT_CALL(*(on_screen_keyboard_bridge()), FocusMock()).Times(3); + const char let_script[] = R"( + let promise; + let logString; + )"; + EXPECT_TRUE(EvaluateScript(let_script, NULL)); + const char event_script[] = R"( + logString = '(empty)'; + window.onScreenKeyboard.onfocus = function() { + logString = 'focus'; + }; + promise = window.onScreenKeyboard.focus(); + logString + )"; + for (int i = 0; i < 3; ++i) { + std::string result; + EXPECT_TRUE(EvaluateScript(event_script, &result)); + EXPECT_EQ("focus", result); + } +} + +TEST_F(OnScreenKeyboardTest, FocusEventListeners) { + std::string result; + EXPECT_CALL(*(on_screen_keyboard_bridge()), FocusMock()); + const char script[] = R"( + let logString1 = '(empty)'; + let logString2 = '(empty)'; + window.onScreenKeyboard.addEventListener('focus', + function() { + logString1 = 'focus1'; + }); + window.onScreenKeyboard.addEventListener('focus', + function() { + logString2 = 'focus2'; + }); + let promise = window.onScreenKeyboard.focus(); + logString1 + )"; + EXPECT_TRUE(EvaluateScript(script, &result)); + EXPECT_EQ("focus1", result); + EXPECT_TRUE(EvaluateScript("logString2", &result)); + EXPECT_EQ("focus2", result); +} + +TEST_F(OnScreenKeyboardTest, BlurEventAttribute) { + EXPECT_CALL(*(on_screen_keyboard_bridge()), BlurMock()).Times(3); + const char let_script[] = R"( + let promise; + let logString; + )"; + EXPECT_TRUE(EvaluateScript(let_script, NULL)); + const char event_script[] = R"( + logString = '(empty)'; + window.onScreenKeyboard.onblur = function() { + logString = 'blur'; + }; + promise = window.onScreenKeyboard.blur(); + logString + )"; + for (int i = 0; i < 3; ++i) { + std::string result; + EXPECT_TRUE(EvaluateScript(event_script, &result)); + EXPECT_EQ("blur", result); + } +} + +TEST_F(OnScreenKeyboardTest, BlurEventListeners) { + std::string result; + EXPECT_CALL(*(on_screen_keyboard_bridge()), BlurMock()); + const char script[] = R"( + let logString1 = '(empty)'; + let logString2 = '(empty)'; + window.onScreenKeyboard.addEventListener('blur', + function() { + logString1 = 'blur1'; + }); + window.onScreenKeyboard.addEventListener('blur', + function() { + logString2 = 'blur2'; + }); + let promise = window.onScreenKeyboard.blur(); + logString1 + )"; + EXPECT_TRUE(EvaluateScript(script, &result)); + EXPECT_EQ("blur1", result); + EXPECT_TRUE(EvaluateScript("logString2", &result)); + EXPECT_EQ(result, "blur2"); +} + +TEST_F(OnScreenKeyboardTest, BoundingRect) { + std::string result; + EXPECT_CALL(*(on_screen_keyboard_bridge()), BoundingRectMock()) + .WillOnce(testing::Return(nullptr)); + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.boundingRect;", &result)); + EXPECT_EQ("null", result); +} + +TEST_F(OnScreenKeyboardTest, KeepFocus) { + std::string result; + { + InSequence seq; + EXPECT_CALL(*(on_screen_keyboard_bridge()), SetKeepFocusMock(true)); + EXPECT_CALL(*(on_screen_keyboard_bridge()), SetKeepFocusMock(false)); + EXPECT_CALL(*(on_screen_keyboard_bridge()), SetKeepFocusMock(true)); + } + + // Check initialization. + EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.keepFocus;", &result)); + EXPECT_EQ("false", result); + + const char script[] = R"( + window.onScreenKeyboard.keepFocus = true; + window.onScreenKeyboard.keepFocus = false; + window.onScreenKeyboard.keepFocus = true; + )"; + EXPECT_TRUE(EvaluateScript(script, NULL)); +} + +} // namespace dom +} // namespace cobalt
diff --git a/src/cobalt/dom/performance.cc b/src/cobalt/dom/performance.cc index 8c437e5..478bfd9 100644 --- a/src/cobalt/dom/performance.cc +++ b/src/cobalt/dom/performance.cc
@@ -36,7 +36,5 @@ tracer->Trace(memory_); } -Performance::~Performance() {} - } // namespace dom } // namespace cobalt
diff --git a/src/cobalt/dom/performance.h b/src/cobalt/dom/performance.h index 39525fa..1e35d76 100644 --- a/src/cobalt/dom/performance.h +++ b/src/cobalt/dom/performance.h
@@ -43,8 +43,6 @@ void TraceMembers(script::Tracer* tracer) override; private: - ~Performance() override; - scoped_refptr<PerformanceTiming> timing_; scoped_refptr<MemoryInfo> memory_;
diff --git a/src/cobalt/dom/testing/stub_window.h b/src/cobalt/dom/testing/stub_window.h index f288042..f11dd17 100644 --- a/src/cobalt/dom/testing/stub_window.h +++ b/src/cobalt/dom/testing/stub_window.h
@@ -59,7 +59,9 @@ dom::kCspEnforcementEnable, base::Closure() /* csp_policy_changed */, base::Closure() /* ran_animation_frame_callbacks */, dom::Window::CloseCallback() /* window_close */, - base::Closure() /* window_minimize */, NULL, NULL, NULL); + base::Closure() /* window_minimize */, NULL, NULL, NULL, + dom::Window::OnStartDispatchEventCallback(), + dom::Window::OnStopDispatchEventCallback()); global_environment_->CreateGlobalObject(window_, &environment_settings_); }
diff --git a/src/cobalt/dom/typed_array.h b/src/cobalt/dom/typed_array.h index b5ff828..fdf9d97 100644 --- a/src/cobalt/dom/typed_array.h +++ b/src/cobalt/dom/typed_array.h
@@ -50,11 +50,7 @@ uint32 length) : ArrayBufferView(new ArrayBuffer(settings, length * kBytesPerElement)) { DCHECK_EQ(this->length(), length); -#if defined(STARBOARD) SbMemoryCopy(this->data(), data, length * kBytesPerElement); -#else - memcpy(this->data(), data, length * kBytesPerElement); -#endif } // Creates a new TypedArray and copies the elements of 'other' into this. @@ -131,13 +127,8 @@ } uint32 source_offset = 0; while (source_offset < source->length()) { -#if defined(STARBOARD) SbMemoryCopy(data() + offset, source->data() + source_offset, sizeof(ElementType)); -#else - memcpy(data() + offset, source->data() + source_offset, - sizeof(ElementType)); -#endif ++offset; ++source_offset; } @@ -151,22 +142,14 @@ // Write a single element of the array. void Set(uint32 index, ElementType val) { if (index < length()) { -#if defined(STARBOARD) SbMemoryCopy(data() + index, &val, sizeof(ElementType)); -#else - memcpy(data() + index, &val, sizeof(ElementType)); -#endif } } ElementType Get(uint32 index) const { if (index < length()) { ElementType val; -#if defined(STARBOARD) SbMemoryCopy(&val, data() + index, sizeof(ElementType)); -#else - memcpy(&val, data() + index, sizeof(ElementType)); -#endif return val; } else { // TODO: an out of bounds index should return undefined.
diff --git a/src/cobalt/dom/video_playback_quality.h b/src/cobalt/dom/video_playback_quality.h index 471b929..d3e0e9b 100644 --- a/src/cobalt/dom/video_playback_quality.h +++ b/src/cobalt/dom/video_playback_quality.h
@@ -27,21 +27,19 @@ class VideoPlaybackQuality : public script::Wrappable { public: VideoPlaybackQuality(double creation_time, uint32 total_video_frames, - uint32 dropped_video_frames, - uint32 corrupted_video_frames, double total_frame_delay) + uint32 dropped_video_frames) : creation_time_(creation_time), total_video_frames_(total_video_frames), - dropped_video_frames_(dropped_video_frames), - corrupted_video_frames_(corrupted_video_frames), - total_frame_delay_(total_frame_delay) {} + dropped_video_frames_(dropped_video_frames) {} // Web API: VideoPlaybackQuality // double creation_time() const { return creation_time_; } uint32 total_video_frames() const { return total_video_frames_; } uint32 dropped_video_frames() const { return dropped_video_frames_; } - uint32 corrupted_video_frames() const { return corrupted_video_frames_; } - double total_frame_delay() const { return total_frame_delay_; } + // Always returns 0 as Cobalt simply stops the playback on encountering + // corrupted video frames. + uint32 corrupted_video_frames() const { return 0; } DEFINE_WRAPPABLE_TYPE(VideoPlaybackQuality); @@ -49,8 +47,6 @@ double creation_time_; uint32 total_video_frames_; uint32 dropped_video_frames_; - uint32 corrupted_video_frames_; - double total_frame_delay_; }; } // namespace dom
diff --git a/src/cobalt/dom/video_playback_quality.idl b/src/cobalt/dom/video_playback_quality.idl index acc06c0..146f170 100644 --- a/src/cobalt/dom/video_playback_quality.idl +++ b/src/cobalt/dom/video_playback_quality.idl
@@ -19,7 +19,6 @@ readonly attribute unsigned long totalVideoFrames; readonly attribute unsigned long droppedVideoFrames; readonly attribute unsigned long corruptedVideoFrames; - readonly attribute double totalFrameDelay; }; typedef double DOMHighResTimeStamp;
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc index 8da7ca4..becff85 100644 --- a/src/cobalt/dom/window.cc +++ b/src/cobalt/dom/window.cc
@@ -83,44 +83,46 @@ const int64_t kPerformanceTimerMinResolutionInMicroseconds = 20; } // namespace -Window::Window(int width, int height, float device_pixel_ratio, - base::ApplicationState initial_application_state, - cssom::CSSParser* css_parser, Parser* dom_parser, - loader::FetcherFactory* fetcher_factory, - render_tree::ResourceProvider** resource_provider, - loader::image::AnimatedImageTracker* animated_image_tracker, - loader::image::ImageCache* image_cache, - loader::image::ReducedCacheCapacityManager* - reduced_image_cache_capacity_manager, - loader::font::RemoteTypefaceCache* remote_typeface_cache, - loader::mesh::MeshCache* mesh_cache, - LocalStorageDatabase* local_storage_database, - media::CanPlayTypeHandler* can_play_type_handler, - media::WebMediaPlayerFactory* web_media_player_factory, - script::ExecutionState* execution_state, - script::ScriptRunner* script_runner, - script::ScriptValueFactory* script_value_factory, - MediaSource::Registry* media_source_registry, - DomStatTracker* dom_stat_tracker, const GURL& url, - const std::string& user_agent, const std::string& language, - const std::string& font_language_script, - const base::Callback<void(const GURL&)> navigation_callback, - const base::Callback<void(const std::string&)>& error_callback, - network_bridge::CookieJar* cookie_jar, - const network_bridge::PostSender& post_sender, - csp::CSPHeaderPolicy require_csp, - CspEnforcementType csp_enforcement_mode, - const base::Closure& csp_policy_changed_callback, - const base::Closure& ran_animation_frame_callbacks_callback, - const CloseCallback& window_close_callback, - const base::Closure& window_minimize_callback, - OnScreenKeyboardBridge* on_screen_keyboard_bridge, - const scoped_refptr<input::Camera3D>& camera_3d, - const scoped_refptr<MediaSession>& media_session, - int csp_insecure_allowed_token, int dom_max_element_depth, - float video_playback_rate_multiplier, ClockType clock_type, - const CacheCallback& splash_screen_cache_callback, - const scoped_refptr<captions::SystemCaptionSettings>& captions) +Window::Window( + int width, int height, float device_pixel_ratio, + base::ApplicationState initial_application_state, + cssom::CSSParser* css_parser, Parser* dom_parser, + loader::FetcherFactory* fetcher_factory, + render_tree::ResourceProvider** resource_provider, + loader::image::AnimatedImageTracker* animated_image_tracker, + loader::image::ImageCache* image_cache, + loader::image::ReducedCacheCapacityManager* + reduced_image_cache_capacity_manager, + loader::font::RemoteTypefaceCache* remote_typeface_cache, + loader::mesh::MeshCache* mesh_cache, + LocalStorageDatabase* local_storage_database, + media::CanPlayTypeHandler* can_play_type_handler, + media::WebMediaPlayerFactory* web_media_player_factory, + script::ExecutionState* execution_state, + script::ScriptRunner* script_runner, + script::ScriptValueFactory* script_value_factory, + MediaSource::Registry* media_source_registry, + DomStatTracker* dom_stat_tracker, const GURL& url, + const std::string& user_agent, const std::string& language, + const std::string& font_language_script, + const base::Callback<void(const GURL&)> navigation_callback, + const base::Callback<void(const std::string&)>& error_callback, + network_bridge::CookieJar* cookie_jar, + const network_bridge::PostSender& post_sender, + csp::CSPHeaderPolicy require_csp, CspEnforcementType csp_enforcement_mode, + const base::Closure& csp_policy_changed_callback, + const base::Closure& ran_animation_frame_callbacks_callback, + const CloseCallback& window_close_callback, + const base::Closure& window_minimize_callback, + OnScreenKeyboardBridge* on_screen_keyboard_bridge, + const scoped_refptr<input::Camera3D>& camera_3d, + const scoped_refptr<MediaSession>& media_session, + const OnStartDispatchEventCallback& on_start_dispatch_event_callback, + const OnStopDispatchEventCallback& on_stop_dispatch_event_callback, + int csp_insecure_allowed_token, int dom_max_element_depth, + float video_playback_rate_multiplier, ClockType clock_type, + const CacheCallback& splash_screen_cache_callback, + const scoped_refptr<captions::SystemCaptionSettings>& captions) : width_(width), height_(height), device_pixel_ratio_(device_pixel_ratio), @@ -159,8 +161,8 @@ csp_insecure_allowed_token, dom_max_element_depth)))), document_loader_(NULL), history_(new History()), - navigator_(new Navigator(user_agent, language, media_session, - captions, script_value_factory)), + navigator_(new Navigator(user_agent, language, media_session, captions, + script_value_factory)), ALLOW_THIS_IN_INITIALIZER_LIST( relay_on_load_event_(new RelayLoadEvent(this))), console_(new Console(execution_state)), @@ -185,7 +187,9 @@ ? new OnScreenKeyboard(on_screen_keyboard_bridge, script_value_factory) : NULL), - splash_screen_cache_callback_(splash_screen_cache_callback) { + splash_screen_cache_callback_(splash_screen_cache_callback), + on_start_dispatch_event_callback_(on_start_dispatch_event_callback), + on_stop_dispatch_event_callback_(on_stop_dispatch_event_callback) { #if !defined(ENABLE_TEST_RUNNER) UNREFERENCED_PARAMETER(clock_type); #endif @@ -249,7 +253,7 @@ } scoped_refptr<cssom::CSSStyleDeclaration> Window::GetComputedStyle( - const scoped_refptr<Element>& elt, const std::string& pseudoElt) { + const scoped_refptr<Element>& elt, const std::string& pseudo_elt) { // The getComputedStyle(elt, pseudoElt) method must run these steps: // https://www.w3.org/TR/2013/WD-cssom-20131205/#dom-window-getcomputedstyle @@ -268,8 +272,8 @@ // 3. If pseudoElt is as an ASCII case-insensitive match for either // ':before' or '::before' let obj be the ::before pseudo-element of elt. - if (LowerCaseEqualsASCII(pseudoElt, ":before") || - LowerCaseEqualsASCII(pseudoElt, "::before")) { + if (LowerCaseEqualsASCII(pseudo_elt, ":before") || + LowerCaseEqualsASCII(pseudo_elt, "::before")) { PseudoElement* pseudo_element = html_element->pseudo_element(kBeforePseudoElementType); obj = pseudo_element ? pseudo_element->css_computed_style_declaration() @@ -278,8 +282,8 @@ // 4. If pseudoElt is as an ASCII case-insensitive match for either ':after' // or '::after' let obj be the ::after pseudo-element of elt. - if (LowerCaseEqualsASCII(pseudoElt, ":after") || - LowerCaseEqualsASCII(pseudoElt, "::after")) { + if (LowerCaseEqualsASCII(pseudo_elt, ":after") || + LowerCaseEqualsASCII(pseudo_elt, "::after")) { PseudoElement* pseudo_element = html_element->pseudo_element(kAfterPseudoElementType); obj = pseudo_element ? pseudo_element->css_computed_style_declaration() @@ -600,6 +604,18 @@ } } +void Window::OnStartDispatchEvent(const scoped_refptr<dom::Event>& event) { + if (!on_start_dispatch_event_callback_.is_null()) { + on_start_dispatch_event_callback_.Run(event); + } +} + +void Window::OnStopDispatchEvent(const scoped_refptr<dom::Event>& event) { + if (!on_stop_dispatch_event_callback_.is_null()) { + on_stop_dispatch_event_callback_.Run(event); + } +} + void Window::TraceMembers(script::Tracer* tracer) { EventTarget::TraceMembers(tracer); @@ -632,6 +648,8 @@ return on_screen_keyboard_; } +void Window::ReleaseOnScreenKeyboard() { on_screen_keyboard_ = nullptr; } + Window::~Window() { html_element_context_->page_visibility_state()->RemoveObserver(this); }
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h index 1afd941..e8d76b6 100644 --- a/src/cobalt/dom/window.h +++ b/src/cobalt/dom/window.h
@@ -104,11 +104,11 @@ typedef AnimationFrameRequestCallbackList::FrameRequestCallback FrameRequestCallback; typedef WindowTimers::TimerCallback TimerCallback; - typedef base::Callback<scoped_ptr<loader::Decoder>( - HTMLElementContext*, const scoped_refptr<Document>&, - const base::SourceLocation&, const base::Closure&, - const base::Callback<void(const std::string&)>&)> - HTMLDecoderCreatorCallback; + typedef base::Callback<void(const scoped_refptr<dom::Event>& event)> + OnStartDispatchEventCallback; + typedef base::Callback<void(const scoped_refptr<dom::Event>& event)> + OnStopDispatchEventCallback; + // Callback that will be called when window.close() is called. The // base::TimeDelta parameter will contain the document's timeline time when // close() was called. @@ -153,6 +153,9 @@ OnScreenKeyboardBridge* on_screen_keyboard_bridge, const scoped_refptr<input::Camera3D>& camera_3d, const scoped_refptr<cobalt::media_session::MediaSession>& media_session, + const OnStartDispatchEventCallback& + start_tracking_dispatch_event_callback, + const OnStopDispatchEventCallback& stop_tracking_dispatch_event_callback, int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0, float video_playback_rate_multiplier = 1.f, ClockType clock_type = kClockTypeSystemTime, @@ -186,7 +189,7 @@ scoped_refptr<cssom::CSSStyleDeclaration> GetComputedStyle( const scoped_refptr<Element>& elt); scoped_refptr<cssom::CSSStyleDeclaration> GetComputedStyle( - const scoped_refptr<Element>& elt, const std::string& pseudoElt); + const scoped_refptr<Element>& elt, const std::string& pseudo_elt); // Web API: Timing control for script-based animations (partial interface) // https://www.w3.org/TR/animation-timing/#Window-interface-extensions @@ -347,6 +350,10 @@ // Custom on screen keyboard. const scoped_refptr<OnScreenKeyboard>& on_screen_keyboard() const; + void ReleaseOnScreenKeyboard(); + + void OnStartDispatchEvent(const scoped_refptr<dom::Event>& event); + void OnStopDispatchEvent(const scoped_refptr<dom::Event>& event); DEFINE_WRAPPABLE_TYPE(Window); void TraceMembers(script::Tracer* tracer) override; @@ -416,6 +423,9 @@ CacheCallback splash_screen_cache_callback_; + OnStartDispatchEventCallback on_start_dispatch_event_callback_; + OnStopDispatchEventCallback on_stop_dispatch_event_callback_; + DISALLOW_COPY_AND_ASSIGN(Window); };
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc index 03d0e76..4e70b90 100644 --- a/src/cobalt/dom/window_test.cc +++ b/src/cobalt/dom/window_test.cc
@@ -60,7 +60,9 @@ kCspEnforcementEnable, base::Closure() /* csp_policy_changed */, base::Closure() /* ran_animation_frame_callbacks */, dom::Window::CloseCallback() /* window_close */, - base::Closure() /* window_minimize */, NULL, NULL, NULL)) {} + base::Closure() /* window_minimize */, NULL, NULL, NULL, + dom::Window::OnStartDispatchEventCallback(), + dom::Window::OnStopDispatchEventCallback())) {} ~WindowTest() override {}
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc index 7041bfa..dcfeba4 100644 --- a/src/cobalt/dom_parser/html_decoder_test.cc +++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -237,14 +237,18 @@ EXPECT_EQ("div", element->local_name()); EXPECT_TRUE(element->HasAttributes()); EXPECT_EQ(4, element->attributes()->length()); - EXPECT_EQ("a", element->attributes()->Item(0)->name()); - EXPECT_EQ("", element->attributes()->Item(0)->value()); - EXPECT_EQ("b", element->attributes()->Item(1)->name()); - EXPECT_EQ("2", element->attributes()->Item(1)->value()); - EXPECT_EQ("c", element->attributes()->Item(2)->name()); - EXPECT_EQ("", element->attributes()->Item(2)->value()); - EXPECT_EQ("d", element->attributes()->Item(3)->name()); - EXPECT_EQ("", element->attributes()->Item(3)->value()); + ASSERT_NE(nullptr, element->attributes()->GetNamedItem("a")); + EXPECT_EQ("a", element->attributes()->GetNamedItem("a")->name()); + EXPECT_EQ("", element->attributes()->GetNamedItem("a")->value()); + ASSERT_NE(nullptr, element->attributes()->GetNamedItem("b")); + EXPECT_EQ("b", element->attributes()->GetNamedItem("b")->name()); + EXPECT_EQ("2", element->attributes()->GetNamedItem("b")->value()); + ASSERT_NE(nullptr, element->attributes()->GetNamedItem("c")); + EXPECT_EQ("c", element->attributes()->GetNamedItem("c")->name()); + EXPECT_EQ("", element->attributes()->GetNamedItem("c")->value()); + ASSERT_NE(nullptr, element->attributes()->GetNamedItem("d")); + EXPECT_EQ("d", element->attributes()->GetNamedItem("d")->name()); + EXPECT_EQ("", element->attributes()->GetNamedItem("d")->value()); } TEST_F(HTMLDecoderTest, CanParseIncompleteAttributesAssignment) { @@ -263,10 +267,12 @@ EXPECT_EQ("div", element->local_name()); EXPECT_TRUE(element->HasAttributes()); EXPECT_EQ(2, element->attributes()->length()); - EXPECT_EQ("a", element->attributes()->Item(0)->name()); - EXPECT_EQ("b=2", element->attributes()->Item(0)->value()); - EXPECT_EQ("c", element->attributes()->Item(1)->name()); - EXPECT_EQ("", element->attributes()->Item(1)->value()); + ASSERT_NE(nullptr, element->attributes()->GetNamedItem("a")); + EXPECT_EQ("a", element->attributes()->GetNamedItem("a")->name()); + EXPECT_EQ("b=2", element->attributes()->GetNamedItem("a")->value()); + ASSERT_NE(nullptr, element->attributes()->GetNamedItem("c")); + EXPECT_EQ("c", element->attributes()->GetNamedItem("c")->name()); + EXPECT_EQ("", element->attributes()->GetNamedItem("c")->value()); } TEST_F(HTMLDecoderTest, CanParseSelfClosingTags) {
diff --git a/src/cobalt/dom_parser/xml_decoder_test.cc b/src/cobalt/dom_parser/xml_decoder_test.cc index a0267ea..986d828 100644 --- a/src/cobalt/dom_parser/xml_decoder_test.cc +++ b/src/cobalt/dom_parser/xml_decoder_test.cc
@@ -112,10 +112,12 @@ EXPECT_EQ("ELEMENT", element->local_name()); EXPECT_TRUE(element->HasAttributes()); EXPECT_EQ(2, element->attributes()->length()); - EXPECT_EQ("a", element->attributes()->Item(0)->name()); - EXPECT_EQ("1", element->attributes()->Item(0)->value()); - EXPECT_EQ("b", element->attributes()->Item(1)->name()); - EXPECT_EQ("2", element->attributes()->Item(1)->value()); + ASSERT_NE(nullptr, element->attributes()->GetNamedItem("a")); + EXPECT_EQ("a", element->attributes()->GetNamedItem("a")->name()); + EXPECT_EQ("1", element->attributes()->GetNamedItem("a")->value()); + ASSERT_NE(nullptr, element->attributes()->GetNamedItem("b")); + EXPECT_EQ("b", element->attributes()->GetNamedItem("b")->name()); + EXPECT_EQ("2", element->attributes()->GetNamedItem("b")->value()); } TEST_F(XMLDecoderTest, TagNamesShouldBeCaseSensitive) { @@ -150,10 +152,12 @@ EXPECT_EQ("ELEMENT", element->local_name()); EXPECT_TRUE(element->HasAttributes()); EXPECT_EQ(2, element->attributes()->length()); - EXPECT_EQ("A", element->attributes()->Item(0)->name()); - EXPECT_EQ("1", element->attributes()->Item(0)->value()); - EXPECT_EQ("a", element->attributes()->Item(1)->name()); - EXPECT_EQ("2", element->attributes()->Item(1)->value()); + ASSERT_NE(nullptr, element->attributes()->GetNamedItem("A")); + EXPECT_EQ("A", element->attributes()->GetNamedItem("A")->name()); + EXPECT_EQ("1", element->attributes()->GetNamedItem("A")->value()); + ASSERT_NE(nullptr, element->attributes()->GetNamedItem("a")); + EXPECT_EQ("a", element->attributes()->GetNamedItem("a")->name()); + EXPECT_EQ("2", element->attributes()->GetNamedItem("a")->value()); } TEST_F(XMLDecoderTest, CanDealWithFileAttack) {
diff --git a/src/cobalt/h5vcc/h5vcc_account_manager.cc b/src/cobalt/h5vcc/h5vcc_account_manager.cc index 2cb65fd..df85443 100644 --- a/src/cobalt/h5vcc/h5vcc_account_manager.cc +++ b/src/cobalt/h5vcc/h5vcc_account_manager.cc
@@ -14,8 +14,10 @@ #include "cobalt/h5vcc/h5vcc_account_manager.h" +#include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "cobalt/browser/switches.h" #include "starboard/user.h" namespace cobalt { @@ -71,6 +73,14 @@ void H5vccAccountManager::RequestOperationInternal( account::UserAuthorizer* user_authorizer, OperationType operation, const base::Callback<void(const std::string&, uint64_t)>& post_result) { +#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) + CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(browser::switches::kDisableSignIn)) { + post_result.Run(std::string(), 0); + return; + } +#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) + SbUser current_user = SbUserGetCurrent(); DCHECK(SbUserIsValid(current_user));
diff --git a/src/cobalt/input/input_poller_impl.cc b/src/cobalt/input/input_poller_impl.cc index ea10e84..dca447d 100644 --- a/src/cobalt/input/input_poller_impl.cc +++ b/src/cobalt/input/input_poller_impl.cc
@@ -89,15 +89,11 @@ input_event->position().x(); break; } - default: - NOTREACHED(); } } break; -#if SB_HAS(ON_SCREEN_KEYBOARD) case system_window::InputEvent::kInput: // Input events (dom::InputEvent) are ignored here. break; -#endif // SB_HAS(ON_SCREEN_KEYBOARD) case system_window::InputEvent::kPointerDown: case system_window::InputEvent::kPointerUp: case system_window::InputEvent::kPointerMove: @@ -107,8 +103,6 @@ case system_window::InputEvent::kWheel: // Pointer and Wheel events are ignored here. break; - default: - NOTREACHED(); } }
diff --git a/src/cobalt/layout/block_container_box.cc b/src/cobalt/layout/block_container_box.cc index 557b666..f07c76a 100644 --- a/src/cobalt/layout/block_container_box.cc +++ b/src/cobalt/layout/block_container_box.cc
@@ -68,9 +68,6 @@ containing_block_width, maybe_nulled_width, maybe_margin_left, maybe_margin_right, maybe_height); break; - default: - NOTREACHED(); - break; } } }
diff --git a/src/cobalt/layout/box_generator.cc b/src/cobalt/layout/box_generator.cc index 7fe2d0e..4217bb1 100644 --- a/src/cobalt/layout/box_generator.cc +++ b/src/cobalt/layout/box_generator.cc
@@ -266,6 +266,7 @@ case cssom::KeywordValue::kCursive: case cssom::KeywordValue::kEllipsis: case cssom::KeywordValue::kEnd: + case cssom::KeywordValue::kEquirectangular: case cssom::KeywordValue::kFantasy: case cssom::KeywordValue::kForwards: case cssom::KeywordValue::kFixed: @@ -298,7 +299,6 @@ case cssom::KeywordValue::kTop: case cssom::KeywordValue::kUppercase: case cssom::KeywordValue::kVisible: - default: NOTREACHED(); break; } @@ -579,6 +579,7 @@ case cssom::KeywordValue::kClip: case cssom::KeywordValue::kEllipsis: case cssom::KeywordValue::kEnd: + case cssom::KeywordValue::kEquirectangular: case cssom::KeywordValue::kFantasy: case cssom::KeywordValue::kFixed: case cssom::KeywordValue::kForwards: @@ -611,7 +612,6 @@ case cssom::KeywordValue::kTop: case cssom::KeywordValue::kUppercase: case cssom::KeywordValue::kVisible: - default: NOTREACHED(); break; } @@ -727,6 +727,7 @@ case cssom::KeywordValue::kCursive: case cssom::KeywordValue::kEllipsis: case cssom::KeywordValue::kEnd: + case cssom::KeywordValue::kEquirectangular: case cssom::KeywordValue::kFantasy: case cssom::KeywordValue::kFixed: case cssom::KeywordValue::kForwards: @@ -760,7 +761,6 @@ case cssom::KeywordValue::kTop: case cssom::KeywordValue::kUppercase: case cssom::KeywordValue::kVisible: - default: NOTREACHED(); } }
diff --git a/src/cobalt/layout/layout_stat_tracker.cc b/src/cobalt/layout/layout_stat_tracker.cc index d608272..e4456ce 100644 --- a/src/cobalt/layout/layout_stat_tracker.cc +++ b/src/cobalt/layout/layout_stat_tracker.cc
@@ -20,14 +20,17 @@ namespace layout { LayoutStatTracker::LayoutStatTracker(const std::string& name) - : total_boxes_(StringPrintf("Count.%s.Layout.Box", name.c_str()), 0, - "Total number of layout boxes."), - is_event_active_(false), - boxes_created_count_(0), - boxes_destroyed_count_(0), - update_size_count_(0), - render_and_animate_count_(0), - update_cross_references_count_(0) { + : count_box_(StringPrintf("Count.%s.Layout.Box", name.c_str()), 0, + "Total number of layout boxes."), + count_box_created_(0), + count_box_destroyed_(0), + is_tracking_event_(false), + event_initial_count_box_(0), + event_count_box_created_(0), + event_count_box_destroyed_(0), + event_count_update_size_(0), + event_count_render_and_animate_(0), + event_count_update_cross_references_(0) { stop_watch_durations_.resize(kNumStopWatchTypes, base::TimeDelta()); } @@ -35,15 +38,62 @@ FlushPeriodicTracking(); // Verify that all of the boxes were destroyed. - DCHECK_EQ(total_boxes_, 0); + DCHECK_EQ(count_box_, 0); } -void LayoutStatTracker::OnStartEvent() { - is_event_active_ = true; +void LayoutStatTracker::OnBoxCreated() { + ++count_box_created_; + if (is_tracking_event_) { + ++event_count_box_created_; + } +} - // Flush the periodic tracking prior to starting the event. This ensures that - // an accurate count of the periodic counts is produced during the event. - FlushPeriodicTracking(); +void LayoutStatTracker::OnBoxDestroyed() { + ++count_box_destroyed_; + if (is_tracking_event_) { + ++event_count_box_destroyed_; + } +} + +void LayoutStatTracker::OnUpdateSize() { + if (is_tracking_event_) { + ++event_count_update_size_; + } +} + +void LayoutStatTracker::OnRenderAndAnimate() { + if (is_tracking_event_) { + ++event_count_render_and_animate_; + } +} + +void LayoutStatTracker::OnUpdateCrossReferences() { + if (is_tracking_event_) { + ++event_count_update_cross_references_; + } +} + +void LayoutStatTracker::FlushPeriodicTracking() { + // Update the CVals before clearing the periodic values. + count_box_ += count_box_created_ - count_box_destroyed_; + + // Now clear the values. + count_box_created_ = 0; + count_box_destroyed_ = 0; +} + +void LayoutStatTracker::StartTrackingEvent() { + DCHECK(!is_tracking_event_); + is_tracking_event_ = true; + + event_initial_count_box_ = + count_box_ + count_box_created_ - count_box_destroyed_; + + event_count_box_created_ = 0; + event_count_box_destroyed_ = 0; + event_count_update_size_ = 0; + event_count_render_and_animate_ = 0; + event_count_update_cross_references_ = 0; // Zero out the stop watch durations so that the numbers will only include the // event. @@ -52,24 +102,14 @@ } } -void LayoutStatTracker::OnEndEvent() { - is_event_active_ = false; - - // Flush the periodic tracking after the event. This updates the cval totals, - // providing an accurate picture of them at the moment the event ends - FlushPeriodicTracking(); +void LayoutStatTracker::StopTrackingEvent() { + DCHECK(is_tracking_event_); + is_tracking_event_ = false; } -void LayoutStatTracker::OnBoxCreated() { ++boxes_created_count_; } - -void LayoutStatTracker::OnBoxDestroyed() { ++boxes_destroyed_count_; } - -void LayoutStatTracker::OnUpdateSize() { ++update_size_count_; } - -void LayoutStatTracker::OnRenderAndAnimate() { ++render_and_animate_count_; } - -void LayoutStatTracker::OnUpdateCrossReferences() { - ++update_cross_references_count_; +int LayoutStatTracker::EventCountBox() const { + return event_initial_count_box_ + event_count_box_created_ - + event_count_box_destroyed_; } base::TimeDelta LayoutStatTracker::GetStopWatchTypeDuration( @@ -78,7 +118,7 @@ } bool LayoutStatTracker::IsStopWatchEnabled(int /*id*/) const { - return is_event_active_; + return is_tracking_event_; } void LayoutStatTracker::OnStopWatchStopped(int id, @@ -86,17 +126,5 @@ stop_watch_durations_[static_cast<size_t>(id)] += time_elapsed; } -void LayoutStatTracker::FlushPeriodicTracking() { - // Update the CVals before clearing the periodic values. - total_boxes_ += boxes_created_count_ - boxes_destroyed_count_; - - // Now clear the values. - boxes_created_count_ = 0; - boxes_destroyed_count_ = 0; - update_size_count_ = 0; - render_and_animate_count_ = 0; - update_cross_references_count_ = 0; -} - } // namespace layout } // namespace cobalt
diff --git a/src/cobalt/layout/layout_stat_tracker.h b/src/cobalt/layout/layout_stat_tracker.h index 1cfff5f..36a6640 100644 --- a/src/cobalt/layout/layout_stat_tracker.h +++ b/src/cobalt/layout/layout_stat_tracker.h
@@ -39,25 +39,29 @@ explicit LayoutStatTracker(const std::string& name); ~LayoutStatTracker(); - // Event-related - void OnStartEvent(); - void OnEndEvent(); - - // Periodic count-related void OnBoxCreated(); void OnBoxDestroyed(); void OnUpdateSize(); void OnRenderAndAnimate(); void OnUpdateCrossReferences(); - int total_boxes() const { return total_boxes_; } + // This function updates the CVals from the periodic values and then clears + // those values. + void FlushPeriodicTracking(); - int boxes_created_count() const { return boxes_created_count_; } - int boxes_destroyed_count() const { return boxes_destroyed_count_; } - int update_size_count() const { return update_size_count_; } - int render_and_animate_count() const { return render_and_animate_count_; } - int update_cross_references_count() const { - return update_cross_references_count_; + // Event-related + void StartTrackingEvent(); + void StopTrackingEvent(); + + int EventCountBox() const; + int event_count_box_created() const { return event_count_box_created_; } + int event_count_box_destroyed() const { return event_count_box_destroyed_; } + int event_count_update_size() const { return event_count_update_size_; } + int event_count_render_and_animate() const { + return event_count_render_and_animate_; + } + int event_count_update_cross_references() const { + return event_count_update_cross_references_; } base::TimeDelta GetStopWatchTypeDuration(StopWatchType type) const; @@ -67,23 +71,22 @@ bool IsStopWatchEnabled(int id) const override; void OnStopWatchStopped(int id, base::TimeDelta time_elapsed) override; - // This function updates the CVals from the periodic values and then clears - // those values. - void FlushPeriodicTracking(); - // Count cvals that are updated when the periodic tracking is flushed. - base::CVal<int, base::CValPublic> total_boxes_; - - // Event-related - bool is_event_active_; + base::CVal<int, base::CValPublic> count_box_; // Periodic counts. The counts are cleared after the count cvals are updated // in |FlushPeriodicTracking|. - int boxes_created_count_; - int boxes_destroyed_count_; - int update_size_count_; - int render_and_animate_count_; - int update_cross_references_count_; + int count_box_created_; + int count_box_destroyed_; + + // Event-related + bool is_tracking_event_; + int event_initial_count_box_; + int event_count_box_created_; + int event_count_box_destroyed_; + int event_count_update_size_; + int event_count_render_and_animate_; + int event_count_update_cross_references_; // Stop watch-related. std::vector<base::TimeDelta> stop_watch_durations_;
diff --git a/src/cobalt/layout/line_box.cc b/src/cobalt/layout/line_box.cc index fc12a78..0fb9747 100644 --- a/src/cobalt/layout/line_box.cc +++ b/src/cobalt/layout/line_box.cc
@@ -496,9 +496,6 @@ case Box::kBlockLevel: child_box->SetStaticPositionLeftFromParent(LayoutUnit()); break; - default: - NOTREACHED(); - break; } child_box->SetStaticPositionTopFromParent(LayoutUnit()); }
diff --git a/src/cobalt/layout/paragraph.cc b/src/cobalt/layout/paragraph.cc index 2675c41..6f8eb8d 100644 --- a/src/cobalt/layout/paragraph.cc +++ b/src/cobalt/layout/paragraph.cc
@@ -320,7 +320,8 @@ Paragraph::BreakPolicy Paragraph::GetBreakPolicyFromWrapOpportunityPolicy( WrapOpportunityPolicy wrap_opportunity_policy, bool does_style_allow_break_word) { - BreakPolicy break_policy; + // Initialize here to prevent a potential compiler warning. + BreakPolicy break_policy = kBreakPolicyNormal; switch (wrap_opportunity_policy) { case kWrapOpportunityPolicyNormal: break_policy = kBreakPolicyNormal; @@ -332,9 +333,6 @@ break_policy = does_style_allow_break_word ? kBreakPolicyBreakWord : kBreakPolicyNormal; break; - default: - NOTREACHED(); - break_policy = kBreakPolicyNormal; } return break_policy; }
diff --git a/src/cobalt/layout/replaced_box.cc b/src/cobalt/layout/replaced_box.cc index 0e3f920..1dd410a 100644 --- a/src/cobalt/layout/replaced_box.cc +++ b/src/cobalt/layout/replaced_box.cc
@@ -360,6 +360,27 @@ set_left(maybe_left.value_or(LayoutUnit())); set_top(maybe_top.value_or(LayoutUnit())); } + // Note that computed height may be "auto", even if it is specified as a + // percentage (depending on conditions of the containing block). See details + // in the spec. https://www.w3.org/TR/CSS22/visudet.html#the-height-property + if (!maybe_height) { + LOG(ERROR) << "ReplacedBox element has computed height \"auto\"!"; + } + if (!maybe_width) { + LOG(ERROR) << "ReplacedBox element has computed width \"auto\"!"; + } + // In order for Cobalt to handle "auto" dimensions correctly for both punchout + // and decode-to-texture we need to use the content's intrinsic dimensions & + // ratio rather than using the content_box_size directly. Until this + // functionality is found to be useful, we avoid the extra complexity + // introduced by its implementation. + if (!maybe_height || !maybe_width) { + LOG(ERROR) + << "Cobalt ReplacedBox does not handle \"auto\" dimensions correctly! " + "\"auto\" dimensions are updated using the intrinsic dimensions of " + "the content (e.g. video width/height), which is often not what is " + "intended."; + } if (!maybe_width) { if (!maybe_height) { if (maybe_intrinsic_width_) { @@ -679,11 +700,11 @@ loader::mesh::MeshProjection::kLeftEyeOrMonoCollection), mesh_projection->GetMesh( loader::mesh::MeshProjection::kRightEyeCollection)); - - filter_node = - new FilterNode(MapToMeshFilter(stereo_mode, builder), animate_node); } + filter_node = + new FilterNode(MapToMeshFilter(stereo_mode, builder), animate_node); + #if !SB_HAS(VIRTUAL_REALITY) // Attach a 3D camera to the map-to-mesh node, so the rendering of its // content can be transformed.
diff --git a/src/cobalt/layout/text_box.cc b/src/cobalt/layout/text_box.cc index 127b1d3..fedd4cd 100644 --- a/src/cobalt/layout/text_box.cc +++ b/src/cobalt/layout/text_box.cc
@@ -568,7 +568,7 @@ Paragraph::GetBreakPolicyFromWrapOpportunityPolicy( wrap_opportunity_policy, style_allows_break_word); - int32 wrap_position; + int32 wrap_position = -1; switch (wrap_at_policy) { case kWrapAtPolicyBefore: // Wrapping before the box is only permitted when the line's existence is @@ -633,9 +633,6 @@ paragraph_->GetNextBreakPosition(search_start_position, break_policy); break; } - default: - NOTREACHED(); - wrap_position = -1; } return wrap_position; }
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc index fcb7639..b18953d 100644 --- a/src/cobalt/layout/used_style.cc +++ b/src/cobalt/layout/used_style.cc
@@ -72,7 +72,8 @@ render_tree::FontStyle ConvertCSSOMFontValuesToRenderTreeFontStyle( cssom::FontStyleValue::Value style, cssom::FontWeightValue::Value weight) { - render_tree::FontStyle::Weight font_weight; + render_tree::FontStyle::Weight font_weight = + render_tree::FontStyle::kNormalWeight; switch (weight) { case cssom::FontWeightValue::kThinAka100: font_weight = render_tree::FontStyle::kThinWeight; @@ -101,8 +102,6 @@ case cssom::FontWeightValue::kBlackAka900: font_weight = render_tree::FontStyle::kBlackWeight; break; - default: - font_weight = render_tree::FontStyle::kNormalWeight; } render_tree::FontStyle::Slant font_slant = @@ -159,10 +158,11 @@ } BackgroundImageTransformData background_image_transform_data( - image_node_size, math::TranslateMatrix(image_node_translate_matrix_x, - image_node_translate_matrix_y) * - math::ScaleMatrix(image_node_scale_matrix_x, - image_node_scale_matrix_y), + image_node_size, + math::TranslateMatrix(image_node_translate_matrix_x, + image_node_translate_matrix_y) * + math::ScaleMatrix(image_node_scale_matrix_x, + image_node_scale_matrix_y), math::PointF(composition_node_translate_matrix_x + frame.x(), composition_node_translate_matrix_y + frame.y())); return background_image_transform_data; @@ -255,6 +255,7 @@ case cssom::KeywordValue::kCursive: case cssom::KeywordValue::kEllipsis: case cssom::KeywordValue::kEnd: + case cssom::KeywordValue::kEquirectangular: case cssom::KeywordValue::kFantasy: case cssom::KeywordValue::kForwards: case cssom::KeywordValue::kFixed: @@ -290,7 +291,6 @@ case cssom::KeywordValue::kTop: case cssom::KeywordValue::kUppercase: case cssom::KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -348,6 +348,7 @@ case cssom::KeywordValue::kCover: case cssom::KeywordValue::kEllipsis: case cssom::KeywordValue::kEnd: + case cssom::KeywordValue::kEquirectangular: case cssom::KeywordValue::kFixed: case cssom::KeywordValue::kForwards: case cssom::KeywordValue::kHidden: @@ -379,7 +380,6 @@ case cssom::KeywordValue::kTop: case cssom::KeywordValue::kUppercase: case cssom::KeywordValue::kVisible: - default: NOTREACHED(); } } @@ -712,10 +712,9 @@ case cssom::LinearGradientValue::kTopRight: return std::make_pair(math::PointF(0, frame_size.height()), math::PointF(frame_size.width(), 0)); - default: - NOTREACHED(); - return std::make_pair(math::PointF(0, 0), math::PointF(0, 0)); } + NOTREACHED(); + return std::make_pair(math::PointF(0, 0), math::PointF(0, 0)); } std::pair<math::PointF, math::PointF> LinearGradientPointsFromAngle( @@ -730,8 +729,8 @@ // we can pass it into the trigonometric functions cos() and sin(). float ccw_angle_from_right = -angle_in_radians + static_cast<float>(M_PI / 2); - return render_tree::LinearGradientPointsFromAngle( - ccw_angle_from_right, frame_size); + return render_tree::LinearGradientPointsFromAngle(ccw_angle_from_right, + frame_size); } // The specifications indicate that if positions are not specified for color
diff --git a/src/cobalt/layout_tests/layout_tests.cc b/src/cobalt/layout_tests/layout_tests.cc index a93de29..b4a2f08 100644 --- a/src/cobalt/layout_tests/layout_tests.cc +++ b/src/cobalt/layout_tests/layout_tests.cc
@@ -21,6 +21,7 @@ #include "cobalt/base/cobalt_paths.h" #include "cobalt/layout_tests/layout_snapshot.h" #include "cobalt/layout_tests/test_parser.h" +#include "cobalt/layout_tests/test_utils.h" #include "cobalt/math/size.h" #include "cobalt/render_tree/animations/animate_node.h" #include "cobalt/renderer/render_tree_pixel_tester.h"
diff --git a/src/cobalt/layout_tests/layout_tests.gyp b/src/cobalt/layout_tests/layout_tests.gyp index e0b1ce6..44d7738 100644 --- a/src/cobalt/layout_tests/layout_tests.gyp +++ b/src/cobalt/layout_tests/layout_tests.gyp
@@ -42,19 +42,20 @@ 'dependencies': [ '<(DEPTH)/cobalt/base/base.gyp:base', '<(DEPTH)/cobalt/browser/browser.gyp:browser', + 'layout_copy_test_data', ], - 'actions': [ - { - 'action_name': 'layout_tests_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/layout_tests/testdata/', - ], - 'output_dir': 'cobalt/layout_tests', - }, - 'includes': ['../build/copy_test_data.gypi'], - } - ], + }, + + { + 'target_name': 'layout_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/cobalt/layout_tests/testdata/', + ], + 'content_test_output_subdir': 'cobalt/layout_tests', + }, + 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], }, { @@ -66,9 +67,6 @@ 'dependencies': [ '<(DEPTH)/cobalt/base/base.gyp:base', '<(DEPTH)/cobalt/browser/browser.gyp:browser', - # Depend on browser_copy_test_data so that we ensure external tests - # files from browser that we link to are available. - '<(DEPTH)/cobalt/browser/browser.gyp:browser_copy_test_data', '<(DEPTH)/cobalt/renderer/renderer.gyp:render_tree_pixel_tester', '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests', '<(DEPTH)/googleurl/googleurl.gyp:googleurl', @@ -99,7 +97,6 @@ 'dependencies': [ '<(DEPTH)/cobalt/base/base.gyp:base', '<(DEPTH)/cobalt/browser/browser.gyp:browser', - '<(DEPTH)/cobalt/browser/browser.gyp:browser_copy_test_data', '<(DEPTH)/cobalt/renderer/renderer.gyp:renderer', '<(DEPTH)/cobalt/system_window/system_window.gyp:system_window', '<(DEPTH)/cobalt/trace_event/trace_event.gyp:run_all_benchmarks', @@ -128,9 +125,6 @@ 'dependencies': [ '<(DEPTH)/cobalt/base/base.gyp:base', '<(DEPTH)/cobalt/browser/browser.gyp:browser', - # Depend on browser_copy_test_data so that we ensure external tests - # files from browser that we link to are available. - '<(DEPTH)/cobalt/browser/browser.gyp:browser_copy_test_data', '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests', '<(DEPTH)/googleurl/googleurl.gyp:googleurl', '<(DEPTH)/testing/gmock.gyp:gmock',
diff --git a/src/cobalt/layout_tests/test_parser.cc b/src/cobalt/layout_tests/test_parser.cc index e9fe7c5..5304f47 100644 --- a/src/cobalt/layout_tests/test_parser.cc +++ b/src/cobalt/layout_tests/test_parser.cc
@@ -21,47 +21,32 @@ #include "base/string_number_conversions.h" #include "base/string_util.h" #include "cobalt/base/cobalt_paths.h" +#include "cobalt/layout_tests/test_utils.h" namespace cobalt { namespace layout_tests { -std::ostream& operator<<(std::ostream& out, const TestInfo& test_info) { - return out << test_info.base_file_path.value(); -} - namespace { // Returns the relative path to Cobalt layout tests. This can be appended -// to either base::DIR_SOURCE_ROOT to get the input directory or +// to either base::DIR_TEST_DATA to get the input directory or // base::DIR_COBALT_TEST_OUT to get the output directory. -FilePath GetDirSourceRootRelativePath() { +FilePath GetTestDirRelativePath() { return FilePath(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("layout_tests")); } GURL GetURLFromBaseFilePath(const FilePath& base_file_path) { - return GURL("file:///" + GetDirSourceRootRelativePath().value() + "/" + + return GURL("file:///" + GetTestDirRelativePath().value() + "/" + base_file_path.AddExtension("html").value()); } -} // namespace - -namespace { - base::optional<TestInfo> ParseLayoutTestCaseLine( const FilePath& top_level, const std::string& line_string) { - // Split the line up by commas, of which there may be none. - std::vector<std::string> test_case_tokens; - Tokenize(line_string, ",", &test_case_tokens); - if (test_case_tokens.empty() || test_case_tokens.size() > 2) { - return base::nullopt; - } - - // Extract the test case file path as the first element before a comma, if - // there is one. The test case file path can optionally be postfixed by a - // colon and a viewport resolution. + // The test case file path can optionally be postfixed by a colon and a + // viewport resolution. std::vector<std::string> file_path_tokens; - Tokenize(test_case_tokens[0], ":", &file_path_tokens); + Tokenize(line_string, ":", &file_path_tokens); DCHECK(!file_path_tokens.empty()); base::optional<math::Size> viewport_size; @@ -86,25 +71,16 @@ } FilePath base_file_path(top_level.Append(base_file_path_string)); - if (test_case_tokens.size() == 1) { - // If there is no comma, determine the URL from the file path. - return TestInfo(base_file_path, GetURLFromBaseFilePath(base_file_path), - viewport_size); - } else { - // If there is a comma, the string that comes after it contains the - // explicitly specified URL. Extract that and use it as the test URL. - DCHECK_EQ(2, test_case_tokens.size()); - std::string url_string; - TrimWhitespaceASCII(test_case_tokens[1], TRIM_ALL, &url_string); - if (url_string.empty()) { - return base::nullopt; - } - return TestInfo(base_file_path, GURL(url_string), viewport_size); - } + return TestInfo(base_file_path, GetURLFromBaseFilePath(base_file_path), + viewport_size); } } // namespace +std::ostream& operator<<(std::ostream& out, const TestInfo& test_info) { + return out << test_info.base_file_path.value(); +} + std::vector<TestInfo> EnumerateLayoutTests(const std::string& top_level) { FilePath test_dir(GetTestInputRootDirectory().Append(top_level)); FilePath layout_tests_list_file(
diff --git a/src/cobalt/layout_tests/test_parser.h b/src/cobalt/layout_tests/test_parser.h index 1e8c457..cd6aefd 100644 --- a/src/cobalt/layout_tests/test_parser.h +++ b/src/cobalt/layout_tests/test_parser.h
@@ -26,16 +26,6 @@ namespace cobalt { namespace layout_tests { -// Returns the root directory that all test input can be found in (e.g. -// the HTML files that define the tests, and the PNG/TXT files that define -// the expected output). -FilePath GetTestInputRootDirectory(); - -// Returns the root directory that all output will be placed within. Output -// is generated when rebaselining test expected output, or when test details -// have been chosen to be output. -FilePath GetTestOutputRootDirectory(); - // Final parsed information about an individual Layout Test entry. struct TestInfo { TestInfo(const FilePath& base_file_path, const GURL& url,
diff --git a/src/cobalt/layout_tests/test_utils.cc b/src/cobalt/layout_tests/test_utils.cc index 762dead..ad251ff 100644 --- a/src/cobalt/layout_tests/test_utils.cc +++ b/src/cobalt/layout_tests/test_utils.cc
@@ -27,9 +27,9 @@ namespace { // Returns the relative path to Cobalt layout tests. This can be appended -// to either base::DIR_SOURCE_ROOT to get the input directory or +// to either base::DIR_TEST_DATA to get the input directory or // base::DIR_COBALT_TEST_OUT to get the output directory. -FilePath GetDirSourceRootRelativePath() { +FilePath GetTestDirRelativePath() { return FilePath(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("layout_tests")); } @@ -37,15 +37,15 @@ } // namespace FilePath GetTestInputRootDirectory() { - FilePath dir_source_root; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &dir_source_root)); - return dir_source_root.Append(GetDirSourceRootRelativePath()); + FilePath dir_test_data; + CHECK(PathService::Get(base::DIR_TEST_DATA, &dir_test_data)); + return dir_test_data.Append(GetTestDirRelativePath()); } FilePath GetTestOutputRootDirectory() { FilePath dir_cobalt_test_out; PathService::Get(cobalt::paths::DIR_COBALT_TEST_OUT, &dir_cobalt_test_out); - return dir_cobalt_test_out.Append(GetDirSourceRootRelativePath()); + return dir_cobalt_test_out.Append(GetTestDirRelativePath()); } LogFilter::LogFilter() {
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/-9-kyTW8ZkZNDHQJ6FgpwQ/1.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/-9-kyTW8ZkZNDHQJ6FgpwQ/1.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/-9-kyTW8ZkZNDHQJ6FgpwQ/1.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/-9-kyTW8ZkZNDHQJ6FgpwQ/1.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/4R8DWoMoI7CAwX8_LjQHig/1.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/4R8DWoMoI7CAwX8_LjQHig/1.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/4R8DWoMoI7CAwX8_LjQHig/1.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/4R8DWoMoI7CAwX8_LjQHig/1.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/Egdi0XIXXZ-qJOFPf4JSKw/1.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/Egdi0XIXXZ-qJOFPf4JSKw/1.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/Egdi0XIXXZ-qJOFPf4JSKw/1.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/Egdi0XIXXZ-qJOFPf4JSKw/1.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/F0pVplsI8R5kcAqgtoRqoA/1.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/F0pVplsI8R5kcAqgtoRqoA/1.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/F0pVplsI8R5kcAqgtoRqoA/1.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/F0pVplsI8R5kcAqgtoRqoA/1.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/OpNcN46UbXVtpKMrmU4Abg/1.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/OpNcN46UbXVtpKMrmU4Abg/1.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/OpNcN46UbXVtpKMrmU4Abg/1.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/OpNcN46UbXVtpKMrmU4Abg/1.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/YfdidRxbB8Qhf0Nx7ioOYw/1.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/YfdidRxbB8Qhf0Nx7ioOYw/1.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/YfdidRxbB8Qhf0Nx7ioOYw/1.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/YfdidRxbB8Qhf0Nx7ioOYw/1.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/i-g4cjqGV7jvU8aeSuj0jQ/1.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/i-g4cjqGV7jvU8aeSuj0jQ/1.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/i-g4cjqGV7jvU8aeSuj0jQ/1.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/i-g4cjqGV7jvU8aeSuj0jQ/1.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/xAgnFbkxldX6YUEvdcNjnA/1.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/xAgnFbkxldX6YUEvdcNjnA/1.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/i/xAgnFbkxldX6YUEvdcNjnA/1.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/i/xAgnFbkxldX6YUEvdcNjnA/1.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/0vEKItRNIb0/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/0vEKItRNIb0/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/0vEKItRNIb0/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/0vEKItRNIb0/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/1dXVzBU5p-Q/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/1dXVzBU5p-Q/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/1dXVzBU5p-Q/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/1dXVzBU5p-Q/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/2A07xMhKC6g/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/2A07xMhKC6g/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/2A07xMhKC6g/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/2A07xMhKC6g/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/59_zMJRhFM0/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/59_zMJRhFM0/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/59_zMJRhFM0/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/59_zMJRhFM0/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/8VstEBbwhnc/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/8VstEBbwhnc/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/8VstEBbwhnc/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/8VstEBbwhnc/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/D-YPnDvTCmI/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/D-YPnDvTCmI/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/D-YPnDvTCmI/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/D-YPnDvTCmI/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/F50yjSws9gQ/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/F50yjSws9gQ/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/F50yjSws9gQ/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/F50yjSws9gQ/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/GwzBLYGRj6c/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/GwzBLYGRj6c/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/GwzBLYGRj6c/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/GwzBLYGRj6c/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/LZoilVdo7Hw/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/LZoilVdo7Hw/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/LZoilVdo7Hw/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/LZoilVdo7Hw/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/M9WlASe5ThU/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/M9WlASe5ThU/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/M9WlASe5ThU/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/M9WlASe5ThU/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/MODTYlzxY9U/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/MODTYlzxY9U/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/MODTYlzxY9U/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/MODTYlzxY9U/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/RL7grUEo960/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/RL7grUEo960/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/RL7grUEo960/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/RL7grUEo960/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/XilhAJZ2qxs/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/XilhAJZ2qxs/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/XilhAJZ2qxs/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/XilhAJZ2qxs/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/Z31LDqbhN8U/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/Z31LDqbhN8U/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/Z31LDqbhN8U/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/Z31LDqbhN8U/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/_TWbD3MKfMI/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/_TWbD3MKfMI/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/_TWbD3MKfMI/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/_TWbD3MKfMI/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/eOOyxSMI0aE/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/eOOyxSMI0aE/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/eOOyxSMI0aE/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/eOOyxSMI0aE/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/q14aPbvbvl8/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/q14aPbvbvl8/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/q14aPbvbvl8/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/q14aPbvbvl8/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/qkKRkfZYhlE/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/qkKRkfZYhlE/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/qkKRkfZYhlE/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/qkKRkfZYhlE/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/qrZcKwcVwk8/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/qrZcKwcVwk8/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/qrZcKwcVwk8/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/qrZcKwcVwk8/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/rs-Ou-gjReQ/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/rs-Ou-gjReQ/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/rs-Ou-gjReQ/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/rs-Ou-gjReQ/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/rtzlT78OEks/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/rtzlT78OEks/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/rtzlT78OEks/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/rtzlT78OEks/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/spC883rn6zk/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/spC883rn6zk/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/spC883rn6zk/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/spC883rn6zk/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/tCehxI5a1y0/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/tCehxI5a1y0/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/tCehxI5a1y0/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/tCehxI5a1y0/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/tnfPNYpi8rc/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/tnfPNYpi8rc/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/tnfPNYpi8rc/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/tnfPNYpi8rc/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/vvFHyFW_jFc/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/vvFHyFW_jFc/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/vvFHyFW_jFc/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/vvFHyFW_jFc/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/wNRUzu4fTgw/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/wNRUzu4fTgw/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/wNRUzu4fTgw/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/wNRUzu4fTgw/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/x5ZxRObLLzE/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/x5ZxRObLLzE/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/x5ZxRObLLzE/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/x5ZxRObLLzE/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/zzmrTdAtEu4/hqdefault.jpg b/src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/zzmrTdAtEu4/hqdefault.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/i.ytimg.com/vi/zzmrTdAtEu4/hqdefault.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/i.ytimg.com/vi/zzmrTdAtEu4/hqdefault.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/layout_tests.txt b/src/cobalt/layout_tests/testdata/benchmarks/layout_tests.txt index e5af5b3..c86c38e 100644 --- a/src/cobalt/layout_tests/testdata/benchmarks/layout_tests.txt +++ b/src/cobalt/layout_tests/testdata/benchmarks/layout_tests.txt
@@ -1 +1 @@ -youtube-2015-q3-initial-layout:1920x1080, file:///cobalt/layout_tests/benchmarks/youtube-2015-q3/index.html +youtube-2015-q3-initial-layout:1920x1080
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/CutiveMono-Regular.woff b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/CutiveMono-Regular.woff similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/CutiveMono-Regular.woff rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/CutiveMono-Regular.woff Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/DancingScript-Regular.woff b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/DancingScript-Regular.woff similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/DancingScript-Regular.woff rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/DancingScript-Regular.woff Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/Handlee-Regular.woff b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/Handlee-Regular.woff similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/Handlee-Regular.woff rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/Handlee-Regular.woff Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/MarcellusSC-Regular.woff b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/MarcellusSC-Regular.woff similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/MarcellusSC-Regular.woff rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/MarcellusSC-Regular.woff Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/PTM55FT.woff b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/PTM55FT.woff similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/PTM55FT.woff rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/PTM55FT.woff Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/PT_Sans-Caption-Web-Regular.woff b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/PT_Sans-Caption-Web-Regular.woff similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/PT_Sans-Caption-Web-Regular.woff rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/PT_Sans-Caption-Web-Regular.woff Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/PT_Serif-Caption-Web-Regular.woff b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/PT_Serif-Caption-Web-Regular.woff similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/fonts/PT_Serif-Caption-Web-Regular.woff rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/fonts/PT_Serif-Caption-Web-Regular.woff Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/app-prod.css b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/app-prod.css similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/app-prod.css rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/app-prod.css
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-fullscreen-active.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-fullscreen-active.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-fullscreen-active.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-fullscreen-active.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-fullscreen-onhover-active.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-fullscreen-onhover-active.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-fullscreen-onhover-active.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-fullscreen-onhover-active.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-mute-active.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-mute-active.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-mute-active.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-mute-active.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-mute-onhover-active.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-mute-onhover-active.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-mute-onhover-active.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-mute-onhover-active.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-unmute-active.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-unmute-active.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-unmute-active.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-unmute-active.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-unmute-onhover-active.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-unmute-onhover-active.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/button-unmute-onhover-active.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/button-unmute-onhover-active.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-gaming-heart.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-gaming-heart.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-gaming-heart.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-gaming-heart.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-mother-goose.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-mother-goose.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-mother-goose.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-mother-goose.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-promo.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-promo.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-promo.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-promo.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-rainbow.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-rainbow.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-rainbow.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-rainbow.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-sesame.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-sesame.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-sesame.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-sesame.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-talking-tom.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-talking-tom.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-talking-tom.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-talking-tom.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-wonderquest.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-wonderquest.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-wonderquest.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-kids-wonderquest.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-loki.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-loki.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-loki.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-loki.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-multi-user.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-multi-user.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-multi-user.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-multi-user.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-tv-queue.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-tv-queue.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-tv-queue.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-tv-queue.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-yt-mix.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-yt-mix.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-yt-mix.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-end-screen-yt-mix.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-music-party-logo.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-music-party-logo.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-music-party-logo.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-music-party-logo.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-music.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-music.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-music.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-music.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-yt-logo.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-yt-logo.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-yt-logo.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/call-to-cast-yt-logo.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/cast_disconnected_blue.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/cast_disconnected_blue.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/cast_disconnected_blue.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/cast_disconnected_blue.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/circle.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/circle.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/circle.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/circle.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/default_bg.jpg b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/default_bg.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/default_bg.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/default_bg.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/dial-sprite.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/dial-sprite.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/dial-sprite.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/dial-sprite.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/edit_tile_dark.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/edit_tile_dark.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/edit_tile_dark.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/edit_tile_dark.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/edit_tile_light.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/edit_tile_light.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/edit_tile_light.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/edit_tile_light.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/flag.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/flag.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/flag.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/flag.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-aaa-engaged.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-aaa-engaged.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-aaa-engaged.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-aaa-engaged.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-feedback.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-feedback.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-feedback.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-feedback.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-fullscreen.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-fullscreen.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-fullscreen.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-fullscreen.png
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-help.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-help.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-help.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-help.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-mute.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-mute.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-mute.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-mute.png
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-playbutton.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-playbutton.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-playbutton.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-playbutton.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-check-dark.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-check-dark.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-check-dark.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-check-dark.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-check-light.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-check-light.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-check-light.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-check-light.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-private-dark.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-private-dark.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-private-dark.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-private-dark.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-private-light.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-private-light.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-private-light.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-private-light.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-public-dark.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-public-dark.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-public-dark.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-public-dark.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-public-light.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-public-light.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-public-light.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-public-light.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-unlisted-dark.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-unlisted-dark.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-unlisted-dark.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-unlisted-dark.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-unlisted-light.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-unlisted-light.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-unlisted-light.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-privacy-unlisted-light.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-unmute.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-unmute.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icon-unmute.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icon-unmute.png
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icons.ttf b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icons.ttf similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/icons.ttf rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/icons.ttf Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-bbb.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-bbb.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-bbb.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-bbb.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ccc.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ccc.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ccc.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ccc.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ddd.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ddd.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ddd.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ddd.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-desktop.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-desktop.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-desktop.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-desktop.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-eee.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-eee.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-eee.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-eee.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-fff.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-fff.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-fff.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-fff.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ggg.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ggg.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ggg.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/legend-sprite-ggg.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/motion-control-sprite.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/motion-control-sprite.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/motion-control-sprite.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/motion-control-sprite.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/pair_promo.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/pair_promo.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/pair_promo.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/pair_promo.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/pairing-promo-combo.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/pairing-promo-combo.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/pairing-promo-combo.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/pairing-promo-combo.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/player-pause.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/player-pause.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/player-pause.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/player-pause.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/player-play.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/player-play.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/player-play.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/player-play.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/trash_tile_dark.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/trash_tile_dark.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/trash_tile_dark.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/trash_tile_dark.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/trash_tile_light.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/trash_tile_light.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/trash_tile_light.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/trash_tile_light.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/volume_0_pressed.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/volume_0_pressed.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/volume_0_pressed.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/volume_0_pressed.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/volume_1_pressed.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/volume_1_pressed.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/volume_1_pressed.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/volume_1_pressed.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/volume_2_pressed.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/volume_2_pressed.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/volume_2_pressed.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/volume_2_pressed.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/volume_mute_pressed.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/volume_mute_pressed.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/volume_mute_pressed.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/volume_mute_pressed.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/yt-kids-logo.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/yt-kids-logo.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/yt-kids-logo.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/yt-kids-logo.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/yt-logo-fullcolor.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/yt-logo-fullcolor.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/yt-logo-fullcolor.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/yt-logo-fullcolor.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/yt-music-logo-small.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/yt-music-logo-small.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/yt-music-logo-small.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/yt-music-logo-small.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/yt-music-logo.png b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/yt-music-logo.png similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/f0d770f4/img/yt-music-logo.png rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/f0d770f4/img/yt-music-logo.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/misc/fonts/Roboto-Regular-20140804.ttf b/src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/misc/fonts/Roboto-Regular-20140804.ttf similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/www.youtube.com/s/tv/html5/misc/fonts/Roboto-Regular-20140804.ttf rename to src/cobalt/layout_tests/testdata/benchmarks/www.youtube.com/s/tv/html5/misc/fonts/Roboto-Regular-20140804.ttf Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/index.html b/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3-initial-layout.html similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/index.html rename to src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3-initial-layout.html
diff --git a/src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/yt3.ggpht.com/-dL2jeHlm2Ok/AAAAAAAAAAI/AAAAAAAAAAA/ZCMMkRj-hrw/s88-c-k-no/photo.jpg b/src/cobalt/layout_tests/testdata/benchmarks/yt3.ggpht.com/-dL2jeHlm2Ok/AAAAAAAAAAI/AAAAAAAAAAA/ZCMMkRj-hrw/s88-c-k-no/photo.jpg similarity index 100% rename from src/cobalt/layout_tests/testdata/benchmarks/youtube-2015-q3/yt3.ggpht.com/-dL2jeHlm2Ok/AAAAAAAAAAI/AAAAAAAAAAA/ZCMMkRj-hrw/s88-c-k-no/photo.jpg rename to src/cobalt/layout_tests/testdata/benchmarks/yt3.ggpht.com/-dL2jeHlm2Ok/AAAAAAAAAAI/AAAAAAAAAAA/ZCMMkRj-hrw/s88-c-k-no/photo.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt index ceee191..c5f7dbf 100644 --- a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt +++ b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
@@ -2,7 +2,6 @@ 100-nested-elements block-and-inline-block-display changing-css-text-triggers-layout -cobalt-oxide, file:///cobalt/browser/testdata/cobalt-oxide/cobalt-oxide.html console-trace-should-not-crash divs-with-background-color-and-text fixed-width-divs-with-background-color @@ -20,3 +19,4 @@ transform-with-background-color url-utils-interfaces user-agent-style-sheet-display +user-agent-test
diff --git a/src/cobalt/layout_tests/testdata/cobalt/repro-27290784.html b/src/cobalt/layout_tests/testdata/cobalt/repro-27290784.html index e0b2faa..4d8238c 100644 --- a/src/cobalt/layout_tests/testdata/cobalt/repro-27290784.html +++ b/src/cobalt/layout_tests/testdata/cobalt/repro-27290784.html
@@ -6,6 +6,9 @@ font-family: tcu-font; src: url(support/tcu-font.woff) } + body { + background-color: #FFFFFF; + } .test { font-size: 5em; font-family: tcu-font;
diff --git a/src/cobalt/layout_tests/testdata/cobalt/support/MEgalopolisExtra.woff2 b/src/cobalt/layout_tests/testdata/cobalt/support/MEgalopolisExtra.woff2 new file mode 100644 index 0000000..2d0def4 --- /dev/null +++ b/src/cobalt/layout_tests/testdata/cobalt/support/MEgalopolisExtra.woff2 Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/user-agent-test-expected.png b/src/cobalt/layout_tests/testdata/cobalt/user-agent-test-expected.png new file mode 100644 index 0000000..dec2811 --- /dev/null +++ b/src/cobalt/layout_tests/testdata/cobalt/user-agent-test-expected.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/user-agent-test.html b/src/cobalt/layout_tests/testdata/cobalt/user-agent-test.html new file mode 100644 index 0000000..a8fb7ec --- /dev/null +++ b/src/cobalt/layout_tests/testdata/cobalt/user-agent-test.html
@@ -0,0 +1,88 @@ +<!DOCTYPE html> +<!-- + | Tests that the platform user agent string will be accepted by one of the + | user agent regexes. +--> +<html> +<head> + <title>user-agent-test</title> + <style> + #result { + width: 100px; + height:100px; + background-color:#0047AB; + } + </style> +</head> +<body> + +<div id="result"></div> + +<script> + +function expect(condition, message) { + if (!condition) { + document.querySelector('#result').style.display = 'none'; + if (typeof message !== 'undefined') { + console.error(message); + } else { + console.error('!condition block reached in |expect|'); + } + } +} + +// This matches a device.py file in Google's internal code repository. +function generateUserAgentRegexes() { + const output = []; + const uaFieldChars = "-.A-Za-z0-9\\,_/()"; + + // Note that ?P<NAME> is used by python for named capture groups. JavaScript + // does not support named capture groups, so they are commented out. + // Additionally, all model names have been replaced with more general regexes. + // YTTV 2016: Operator_DeviceType_Chipset/Firmware (Brand, Model, Connection) + const string = "("/*?P<device>*/ + "(" + /*?P<operator>*/"[" + uaFieldChars + "'/]*)_" + + "("/*?P<type>*/ + "[" + uaFieldChars + "/]+)_" + + "("/*?P<chipset>*/ + "[" + uaFieldChars + "/_]+)) ?/ ?" + + "[" + uaFieldChars + "_]* " + + "\\(("/*?P<brand>*/ + "[" + uaFieldChars + "/_ ]+), ?("/*?P<model>*/ + "[^,]*), " + + "?[WIREDLSwiredls\\/]*\\)"; + output.push(string); + + // TTV 2013: Device/Firmware (Brand, Model, Connection) + output.push("("/*?P<device>*/ + "[-_.A-Za-z0-9\\/]+) ?/ ?[-_.A-Za-z0-9\\\\]* " + + "\\(("/*?P<brand>*/ + "[-_.A-Za-z0-9\\/ ]+), ?("/*?P<model>*/ + "[^,]*), " + + "?[WIREDLSwiredls\\/]*\\)"); + + // YTTV 2012: Vendor Device/Firmware (Model, SKU, Lang, Country) + output.push("("/*?P<brand>*/ + "[-_.A-Za-z0-9\\\\/]+) " + + "("/*?P<device>*/ + "[-_.A-Za-z0-9\\\\]+)/[-_.A-Za-z0-9\\\\/]* \\(("/*?P<model>*/ + "[^,]*), " + + "?[-_.A-Za-z0-9\\\\/]*,[^,]+,[^)]+\\)"); + + // (REMOVED) Spec: + output.push("("/*?P<device>*/ + "[a-zA-Z]+)/[0-9.]+ \\([^;]*; ?("/*?P<brand>*/ + "[^;]*); " + + "?("/*?P<model>*/ + "[^;]*);[^;]*;[^;]*;[^)]*\\)"); + + // (REMOVED) 2012: + output.push("("/*?P<brand>*/ + "[G-L]+) Browser/[0-9.]+\\([^;]*; [E-L]+; " + + "?("/*?P<model>*/ + "[^;]*);[^;]*;[^;]*;\\); [G-L]+ ("/*?P<device>*/ + "[-_.A-Za-z0-9\\\\/]+) "); + + // (REMOVED) + // Note: device is more of a firmware version, but model should feasibly always match. + output.push("("/*?P<brand>*/ + "[a-zA-Z]+)/("/*?P<model>*/ + "\\S+) \\(("/*?P<device>*/ + "[^)]+)\\)"); + + output.push("[.A-Za-z0-9\\\\/]+ [a-zA-Z]+/[.0-9]+ ("/*?P<brand>*/ + "[a-zA-Z]+)[T-V]+/[.0-9]+ " + + "model/("/*?P<model>*/ + "\\S+) ?[-_.A-Za-z0-9\\\\/]* build/[A-Za-z0-9]+"); + return output; +} + +expect( + generateUserAgentRegexes() + .map(regex => new RegExp(regex, 'g').test(navigator.userAgent)) + .some(value => value), + `userAgent string '${navigator.userAgent}' failed to match any acceptable regular expression` +); + +</script> + +</body> +</html>
diff --git a/src/cobalt/layout_tests/testdata/cobalt/woff2-decoding-expected.png b/src/cobalt/layout_tests/testdata/cobalt/woff2-decoding-expected.png new file mode 100644 index 0000000..bb5f747 --- /dev/null +++ b/src/cobalt/layout_tests/testdata/cobalt/woff2-decoding-expected.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/woff2-decoding.html b/src/cobalt/layout_tests/testdata/cobalt/woff2-decoding.html new file mode 100644 index 0000000..c386c57 --- /dev/null +++ b/src/cobalt/layout_tests/testdata/cobalt/woff2-decoding.html
@@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> +<style>@font-face { + font-family: MEgalopolisExtra; + src: url(support/MEgalopolisExtra.woff2) + } + body { + background-color: #FFFFFF; + } +.test { + font-size: 5em; + font-family: MEgalopolisExtra; +</style> +<script> + if (window.testRunner) { + window.testRunner.waitUntilDone(); + } + + // Wait for the font to load before taking a snapshot. + // TODO: This is very flaky, but we don't really have any other way to wait + // for a font to load. Because of this flakiness, we do not + // automatically run this test. + window.setTimeout(function () { + // First do a layout so that Cobalt knows the font is being used and that + // it should start loading it. + window.testRunner.DoNonMeasuredLayout(); + // Then, after waiting for the font to load for half of a second, go ahead + // and take a snapshot. + window.setTimeout(function () { + window.testRunner.notifyDone(); + }, 500); + }, 1); +</script> +</head> +<body> + <div class="test"> +Cobalt + </div> +</body> +</html>
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc index 473095a..50bbaaa 100644 --- a/src/cobalt/layout_tests/web_platform_tests.cc +++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -324,17 +324,26 @@ INSTANTIATE_TEST_CASE_P(cors, WebPlatformTest, ::testing::ValuesIn(EnumerateWebPlatformTests("cors"))); +// Temporarily disabled to allow V8 to start testing as the default JavaScript +// engine on linux/android/raspi. In order to get these to work we need to: +// * Decouple our fetch/streams polyfills from our promise polfill. +// * Fix ArrayBuffers (V8 optimizer is still getting tricked by these...) +#if 0 INSTANTIATE_TEST_CASE_P( fetch, WebPlatformTest, ::testing::ValuesIn(EnumerateWebPlatformTests("fetch", "'fetch' in this"))); +#endif INSTANTIATE_TEST_CASE_P( mediasession, WebPlatformTest, ::testing::ValuesIn(EnumerateWebPlatformTests("mediasession"))); +// Disabled for the same reason as fetch. +#if 0 INSTANTIATE_TEST_CASE_P(streams, WebPlatformTest, ::testing::ValuesIn(EnumerateWebPlatformTests( "streams", "'ReadableStream' in this"))); +#endif INSTANTIATE_TEST_CASE_P( cobalt_special, WebPlatformTest,
diff --git a/src/cobalt/loader/fetcher_factory.cc b/src/cobalt/loader/fetcher_factory.cc index d582d15..e29e061 100644 --- a/src/cobalt/loader/fetcher_factory.cc +++ b/src/cobalt/loader/fetcher_factory.cc
@@ -39,7 +39,6 @@ const char kAboutScheme[] = "about"; #endif -#if defined(COBALT_ENABLE_FILE_SCHEME) bool FileURLToFilePath(const GURL& url, FilePath* file_path) { DCHECK(url.is_valid() && url.SchemeIsFile()); std::string path = url.path(); @@ -48,7 +47,6 @@ *file_path = FilePath(path); return !file_path->empty(); } -#endif std::string ClipUrl(const GURL& url, size_t length) { const std::string& spec = url.possibly_invalid_spec(); @@ -56,7 +54,11 @@ return spec; } - return spec.substr(0, length - 5) + "[...]"; + size_t remain = length - 5; + size_t head = remain / 2; + size_t tail = remain - head; + + return spec.substr(0, head) + "[...]" + spec.substr(spec.size() - tail); } } // namespace @@ -100,7 +102,7 @@ scoped_ptr<Fetcher> FetcherFactory::CreateSecureFetcher( const GURL& url, const csp::SecurityCallback& url_security_callback, RequestMode request_mode, const Origin& origin, Fetcher::Handler* handler) { - DLOG(INFO) << "Fetching: " << ClipUrl(url, 60); + DLOG(INFO) << "Fetching: " << ClipUrl(url, 80); if (!url.is_valid()) { std::stringstream error_message; @@ -135,7 +137,6 @@ read_cache_callback_)); } -#if defined(COBALT_ENABLE_FILE_SCHEME) if (url.SchemeIsFile()) { FilePath file_path; if (!FileURLToFilePath(url, &file_path)) { @@ -150,7 +151,6 @@ options.extra_search_dir = extra_search_dir_; return scoped_ptr<Fetcher>(new FileFetcher(file_path, handler, options)); } -#endif #if defined(ENABLE_ABOUT_SCHEME) if (url.SchemeIs(kAboutScheme)) {
diff --git a/src/cobalt/loader/file_fetcher.cc b/src/cobalt/loader/file_fetcher.cc index 225b1a1..b932f56 100644 --- a/src/cobalt/loader/file_fetcher.cc +++ b/src/cobalt/loader/file_fetcher.cc
@@ -70,11 +70,11 @@ PathService::Get(paths::DIR_COBALT_WEB_ROOT, &search_dir); search_path_.push_back(search_dir); -// We also search DIR_SOURCE_ROOT in non-release builds. -#if defined(ENABLE_DIR_SOURCE_ROOT_ACCESS) - PathService::Get(base::DIR_SOURCE_ROOT, &search_dir); +// We also search DIR_TEST_DATA in non-release builds. +#if defined(ENABLE_TEST_DATA) + PathService::Get(base::DIR_TEST_DATA, &search_dir); search_path_.push_back(search_dir); -#endif // ENABLE_DIR_SOURCE_ROOT_ACCESS +#endif // ENABLE_TEST_DATA } void FileFetcher::TryFileOpen() { @@ -150,7 +150,6 @@ case base::PLATFORM_FILE_ERROR_INVALID_OPERATION: case base::PLATFORM_FILE_ERROR_NOT_EMPTY: case base::PLATFORM_FILE_ERROR_MAX: - default: break; } return kPlatformFileErrorNotDefined;
diff --git a/src/cobalt/loader/file_fetcher_test.cc b/src/cobalt/loader/file_fetcher_test.cc index e7ca746..6de666e 100644 --- a/src/cobalt/loader/file_fetcher_test.cc +++ b/src/cobalt/loader/file_fetcher_test.cc
@@ -38,7 +38,7 @@ ~FileFetcherTest() override {} FilePath data_dir_; - FilePath dir_source_root_; + FilePath dir_test_data_; MessageLoop message_loop_; scoped_ptr<FileFetcher> file_fetcher_; }; @@ -47,7 +47,7 @@ data_dir_ = data_dir_.Append(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("loader")) .Append(FILE_PATH_LITERAL("testdata")); - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &dir_source_root_)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &dir_test_data_)); } TEST_F(FileFetcherTest, NonExisitingPath) { @@ -112,7 +112,7 @@ std::string loaded_text = fetcher_handler_mock.data(); std::string expected_text; - EXPECT_TRUE(file_util::ReadFileToString(dir_source_root_.Append(file_path), + EXPECT_TRUE(file_util::ReadFileToString(dir_test_data_.Append(file_path), &expected_text)); EXPECT_EQ(expected_text, loaded_text); @@ -146,7 +146,7 @@ std::string loaded_text = fetcher_handler_mock.data(); std::string expected_text; - EXPECT_TRUE(file_util::ReadFileToString(dir_source_root_.Append(file_path), + EXPECT_TRUE(file_util::ReadFileToString(dir_test_data_.Append(file_path), &expected_text)); expected_text = expected_text.substr(kStartOffset); EXPECT_EQ(expected_text, loaded_text); @@ -183,7 +183,7 @@ std::string loaded_text = fetcher_handler_mock.data(); std::string expected_text; - EXPECT_TRUE(file_util::ReadFileToString(dir_source_root_.Append(file_path), + EXPECT_TRUE(file_util::ReadFileToString(dir_test_data_.Append(file_path), &expected_text)); expected_text = expected_text.substr(kStartOffset, kBytesToRead); EXPECT_EQ(expected_text, loaded_text);
diff --git a/src/cobalt/loader/font/typeface_decoder_test.cc b/src/cobalt/loader/font/typeface_decoder_test.cc index 7ece0f5..dd29b3b 100644 --- a/src/cobalt/loader/font/typeface_decoder_test.cc +++ b/src/cobalt/loader/font/typeface_decoder_test.cc
@@ -32,6 +32,7 @@ const char kTtfTestTypeface[] = "icons.ttf"; const char kWoffTestTypeface[] = "icons.woff"; +const char kWoff2TestTypeface[] = "icons.woff2"; struct MockTypefaceDecoderCallback { void SuccessCallback(const scoped_refptr<render_tree::Typeface>& value) { @@ -96,7 +97,7 @@ FilePath GetTestTypefacePath(const char* file_name) { FilePath data_directory; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_directory)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &data_directory)); return data_directory.Append(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("loader")) .Append(FILE_PATH_LITERAL("testdata")) @@ -187,6 +188,37 @@ EXPECT_TRUE(typeface_decoder.Typeface()); } +// Test that we can decode a woff2 typeface received in one chunk. +TEST(TypefaceDecoderTest, DecodeWoff2Typeface) { + MockTypefaceDecoder typeface_decoder; + + std::vector<uint8> typeface_data = + GetTypefaceData(GetTestTypefacePath(kWoff2TestTypeface)); + typeface_decoder.DecodeChunk(reinterpret_cast<char*>(&typeface_data[0]), + typeface_data.size()); + typeface_decoder.Finish(); + + EXPECT_TRUE(typeface_decoder.Typeface()); +} + +// Test that we can decode a woff2 typeface received in multiple chunks. +TEST(TypefaceDecoderTest, DecodeWoff2TypefaceWithMultipleChunks) { + MockTypefaceDecoder typeface_decoder; + + std::vector<uint8> typeface_data = + GetTypefaceData(GetTestTypefacePath(kWoff2TestTypeface)); + typeface_decoder.DecodeChunk(reinterpret_cast<char*>(&typeface_data[0]), 4); + typeface_decoder.DecodeChunk(reinterpret_cast<char*>(&typeface_data[4]), 2); + typeface_decoder.DecodeChunk(reinterpret_cast<char*>(&typeface_data[6]), 94); + typeface_decoder.DecodeChunk(reinterpret_cast<char*>(&typeface_data[100]), + 100); + typeface_decoder.DecodeChunk(reinterpret_cast<char*>(&typeface_data[200]), + typeface_data.size() - 200); + typeface_decoder.Finish(); + + EXPECT_TRUE(typeface_decoder.Typeface()); +} + // Test that decoding a too large typeface is handled gracefully and does not // crash // or return a typeface.
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc index 52a5f6a..0d12b57 100644 --- a/src/cobalt/loader/image/image_decoder.cc +++ b/src/cobalt/loader/image/image_decoder.cc
@@ -254,8 +254,7 @@ } break; case kNotApplicable: case kUnsupportedImageFormat: - case kSuspended: - default: { + case kSuspended: { // Do not attempt to continue processing data. DCHECK(!decoder_); } break;
diff --git a/src/cobalt/loader/image/image_decoder_test.cc b/src/cobalt/loader/image/image_decoder_test.cc index 23d6c20..6a03e61 100644 --- a/src/cobalt/loader/image/image_decoder_test.cc +++ b/src/cobalt/loader/image/image_decoder_test.cc
@@ -105,7 +105,7 @@ FilePath GetTestImagePath(const char* file_name) { FilePath data_directory; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_directory)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &data_directory)); return data_directory.Append(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("loader")) .Append(FILE_PATH_LITERAL("testdata"))
diff --git a/src/cobalt/loader/image/sandbox/image_decoder_sandbox.cc b/src/cobalt/loader/image/sandbox/image_decoder_sandbox.cc index 414c42d..8e86d21 100644 --- a/src/cobalt/loader/image/sandbox/image_decoder_sandbox.cc +++ b/src/cobalt/loader/image/sandbox/image_decoder_sandbox.cc
@@ -52,7 +52,7 @@ std::vector<FilePath> GetImagePaths(const char* extention) { FilePath image_path; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &image_path)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &image_path)); image_path = image_path.Append(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("loader")) .Append(FILE_PATH_LITERAL("testdata"));
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp index 0a2931a..a7e4105 100644 --- a/src/cobalt/loader/loader.gyp +++ b/src/cobalt/loader/loader.gyp
@@ -144,6 +144,7 @@ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests', '<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/ots/ots.gyp:ots', 'loader', 'loader_copy_test_data', '<@(cobalt_platform_dependencies)', @@ -165,18 +166,13 @@ { 'target_name': 'loader_copy_test_data', 'type': 'none', - 'actions': [ - { - 'action_name': 'loader_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/loader/testdata/', - ], - 'output_dir': 'cobalt/loader/testdata/', - }, - 'includes': [ '../build/copy_test_data.gypi' ], - }, - ], + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/cobalt/loader/testdata/', + ], + 'content_test_output_subdir': 'cobalt/loader/testdata/', + }, + 'includes': [ '<(DEPTH)/starboard/build/copy_test_data.gypi' ], }, {
diff --git a/src/cobalt/loader/loader_test.cc b/src/cobalt/loader/loader_test.cc index 8f6fdd4..a5e58c9 100644 --- a/src/cobalt/loader/loader_test.cc +++ b/src/cobalt/loader/loader_test.cc
@@ -16,6 +16,7 @@ #include "base/bind.h" #include "base/file_util.h" +#include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/run_loop.h" @@ -41,8 +42,8 @@ public: explicit TextDecoderCallback(base::RunLoop* run_loop) : run_loop_(run_loop) {} - void OnDone(const std::string& text, const Origin&) { - text_ = text; + void OnDone(const Origin&, scoped_ptr<std::string> text) { + text_ = *text; MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure()); } @@ -252,9 +253,9 @@ // Compare the result with that of other API's. std::string expected_text; - FilePath dir_source_root; - EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &dir_source_root)); - EXPECT_TRUE(file_util::ReadFileToString(dir_source_root.Append(file_path), + FilePath dir_test_data; + EXPECT_TRUE(PathService::Get(base::DIR_TEST_DATA, &dir_test_data)); + EXPECT_TRUE(file_util::ReadFileToString(dir_test_data.Append(file_path), &expected_text)); EXPECT_EQ(expected_text, loaded_text); }
diff --git a/src/cobalt/loader/mesh/mesh_decoder_test.cc b/src/cobalt/loader/mesh/mesh_decoder_test.cc index 746cf2e..34bec5c 100644 --- a/src/cobalt/loader/mesh/mesh_decoder_test.cc +++ b/src/cobalt/loader/mesh/mesh_decoder_test.cc
@@ -95,7 +95,7 @@ FilePath GetTestMeshPath(const char* file_name) { FilePath data_directory; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_directory)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &data_directory)); return data_directory.Append(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("loader")) .Append(FILE_PATH_LITERAL("testdata"))
diff --git a/src/cobalt/loader/net_fetcher.cc b/src/cobalt/loader/net_fetcher.cc index 27e37fe..ca3481f 100644 --- a/src/cobalt/loader/net_fetcher.cc +++ b/src/cobalt/loader/net_fetcher.cc
@@ -29,16 +29,6 @@ #endif // SB_HAS(CORE_DUMP_HANDLER_SUPPORT) #endif // OS_STARBOARD -#if defined(COBALT_ENABLE_XHR_HEADER_FILTERING) - -namespace cobalt { -namespace loader { -std::string CobaltFetchMaybeAddHeader(const GURL& url); -} // namespace loader -} // namespace cobalt - -#endif // defined(COBALT_ENABLE_XHR_HEADER_FILTERING) - namespace cobalt { namespace loader { @@ -109,13 +99,6 @@ url_fetcher_->SetLoadFlags(kDisableCookiesLoadFlags); } -#if defined(COBALT_ENABLE_XHR_HEADER_FILTERING) - std::string added_header = CobaltFetchMaybeAddHeader(url); - if (!added_header.empty()) { - url_fetcher_->AddExtraRequestHeader(added_header); - } -#endif - // Delay the actual start until this function is complete. Otherwise we might // call handler's callbacks at an unexpected time- e.g. receiving OnError() // while a loader is still being constructed.
diff --git a/src/cobalt/loader/resource_cache.h b/src/cobalt/loader/resource_cache.h index 7453c03..6185752 100644 --- a/src/cobalt/loader/resource_cache.h +++ b/src/cobalt/loader/resource_cache.h
@@ -592,12 +592,13 @@ base::CVal<base::cval::SizeInBytes, base::CValPublic> memory_size_in_bytes_; base::CVal<base::cval::SizeInBytes, base::CValPublic> memory_capacity_in_bytes_; - base::CVal<base::cval::SizeInBytes> memory_resources_loaded_in_bytes_; + base::CVal<base::cval::SizeInBytes, base::CValPublic> + memory_resources_loaded_in_bytes_; - base::CVal<int> count_resources_requested_; - base::CVal<int> count_resources_loading_; - base::CVal<int> count_resources_loaded_; - base::CVal<int> count_pending_callbacks_; + base::CVal<int, base::CValPublic> count_resources_requested_; + base::CVal<int, base::CValPublic> count_resources_loading_; + base::CVal<int, base::CValPublic> count_resources_loaded_; + base::CVal<int, base::CValPublic> count_pending_callbacks_; DISALLOW_COPY_AND_ASSIGN(ResourceCache); };
diff --git a/src/cobalt/loader/testdata/icons.woff2 b/src/cobalt/loader/testdata/icons.woff2 new file mode 100644 index 0000000..3f887d9 --- /dev/null +++ b/src/cobalt/loader/testdata/icons.woff2 Binary files differ
diff --git a/src/cobalt/loader/text_decoder.h b/src/cobalt/loader/text_decoder.h index f055dec..bbb24a2 100644 --- a/src/cobalt/loader/text_decoder.h +++ b/src/cobalt/loader/text_decoder.h
@@ -17,6 +17,7 @@ #include <algorithm> #include <string> +#include <utility> #include "base/callback.h" #include "base/compiler_specific.h" @@ -34,14 +35,14 @@ class TextDecoder : public Decoder { public: explicit TextDecoder( - base::Callback<void(const std::string&, const loader::Origin&)> + base::Callback<void(const loader::Origin&, scoped_ptr<std::string>)> done_callback) : done_callback_(done_callback), suspended_(false) {} ~TextDecoder() override {} // This function is used for binding callback for creating TextDecoder. static scoped_ptr<Decoder> Create( - base::Callback<void(const std::string&, const loader::Origin&)> + base::Callback<void(const loader::Origin&, scoped_ptr<std::string>)> done_callback) { return scoped_ptr<Decoder>(new TextDecoder(done_callback)); } @@ -52,7 +53,10 @@ if (suspended_) { return; } - text_.append(data, size); + if (!text_) { + text_.reset(new std::string); + } + text_->append(data, size); } void DecodeChunkPassed(scoped_ptr<std::string> data) override { @@ -62,10 +66,10 @@ return; } - if (text_.empty()) { - std::swap(*data, text_); + if (!text_) { + text_ = data.Pass(); } else { - text_.append(*data); + text_->append(*data); } } @@ -74,11 +78,14 @@ if (suspended_) { return; } - done_callback_.Run(text_, last_url_origin_); + if (!text_) { + text_.reset(new std::string); + } + done_callback_.Run(last_url_origin_, text_.Pass()); } bool Suspend() override { suspended_ = true; - text_.clear(); + text_.reset(); return true; } void Resume(render_tree::ResourceProvider* /*resource_provider*/) override { @@ -90,11 +97,11 @@ private: base::ThreadChecker thread_checker_; - std::string text_; - base::Callback<void(const std::string&, const loader::Origin&)> + base::Callback<void(const loader::Origin&, scoped_ptr<std::string>)> done_callback_; - bool suspended_; loader::Origin last_url_origin_; + scoped_ptr<std::string> text_; + bool suspended_; }; } // namespace loader
diff --git a/src/cobalt/loader/text_decoder_test.cc b/src/cobalt/loader/text_decoder_test.cc index b6ddb19..0e6866f 100644 --- a/src/cobalt/loader/text_decoder_test.cc +++ b/src/cobalt/loader/text_decoder_test.cc
@@ -15,6 +15,7 @@ #include "cobalt/loader/text_decoder.h" #include "base/bind.h" +#include "base/memory/scoped_ptr.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,8 +24,8 @@ namespace { struct TextDecoderCallback { - void Callback(const std::string& value, const Origin& last_url_origin) { - text = value; + void Callback(const Origin& last_url_origin, scoped_ptr<std::string> value) { + text = *value; last_url_origin_ = last_url_origin; } std::string text;
diff --git a/src/cobalt/loader/unencoded_data_urls/lozenge_1080.png b/src/cobalt/loader/unencoded_data_urls/lozenge_1080.png deleted file mode 100644 index 1e131c4..0000000 --- a/src/cobalt/loader/unencoded_data_urls/lozenge_1080.png +++ /dev/null Binary files differ
diff --git a/src/cobalt/loader/unencoded_data_urls/lozenge_2160.png b/src/cobalt/loader/unencoded_data_urls/lozenge_2160.png deleted file mode 100644 index 49f2670..0000000 --- a/src/cobalt/loader/unencoded_data_urls/lozenge_2160.png +++ /dev/null Binary files differ
diff --git a/src/cobalt/loader/unencoded_data_urls/lozenge_720.png b/src/cobalt/loader/unencoded_data_urls/lozenge_720.png deleted file mode 100644 index 1a10d3f..0000000 --- a/src/cobalt/loader/unencoded_data_urls/lozenge_720.png +++ /dev/null Binary files differ
diff --git a/src/cobalt/loader/unencoded_data_urls/wordmark_1080.png b/src/cobalt/loader/unencoded_data_urls/wordmark_1080.png deleted file mode 100644 index 1c153f1..0000000 --- a/src/cobalt/loader/unencoded_data_urls/wordmark_1080.png +++ /dev/null Binary files differ
diff --git a/src/cobalt/loader/unencoded_data_urls/wordmark_2160.png b/src/cobalt/loader/unencoded_data_urls/wordmark_2160.png deleted file mode 100644 index 3d106c5..0000000 --- a/src/cobalt/loader/unencoded_data_urls/wordmark_2160.png +++ /dev/null Binary files differ
diff --git a/src/cobalt/loader/unencoded_data_urls/wordmark_720.png b/src/cobalt/loader/unencoded_data_urls/wordmark_720.png deleted file mode 100644 index 7fcb9f1..0000000 --- a/src/cobalt/loader/unencoded_data_urls/wordmark_720.png +++ /dev/null Binary files differ
diff --git a/src/cobalt/media/base/drm_system.cc b/src/cobalt/media/base/drm_system.cc index a5f2457..8ecc2a3 100644 --- a/src/cobalt/media/base/drm_system.cc +++ b/src/cobalt/media/base/drm_system.cc
@@ -20,19 +20,32 @@ namespace cobalt { namespace media { -#if SB_HAS(DRM_KEY_STATUSES) DrmSystem::Session::Session( - DrmSystem* drm_system, - SessionUpdateKeyStatusesCallback update_key_statuses_callback) - : drm_system_(drm_system), - update_key_statuses_callback_(update_key_statuses_callback), - closed_(false) { - DCHECK(!update_key_statuses_callback_.is_null()); -} -#else // SB_HAS(DRM_KEY_STATUSES) -DrmSystem::Session::Session(DrmSystem* drm_system) - : drm_system_(drm_system), closed_(false) {} + DrmSystem* drm_system +#if SB_HAS(DRM_KEY_STATUSES) + , + SessionUpdateKeyStatusesCallback update_key_statuses_callback +#if SB_HAS(DRM_SESSION_CLOSED) + , + SessionClosedCallback session_closed_callback +#endif // SB_HAS(DRM_SESSION_CLOSED) #endif // SB_HAS(DRM_KEY_STATUSES) + ) + : drm_system_(drm_system), +#if SB_HAS(DRM_KEY_STATUSES) + update_key_statuses_callback_(update_key_statuses_callback), +#if SB_HAS(DRM_SESSION_CLOSED) + session_closed_callback_(session_closed_callback), +#endif // SB_HAS(DRM_SESSION_CLOSED) +#endif // SB_HAS(DRM_KEY_STATUSES) + closed_(false) { +#if SB_HAS(DRM_KEY_STATUSES) + DCHECK(!update_key_statuses_callback_.is_null()); +#if SB_HAS(DRM_SESSION_CLOSED) + DCHECK(!session_closed_callback_.is_null()); +#endif // SB_HAS(DRM_SESSION_CLOSED) +#endif // SB_HAS(DRM_KEY_STATUSES) +} DrmSystem::Session::~Session() { if (id_ && !closed_) { @@ -80,6 +93,10 @@ #if SB_HAS(DRM_KEY_STATUSES) , OnSessionKeyStatusesChangedFunc +#if SB_HAS(DRM_SESSION_CLOSED) + , + OnSessionClosedFunc +#endif // SB_HAS(DRM_SESSION_CLOSED) #endif // SB_HAS(DRM_KEY_STATUSES) )), // NOLINT(whitespace/parens) message_loop_(MessageLoop::current()), @@ -92,17 +109,26 @@ DrmSystem::~DrmSystem() { SbDrmDestroySystem(wrapped_drm_system_); } -#if SB_HAS(DRM_KEY_STATUSES) scoped_ptr<DrmSystem::Session> DrmSystem::CreateSession( - SessionUpdateKeyStatusesCallback session_update_key_statuses_callback) { - return make_scoped_ptr( - new Session(this, session_update_key_statuses_callback)); -} -#else // SB_HAS(DRM_KEY_STATUSES) -scoped_ptr<DrmSystem::Session> DrmSystem::CreateSession() { - return make_scoped_ptr(new Session(this)); -} +#if SB_HAS(DRM_KEY_STATUSES) + SessionUpdateKeyStatusesCallback session_update_key_statuses_callback +#if SB_HAS(DRM_SESSION_CLOSED) + , + SessionClosedCallback session_closed_callback +#endif // SB_HAS(DRM_SESSION_CLOSED) #endif // SB_HAS(DRM_KEY_STATUSES) + ) { // NOLINT(whitespace/parens) + return make_scoped_ptr(new Session(this +#if SB_HAS(DRM_KEY_STATUSES) + , + session_update_key_statuses_callback +#if SB_HAS(DRM_SESSION_CLOSED) + , + session_closed_callback +#endif // SB_HAS(DRM_SESSION_CLOSED) +#endif // SB_HAS(DRM_KEY_STATUSES) + )); // NOLINT(whitespace/parens) +} void DrmSystem::GenerateSessionUpdateRequest( Session* session, const std::string& type, const uint8_t* init_data, @@ -243,6 +269,22 @@ } #endif // SB_HAS(DRM_KEY_STATUSES) +#if SB_HAS(DRM_SESSION_CLOSED) +void DrmSystem::OnSessionClosed(const std::string& session_id) { + // Find the session by ID. + IdToSessionMap::iterator session_iterator = + id_to_session_map_.find(session_id); + if (session_iterator == id_to_session_map_.end()) { + LOG(ERROR) << "Unknown session id: " << session_id << "."; + return; + } + Session* session = session_iterator->second; + + session->session_closed_callback().Run(); + id_to_session_map_.erase(session_iterator); +} +#endif // SB_HAS(DRM_SESSION_CLOSED) + // static void DrmSystem::OnSessionUpdateRequestGeneratedFunc( SbDrmSystem wrapped_drm_system, void* context, int ticket, @@ -317,5 +359,26 @@ } #endif // SB_HAS(DRM_KEY_STATUSES) +#if SB_HAS(DRM_SESSION_CLOSED) +// static +void DrmSystem::OnSessionClosedFunc(SbDrmSystem wrapped_drm_system, + void* context, const void* session_id, + int session_id_size) { + DCHECK(context); + DrmSystem* drm_system = static_cast<DrmSystem*>(context); + DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_); + + DCHECK(session_id != NULL); + + std::string session_id_copy = + std::string(static_cast<const char*>(session_id), + static_cast<const char*>(session_id) + session_id_size); + + drm_system->message_loop_->PostTask( + FROM_HERE, base::Bind(&DrmSystem::OnSessionClosed, drm_system->weak_this_, + session_id_copy)); +} +#endif // SB_HAS(DRM_SESSION_CLOSED) + } // namespace media } // namespace cobalt
diff --git a/src/cobalt/media/base/drm_system.h b/src/cobalt/media/base/drm_system.h index 6eea0af..b1c85af 100644 --- a/src/cobalt/media/base/drm_system.h +++ b/src/cobalt/media/base/drm_system.h
@@ -45,6 +45,9 @@ const std::vector<SbDrmKeyStatus>& key_statuses)> SessionUpdateKeyStatusesCallback; #endif // SB_HAS(DRM_KEY_STATUSES) +#if SB_HAS(DRM_SESSION_CLOSED) + typedef base::Callback<void()> SessionClosedCallback; +#endif // SB_HAS(DRM_SESSION_CLOSED) // Flyweight that provides RAII semantics for sessions. // Most of logic is implemented by |DrmSystem| and thus sessions must be @@ -93,6 +96,10 @@ , SessionUpdateKeyStatusesCallback update_key_statuses_callback #endif // SB_HAS(DRM_KEY_STATUSES) +#if SB_HAS(DRM_SESSION_CLOSED) + , + SessionClosedCallback session_closed_callback +#endif // SB_HAS(SESSION_CLOSED) ); // NOLINT(whitespace/parens) void set_id(const std::string& id) { id_ = id; } const SessionUpdateRequestGeneratedCallback& @@ -105,15 +112,23 @@ return update_key_statuses_callback_; } #endif // SB_HAS(DRM_KEY_STATUSES) +#if SB_HAS(DRM_SESSION_CLOSED) + const SessionClosedCallback& session_closed_callback() const { + return session_closed_callback_; + } +#endif // SB_HAS(DRM_SESSION_CLOSED) DrmSystem* const drm_system_; +#if SB_HAS(DRM_KEY_STATUSES) + SessionUpdateKeyStatusesCallback update_key_statuses_callback_; +#endif // SB_HAS(DRM_KEY_STATUSES) +#if SB_HAS(DRM_SESSION_CLOSED) + SessionClosedCallback session_closed_callback_; +#endif // SB_HAS(DRM_SESSION_CLOSED) bool closed_; base::optional<std::string> id_; // Supports spontaneous invocations of |SbDrmSessionUpdateRequestFunc|. SessionUpdateRequestGeneratedCallback update_request_generated_callback_; -#if SB_HAS(DRM_KEY_STATUSES) - SessionUpdateKeyStatusesCallback update_key_statuses_callback_; -#endif // SB_HAS(DRM_KEY_STATUSES) friend class DrmSystem; @@ -129,6 +144,10 @@ #if SB_HAS(DRM_KEY_STATUSES) SessionUpdateKeyStatusesCallback session_update_key_statuses_callback #endif // SB_HAS(DRM_KEY_STATUSES) +#if SB_HAS(DRM_SESSION_CLOSED) + , + SessionClosedCallback session_closed_callback +#endif // SB_HAS(DRM_SESSION_CLOSED) ); // NOLINT(whitespace/parens) private: @@ -174,7 +193,9 @@ const std::string& session_id, const std::vector<std::string>& key_ids, const std::vector<SbDrmKeyStatus>& key_statuses); #endif // SB_HAS(DRM_KEY_STATUSES) - +#if SB_HAS(DRM_SESSION_CLOSED) + void OnSessionClosed(const std::string& session_id); +#endif // SB_HAS(DRM_SESSION_CLOSED) // Called on any thread, parameters need to be copied immediately. static void OnSessionUpdateRequestGeneratedFunc( SbDrmSystem wrapped_drm_system, void* context, int ticket, @@ -191,6 +212,12 @@ const SbDrmKeyStatus* key_statuses); #endif // SB_HAS(DRM_KEY_STATUSES) +#if SB_HAS(DRM_SESSION_CLOSED) + static void OnSessionClosedFunc(SbDrmSystem wrapped_drm_system, + void* context, + const void* session_id, + int session_id_size); +#endif // SB_HAS(DRM_SESSION_CLOSED) const SbDrmSystem wrapped_drm_system_; MessageLoop* const message_loop_;
diff --git a/src/cobalt/media/base/mock_media_log.h b/src/cobalt/media/base/mock_media_log.h index 2205e15..c17c40d 100644 --- a/src/cobalt/media/base/mock_media_log.h +++ b/src/cobalt/media/base/mock_media_log.h
@@ -32,7 +32,7 @@ // Trampoline method to workaround GMOCK problems with std::unique_ptr<>. // Also simplifies tests to be able to string match on the log string // representation on the added event. - void AddEvent(std::unique_ptr<MediaLogEvent> event) override { + void AddEvent(scoped_ptr<MediaLogEvent> event) override { DoAddEventLogString(MediaEventToLogString(*event)); }
diff --git a/src/cobalt/media/base/pipeline.h b/src/cobalt/media/base/pipeline.h index 4dd622f..5530b30 100644 --- a/src/cobalt/media/base/pipeline.h +++ b/src/cobalt/media/base/pipeline.h
@@ -21,7 +21,6 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/message_loop_proxy.h" -#include "base/time.h" #include "cobalt/media/base/demuxer.h" #include "cobalt/media/base/media_export.h" #include "cobalt/media/base/pipeline_status.h" @@ -45,6 +44,8 @@ // Callback to set an DrmSystemReadyCB. typedef base::Callback<void(const DrmSystemReadyCB&)> SetDrmSystemReadyCB; +typedef base::Callback<void(PipelineStatus, const std::string&)> ErrorCB; + // Pipeline contains the common interface for media pipelines. It provides // functions to perform asynchronous initialization, pausing, seeking and // playing. @@ -109,8 +110,7 @@ encrypted_media_init_data_encountered_cb, const std::string& source_url, #endif // SB_HAS(PLAYER_WITH_URL) - const PipelineStatusCB& ended_cb, - const PipelineStatusCB& error_cb, + const PipelineStatusCB& ended_cb, const ErrorCB& error_cb, const PipelineStatusCB& seek_cb, const BufferingStateCB& buffering_state_cb, const base::Closure& duration_change_cb,
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc index 85fbc6b..9e6741b 100644 --- a/src/cobalt/media/base/sbplayer_pipeline.cc +++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -57,7 +57,7 @@ Demuxer* demuxer; SetDrmSystemReadyCB set_drm_system_ready_cb; PipelineStatusCB ended_cb; - PipelineStatusCB error_cb; + ErrorCB error_cb; PipelineStatusCB seek_cb; Pipeline::BufferingStateCB buffering_state_cb; base::Closure duration_change_cb; @@ -90,7 +90,7 @@ on_encrypted_media_init_data_encountered_cb, const std::string& source_url, #endif // SB_HAS(PLAYER_WITH_URL) - const PipelineStatusCB& ended_cb, const PipelineStatusCB& error_cb, + const PipelineStatusCB& ended_cb, const ErrorCB& error_cb, const PipelineStatusCB& seek_cb, const BufferingStateCB& buffering_state_cb, const base::Closure& duration_change_cb, @@ -153,6 +153,9 @@ void OnNeedData(DemuxerStream::Type type) override; #endif // !SB_HAS(PLAYER_WITH_URL) void OnPlayerStatus(SbPlayerState state) override; +#if SB_HAS(PLAYER_ERROR_MESSAGE) + void OnPlayerError(SbPlayerError error, const std::string& message) override; +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) void UpdateDecoderConfig(DemuxerStream* stream); @@ -207,7 +210,7 @@ // Permanent callbacks passed in via Start(). SetDrmSystemReadyCB set_drm_system_ready_cb_; PipelineStatusCB ended_cb_; - PipelineStatusCB error_cb_; + ErrorCB error_cb_; BufferingStateCB buffering_state_cb_; base::Closure duration_change_cb_; base::Closure output_mode_change_cb_; @@ -318,18 +321,20 @@ } #endif // SB_HAS(PLAYER_WITH_URL) -void SbPlayerPipeline::Start( - Demuxer* demuxer, const SetDrmSystemReadyCB& set_drm_system_ready_cb, +void SbPlayerPipeline::Start(Demuxer* demuxer, + const SetDrmSystemReadyCB& set_drm_system_ready_cb, #if SB_HAS(PLAYER_WITH_URL) - const OnEncryptedMediaInitDataEncounteredCB& - on_encrypted_media_init_data_encountered_cb, - const std::string& source_url, + const OnEncryptedMediaInitDataEncounteredCB& + on_encrypted_media_init_data_encountered_cb, + const std::string& source_url, #endif // SB_HAS(PLAYER_WITH_URL) - const PipelineStatusCB& ended_cb, const PipelineStatusCB& error_cb, - const PipelineStatusCB& seek_cb, const BufferingStateCB& buffering_state_cb, - const base::Closure& duration_change_cb, - const base::Closure& output_mode_change_cb, - const base::Closure& content_size_change_cb) { + const PipelineStatusCB& ended_cb, + const ErrorCB& error_cb, + const PipelineStatusCB& seek_cb, + const BufferingStateCB& buffering_state_cb, + const base::Closure& duration_change_cb, + const base::Closure& output_mode_change_cb, + const base::Closure& content_size_change_cb) { TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::Start"); DCHECK(!ended_cb.is_null()); @@ -439,7 +444,7 @@ } #if SB_HAS(PLAYER_WITH_URL) player_->Seek(seek_time_); -#else // SB_HAS(PLAYER_WITH_URL) +#else // SB_HAS(PLAYER_WITH_URL) demuxer_->Seek(time, BindToCurrentLoop(base::Bind( &SbPlayerPipeline::OnDemuxerSeeked, this))); #endif // SB_HAS(PLAYER_WITH_URL) @@ -506,7 +511,7 @@ natural_size_ = gfx::Size(frame_width, frame_height); content_size_change_cb_.Run(); } -#else // SB_HAS(PLAYER_WITH_URL) +#else // SB_HAS(PLAYER_WITH_URL) player_->GetInfo(&statistics_.video_frames_decoded, &statistics_.video_frames_dropped, &media_time); #endif // SB_HAS(PLAYER_WITH_URL) @@ -543,8 +548,8 @@ int64 new_start_seconds = buffer_start_time.InSeconds(); int64 old_length_seconds = old_buffer_length_time.InSeconds(); int64 new_length_seconds = buffer_length_time.InSeconds(); - if (old_start_seconds != new_start_seconds - || old_length_seconds != new_length_seconds) { + if (old_start_seconds != new_start_seconds || + old_length_seconds != new_length_seconds) { did_loading_progress_ = true; } } else { @@ -555,7 +560,7 @@ return time_ranges; } -#else // SB_HAS(PLAYER_WITH_URL) +#else // SB_HAS(PLAYER_WITH_URL) Ranges<TimeDelta> SbPlayerPipeline::GetBufferedTimeRanges() { base::AutoLock auto_lock(lock_); @@ -709,7 +714,7 @@ } if (error != PIPELINE_OK) { - ResetAndRunIfNotNull(&error_cb_, error); + ResetAndRunIfNotNull(&error_cb_, error, "Demuxer error."); } } @@ -871,7 +876,7 @@ } if (status != PIPELINE_OK) { - ResetAndRunIfNotNull(&error_cb_, status); + ResetAndRunIfNotNull(&error_cb_, status, "Demuxer initialization error."); return; } @@ -886,17 +891,19 @@ DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO); DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO); -#if SB_API_VERSION < SB_AUDIOLESS_VIDEO_API_VERSION +#if !SB_HAS(AUDIOLESS_VIDEO) if (audio_stream == NULL) { LOG(INFO) << "The video has to contain an audio track."; - ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS); + ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS, + "The video has to contain an audio track."); return; } -#endif // SB_API_VERSION < SB_AUDIOLESS_VIDEO_API_VERSION +#endif // !SB_HAS(AUDIOLESS_VIDEO) if (video_stream == NULL) { LOG(INFO) << "The video has to contain a video track."; - ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS); + ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS, + "The video has to contain a video track."); return; } @@ -1088,24 +1095,43 @@ break; case kSbPlayerStateDestroyed: break; -#if SB_HAS(PLAYER_WITH_URL) - case kSbPlayerWithUrlStateNetworkError: - ResetAndRunIfNotNull(&error_cb_, PIPELINE_ERROR_NETWORK); - break; - case kSbPlayerWithUrlStateDecodeError: - ResetAndRunIfNotNull(&error_cb_, PIPELINE_ERROR_DECODE); - break; - case kSbPlayerWithUrlStateSrcNotSupportedError: - ResetAndRunIfNotNull(&error_cb_, DECODER_ERROR_NOT_SUPPORTED); - break; -#else // SB_HAS(PLAYER_WITH_URL) +#if !SB_HAS(PLAYER_ERROR_MESSAGE) case kSbPlayerStateError: - ResetAndRunIfNotNull(&error_cb_, PIPELINE_ERROR_DECODE); + ResetAndRunIfNotNull(&error_cb_, PIPELINE_ERROR_DECODE, + "Pipeline player state error."); break; -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // !SB_HAS(PLAYER_ERROR_MESSAGE) } } +#if SB_HAS(PLAYER_ERROR_MESSAGE) +void SbPlayerPipeline::OnPlayerError(SbPlayerError error, + const std::string& message) { + DCHECK(message_loop_->BelongsToCurrentThread()); + + // In case if Stop() has been called. + if (!player_) { + return; + } +#if SB_HAS(PLAYER_WITH_URL) + switch (error) { + case kSbPlayerErrorNetwork: + ResetAndRunIfNotNull(&error_cb_, PIPELINE_ERROR_NETWORK, message); + break; + case kSbPlayerErrorDecode: + ResetAndRunIfNotNull(&error_cb_, PIPELINE_ERROR_DECODE, message); + break; + case kSbPlayerErrorSrcNotSupported: + ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_COULD_NOT_OPEN, message); + break; + } +#else + DCHECK_EQ(error, kSbPlayerErrorDecode); + ResetAndRunIfNotNull(&error_cb_, PIPELINE_ERROR_DECODE, message); +#endif // SB_HAS(PLAYER_WITH_URL) +} +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + void SbPlayerPipeline::UpdateDecoderConfig(DemuxerStream* stream) { DCHECK(message_loop_->BelongsToCurrentThread());
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc index 5bed9c5..3850c6e 100644 --- a/src/cobalt/media/base/starboard_player.cc +++ b/src/cobalt/media/base/starboard_player.cc
@@ -57,6 +57,16 @@ } } +#if SB_HAS(PLAYER_ERROR_MESSAGE) +void StarboardPlayer::CallbackHelper::OnPlayerError( + SbPlayer player, SbPlayerError error, const std::string& message) { + base::AutoLock auto_lock(lock_); + if (player_) { + player_->OnPlayerError(player, error, message); + } +} +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + void StarboardPlayer::CallbackHelper::OnDeallocateSample( const void* sample_buffer) { base::AutoLock auto_lock(lock_); @@ -71,6 +81,7 @@ } #if SB_HAS(PLAYER_WITH_URL) + StarboardPlayer::StarboardPlayer( const scoped_refptr<base::MessageLoopProxy>& message_loop, const std::string& url, SbWindow window, Host* host, @@ -108,7 +119,8 @@ base::Bind(&StarboardPlayer::CallbackHelper::ClearDecoderBufferCache, callback_helper_)); } -#else + +#else // SB_HAS(PLAYER_WITH_URL) StarboardPlayer::StarboardPlayer( const scoped_refptr<base::MessageLoopProxy>& message_loop, @@ -206,15 +218,16 @@ #endif // !SB_HAS(PLAYER_WITH_URL) void StarboardPlayer::SetBounds(int z_index, const gfx::Rect& rect) { + base::AutoLock auto_lock(lock_); + + set_bounds_z_index_ = z_index; + set_bounds_rect_ = rect; + if (state_ == kSuspended) { - pending_set_bounds_z_index_ = z_index; - pending_set_bounds_rect_ = rect; return; } - DCHECK(SbPlayerIsValid(player_)); - SbPlayerSetBounds(player_, z_index, rect.x(), rect.y(), rect.width(), - rect.height()); + UpdateBounds_Locked(); } void StarboardPlayer::PrepareForSeek() { @@ -249,7 +262,8 @@ DCHECK(SbPlayerIsValid(player_)); ++ticket_; - SbPlayerSeek(player_, TimeDeltaToSbMediaTime(time), ticket_); + SbPlayerSeek(player_, SB_TIME_TO_SB_MEDIA_TIME(time.InMicroseconds()), + ticket_); seek_pending_ = false; SbPlayerSetPlaybackRate(player_, playback_rate_); } @@ -317,13 +331,16 @@ *video_frames_dropped = info.dropped_video_frames; } if (media_time) { - *media_time = SbMediaTimeToTimeDelta(info.current_media_pts); + *media_time = base::TimeDelta::FromMicroseconds( + SB_MEDIA_TIME_TO_SB_TIME(info.current_media_pts)); } if (buffer_start_time) { - *buffer_start_time = SbMediaTimeToTimeDelta(info.buffer_start_pts); + *buffer_start_time = base::TimeDelta::FromMicroseconds( + SB_MEDIA_TIME_TO_SB_TIME(info.buffer_start_pts)); } if (buffer_length_time) { - *buffer_length_time = SbMediaTimeToTimeDelta(info.buffer_duration_pts); + *buffer_length_time = base::TimeDelta::FromMicroseconds( + SB_MEDIA_TIME_TO_SB_TIME(info.buffer_duration_pts)); } if (frame_width) { *frame_width = info.frame_width; @@ -364,7 +381,8 @@ *video_frames_dropped = info.dropped_video_frames; } if (media_time) { - *media_time = SbMediaTimeToTimeDelta(info.current_media_pts); + *media_time = base::TimeDelta::FromMicroseconds( + SB_MEDIA_TIME_TO_SB_TIME(info.current_media_pts)); } } @@ -380,7 +398,8 @@ SbPlayerInfo info; SbPlayerGetInfo(player_, &info); DCHECK_NE(info.duration_pts, SB_PLAYER_NO_DURATION); - return SbMediaTimeToTimeDelta(info.duration_pts); + return base::TimeDelta::FromMicroseconds( + SB_MEDIA_TIME_TO_SB_TIME(info.duration_pts)); } base::TimeDelta StarboardPlayer::GetStartDate() { @@ -422,7 +441,8 @@ SbPlayerGetInfo(player_, &info); cached_video_frames_decoded_ = info.total_video_frames; cached_video_frames_dropped_ = info.dropped_video_frames; - preroll_timestamp_ = SbMediaTimeToTimeDelta(info.current_media_pts); + preroll_timestamp_ = base::TimeDelta::FromMicroseconds( + SB_MEDIA_TIME_TO_SB_TIME(info.current_media_pts)); set_bounds_helper_->SetPlayer(NULL); video_frame_provider_->SetOutputMode(VideoFrameProvider::kOutputModeInvalid); @@ -455,6 +475,7 @@ base::AutoLock auto_lock(lock_); state_ = kResuming; + UpdateBounds_Locked(); } namespace { @@ -496,7 +517,11 @@ player_ = SbPlayerCreateWithUrl( url.c_str(), window_, SB_PLAYER_NO_DURATION, &StarboardPlayer::PlayerStatusCB, - &StarboardPlayer::EncryptedMediaInitDataEncounteredCB, this); + &StarboardPlayer::EncryptedMediaInitDataEncounteredCB, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + &StarboardPlayer::PlayerErrorCB, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + this); DCHECK(SbPlayerIsValid(player_)); if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) { @@ -510,11 +535,8 @@ set_bounds_helper_->SetPlayer(this); - if (pending_set_bounds_z_index_ && pending_set_bounds_rect_) { - SetBounds(*pending_set_bounds_z_index_, *pending_set_bounds_rect_); - pending_set_bounds_z_index_ = base::nullopt_t(); - pending_set_bounds_rect_ = base::nullopt_t(); - } + base::AutoLock auto_lock(lock_); + UpdateBounds_Locked(); } #else @@ -539,8 +561,11 @@ player_ = SbPlayerCreate( window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_, has_audio ? &audio_header : NULL, &StarboardPlayer::DeallocateSampleCB, - &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, this, - output_mode_, + &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + &StarboardPlayer::PlayerErrorCB, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + this, output_mode_, ShellMediaPlatform::Instance() ->GetSbDecodeTargetGraphicsContextProvider()); DCHECK(SbPlayerIsValid(player_)); @@ -553,14 +578,10 @@ } video_frame_provider_->SetOutputMode( ToVideoFrameProviderOutputMode(output_mode_)); - set_bounds_helper_->SetPlayer(this); - if (pending_set_bounds_z_index_ && pending_set_bounds_rect_) { - SetBounds(*pending_set_bounds_z_index_, *pending_set_bounds_rect_); - pending_set_bounds_z_index_ = base::nullopt_t(); - pending_set_bounds_rect_ = base::nullopt_t(); - } + base::AutoLock auto_lock(lock_); + UpdateBounds_Locked(); } void StarboardPlayer::WriteNextBufferFromCache(DemuxerStream::Type type) { @@ -607,17 +628,18 @@ FillDrmSampleInfo(buffer, &drm_info, &subsample_mapping); } - SbPlayerWriteSample(player_, DemuxerStreamTypeToSbMediaType(type), + SbPlayerWriteSample( + player_, DemuxerStreamTypeToSbMediaType(type), #if SB_API_VERSION >= 6 - allocations.buffers(), allocations.buffer_sizes(), + allocations.buffers(), allocations.buffer_sizes(), #else // SB_API_VERSION >= 6 - const_cast<const void**>(allocations.buffers()), - const_cast<int*>(allocations.buffer_sizes()), + const_cast<const void**>(allocations.buffers()), + const_cast<int*>(allocations.buffer_sizes()), #endif // SB_API_VERSION >= 6 - allocations.number_of_buffers(), - TimeDeltaToSbMediaTime(buffer->timestamp()), - type == DemuxerStream::VIDEO ? &video_info : NULL, - drm_info.subsample_count > 0 ? &drm_info : NULL); + allocations.number_of_buffers(), + SB_TIME_TO_SB_MEDIA_TIME(buffer->timestamp().InMicroseconds()), + type == DemuxerStream::VIDEO ? &video_info : NULL, + drm_info.subsample_count > 0 ? &drm_info : NULL); } #endif // SB_HAS(PLAYER_WITH_URL) @@ -630,6 +652,19 @@ return output_mode_; } +void StarboardPlayer::UpdateBounds_Locked() { + lock_.AssertAcquired(); + DCHECK(SbPlayerIsValid(player_)); + + if (!set_bounds_z_index_ || !set_bounds_rect_) { + return; + } + + auto& rect = *set_bounds_rect_; + SbPlayerSetBounds(player_, *set_bounds_z_index_, rect.x(), rect.y(), + rect.width(), rect.height()); +} + void StarboardPlayer::ClearDecoderBufferCache() { DCHECK(message_loop_->BelongsToCurrentThread()); @@ -704,7 +739,9 @@ if (ticket_ == SB_PLAYER_INITIAL_TICKET) { ++ticket_; } - SbPlayerSeek(player_, TimeDeltaToSbMediaTime(preroll_timestamp_), ticket_); + SbPlayerSeek(player_, + SB_TIME_TO_SB_MEDIA_TIME(preroll_timestamp_.InMicroseconds()), + ticket_); SetVolume(volume_); SbPlayerSetPlaybackRate(player_, playback_rate_); return; @@ -712,6 +749,17 @@ host_->OnPlayerStatus(state); } +#if SB_HAS(PLAYER_ERROR_MESSAGE) +void StarboardPlayer::OnPlayerError(SbPlayer player, SbPlayerError error, + const std::string& message) { + DCHECK(message_loop_->BelongsToCurrentThread()); + + if (player_ != player) { + return; + } + host_->OnPlayerError(error, message); +} +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) void StarboardPlayer::OnDeallocateSample(const void* sample_buffer) { DCHECK(message_loop_->BelongsToCurrentThread()); @@ -732,7 +780,7 @@ void StarboardPlayer::DecoderStatusCB(SbPlayer player, void* context, SbMediaType type, SbPlayerDecoderState state, int ticket) { - StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context); + StarboardPlayer* helper = static_cast<StarboardPlayer*>(context); helper->message_loop_->PostTask( FROM_HERE, base::Bind(&StarboardPlayer::CallbackHelper::OnDecoderStatus, @@ -742,16 +790,28 @@ // static void StarboardPlayer::PlayerStatusCB(SbPlayer player, void* context, SbPlayerState state, int ticket) { - StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context); + StarboardPlayer* helper = static_cast<StarboardPlayer*>(context); helper->message_loop_->PostTask( FROM_HERE, base::Bind(&StarboardPlayer::CallbackHelper::OnPlayerStatus, helper->callback_helper_, player, state, ticket)); } +#if SB_HAS(PLAYER_ERROR_MESSAGE) +// static +void StarboardPlayer::PlayerErrorCB(SbPlayer player, void* context, + SbPlayerError error, const char* message) { + StarboardPlayer* helper = static_cast<StarboardPlayer*>(context); + helper->message_loop_->PostTask( + FROM_HERE, base::Bind(&StarboardPlayer::CallbackHelper::OnPlayerError, + helper->callback_helper_, player, error, + message ? std::string(message) : "")); +} +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + // static void StarboardPlayer::DeallocateSampleCB(SbPlayer player, void* context, const void* sample_buffer) { - StarboardPlayer* helper = reinterpret_cast<StarboardPlayer*>(context); + StarboardPlayer* helper = static_cast<StarboardPlayer*>(context); helper->message_loop_->PostTask( FROM_HERE, base::Bind(&StarboardPlayer::CallbackHelper::OnDeallocateSample,
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h index 43522a4..a68f733 100644 --- a/src/cobalt/media/base/starboard_player.h +++ b/src/cobalt/media/base/starboard_player.h
@@ -45,6 +45,10 @@ virtual void OnNeedData(DemuxerStream::Type type) = 0; #endif // !SB_HAS(PLAYER_WITH_URL) virtual void OnPlayerStatus(SbPlayerState state) = 0; +#if SB_HAS(PLAYER_ERROR_MESSAGE) + virtual void OnPlayerError(SbPlayerError error, + const std::string& message) = 0; +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) protected: ~Host() {} @@ -126,6 +130,10 @@ void OnDecoderStatus(SbPlayer player, SbMediaType type, SbPlayerDecoderState state, int ticket); void OnPlayerStatus(SbPlayer player, SbPlayerState state, int ticket); +#if SB_HAS(PLAYER_ERROR_MESSAGE) + void OnPlayerError(SbPlayer player, SbPlayerError error, + const std::string& message); +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) void OnDeallocateSample(const void* sample_buffer); void ResetPlayer(); @@ -154,17 +162,27 @@ void WriteNextBufferFromCache(DemuxerStream::Type type); #endif // SB_HAS(PLAYER_WITH_URL) + void UpdateBounds_Locked(); + void ClearDecoderBufferCache(); void OnDecoderStatus(SbPlayer player, SbMediaType type, SbPlayerDecoderState state, int ticket); void OnPlayerStatus(SbPlayer player, SbPlayerState state, int ticket); +#if SB_HAS(PLAYER_ERROR_MESSAGE) + void OnPlayerError(SbPlayer player, SbPlayerError error, + const std::string& message); +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) void OnDeallocateSample(const void* sample_buffer); static void DecoderStatusCB(SbPlayer player, void* context, SbMediaType type, SbPlayerDecoderState state, int ticket); static void PlayerStatusCB(SbPlayer player, void* context, SbPlayerState state, int ticket); +#if SB_HAS(PLAYER_ERROR_MESSAGE) + static void PlayerErrorCB(SbPlayer player, void* context, SbPlayerError error, + const char* message); +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) static void DeallocateSampleCB(SbPlayer player, void* context, const void* sample_buffer); @@ -206,15 +224,14 @@ bool paused_; bool seek_pending_; DecoderBufferCache decoder_buffer_cache_; - // If |SetBounds| is called while we are in a suspended state, then the - // |z_index| and |rect| that we are passed will be saved to here, and then - // immediately set on the new player that we construct when we are resumed. - base::optional<int> pending_set_bounds_z_index_; - base::optional<gfx::Rect> pending_set_bounds_rect_; // The following variables can be accessed from GetInfo(), which can be called // from any threads. So some of their usages have to be guarded by |lock_|. base::Lock lock_; + + // Stores the |z_index| and |rect| parameters of the latest SetBounds() call. + base::optional<int> set_bounds_z_index_; + base::optional<gfx::Rect> set_bounds_rect_; State state_; SbPlayer player_; uint32 cached_video_frames_decoded_;
diff --git a/src/cobalt/media/base/starboard_utils.cc b/src/cobalt/media/base/starboard_utils.cc index b5cf1c6..33d7051 100644 --- a/src/cobalt/media/base/starboard_utils.cc +++ b/src/cobalt/media/base/starboard_utils.cc
@@ -90,16 +90,6 @@ return audio_header; } -TimeDelta SbMediaTimeToTimeDelta(SbMediaTime timestamp) { - return TimeDelta::FromMicroseconds(timestamp * Time::kMicrosecondsPerSecond / - kSbMediaTimeSecond); -} - -SbMediaTime TimeDeltaToSbMediaTime(TimeDelta timedelta) { - return timedelta.InMicroseconds() * kSbMediaTimeSecond / - Time::kMicrosecondsPerSecond; -} - DemuxerStream::Type SbMediaTypeToDemuxerStreamType(SbMediaType type) { if (type == kSbMediaTypeAudio) { return DemuxerStream::AUDIO;
diff --git a/src/cobalt/media/base/starboard_utils.h b/src/cobalt/media/base/starboard_utils.h index aa13513..fcde227 100644 --- a/src/cobalt/media/base/starboard_utils.h +++ b/src/cobalt/media/base/starboard_utils.h
@@ -32,9 +32,6 @@ SbMediaAudioHeader MediaAudioConfigToSbMediaAudioHeader( const AudioDecoderConfig& audio_decoder_config); -base::TimeDelta SbMediaTimeToTimeDelta(SbMediaTime timestamp); -SbMediaTime TimeDeltaToSbMediaTime(base::TimeDelta timedelta); - DemuxerStream::Type SbMediaTypeToDemuxerStreamType(SbMediaType type); SbMediaType DemuxerStreamTypeToSbMediaType(DemuxerStream::Type type);
diff --git a/src/cobalt/media/base/test_data_util.cc b/src/cobalt/media/base/test_data_util.cc index 9153e82..2829658 100644 --- a/src/cobalt/media/base/test_data_util.cc +++ b/src/cobalt/media/base/test_data_util.cc
@@ -19,7 +19,7 @@ base::FilePath GetTestDataFilePath(const std::string& name) { base::FilePath file_path; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &file_path)); return file_path.Append(GetTestDataPath()).AppendASCII(name); }
diff --git a/src/cobalt/media/base/yuv_convert_perftest.cc b/src/cobalt/media/base/yuv_convert_perftest.cc index 229f7ba..b33aa5d 100644 --- a/src/cobalt/media/base/yuv_convert_perftest.cc +++ b/src/cobalt/media/base/yuv_convert_perftest.cc
@@ -46,7 +46,7 @@ : yuv_bytes_(new uint8_t[kYUV12Size]), rgb_bytes_converted_(new uint8_t[kRGBSize]) { base::FilePath path; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &path)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &path)); path = path.Append(FILE_PATH_LITERAL("media")) .Append(FILE_PATH_LITERAL("test")) .Append(FILE_PATH_LITERAL("data"))
diff --git a/src/cobalt/media/base/yuv_convert_unittest.cc b/src/cobalt/media/base/yuv_convert_unittest.cc index ed6ae69..6361dae 100644 --- a/src/cobalt/media/base/yuv_convert_unittest.cc +++ b/src/cobalt/media/base/yuv_convert_unittest.cc
@@ -54,7 +54,7 @@ data->reset(new uint8_t[expected_size]); base::FilePath path; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &path)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &path)); path = path.Append(FILE_PATH_LITERAL("media")) .Append(FILE_PATH_LITERAL("test")) .Append(FILE_PATH_LITERAL("data")) @@ -376,7 +376,7 @@ // Read YUV reference data from file. base::FilePath yuv_url; - EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); + EXPECT_TRUE(PathService::Get(base::DIR_TEST_DATA, &yuv_url)); yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) .Append(FILE_PATH_LITERAL("test")) .Append(FILE_PATH_LITERAL("data")) @@ -430,7 +430,7 @@ TEST(YUVConvertTest, DownScaleYUVToRGB32WithRect) { // Read YUV reference data from file. base::FilePath yuv_url; - EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); + EXPECT_TRUE(PathService::Get(base::DIR_TEST_DATA, &yuv_url)); yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) .Append(FILE_PATH_LITERAL("test")) .Append(FILE_PATH_LITERAL("data"))
diff --git a/src/cobalt/media/filters/source_buffer_stream.cc b/src/cobalt/media/filters/source_buffer_stream.cc index 4f9ffb0..6327853 100644 --- a/src/cobalt/media/filters/source_buffer_stream.cc +++ b/src/cobalt/media/filters/source_buffer_stream.cc
@@ -731,10 +731,29 @@ return false; } - // Return if we're under or at the memory limit. - if (ranges_size + newDataSize <= memory_limit_) return true; + base::TimeDelta duration = GetBufferedDurationForGarbageCollection(); - size_t bytes_to_free = ranges_size + newDataSize - memory_limit_; + size_t bytes_to_free = 0; + + // Check if we're under or at the memory/duration limit. + const auto kGcDurationThresholdInMilliseconds = + COBALT_MEDIA_SOURCE_GARBAGE_COLLECTION_DURATION_THRESHOLD_IN_SECONDS * + 1000; + + if (ranges_size + newDataSize > memory_limit_) { + bytes_to_free = ranges_size + newDataSize - memory_limit_; + } else if (duration.InMilliseconds() > kGcDurationThresholdInMilliseconds) { + // Estimate the size to free. + auto duration_to_free = + duration.InMilliseconds() - kGcDurationThresholdInMilliseconds; + bytes_to_free = ranges_size * duration_to_free / duration.InMilliseconds(); + } + + if (bytes_to_free == 0) { + return true; + } + + DCHECK_GT(bytes_to_free, 0); DVLOG(2) << __func__ << " " << GetStreamTypeName() << ": Before GC media_time=" << media_time.InSecondsF() @@ -1834,5 +1853,14 @@ return true; } +base::TimeDelta SourceBufferStream::GetBufferedDurationForGarbageCollection() + const { + base::TimeDelta duration; + for (auto range : ranges_) { + duration += range->GetEndTimestamp() - range->GetStartTimestamp(); + } + return duration; +} + } // namespace media } // namespace cobalt
diff --git a/src/cobalt/media/filters/source_buffer_stream.h b/src/cobalt/media/filters/source_buffer_stream.h index edeac1a..de9d343 100644 --- a/src/cobalt/media/filters/source_buffer_stream.h +++ b/src/cobalt/media/filters/source_buffer_stream.h
@@ -471,6 +471,10 @@ // appropriately and returns true. Otherwise returns false. bool SetPendingBuffer(scoped_refptr<StreamParserBuffer>* out_buffer); + // Returns the accumulated duration of all ranges. This is solely used by + // garbage collection. + base::TimeDelta GetBufferedDurationForGarbageCollection() const; + // Used to report log messages that can help the web developer figure out what // is wrong with the content. scoped_refptr<MediaLog> media_log_;
diff --git a/src/cobalt/media/formats/webm/webm_parser.cc b/src/cobalt/media/formats/webm/webm_parser.cc index b14e3e1..2b6ec94 100644 --- a/src/cobalt/media/formats/webm/webm_parser.cc +++ b/src/cobalt/media/formats/webm/webm_parser.cc
@@ -612,7 +612,7 @@ case SKIP: result = element_size; break; - default: + case UNKNOWN: DVLOG(1) << "Unhandled ID type " << type; return -1; }
diff --git a/src/cobalt/media/formats/webm/webm_video_client.cc b/src/cobalt/media/formats/webm/webm_video_client.cc index 9520015..eed0e40 100644 --- a/src/cobalt/media/formats/webm/webm_video_client.cc +++ b/src/cobalt/media/formats/webm/webm_video_client.cc
@@ -10,6 +10,22 @@ namespace cobalt { namespace media { +namespace { +// Tries to parse |data| to extract the VP9 Profile ID, or returns Profile 0. +media::VideoCodecProfile GetVP9CodecProfile(const std::vector<uint8_t>& data) { + // VP9 CodecPrivate (http://wiki.webmproject.org/vp9-codecprivate) might have + // Profile information in the first field, if present. + constexpr uint8_t kVP9ProfileFieldId = 0x01; + constexpr uint8_t kVP9ProfileFieldLength = 1; + if (data.size() < 3 || data[0] != kVP9ProfileFieldId || + data[1] != kVP9ProfileFieldLength || data[2] > 3) { + return VP9PROFILE_PROFILE0; + } + return static_cast<VideoCodecProfile>( + static_cast<size_t>(VP9PROFILE_PROFILE0) + data[2]); +} +} // namespace + WebMVideoClient::WebMVideoClient(const scoped_refptr<MediaLog>& media_log) : media_log_(media_log), colour_parsed_(false) { Reset(); @@ -44,9 +60,7 @@ profile = VP8PROFILE_ANY; } else if (codec_id == "V_VP9") { video_codec = kCodecVP9; - // TODO: Find a way to read actual VP9 profile from WebM. - // crbug.com/592074 - profile = VP9PROFILE_PROFILE0; + profile = GetVP9CodecProfile(codec_private); } else { MEDIA_LOG(ERROR, media_log_) << "Unsupported video codec_id " << codec_id; return false;
diff --git a/src/cobalt/media/formats/webm/webm_video_client.h b/src/cobalt/media/formats/webm/webm_video_client.h index fb14909..77d26ca 100644 --- a/src/cobalt/media/formats/webm/webm_video_client.h +++ b/src/cobalt/media/formats/webm/webm_video_client.h
@@ -9,6 +9,7 @@ #include <vector> #include "base/basictypes.h" +#include "cobalt/media/base/media_export.h" #include "cobalt/media/base/media_log.h" #include "cobalt/media/formats/webm/webm_colour_parser.h" #include "cobalt/media/formats/webm/webm_parser.h" @@ -20,7 +21,7 @@ class VideoDecoderConfig; // Helper class used to parse a Video element inside a TrackEntry element. -class WebMVideoClient : public WebMParserClient { +class MEDIA_EXPORT WebMVideoClient : public WebMParserClient { public: explicit WebMVideoClient(const scoped_refptr<MediaLog>& media_log); ~WebMVideoClient() override; @@ -41,6 +42,8 @@ VideoDecoderConfig* config); private: + friend class WebMVideoClientTest; + // WebMParserClient implementation. WebMParserClient* OnListStart(int id) override; bool OnListEnd(int id) override;
diff --git a/src/cobalt/media/formats/webm/webm_video_client_unittest.cc b/src/cobalt/media/formats/webm/webm_video_client_unittest.cc new file mode 100644 index 0000000..02ffefb --- /dev/null +++ b/src/cobalt/media/formats/webm/webm_video_client_unittest.cc
@@ -0,0 +1,82 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cobalt/media/formats/webm/webm_video_client.h" + +#include "cobalt/media/base/media_util.h" +#include "cobalt/media/base/mock_media_log.h" +#include "cobalt/media/base/video_decoder_config.h" +#include "cobalt/media/formats/webm/webm_constants.h" + +using testing::StrictMock; + +namespace cobalt { +namespace media { + +namespace { +const gfx::Size kCodedSize(321, 243); + +struct CodecTestParams { + VideoCodecProfile profile; + const std::vector<uint8_t> codec_private; +}; +} // namespace + +CodecTestParams kCodecTestParams[] = { + {VP9PROFILE_PROFILE0, {}}, + {VP9PROFILE_PROFILE2, + // Valid VP9 Profile 2 example, extracted out of a sample file at + // https://www.webmproject.org/vp9/levels/#test-bitstreams + {0x01, 0x01, 0x02, 0x02, 0x01, 0x0a, 0x3, 0x1, 0xa, 0x4, 0x1, 0x1}}, + // Invalid VP9 CodecPrivate: too short. + {VP9PROFILE_PROFILE0, {0x01, 0x01}}, + // Invalid VP9 CodecPrivate: wrong field id. + {VP9PROFILE_PROFILE0, {0x77, 0x01, 0x02}}, + // Invalid VP9 CodecPrivate: wrong field length. + {VP9PROFILE_PROFILE0, {0x01, 0x75, 0x02}}, + // Invalid VP9 CodecPrivate: wrong Profile (can't be > 3). + {VP9PROFILE_PROFILE0, {0x01, 0x01, 0x34}}}; + +class WebMVideoClientTest : public testing::TestWithParam<CodecTestParams> { + public: + WebMVideoClientTest() + : media_log_(new StrictMock<MockMediaLog>()), + webm_video_client_(media_log_) { + // Simulate configuring width and height in the |webm_video_client_|. + webm_video_client_.OnUInt(kWebMIdPixelWidth, kCodedSize.width()); + webm_video_client_.OnUInt(kWebMIdPixelHeight, kCodedSize.height()); + } + + scoped_refptr<StrictMock<MockMediaLog>> media_log_; + WebMVideoClient webm_video_client_; + + private: + DISALLOW_COPY_AND_ASSIGN(WebMVideoClientTest); +}; + +TEST_P(WebMVideoClientTest, InitializeConfigVP9Profiles) { + const std::string kCodecId = "V_VP9"; + const VideoCodecProfile profile = GetParam().profile; + const std::vector<uint8_t> codec_private = GetParam().codec_private; + + VideoDecoderConfig config; + + EXPECT_TRUE(webm_video_client_.InitializeConfig(kCodecId, codec_private, + EncryptionScheme(), &config)); + + VideoDecoderConfig expected_config( + kCodecVP9, profile, PIXEL_FORMAT_YV12, COLOR_SPACE_HD_REC709, kCodedSize, + gfx::Rect(kCodedSize), kCodedSize, codec_private, Unencrypted()); + + EXPECT_TRUE(config.Matches(expected_config)) + << "Config (" << config.AsHumanReadableString() + << ") does not match expected (" + << expected_config.AsHumanReadableString() << ")"; +} + +INSTANTIATE_TEST_CASE_P(/* No prefix. */, WebMVideoClientTest, + ::testing::ValuesIn(kCodecTestParams)); + +} // namespace media +} // namespace cobalt
diff --git a/src/cobalt/media/media2.gyp b/src/cobalt/media/media2.gyp index 8a5e99d..289ef3a 100644 --- a/src/cobalt/media/media2.gyp +++ b/src/cobalt/media/media2.gyp
@@ -227,6 +227,23 @@ ], }, { + 'target_name': 'media2_format_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'media2', + '<(DEPTH)/cobalt/base/base.gyp:base', + '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests', + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/ui/ui.gyp:ui', + ], + 'sources': [ + 'formats/webm/webm_video_client_unittest.cc', + 'base/mock_media_log.cc', + ], + }, + + { # Rename 'media2_unittests' to 'media_unittests' once the original media # is removed. 'target_name': 'media2_unittests', @@ -239,7 +256,7 @@ '<(DEPTH)/base/base.gyp:test_support_base', '<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gtest.gyp:gtest', - '<(DEPTH)/ui/ui.gyp:ui', + ], 'sources': [ 'base/audio_bus_unittest.cc',
diff --git a/src/cobalt/media/media_module_starboard.cc b/src/cobalt/media/media_module_starboard.cc index d0c01cd..9aa0ea4 100644 --- a/src/cobalt/media/media_module_starboard.cc +++ b/src/cobalt/media/media_module_starboard.cc
@@ -16,18 +16,11 @@ #include "base/compiler_specific.h" #include "cobalt/math/size.h" -#include "cobalt/media/shell_media_platform_starboard.h" -#include "cobalt/system_window/system_window.h" -#if defined(COBALT_MEDIA_SOURCE_2016) #include "cobalt/media/base/media_log.h" #include "cobalt/media/decoder_buffer_allocator.h" #include "cobalt/media/player/web_media_player_impl.h" -#else // defined(COBALT_MEDIA_SOURCE_2016) -#include "media/base/filter_collection.h" -#include "media/base/media_log.h" -#include "media/base/message_loop_factory.h" -#include "media/player/web_media_player_impl.h" -#endif // defined(COBALT_MEDIA_SOURCE_2016) +#include "cobalt/media/shell_media_platform_starboard.h" +#include "cobalt/system_window/system_window.h" #include "nb/memory_scope.h" #include "starboard/media.h" #include "starboard/window.h" @@ -37,13 +30,6 @@ namespace { -#if !defined(COBALT_MEDIA_SOURCE_2016) -typedef ::media::FilterCollection FilterCollection; -typedef ::media::MessageLoopFactory MessageLoopFactory; -typedef ::media::WebMediaPlayerClient WebMediaPlayerClient; -typedef ::media::ShellMediaPlatformStarboard ShellMediaPlatformStarboard; -#endif // !defined(COBALT_MEDIA_SOURCE_2016) - class CanPlayTypeHandlerStarboard : public CanPlayTypeHandler { public: std::string CanPlayType(const std::string& mime_type, @@ -75,27 +61,12 @@ scoped_ptr<WebMediaPlayer> CreateWebMediaPlayer( WebMediaPlayerClient* client) override { TRACK_MEMORY_SCOPE("Media"); -#if defined(COBALT_MEDIA_SOURCE_2016) SbWindow window = kSbWindowInvalid; if (system_window_) { window = system_window_->GetSbWindow(); } return make_scoped_ptr<WebMediaPlayer>(new media::WebMediaPlayerImpl( window, client, this, &decoder_buffer_allocator_, new media::MediaLog)); -#else // defined(COBALT_MEDIA_SOURCE_2016) - scoped_ptr<MessageLoopFactory> message_loop_factory(new MessageLoopFactory); - scoped_refptr<base::MessageLoopProxy> pipeline_message_loop = - message_loop_factory->GetMessageLoop(MessageLoopFactory::kPipeline); - - SbWindow window = kSbWindowInvalid; - if (system_window_) { - window = system_window_->GetSbWindow(); - } - return make_scoped_ptr<WebMediaPlayer>(new ::media::WebMediaPlayerImpl( - window, client, this, media_platform_.GetVideoFrameProvider(), - scoped_ptr<FilterCollection>(new FilterCollection), NULL, - message_loop_factory.Pass(), new ::media::MediaLog)); -#endif // defined(COBALT_MEDIA_SOURCE_2016) } system_window::SystemWindow* system_window() const override { @@ -111,9 +82,7 @@ private: const Options options_; system_window::SystemWindow* system_window_; -#if defined(COBALT_MEDIA_SOURCE_2016) DecoderBufferAllocator decoder_buffer_allocator_; -#endif // defined(COBALT_MEDIA_SOURCE_2016) ShellMediaPlatformStarboard media_platform_; };
diff --git a/src/cobalt/media/player/web_media_player.h b/src/cobalt/media/player/web_media_player.h index 1c4055c..61a379d 100644 --- a/src/cobalt/media/player/web_media_player.h +++ b/src/cobalt/media/player/web_media_player.h
@@ -200,6 +200,7 @@ class WebMediaPlayerClient { public: virtual void NetworkStateChanged() = 0; + virtual void NetworkError(const std::string&) = 0; virtual void ReadyStateChanged() = 0; virtual void TimeChanged(bool eos_played) = 0; virtual void DurationChanged() = 0;
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc index cd232c7..805b2c7 100644 --- a/src/cobalt/media/player/web_media_player_impl.cc +++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -101,7 +101,8 @@ // TODO(acolwell): Investigate whether the key_system & session_id parameters // are really necessary. typedef base::Callback<void(const std::string&, const std::string&, - scoped_array<uint8>, int)> OnNeedKeyCB; + scoped_array<uint8>, int)> + OnNeedKeyCB; WebMediaPlayerImpl::WebMediaPlayerImpl( PipelineWindow window, WebMediaPlayerClient* client, @@ -612,7 +613,7 @@ } if (status != PIPELINE_OK) { - OnPipelineError(status); + OnPipelineError(status, "Failed pipeline seek."); return; } @@ -626,7 +627,7 @@ void WebMediaPlayerImpl::OnPipelineEnded(PipelineStatus status) { DCHECK_EQ(main_loop_, MessageLoop::current()); if (status != PIPELINE_OK) { - OnPipelineError(status); + OnPipelineError(status, "Failed pipeline end."); return; } @@ -634,7 +635,8 @@ GetClient()->TimeChanged(eos_played); } -void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) { +void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error, + const std::string& message) { DCHECK_EQ(main_loop_, MessageLoop::current()); if (suppress_destruction_errors_) return; @@ -644,42 +646,96 @@ if (ready_state_ == WebMediaPlayer::kReadyStateHaveNothing) { // Any error that occurs before reaching ReadyStateHaveMetadata should // be considered a format error. - SetNetworkState(WebMediaPlayer::kNetworkStateFormatError); + SetNetworkError(WebMediaPlayer::kNetworkStateFormatError, + "Ready state have nothing."); return; } + std::string default_message; switch (error) { case PIPELINE_OK: NOTREACHED() << "PIPELINE_OK isn't an error!"; break; case PIPELINE_ERROR_NETWORK: + SetNetworkError(WebMediaPlayer::kNetworkStateNetworkError, + message.empty() ? "Pipeline network error." : message); + break; case PIPELINE_ERROR_READ: + SetNetworkError(WebMediaPlayer::kNetworkStateNetworkError, + message.empty() ? "Pipeline read error." : message); + break; case CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR: - SetNetworkState(WebMediaPlayer::kNetworkStateNetworkError); + SetNetworkError( + WebMediaPlayer::kNetworkStateNetworkError, + message.empty() ? "Chunk demuxer eos network error." : message); break; // TODO(vrk): Because OnPipelineInitialize() directly reports the // NetworkStateFormatError instead of calling OnPipelineError(), I believe // this block can be deleted. Should look into it! (crbug.com/126070) case PIPELINE_ERROR_INITIALIZATION_FAILED: + SetNetworkError( + WebMediaPlayer::kNetworkStateFormatError, + message.empty() ? "Pipeline initialization failed." : message); + break; case PIPELINE_ERROR_COULD_NOT_RENDER: + SetNetworkError(WebMediaPlayer::kNetworkStateFormatError, + message.empty() ? "Pipeline could not render." : message); + break; case PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED: + SetNetworkError( + WebMediaPlayer::kNetworkStateFormatError, + message.empty() ? "Pipeline extenrnal renderer failed." : message); + break; case DEMUXER_ERROR_COULD_NOT_OPEN: + SetNetworkError(WebMediaPlayer::kNetworkStateFormatError, + message.empty() ? "Demuxer could not open." : message); + break; case DEMUXER_ERROR_COULD_NOT_PARSE: + SetNetworkError(WebMediaPlayer::kNetworkStateFormatError, + message.empty() ? "Demuxer could not parse." : message); + break; case DEMUXER_ERROR_NO_SUPPORTED_STREAMS: + SetNetworkError( + WebMediaPlayer::kNetworkStateFormatError, + message.empty() ? "Demuxer no supported streams." : message); + break; case DECODER_ERROR_NOT_SUPPORTED: - SetNetworkState(WebMediaPlayer::kNetworkStateFormatError); + SetNetworkError(WebMediaPlayer::kNetworkStateFormatError, + message.empty() ? "Decoder not supported." : message); break; case PIPELINE_ERROR_DECODE: + SetNetworkError(WebMediaPlayer::kNetworkStateDecodeError, + message.empty() ? "Pipeline decode error." : message); + break; case PIPELINE_ERROR_ABORT: + SetNetworkError(WebMediaPlayer::kNetworkStateDecodeError, + message.empty() ? "Pipeline abort." : message); + break; case PIPELINE_ERROR_INVALID_STATE: + SetNetworkError(WebMediaPlayer::kNetworkStateDecodeError, + message.empty() ? "Pipeline invalid state." : message); + break; case CHUNK_DEMUXER_ERROR_APPEND_FAILED: + SetNetworkError( + WebMediaPlayer::kNetworkStateDecodeError, + message.empty() ? "Chunk demuxer append failed." : message); + break; case CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR: + SetNetworkError( + WebMediaPlayer::kNetworkStateDecodeError, + message.empty() ? "Chunk demuxer eos decode error." : message); + break; case AUDIO_RENDERER_ERROR: + SetNetworkError(WebMediaPlayer::kNetworkStateDecodeError, + message.empty() ? "Audio renderer error." : message); + break; case AUDIO_RENDERER_ERROR_SPLICE_FAILED: - SetNetworkState(WebMediaPlayer::kNetworkStateDecodeError); + SetNetworkError( + WebMediaPlayer::kNetworkStateDecodeError, + message.empty() ? "Audio renderer splice failed." : message); break; } } @@ -770,6 +826,15 @@ GetClient()->NetworkStateChanged(); } +void WebMediaPlayerImpl::SetNetworkError(WebMediaPlayer::NetworkState state, + const std::string& message) { + DCHECK_EQ(main_loop_, MessageLoop::current()); + DVLOG(1) << "SetNetworkError: " << state << " message: " << message; + network_state_ = state; + // Always notify to ensure client has the latest value. + GetClient()->NetworkError(message); +} + void WebMediaPlayerImpl::SetReadyState(WebMediaPlayer::ReadyState state) { DCHECK_EQ(main_loop_, MessageLoop::current()); DVLOG(1) << "SetReadyState: " << state;
diff --git a/src/cobalt/media/player/web_media_player_impl.h b/src/cobalt/media/player/web_media_player_impl.h index f9e62c1..97bbd92 100644 --- a/src/cobalt/media/player/web_media_player_impl.h +++ b/src/cobalt/media/player/web_media_player_impl.h
@@ -192,7 +192,7 @@ void OnPipelineSeek(PipelineStatus status); void OnPipelineEnded(PipelineStatus status); - void OnPipelineError(PipelineStatus error); + void OnPipelineError(PipelineStatus error, const std::string& message); void OnPipelineBufferingState(Pipeline::BufferingState buffering_state); void OnDemuxerOpened(); void SetOpaque(bool); @@ -211,6 +211,8 @@ // Helpers that set the network/ready state and notifies the client if // they've changed. void SetNetworkState(WebMediaPlayer::NetworkState state); + void SetNetworkError(WebMediaPlayer::NetworkState state, + const std::string& message); void SetReadyState(WebMediaPlayer::ReadyState state); // Destroy resources held.
diff --git a/src/cobalt/media/sandbox/media2_sandbox.cc b/src/cobalt/media/sandbox/media2_sandbox.cc index 7b362f9..3d35834 100644 --- a/src/cobalt/media/sandbox/media2_sandbox.cc +++ b/src/cobalt/media/sandbox/media2_sandbox.cc
@@ -55,7 +55,7 @@ FilePath file_path(file_name); if (!file_path.IsAbsolute()) { FilePath content_path; - PathService::Get(base::DIR_SOURCE_ROOT, &content_path); + PathService::Get(base::DIR_TEST_DATA, &content_path); DCHECK(content_path.IsAbsolute()); file_path = content_path.Append(file_path); } @@ -97,7 +97,7 @@ int SandboxMain(int argc, char** argv) { if (argc != 3) { // Path should be in the form of - // "cobalt/browser/testdata/media-element-demo/dash-video-240p.mp4". + // "cobalt/demos/media-element-demo/dash-video-240p.mp4". LOG(ERROR) << "Usage: " << argv[0] << " <audio_path> <video_path>"; return 1; }
diff --git a/src/cobalt/media/sandbox/sandbox.gyp b/src/cobalt/media/sandbox/sandbox.gyp index c9870c8..383a9ad 100644 --- a/src/cobalt/media/sandbox/sandbox.gyp +++ b/src/cobalt/media/sandbox/sandbox.gyp
@@ -54,8 +54,8 @@ 'dependencies': [ 'media', '<(DEPTH)/cobalt/base/base.gyp:base', - # Use test data from browser to avoid keeping two copies of video files. - '<(DEPTH)/cobalt/browser/browser.gyp:browser_copy_test_data', + # Use test data from demos to avoid keeping two copies of video files. + '<(DEPTH)/cobalt/demos/demos.gyp:copy_demos', '<(DEPTH)/cobalt/loader/loader.gyp:loader', '<(DEPTH)/cobalt/network/network.gyp:network', '<(DEPTH)/cobalt/renderer/renderer.gyp:renderer', @@ -126,8 +126,8 @@ ], 'dependencies': [ '<(DEPTH)/cobalt/base/base.gyp:base', - # Use test data from browser to avoid keeping two copies of video files. - '<(DEPTH)/cobalt/browser/browser.gyp:browser_copy_test_data', + # Use test data from demos to avoid keeping two copies of video files. + '<(DEPTH)/cobalt/demos/demos.gyp:copy_demos', '<(DEPTH)/cobalt/loader/loader.gyp:loader', '<(DEPTH)/cobalt/media/media.gyp:media', '<(DEPTH)/cobalt/network/network.gyp:network', @@ -169,8 +169,8 @@ ], 'dependencies': [ '<(DEPTH)/cobalt/base/base.gyp:base', - # Use test data from browser to avoid keeping two copies of video files. - '<(DEPTH)/cobalt/browser/browser.gyp:browser_copy_test_data', + # Use test data from demos to avoid keeping two copies of video files. + '<(DEPTH)/cobalt/demos/demos.gyp:copy_demos', '<(DEPTH)/cobalt/loader/loader.gyp:loader', '<(DEPTH)/cobalt/media/media.gyp:media', '<(DEPTH)/cobalt/network/network.gyp:network',
diff --git a/src/cobalt/media/sandbox/web_media_player_helper.cc b/src/cobalt/media/sandbox/web_media_player_helper.cc index d017bb4..42cf656 100644 --- a/src/cobalt/media/sandbox/web_media_player_helper.cc +++ b/src/cobalt/media/sandbox/web_media_player_helper.cc
@@ -40,6 +40,7 @@ private: // WebMediaPlayerClient methods void NetworkStateChanged() override {} + void NetworkError(const std::string&) override {} void ReadyStateChanged() override {} void TimeChanged(bool) override {} void DurationChanged() override {}
diff --git a/src/cobalt/media/sandbox/web_media_player_sandbox.cc b/src/cobalt/media/sandbox/web_media_player_sandbox.cc index b6e63b2..a950027 100644 --- a/src/cobalt/media/sandbox/web_media_player_sandbox.cc +++ b/src/cobalt/media/sandbox/web_media_player_sandbox.cc
@@ -57,7 +57,7 @@ FilePath result(path); if (!result.IsAbsolute()) { FilePath content_path; - PathService::Get(base::DIR_SOURCE_ROOT, &content_path); + PathService::Get(base::DIR_TEST_DATA, &content_path); CHECK(content_path.IsAbsolute()); result = content_path.Append(result); } @@ -87,9 +87,9 @@ std::string executable_file_name = FilePath(executable_path_name).BaseName().value(); const char kExampleAdaptiveAudioPathName[] = - "cobalt/browser/testdata/media-element-demo/dash-audio.mp4"; + "cobalt/demos/media-element-demo/dash-audio.mp4"; const char kExampleAdaptiveVideoPathName[] = - "cobalt/browser/testdata/media-element-demo/dash-video-1080p.mp4"; + "cobalt/demos/media-element-demo/dash-video-1080p.mp4"; const char kExampleProgressiveUrl[] = "https://storage.googleapis.com/yt-cobalt-media-element-demo/" "progressive.mp4";
diff --git a/src/cobalt/media/shell_media_platform_starboard.h b/src/cobalt/media/shell_media_platform_starboard.h index 9229fee..4164762 100644 --- a/src/cobalt/media/shell_media_platform_starboard.h +++ b/src/cobalt/media/shell_media_platform_starboard.h
@@ -17,11 +17,8 @@ #include "base/compiler_specific.h" #include "base/logging.h" -#include "cobalt/render_tree/resource_provider.h" - -#if defined(COBALT_MEDIA_SOURCE_2016) - #include "cobalt/media/base/shell_media_platform.h" +#include "cobalt/render_tree/resource_provider.h" namespace cobalt { namespace media { @@ -78,76 +75,4 @@ } // namespace media } // namespace cobalt -#else // defined(COBALT_MEDIA_SOURCE_2016) - -#include "media/base/shell_media_platform.h" - -#include "base/memory/aligned_memory.h" -#include "base/memory/scoped_ptr.h" -#include "cobalt/media/shell_video_data_allocator_common.h" -#include "nb/memory_pool.h" -#include "starboard/common/locked_ptr.h" - -namespace media { - -class ShellMediaPlatformStarboard : public ShellMediaPlatform { - public: - ShellMediaPlatformStarboard( - cobalt::render_tree::ResourceProvider* resource_provider); - ~ShellMediaPlatformStarboard() override; - - void* AllocateBuffer(size_t size) override; - void FreeBuffer(void* ptr) override; - size_t GetSourceBufferStreamAudioMemoryLimit() const override { - return SB_MEDIA_SOURCE_BUFFER_STREAM_AUDIO_MEMORY_LIMIT; - } - size_t GetSourceBufferStreamVideoMemoryLimit() const override { - return SB_MEDIA_SOURCE_BUFFER_STREAM_VIDEO_MEMORY_LIMIT; - } - int GetMaxVideoPrerollFrames() const override { - return SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES; - } - int GetMaxVideoFrames() const override { - return SB_MEDIA_MAXIMUM_VIDEO_FRAMES; - } - scoped_refptr<DecoderBuffer> ProcessBeforeLeavingDemuxer( - const scoped_refptr<DecoderBuffer>& buffer) override; - bool IsOutputProtected() override; - - SbDecodeTargetGraphicsContextProvider* - GetSbDecodeTargetGraphicsContextProvider() override { -#if SB_HAS(GRAPHICS) - return resource_provider_->GetSbDecodeTargetGraphicsContextProvider(); -#else // SB_HAS(GRAPHICS) - return NULL; -#endif // SB_HAS(GRAPHICS) - } - - void Suspend() override { resource_provider_ = NULL; } - void Resume( - cobalt::render_tree::ResourceProvider* resource_provider) override { - resource_provider_ = resource_provider; - } - - private: - cobalt::render_tree::ResourceProvider* resource_provider_; - - // Optional GPU Memory buffer pool, for buffer offloading. - scoped_ptr<cobalt::render_tree::RawImageMemory> gpu_memory_buffer_space_; - starboard::LockedPtr<nb::MemoryPool> gpu_memory_pool_; - - // Main Memory buffer pool. - scoped_ptr_malloc<uint8, base::ScopedPtrAlignedFree> - main_memory_buffer_space_; - starboard::LockedPtr<nb::MemoryPool> main_memory_pool_; - - ShellVideoDataAllocatorCommon video_data_allocator_; - - DISALLOW_COPY_AND_ASSIGN(ShellMediaPlatformStarboard); -}; - -} // namespace media - -#endif // defined(COBALT_MEDIA_SOURCE_2016) - #endif // COBALT_MEDIA_SHELL_MEDIA_PLATFORM_STARBOARD_H_
diff --git a/src/cobalt/media/test/data/eme_player_js/player_utils.js b/src/cobalt/media/test/data/eme_player_js/player_utils.js index e640d90..1cffaaf 100644 --- a/src/cobalt/media/test/data/eme_player_js/player_utils.js +++ b/src/cobalt/media/test/data/eme_player_js/player_utils.js
@@ -127,7 +127,7 @@ config.videoCapabilities = [{contentType: 'video/webm; codecs="vp9"'}]; } else { // Some tests (e.g. mse_different_containers.html) specify audio and - // video codecs seperately. + // video codecs separately. if (player.testConfig.videoFormat == 'ENCRYPTED_MP4' || player.testConfig.videoFormat == 'CLEAR_MP4') { config.videoCapabilities =
diff --git a/src/cobalt/media_capture/media_devices.cc b/src/cobalt/media_capture/media_devices.cc index 0c6e7ce..b9a194b 100644 --- a/src/cobalt/media_capture/media_devices.cc +++ b/src/cobalt/media_capture/media_devices.cc
@@ -42,19 +42,14 @@ } // namespace. -using PromiseSequenceMediaInfo = MediaDevices::PromiseSequenceMediaInfo; - MediaDevices::MediaDevices(script::ScriptValueFactory* script_value_factory) : script_value_factory_(script_value_factory) { } -scoped_ptr<PromiseSequenceMediaInfo> MediaDevices::EnumerateDevices() { - scoped_ptr<PromiseSequenceMediaInfo> promise = - script_value_factory_->CreateBasicPromise< - script::Sequence<scoped_refptr<script::Wrappable>>>(); - - MediaDevices::PromiseSequenceMediaInfo::StrongReference promise_reference( - *promise); +script::Handle<MediaDevices::MediaInfoSequencePromise> +MediaDevices::EnumerateDevices() { + script::Handle<MediaInfoSequencePromise> promise = + script_value_factory_->CreateBasicPromise<MediaInfoSequence>(); script::Sequence<scoped_refptr<Wrappable>> output; scoped_ptr<speech::Microphone> microphone = CreateMicrophone(); if (microphone) { @@ -64,8 +59,8 @@ output.push_back(media_device); } - promise_reference.value().Resolve(output); - return promise.Pass(); + promise->Resolve(output); + return promise; } } // namespace media_capture
diff --git a/src/cobalt/media_capture/media_devices.h b/src/cobalt/media_capture/media_devices.h index 7ab063a..b917475 100644 --- a/src/cobalt/media_capture/media_devices.h +++ b/src/cobalt/media_capture/media_devices.h
@@ -32,20 +32,19 @@ // https://www.w3.org/TR/mediacapture-streams/#mediadevices class MediaDevices : public script::Wrappable { public: - using PromiseSequenceMediaInfo = - script::ScriptValue< - script::Promise<script::Sequence<scoped_refptr<script::Wrappable>>>>; + using MediaInfoSequence = script::Sequence<scoped_refptr<script::Wrappable>>; + using MediaInfoSequencePromise = script::Promise<MediaInfoSequence>; explicit MediaDevices(script::ScriptValueFactory* script_value_factory); - scoped_ptr<PromiseSequenceMediaInfo> EnumerateDevices(); + script::Handle<MediaInfoSequencePromise> EnumerateDevices(); DEFINE_WRAPPABLE_TYPE(MediaDevices); private: script::ScriptValueFactory* script_value_factory_; - SB_DISALLOW_COPY_AND_ASSIGN(MediaDevices); + DISALLOW_COPY_AND_ASSIGN(MediaDevices); }; } // namespace media_capture
diff --git a/src/cobalt/media_session/media_session_client.cc b/src/cobalt/media_session/media_session_client.cc index f6a635b..6cf769c 100644 --- a/src/cobalt/media_session/media_session_client.cc +++ b/src/cobalt/media_session/media_session_client.cc
@@ -71,7 +71,8 @@ // play from available actions." result[kMediaSessionActionPlay] = false; break; - default: + case kMediaSessionPlaybackStateNone: + case kMediaSessionPlaybackStatePaused: // "Otherwise, remove pause from available actions." result[kMediaSessionActionPause] = false; break;
diff --git a/src/cobalt/network/local_network.cc b/src/cobalt/network/local_network.cc index 964a54e..0031ae5 100644 --- a/src/cobalt/network/local_network.cc +++ b/src/cobalt/network/local_network.cc
@@ -45,15 +45,15 @@ case kSbSocketAddressTypeIpv4: return CompareNBytesOfAddress(ip, source_address, netmask, net::kIPv4AddressSize); -#if SB_HAS(IPV6) case kSbSocketAddressTypeIpv6: +#if SB_HAS(IPV6) return CompareNBytesOfAddress(ip, source_address, netmask, net::kIPv6AddressSize); -#endif - default: +#else // SB_HAS(IPV6) NOTREACHED() << "Invalid IP type " << ip.type; - return false; +#endif // SB_HAS(IPV6) } + return false; } } // namespace
diff --git a/src/cobalt/network/network.gyp b/src/cobalt/network/network.gyp index a584ce0..011c295 100644 --- a/src/cobalt/network/network.gyp +++ b/src/cobalt/network/network.gyp
@@ -95,6 +95,11 @@ 'destination': '<(sb_static_contents_output_data_dir)/ssl', 'files': ['<(static_contents_source_dir)/ssl/certs/'], }], + 'all_dependent_settings': { + 'variables': { + 'content_deploy_subdirs': [ 'ssl/certs' ] + } + }, }, { 'target_name': 'network_test',
diff --git a/src/cobalt/network/network_module.cc b/src/cobalt/network/network_module.cc index 059ced9..b4ec250 100644 --- a/src/cobalt/network/network_module.cc +++ b/src/cobalt/network/network_module.cc
@@ -122,7 +122,11 @@ base::Thread::Options thread_options; thread_options.message_loop_type = MessageLoop::TYPE_IO; thread_options.stack_size = 256 * 1024; - thread_options.priority = base::kThreadPriority_High; + // It was found that setting the thread priority here to high could result + // in an increase in unresponsiveness and input latency on single-core + // devices. Keeping it at normal so that it doesn't take precedence over + // user interaction processing. + thread_options.priority = base::kThreadPriority_Normal; thread_->StartWithOptions(thread_options); base::WaitableEvent creation_event(true, false);
diff --git a/src/cobalt/network/user_agent_string_factory.cc b/src/cobalt/network/user_agent_string_factory.cc index 576b6e2..6c82452 100644 --- a/src/cobalt/network/user_agent_string_factory.cc +++ b/src/cobalt/network/user_agent_string_factory.cc
@@ -137,12 +137,12 @@ } else if (ShouldOverrideDevice()) { // When Starboard does not report platform info but we are overriding the // device. - base::StringAppendF(&user_agent, ", _%s_ (%s, %s)", - CreateDeviceTypeString().c_str(), - Sanitize(g_brand_name_override_set ? - g_brand_name_override : "").c_str(), - Sanitize(g_model_name_override_set ? - g_model_name_override : "").c_str()); + base::StringAppendF( + &user_agent, ", _%s_/ (%s, %s,)", CreateDeviceTypeString().c_str(), + Sanitize(g_brand_name_override_set ? g_brand_name_override : "") + .c_str(), + Sanitize(g_model_name_override_set ? g_model_name_override : "") + .c_str()); } if (!aux_field_.empty()) {
diff --git a/src/cobalt/render_tree/image.h b/src/cobalt/render_tree/image.h index 9e4e276..acca4ed 100644 --- a/src/cobalt/render_tree/image.h +++ b/src/cobalt/render_tree/image.h
@@ -49,8 +49,8 @@ return 1; case kPixelFormatUV8: return 2; + case kPixelFormatUYVY: case kPixelFormatInvalid: - default: DLOG(FATAL) << "Unexpected pixel format."; } return -1;
diff --git a/src/cobalt/render_tree/render_tree.gyp b/src/cobalt/render_tree/render_tree.gyp index 643e123..55c827c 100644 --- a/src/cobalt/render_tree/render_tree.gyp +++ b/src/cobalt/render_tree/render_tree.gyp
@@ -73,6 +73,7 @@ 'dependencies': [ '<(DEPTH)/cobalt/base/base.gyp:base', '<(DEPTH)/cobalt/math/math.gyp:math', + '<(DEPTH)/third_party/ots/ots.gyp:ots', ], },
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h index 2e535ee..47a4af2 100644 --- a/src/cobalt/render_tree/resource_provider_stub.h +++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -26,6 +26,8 @@ #include "cobalt/render_tree/image.h" #include "cobalt/render_tree/mesh.h" #include "cobalt/render_tree/resource_provider.h" +#include "third_party/ots/include/opentype-sanitiser.h" +#include "third_party/ots/include/ots-memory-stream.h" namespace cobalt { namespace render_tree { @@ -313,8 +315,19 @@ scoped_refptr<Typeface> CreateTypefaceFromRawData( scoped_ptr<RawTypefaceDataVector> raw_data, std::string* error_string) override { - UNREFERENCED_PARAMETER(raw_data); - UNREFERENCED_PARAMETER(error_string); + if (raw_data == NULL) { + *error_string = "No data to process"; + return NULL; + } + + ots::OTSContext context; + ots::ExpandingMemoryStream sanitized_data( + raw_data->size(), render_tree::ResourceProvider::kMaxTypefaceDataSize); + if (!context.Process(&sanitized_data, &((*raw_data)[0]), + raw_data->size())) { + *error_string = "OpenType sanitizer unable to process data"; + return NULL; + } return make_scoped_refptr(new TypefaceStub(NULL)); }
diff --git a/src/cobalt/renderer/backend/default_graphics_system.h b/src/cobalt/renderer/backend/default_graphics_system.h index 9e1e7fe..69abf3f 100644 --- a/src/cobalt/renderer/backend/default_graphics_system.h +++ b/src/cobalt/renderer/backend/default_graphics_system.h
@@ -16,6 +16,7 @@ #define COBALT_RENDERER_BACKEND_DEFAULT_GRAPHICS_SYSTEM_H_ #include "cobalt/renderer/backend/graphics_system.h" +#include "cobalt/system_window/system_window.h" namespace cobalt { namespace renderer { @@ -24,8 +25,15 @@ // The implementation of this function should be platform specific, and will // create and return a platform-specific graphics system object. This function // is expected to be the main entry-point into the graphics system for most -// Cobalt platforms. -scoped_ptr<GraphicsSystem> CreateDefaultGraphicsSystem(); +// Cobalt platforms. A |system_window| can optionally be provided, in which +// case the returned graphics system will be guaranteed to be compatible with +// the window passed in. +scoped_ptr<GraphicsSystem> CreateDefaultGraphicsSystem( + system_window::SystemWindow* system_window); + +inline scoped_ptr<GraphicsSystem> CreateDefaultGraphicsSystem() { + return CreateDefaultGraphicsSystem(nullptr); +} } // namespace backend } // namespace renderer
diff --git a/src/cobalt/renderer/backend/egl/display.cc b/src/cobalt/renderer/backend/egl/display.cc index 54ba0b3..7f2e1c4 100644 --- a/src/cobalt/renderer/backend/egl/display.cc +++ b/src/cobalt/renderer/backend/egl/display.cc
@@ -22,10 +22,7 @@ class DisplayRenderTargetEGL : public RenderTargetEGL { public: - // The DisplayRenderTargetEGL is constructed with a handle to a native - // window to use as the render target. - DisplayRenderTargetEGL(EGLDisplay display, EGLConfig config, - EGLNativeWindowType window_handle); + DisplayRenderTargetEGL(EGLDisplay display, EGLSurface surface); const math::Size& GetSize() const override; @@ -39,21 +36,15 @@ ~DisplayRenderTargetEGL() override; EGLDisplay display_; - EGLConfig config_; - - EGLNativeWindowType native_window_; EGLSurface surface_; math::Size size_; }; -DisplayRenderTargetEGL::DisplayRenderTargetEGL( - EGLDisplay display, EGLConfig config, EGLNativeWindowType window_handle) - : display_(display), config_(config), native_window_(window_handle) { - surface_ = eglCreateWindowSurface(display_, config_, native_window_, NULL); - CHECK_EQ(EGL_SUCCESS, eglGetError()); - +DisplayRenderTargetEGL::DisplayRenderTargetEGL(EGLDisplay display, + EGLSurface surface) + : display_(display), surface_(surface) { #if defined(COBALT_RENDER_DIRTY_REGION_ONLY) // Configure the surface to preserve contents on swap. EGLBoolean surface_attrib_set = @@ -82,10 +73,9 @@ EGLSurface DisplayRenderTargetEGL::GetSurface() const { return surface_; } -DisplayEGL::DisplayEGL(EGLDisplay display, EGLConfig config, - EGLNativeWindowType window_handle) { +DisplayEGL::DisplayEGL(EGLDisplay display, EGLSurface surface) { // A display effectively just hosts a DisplayRenderTargetEGL. - render_target_ = new DisplayRenderTargetEGL(display, config, window_handle); + render_target_ = new DisplayRenderTargetEGL(display, surface); } scoped_refptr<RenderTarget> DisplayEGL::GetRenderTarget() {
diff --git a/src/cobalt/renderer/backend/egl/display.h b/src/cobalt/renderer/backend/egl/display.h index a951122..cd73da1 100644 --- a/src/cobalt/renderer/backend/egl/display.h +++ b/src/cobalt/renderer/backend/egl/display.h
@@ -30,9 +30,9 @@ class DisplayEGL : public Display { public: - // A native window handle is passed in to use as the render target. - DisplayEGL(EGLDisplay display, EGLConfig config, - EGLNativeWindowType window_handle); + // The DisplayEGL is constructed with a EGLSurface created from + // a call to eglCreateWindowSurface(). + DisplayEGL(EGLDisplay display, EGLSurface surface); scoped_refptr<RenderTarget> GetRenderTarget() override;
diff --git a/src/cobalt/renderer/backend/egl/graphics_system.cc b/src/cobalt/renderer/backend/egl/graphics_system.cc index 3f4bf71..30159b6 100644 --- a/src/cobalt/renderer/backend/egl/graphics_system.cc +++ b/src/cobalt/renderer/backend/egl/graphics_system.cc
@@ -18,6 +18,7 @@ #if defined(ENABLE_GLIMP_TRACING) #include "base/debug/trace_event.h" #endif +#include "base/optional.h" #if defined(GLES3_SUPPORTED) #include "cobalt/renderer/backend/egl/texture_data_pbo.h" #else @@ -49,7 +50,81 @@ GlimpToBaseTraceEventBridge s_glimp_to_base_trace_event_bridge; #endif // #if defined(ENABLE_GLIMP_TRACING) -GraphicsSystemEGL::GraphicsSystemEGL() { +namespace { + +struct ChooseConfigResult { + ChooseConfigResult() : window_surface(EGL_NO_SURFACE) {} + + EGLConfig config; + // This will be EGL_NO_SURFACE if no window was provided to find a config to + // match with. + EGLSurface window_surface; +}; + +// Returns a config that matches the |attribute_list|. Optionally, +// |system_window| can also be provided in which case the config returned will +// also be guaranteed to match with it, and a EGLSurface will be returned +// for that window. +base::optional<ChooseConfigResult> ChooseConfig( + EGLDisplay display, EGLint* attribute_list, + system_window::SystemWindow* system_window) { + if (!system_window) { + // If there is no system window provided then this task is much easier, + // just return the first config that matches the provided attributes. + EGLint num_configs; + EGLConfig config; + eglChooseConfig(display, attribute_list, &config, 1, &num_configs); + DCHECK_GE(1, num_configs); + + if (num_configs == 1) { + ChooseConfigResult results; + results.config = config; + return results; + } else { + LOG(ERROR) << "Could not find a EGLConfig compatible with the specified " + << "attributes."; + return base::nullopt; + } + } + + // Retrieve *all* configs that match the attribute list, and for each of them + // test to see if we can successfully call eglCreateWindowSurface() with them. + // Return the first config that succeeds the above test. + EGLint num_configs = 0; + eglChooseConfig(display, attribute_list, NULL, 0, &num_configs); + CHECK_EQ(EGL_SUCCESS, eglGetError()); + CHECK_LT(0, num_configs); + + scoped_array<EGLConfig> configs(new EGLConfig[num_configs]); + eglChooseConfig(display, attribute_list, configs.get(), num_configs, + &num_configs); + + EGLNativeWindowType native_window = + (EGLNativeWindowType)(system_window->GetWindowHandle()); + + for (EGLint i = 0; i < num_configs; ++i) { + EGLSurface surface = + eglCreateWindowSurface(display, configs[i], native_window, NULL); + if (EGL_SUCCESS == eglGetError()) { + // We did it, we found a config that allows us to successfully create + // a surface. Return the config along with the created surface. + ChooseConfigResult result; + result.config = configs[i]; + result.window_surface = surface; + return result; + } + } + + // We could not find a config with the provided window, return a failure. + LOG(ERROR) << "Could not find a EGLConfig compatible with the specified " + << "EGLNativeWindowType object and attributes."; + return base::nullopt; +} + +} // namespace + +GraphicsSystemEGL::GraphicsSystemEGL( + system_window::SystemWindow* system_window) { #if defined(ENABLE_GLIMP_TRACING) // If glimp tracing is enabled, hook up glimp trace calls to Chromium's // base trace_event calls. @@ -93,8 +168,8 @@ EGL_NONE }; - EGLint num_configs; - eglChooseConfig(display_, attribute_list, &config_, 1, &num_configs); + base::optional<ChooseConfigResult> choose_config_results = + ChooseConfig(display_, attribute_list, system_window); #if defined(COBALT_RENDER_DIRTY_REGION_ONLY) // Try to allow preservation of the frame contents between swap calls -- @@ -102,16 +177,21 @@ DCHECK_EQ(EGL_SURFACE_TYPE, attribute_list[0]); EGLint& surface_type_value = attribute_list[1]; - if (eglGetError() != EGL_SUCCESS || num_configs == 0) { - // Swap buffer preservation may not be supported. Try to find a config - // without the feature. + // Swap buffer preservation may not be supported. If we failed to find a + // config with the EGL_SWAP_BEHAVIOR_PRESERVED_BIT attribute, try again + // without it. + if (!choose_config_results) { surface_type_value &= ~EGL_SWAP_BEHAVIOR_PRESERVED_BIT; - EGL_CALL( - eglChooseConfig(display_, attribute_list, &config_, 1, &num_configs)); + choose_config_results = + ChooseConfig(display_, attribute_list, system_window); } #endif // #if defined(COBALT_RENDER_DIRTY_REGION_ONLY) - DCHECK_EQ(1, num_configs); + DCHECK(choose_config_results); + + config_ = choose_config_results->config; + system_window_ = system_window; + window_surface_ = choose_config_results->window_surface; #if defined(GLES3_SUPPORTED) resource_context_.emplace(display_, config_); @@ -120,13 +200,29 @@ GraphicsSystemEGL::~GraphicsSystemEGL() { resource_context_ = base::nullopt; + + if (window_surface_ != EGL_NO_SURFACE) { + eglDestroySurface(display_, window_surface_); + } + eglTerminate(display_); } scoped_ptr<Display> GraphicsSystemEGL::CreateDisplay( system_window::SystemWindow* system_window) { - EGLNativeWindowType window_handle = GetHandleFromSystemWindow(system_window); - return scoped_ptr<Display>(new DisplayEGL(display_, config_, window_handle)); + EGLSurface surface; + if (system_window == system_window_ && window_surface_ != EGL_NO_SURFACE) { + // Hand-off our precreated window into the display being created. + surface = window_surface_; + window_surface_ = EGL_NO_SURFACE; + } else { + EGLNativeWindowType native_window = + (EGLNativeWindowType)(system_window->GetWindowHandle()); + surface = eglCreateWindowSurface(display_, config_, native_window, NULL); + CHECK_EQ(EGL_SUCCESS, eglGetError()); + } + + return scoped_ptr<Display>(new DisplayEGL(display_, surface)); } scoped_ptr<GraphicsContext> GraphicsSystemEGL::CreateGraphicsContext() {
diff --git a/src/cobalt/renderer/backend/egl/graphics_system.h b/src/cobalt/renderer/backend/egl/graphics_system.h index 4402d04..51ee273 100644 --- a/src/cobalt/renderer/backend/egl/graphics_system.h +++ b/src/cobalt/renderer/backend/egl/graphics_system.h
@@ -27,17 +27,11 @@ namespace renderer { namespace backend { -// Helper function that gets the handle of a system window. -// Each platform that uses an EGL graphics system must provide an -// implementation of this function. -EGLNativeWindowType GetHandleFromSystemWindow( - system_window::SystemWindow* system_window); - // Returns a EGL-specific graphics system that is implemented via EGL and // OpenGL ES. class GraphicsSystemEGL : public GraphicsSystem { public: - GraphicsSystemEGL(); + explicit GraphicsSystemEGL(system_window::SystemWindow* system_window); ~GraphicsSystemEGL() override; EGLDisplay GetDisplay() { return display_; } @@ -56,6 +50,15 @@ EGLDisplay display_; EGLConfig config_; + // Track the system window that the precreated window surface is + // associated with. + system_window::SystemWindow* system_window_; + + // Part of our config selection process involves testing to see if the config + // can actually be used to create a surface. If successful, we store the + // created surface to avoid re-creating it when it is needed later. + EGLSurface window_surface_; + // A special graphics context which is used exclusively for mapping/unmapping // texture memory. base::optional<ResourceContext> resource_context_;
diff --git a/src/cobalt/renderer/backend/starboard/default_graphics_system_blitter.cc b/src/cobalt/renderer/backend/starboard/default_graphics_system_blitter.cc index e4e0ec8..29006ec 100644 --- a/src/cobalt/renderer/backend/starboard/default_graphics_system_blitter.cc +++ b/src/cobalt/renderer/backend/starboard/default_graphics_system_blitter.cc
@@ -21,7 +21,9 @@ namespace renderer { namespace backend { -scoped_ptr<GraphicsSystem> CreateDefaultGraphicsSystem() { +scoped_ptr<GraphicsSystem> CreateDefaultGraphicsSystem( + system_window::SystemWindow* system_window) { + UNREFERENCED_PARAMETER(system_window); return scoped_ptr<GraphicsSystem>(new GraphicsSystemBlitter()); }
diff --git a/src/cobalt/renderer/backend/starboard/default_graphics_system_egl.cc b/src/cobalt/renderer/backend/starboard/default_graphics_system_egl.cc index 938f02e..223bde3 100644 --- a/src/cobalt/renderer/backend/starboard/default_graphics_system_egl.cc +++ b/src/cobalt/renderer/backend/starboard/default_graphics_system_egl.cc
@@ -21,13 +21,9 @@ namespace renderer { namespace backend { -scoped_ptr<GraphicsSystem> CreateDefaultGraphicsSystem() { - return scoped_ptr<GraphicsSystem>(new GraphicsSystemEGL()); -} - -EGLNativeWindowType GetHandleFromSystemWindow( +scoped_ptr<GraphicsSystem> CreateDefaultGraphicsSystem( system_window::SystemWindow* system_window) { - return (EGLNativeWindowType)(system_window->GetWindowHandle()); + return scoped_ptr<GraphicsSystem>(new GraphicsSystemEGL(system_window)); } } // namespace backend
diff --git a/src/cobalt/renderer/backend/starboard/default_graphics_system_stub.cc b/src/cobalt/renderer/backend/starboard/default_graphics_system_stub.cc index 855f61c..af29901 100644 --- a/src/cobalt/renderer/backend/starboard/default_graphics_system_stub.cc +++ b/src/cobalt/renderer/backend/starboard/default_graphics_system_stub.cc
@@ -19,7 +19,9 @@ namespace renderer { namespace backend { -scoped_ptr<GraphicsSystem> CreateDefaultGraphicsSystem() { +scoped_ptr<GraphicsSystem> CreateDefaultGraphicsSystem( + system_window::SystemWindow* system_window) { + UNREFERENCED_PARAMETER(system_window); return scoped_ptr<GraphicsSystem>(new GraphicsSystemStub()); }
diff --git a/src/cobalt/renderer/copy_font_data.gypi b/src/cobalt/renderer/copy_font_data.gypi index 4617aea..5575237 100644 --- a/src/cobalt/renderer/copy_font_data.gypi +++ b/src/cobalt/renderer/copy_font_data.gypi
@@ -346,4 +346,10 @@ ], }, ], + + 'all_dependent_settings': { + 'variables': { + 'content_deploy_subdirs': [ 'fonts' ] + } + }, }
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc index 0cb3df3..51fb5d7 100644 --- a/src/cobalt/renderer/pipeline.cc +++ b/src/cobalt/renderer/pipeline.cc
@@ -80,9 +80,6 @@ last_did_rasterize_(false), last_animations_expired_(true), last_stat_tracked_animations_expired_(true), - rasterize_periodic_timer_("Renderer.Rasterize.Duration", - kRasterizePeriodicTimerEntriesPerUpdate, - false /*enable_entry_list_c_val*/), rasterize_animations_timer_("Renderer.Rasterize.Animations", kRasterizeAnimationsTimerMaxEntries, true /*enable_entry_list_c_val*/), @@ -357,14 +354,6 @@ bool animations_active = !are_stat_tracked_animations_expired && did_rasterize; - // The rasterization is only timed with the periodic timer when the render - // tree has changed. This ensures that the frames being timed are consistent - // between platforms that submit unchanged trees and those that don't. - if (did_rasterize) { - rasterize_periodic_timer_.Start(start_time); - rasterize_periodic_timer_.Stop(end_time); - } - if (last_animations_active || animations_active) { // The rasterization is only timed with the animations timer when there are // animations to track. This applies when animations were active during
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h index f2c3663..5eb6228 100644 --- a/src/cobalt/renderer/pipeline.h +++ b/src/cobalt/renderer/pipeline.h
@@ -244,13 +244,9 @@ bool last_did_rasterize_; // Timer tracking the amount of time spent in - // |RasterizeSubmissionToRenderTarget| when the render tree has changed. - // The tracking is flushed when the max count is hit. - base::CValCollectionTimerStats<base::CValPublic> rasterize_periodic_timer_; - // Timer tracking the amount of time spent in // |RasterizeSubmissionToRenderTarget| while animations are active. The // tracking is flushed when the animations expire. - base::CValCollectionTimerStats<base::CValDebug> rasterize_animations_timer_; + base::CValCollectionTimerStats<base::CValPublic> rasterize_animations_timer_; // Accumulates render tree rasterization interval times but does not flush // them until the maximum number of samples is gathered. @@ -264,16 +260,16 @@ rasterize_animations_interval_timer_; // The total number of new render trees that have been rasterized. - base::CVal<int> new_render_tree_rasterize_count_; + base::CVal<int, base::CValPublic> new_render_tree_rasterize_count_; // The last time that a newly encountered render tree was first rasterized. - base::CVal<int64> new_render_tree_rasterize_time_; + base::CVal<int64, base::CValPublic> new_render_tree_rasterize_time_; // Whether or not animations are currently playing. - base::CVal<bool> has_active_animations_c_val_; + base::CVal<bool, base::CValPublic> has_active_animations_c_val_; // The most recent time animations started playing. - base::CVal<int64> animations_start_time_; + base::CVal<int64, base::CValPublic> animations_start_time_; // The most recent time animations ended playing. - base::CVal<int64> animations_end_time_; + base::CVal<int64, base::CValPublic> animations_end_time_; #if defined(ENABLE_DEBUG_CONSOLE) // Dumps the current render tree to the console.
diff --git a/src/cobalt/renderer/rasterizer/benchmark.cc b/src/cobalt/renderer/rasterizer/benchmark.cc index 4989c3a..5403068 100644 --- a/src/cobalt/renderer/rasterizer/benchmark.cc +++ b/src/cobalt/renderer/rasterizer/benchmark.cc
@@ -76,9 +76,18 @@ // be done. base::debug::TraceLog::GetInstance()->SetEnabled(false); + base::EventDispatcher event_dispatcher; + scoped_ptr<SystemWindow> test_system_window; + if (output_surface_type == kOutputSurfaceTypeDisplay) { + test_system_window.reset(new cobalt::system_window::SystemWindow( + &event_dispatcher, + cobalt::math::Size(kViewportWidth, kViewportHeight))); + } + // Setup our graphics system. scoped_ptr<GraphicsSystem> graphics_system = - cobalt::renderer::backend::CreateDefaultGraphicsSystem(); + cobalt::renderer::backend::CreateDefaultGraphicsSystem( + test_system_window.get()); scoped_ptr<GraphicsContext> graphics_context = graphics_system->CreateGraphicsContext(); @@ -86,14 +95,9 @@ scoped_ptr<Rasterizer> rasterizer = CreateDefaultRasterizer(graphics_context.get()); - base::EventDispatcher event_dispatcher; - scoped_ptr<SystemWindow> test_system_window; scoped_ptr<Display> test_display; scoped_refptr<RenderTarget> test_surface; if (output_surface_type == kOutputSurfaceTypeDisplay) { - test_system_window.reset(new cobalt::system_window::SystemWindow( - &event_dispatcher, - cobalt::math::Size(kViewportWidth, kViewportHeight))); test_display = graphics_system->CreateDisplay(test_system_window.get()); test_surface = test_display->GetRenderTarget(); } else if (output_surface_type == kOutputSurfaceTypeOffscreen) {
diff --git a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc index 0e42b4a..066fb91 100644 --- a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc +++ b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
@@ -235,6 +235,7 @@ // This surface may have already been in the cache if it was in there // with a different scale. In that case, replace the old one. cache_memory_usage_ -= found->second.GetEstimatedMemoryUsage(); + SbBlitterDestroySurface(found->second.surface); surface_map_.erase(found); }
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc index 2f6860e..901661d 100644 --- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc +++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
@@ -178,9 +178,14 @@ DCHECK(thread_checker_.CalledOnValidThread()); TRACE_EVENT0("cobalt::renderer", "SubmitToFallbackRasterizer"); + if (!scissor.IsExpressibleAsRect()) { + DLOG(WARNING) << "Invalid scissor of " << scissor.ToString() + << " passed into SubmitToFallbackRasterizer."; + return; + } + // Use skia to rasterize to the allocated offscreen target. fallback_render_target->save(); - fallback_render_target->clipRect(SkRect::MakeXYWH( scissor.x(), scissor.y(), scissor.width(), scissor.height())); fallback_render_target->concat(skia::CobaltMatrixToSkia(transform));
diff --git a/src/cobalt/renderer/rasterizer/lib/exported/graphics.h b/src/cobalt/renderer/rasterizer/lib/exported/graphics.h index 4705228..bd453bf 100644 --- a/src/cobalt/renderer/rasterizer/lib/exported/graphics.h +++ b/src/cobalt/renderer/rasterizer/lib/exported/graphics.h
@@ -48,9 +48,7 @@ } CbLibSize; typedef void (*CbLibGraphicsContextCreatedCallback)(void* context); -typedef void (*CbLibGraphicsBeginRenderFrameCallback)( - void* context, CbLibRenderContext* host_render_context); -typedef void (*CbLibGraphicsEndRenderFrameCallback)(void* context); +typedef void (*CbLibGraphicsRenderFrameCallback)(void* context); // Sets a callback which will be called from the rasterization thread once the // graphics context has been created. @@ -59,13 +57,10 @@ // Sets a callback which will be called as often as the platform can swap // buffers from the rasterization thread. -SB_EXPORT_PLATFORM void CbLibGraphicsSetBeginRenderFrameCallback( - void* context, CbLibGraphicsBeginRenderFrameCallback callback); - -// Sets a callback which will be called at the end of rendering, after swap -// buffers has been called. -SB_EXPORT_PLATFORM void CbLibGraphicsSetEndRenderFrameCallback( - void* context, CbLibGraphicsEndRenderFrameCallback callback); +// +// All rendering must be performed inside of this callback. +SB_EXPORT_PLATFORM void CbLibGraphicsSetRenderFrameCallback( + void* context, CbLibGraphicsRenderFrameCallback callback); // Returns the texture ID for the current RenderTree. This should be // re-retrieved each frame in the event that the underlying texture has @@ -83,6 +78,30 @@ SB_EXPORT_PLATFORM void CbLibGraphicsSetTargetMainTextureSize( const CbLibSize& target_render_size); +// Performs all Cobalt-related rendering, including the browser UI and the +// video if one is playing. +// +// This should only be called from the Cobalt rendering thread, inside of a +// callback set by CbLibGraphicsSetRenderFrameCallback. +SB_EXPORT_PLATFORM void CbLibGraphicsRenderCobalt(); + +// Copies the contents of the offscreen backbuffer to the Cobalt system window. +// Note that this call only copies the contents of the buffer but does not +// perform any presentation. You must also call CbLibGraphicsSwapBackbuffer +// in order to make the contents of the offscreen backbuffer visible! +// +// This should only be called from the Cobalt rendering thread, inside of a +// callback set by CbLibGraphicsSetRenderFrameCallback. +SB_EXPORT_PLATFORM void CbLibGraphicsCopyBackbuffer(uintptr_t surface, + float width_scale); + +// Swaps the backbuffer for the Cobalt system window, making a new frame +// visibile. +// +// This should only be called from the Cobalt rendering thread, inside of a +// callback set by CbLibGraphicsSetRenderFrameCallback. +SB_EXPORT_PLATFORM void CbLibGraphicsSwapBackbuffer(); + #ifdef __cplusplus } // extern "C" #endif
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc index b8df300..2244592 100644 --- a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc +++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
@@ -137,10 +137,7 @@ INSTANCE_CALLBACK_UPDATE(UpdateAspectRatio, CbLibVideoSetOnUpdateAspectRatio); INSTANCE_CALLBACK_UPDATE(GraphicsContextCreated, CbLibGraphicsSetContextCreatedCallback); -INSTANCE_CALLBACK_UPDATE(BeginRenderFrame, - CbLibGraphicsSetBeginRenderFrameCallback); -INSTANCE_CALLBACK_UPDATE(EndRenderFrame, - CbLibGraphicsSetEndRenderFrameCallback); +INSTANCE_CALLBACK_UPDATE(RenderFrame, CbLibGraphicsSetRenderFrameCallback); #undef INSTANCE_CALLBACK_UPDATE UpdateMeshes::LazyCallback g_update_meshes_callback = LAZY_INSTANCE_INITIALIZER; @@ -153,10 +150,7 @@ LAZY_INSTANCE_INITIALIZER; GraphicsContextCreated::LazyCallback g_graphics_context_created_callback = LAZY_INSTANCE_INITIALIZER; -BeginRenderFrame::LazyCallback g_begin_render_frame_callback = - LAZY_INSTANCE_INITIALIZER; -EndRenderFrame::LazyCallback g_end_render_frame_callback = - LAZY_INSTANCE_INITIALIZER; +RenderFrame::LazyCallback g_render_frame_callback = LAZY_INSTANCE_INITIALIZER; bool ApproxEqual(const cobalt::math::Size& a, const cobalt::math::Size& b, float epsilon) { @@ -191,6 +185,9 @@ void Submit(const scoped_refptr<render_tree::Node>& render_tree, const scoped_refptr<backend::RenderTarget>& render_target); + void RenderCobalt(); + void CopyBackbuffer(uintptr_t surface, float width_scale); + void SwapBackbuffer(); render_tree::ResourceProvider* GetResourceProvider(); @@ -206,7 +203,7 @@ private: void RenderOffscreenVideo(render_tree::FilterNode* map_to_mesh_filter_node); - scoped_refptr<render_tree::MatrixTransformNode> UpdateTextureSizeAndWrapNode( + void SubmitWithUpdatedTextureSize( const cobalt::math::Size& native_render_target_size, const scoped_refptr<render_tree::Node>& render_tree); @@ -221,6 +218,12 @@ #endif Rasterizer::Options options_; + // Temporary reference to the render tree and main window render target. + // + // Only valid inside of Submit(). + scoped_refptr<render_tree::Node> render_tree_temp_; + backend::RenderTargetEGL* main_render_target_temp_; + // The main offscreen render target to use when rendering UI or rectangular // video. scoped_refptr<backend::RenderTarget> main_offscreen_render_target_; @@ -258,14 +261,18 @@ hardware_rasterizer_(graphics_context, skia_atlas_width, skia_atlas_height, skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes, + #if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER) offscreen_target_cache_size_in_bytes, #endif purge_skia_font_caches_on_destruction #if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER) - , disable_rasterizer_caching + , + disable_rasterizer_caching #endif ), + render_tree_temp_(nullptr), + main_render_target_temp_(nullptr), video_projection_type_(kCbLibVideoProjectionTypeNone), video_stereo_mode_(render_tree::StereoMode::kMono), video_texture_rgb_(0), @@ -296,6 +303,8 @@ void ExternalRasterizer::Impl::Submit( const scoped_refptr<render_tree::Node>& render_tree, const scoped_refptr<backend::RenderTarget>& render_target) { + DCHECK(thread_checker_.CalledOnValidThread()); + backend::RenderTargetEGL* render_target_egl = base::polymorphic_downcast<backend::RenderTargetEGL*>( render_target.get()); @@ -306,15 +315,34 @@ // client implementing CbLibRenderFrame. if (!render_target_egl->IsWindowRenderTarget()) { hardware_rasterizer_.Submit(render_tree, render_target, options_); - return; - } + } else { + // Store the pointers to the render tree and target temporarily so they + // can be used from callbacks triggered from the app's RenderFrameCallback. + render_tree_temp_ = render_tree.get(); + main_render_target_temp_ = render_target_egl; - graphics_context_->MakeCurrentWithSurface(render_target_egl); + // TODO: Allow clients to specify arbitrary subtrees to render into + // different textures? + g_render_frame_callback.Get().Run(); + + render_tree_temp_ = nullptr; + main_render_target_temp_ = nullptr; + } +} + +void ExternalRasterizer::Impl::RenderCobalt() { + // This method must be called from the g_render_frame_callback set by the + // host, otherwise these members will be nullptr. + CHECK(main_render_target_temp_); + CHECK(render_tree_temp_.get()); + DCHECK(thread_checker_.CalledOnValidThread()); + + graphics_context_->MakeCurrentWithSurface(main_render_target_temp_); // Attempt to find map to mesh filter node, then render video subtree // offscreen. auto map_to_mesh_search = common::FindNode<render_tree::FilterNode>( - render_tree, base::Bind(common::HasMapToMesh), + render_tree_temp_, base::Bind(common::HasMapToMesh), base::Bind(common::ReplaceWithEmptyCompositionNode)); if (map_to_mesh_search.found_node != NULL) { base::optional<render_tree::MapToMeshFilter> filter = @@ -406,26 +434,25 @@ } } - const scoped_refptr<render_tree::MatrixTransformNode> scaled_main_node = - UpdateTextureSizeAndWrapNode(render_target->GetSize(), - map_to_mesh_search.replaced_tree); - hardware_rasterizer_.Submit(scaled_main_node, main_offscreen_render_target_, - options_); + SubmitWithUpdatedTextureSize(main_render_target_temp_->GetSize(), + map_to_mesh_search.replaced_tree); +} - CbLibRenderContext host_render_context; - // TODO: Allow clients to specify arbitrary subtrees to render into - // different textures? - g_begin_render_frame_callback.Get().Run(&host_render_context); +void ExternalRasterizer::Impl::CopyBackbuffer(uintptr_t surface, + float width_scale) { + // This method must be called from the g_render_frame_callback set by the + // host, otherwise this member will be nullptr. + CHECK(main_render_target_temp_); + DCHECK(thread_checker_.CalledOnValidThread()); // If there is any host-provided surface, mirror it directly into the // system window with a blit. The surface will be streched or shrunk as // appropriate so that it fits the system window's backbuffer. - if (host_render_context.surface_to_mirror != 0) { + if (surface != 0) { #ifdef ANGLE_ENABLE_D3D11 EGLContext egl_context = eglGetCurrentContext(); - EGLSurface egl_mirror_surface = reinterpret_cast<EGLSurface>( - host_render_context.surface_to_mirror); - EGLSurface egl_window_surface = render_target_egl->GetSurface(); + EGLSurface egl_mirror_surface = reinterpret_cast<EGLSurface>(surface); + EGLSurface egl_window_surface = main_render_target_temp_->GetSurface(); ::gl::Context* angle_context = reinterpret_cast<::gl::Context*>(egl_context); ::egl::Surface* angle_mirror_surface = reinterpret_cast<::egl::Surface*>( @@ -443,8 +470,7 @@ static_cast<::rx::SwapChain11*>(d3d_mirror_surface->getSwapChain()); ::rx::SwapChain11* d3d11_window_swapchain = static_cast<::rx::SwapChain11*>(d3d_window_surface->getSwapChain()); - float src_width_scale = - std::min(1.0f, std::max(host_render_context.width_to_mirror, 0.0f)); + float src_width_scale = std::min(1.0f, std::max(width_scale, 0.0f)); d3d11_renderer->getBlitter()->copyTexture( d3d11_mirror_swapchain->getRenderTargetShaderResource(), @@ -463,15 +489,26 @@ << "under DirectX."; #endif } - graphics_context_->SwapBuffers(render_target_egl); - g_end_render_frame_callback.Get().Run(); +} + +void ExternalRasterizer::Impl::SwapBackbuffer() { + // This method must be called from the g_render_frame_callback set by the + // host, otherwise this member will be nullptr. + CHECK(main_render_target_temp_); + DCHECK(thread_checker_.CalledOnValidThread()); + + graphics_context_->SwapBuffers(main_render_target_temp_); } // TODO: Share this logic with the ComponentRenderer. -scoped_refptr<render_tree::MatrixTransformNode> -ExternalRasterizer::Impl::UpdateTextureSizeAndWrapNode( +void ExternalRasterizer::Impl::SubmitWithUpdatedTextureSize( const cobalt::math::Size& native_render_target_size, const scoped_refptr<render_tree::Node>& render_tree) { + // We need to make sure that our texture ID gets updated after the + // HardwareRasterizer submits, because the render target in use might get + // swapped during the submit. + bool texture_id_needs_update = false; + // Create a new offscreen render target if the exist one's size is far enough // off from the target/ideal size. if (!main_offscreen_render_target_ || @@ -482,14 +519,7 @@ main_offscreen_render_target_ = graphics_context_->CreateOffscreenRenderTarget( target_main_render_target_size_); - // Note: The TextureEGL this pointer references must first be destroyed by - // calling reset() before a new TextureEGL can be constructed. - main_texture_.reset(); - main_texture_.reset(new backend::TextureEGL( - graphics_context_, - make_scoped_refptr( - base::polymorphic_downcast<backend::RenderTargetEGL*>( - main_offscreen_render_target_.get())))); + texture_id_needs_update = true; } DCHECK(native_render_target_size.width()); @@ -509,8 +539,22 @@ glm::mat4(1.0f), glm::vec3(texture_x_scale, texture_y_scale, 1))); const cobalt::math::Matrix3F root_transform_matrix = cobalt::math::Matrix3F::FromArray(glm::value_ptr(scale_mat)); - return scoped_refptr<render_tree::MatrixTransformNode>( + scoped_refptr<render_tree::MatrixTransformNode> scaled_main_node( new render_tree::MatrixTransformNode(render_tree, root_transform_matrix)); + + hardware_rasterizer_.Submit(scaled_main_node, main_offscreen_render_target_, + options_); + + if (texture_id_needs_update) { + // Note: The TextureEGL this pointer references must first be destroyed by + // calling reset() before a new TextureEGL can be constructed. + main_texture_.reset(); + main_texture_.reset(new backend::TextureEGL( + graphics_context_, + make_scoped_refptr( + base::polymorphic_downcast<backend::RenderTargetEGL*>( + main_offscreen_render_target_.get())))); + } } render_tree::ResourceProvider* ExternalRasterizer::Impl::GetResourceProvider() { @@ -548,21 +592,18 @@ 1.0f, kMaxRenderTargetSize); const math::Size video_size(target_width, target_height); + // We need to make sure that our texture ID gets updated after the + // HardwareRasterizer submits, because the render target in use might get + // swapped during the submit. + bool texture_id_needs_update = false; + if (!video_offscreen_render_target_ || video_offscreen_render_target_->GetSize() != video_size) { video_offscreen_render_target_ = graphics_context_->CreateOffscreenRenderTarget(video_size); DLOG(INFO) << "Created new video_offscreen_render_target_: " << video_offscreen_render_target_->GetSize(); - - // Note: The TextureEGL this pointer references must first be destroyed by - // calling reset() before a new TextureEGL can be constructed. - video_texture_.reset(); - video_texture_.reset(new backend::TextureEGL( - graphics_context_, - make_scoped_refptr( - base::polymorphic_downcast<backend::RenderTargetEGL*>( - video_offscreen_render_target_.get())))); + texture_id_needs_update = true; } if (image_node.get()) { @@ -581,6 +622,17 @@ hardware_rasterizer_.Submit(correctly_scaled_image_node, video_offscreen_render_target_, options_); + if (texture_id_needs_update) { + // Note: The TextureEGL this pointer references must first be destroyed by + // calling reset() before a new TextureEGL can be constructed. + video_texture_.reset(); + video_texture_.reset(new backend::TextureEGL( + graphics_context_, + make_scoped_refptr( + base::polymorphic_downcast<backend::RenderTargetEGL*>( + video_offscreen_render_target_.get())))); + } + const intptr_t video_texture_handle = video_texture_->GetPlatformHandle(); if (video_texture_rgb_ != video_texture_handle) { video_texture_rgb_ = video_texture_handle; @@ -664,18 +716,11 @@ : base::Bind(&GraphicsContextCreated::DefaultImplementation); } -void CbLibGraphicsSetBeginRenderFrameCallback( - void* context, CbLibGraphicsBeginRenderFrameCallback callback) { - g_begin_render_frame_callback.Get() = +void CbLibGraphicsSetRenderFrameCallback( + void* context, CbLibGraphicsRenderFrameCallback callback) { + g_render_frame_callback.Get() = callback ? base::Bind(callback, context) - : base::Bind(&BeginRenderFrame::DefaultImplementation); -} - -void CbLibGraphicsSetEndRenderFrameCallback( - void* context, CbLibGraphicsEndRenderFrameCallback callback) { - g_end_render_frame_callback.Get() = - callback ? base::Bind(callback, context) - : base::Bind(&EndRenderFrame::DefaultImplementation); + : base::Bind(&RenderFrame::DefaultImplementation); } intptr_t CbLibGrapicsGetMainTextureHandle() { @@ -700,3 +745,33 @@ const cobalt::math::Size size = CobaltSizeFromCbLibSize(target_render_size); g_external_rasterizer_impl->SetTargetMainTextureSize(size); } + +void CbLibGraphicsRenderCobalt() { + DCHECK(g_external_rasterizer_impl); + if (!g_external_rasterizer_impl) { + LOG(WARNING) << __FUNCTION__ + << "ExternalRasterizer not yet created; unable to progress."; + return; + } + g_external_rasterizer_impl->RenderCobalt(); +} + +void CbLibGraphicsCopyBackbuffer(uintptr_t surface, float width_scale) { + DCHECK(g_external_rasterizer_impl); + if (!g_external_rasterizer_impl) { + LOG(WARNING) << __FUNCTION__ + << "ExternalRasterizer not yet created; unable to progress."; + return; + } + g_external_rasterizer_impl->CopyBackbuffer(surface, width_scale); +} + +void CbLibGraphicsSwapBackbuffer() { + DCHECK(g_external_rasterizer_impl); + if (!g_external_rasterizer_impl) { + LOG(WARNING) << __FUNCTION__ + << "ExternalRasterizer not yet created; unable to progress."; + return; + } + g_external_rasterizer_impl->SwapBackbuffer(); +}
diff --git a/src/cobalt/renderer/rasterizer/pixel_test_fixture.cc b/src/cobalt/renderer/rasterizer/pixel_test_fixture.cc index 7701232..acab1be 100644 --- a/src/cobalt/renderer/rasterizer/pixel_test_fixture.cc +++ b/src/cobalt/renderer/rasterizer/pixel_test_fixture.cc
@@ -70,7 +70,7 @@ // be found in. Files should be placed in this location by the build process. FilePath GetTestInputDirectory() { FilePath in_file_dir; - PathService::Get(base::DIR_SOURCE_ROOT, &in_file_dir); + PathService::Get(base::DIR_TEST_DATA, &in_file_dir); return in_file_dir.Append(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("renderer")) .Append(FILE_PATH_LITERAL("rasterizer"))
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc index 57e3e24..3d936c4 100644 --- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc +++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -496,7 +496,8 @@ ots::ExpandingMemoryStream sanitized_data( raw_data->size(), render_tree::ResourceProvider::kMaxTypefaceDataSize); - if (!ots::Process(&sanitized_data, &((*raw_data)[0]), raw_data->size())) { + ots::OTSContext context; + if (!context.Process(&sanitized_data, &((*raw_data)[0]), raw_data->size())) { *error_string = "OpenType sanitizer unable to process data"; return NULL; }
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc index d19e7b8..9dae26d 100644 --- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc +++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -1166,11 +1166,11 @@ const math::RectF& content_rect, const render_tree::RoundedCorners& inner_rounded_corners, const render_tree::Border& border) { - // Render each border edge seperately using SkCanvas::clipPath() to clip out + // Render each border edge separately using SkCanvas::clipPath() to clip out // each edge's region from the overall RRect. // Divide the area into 4 trapezoids, each represented by 4 points that // encompass the clipped out region for a border edge (including the rounded - // corners). Draw within each clipped out region seperately. + // corners). Draw within each clipped out region separately. // A ___________ B // |\_________/| // ||E F||
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc index c892cf3..979a5e4 100644 --- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc +++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.cc
@@ -191,7 +191,8 @@ ots::ExpandingMemoryStream sanitized_data( raw_data->size(), render_tree::ResourceProvider::kMaxTypefaceDataSize); - if (!ots::Process(&sanitized_data, &((*raw_data)[0]), raw_data->size())) { + ots::OTSContext context; + if (!context.Process(&sanitized_data, &((*raw_data)[0]), raw_data->size())) { *error_string = "OpenType sanitizer unable to process data"; return NULL; }
diff --git a/src/cobalt/renderer/render_tree_pixel_tester.cc b/src/cobalt/renderer/render_tree_pixel_tester.cc index cfe8807..d30e1d3 100644 --- a/src/cobalt/renderer/render_tree_pixel_tester.cc +++ b/src/cobalt/renderer/render_tree_pixel_tester.cc
@@ -182,7 +182,7 @@ bitmap_diff.rowBytes() * r / sizeof(uint32_t); for (int c = 0; c < bitmap_a.width(); ++c) { // Check each pixel in the current row for differences. We do this by - // looking at each color channel seperately and taking the max of the + // looking at each color channel separately and taking the max of the // differences we see in each channel. int max_diff = 0; for (int i = 0; i < 4; ++i) {
diff --git a/src/cobalt/renderer/renderer.gyp b/src/cobalt/renderer/renderer.gyp index ae1e134..b7752ee 100644 --- a/src/cobalt/renderer/renderer.gyp +++ b/src/cobalt/renderer/renderer.gyp
@@ -98,24 +98,24 @@ '<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gtest.gyp:gtest', 'render_tree_pixel_tester', + 'renderer_copy_test_data', ], 'conditions': [ ['enable_map_to_mesh == 1', { 'defines' : ['ENABLE_MAP_TO_MESH'], }], ], - 'actions': [ - { - 'action_name': 'renderer_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/renderer/rasterizer/testdata', - ], - 'output_dir': 'cobalt/renderer/rasterizer', - }, - 'includes': ['../build/copy_test_data.gypi'], - } - ], + }, + { + 'target_name': 'renderer_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/cobalt/renderer/rasterizer/testdata', + ], + 'content_test_output_subdir': 'cobalt/renderer/rasterizer', + }, + 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], }, { 'target_name': 'renderer_test_deploy',
diff --git a/src/cobalt/renderer/renderer_module.cc b/src/cobalt/renderer/renderer_module.cc index 235fab1..99d6a09 100644 --- a/src/cobalt/renderer/renderer_module.cc +++ b/src/cobalt/renderer/renderer_module.cc
@@ -58,7 +58,7 @@ // Load up the platform's default graphics system. { TRACE_EVENT0("cobalt::renderer", "backend::CreateDefaultGraphicsSystem()"); - graphics_system_ = backend::CreateDefaultGraphicsSystem(); + graphics_system_ = backend::CreateDefaultGraphicsSystem(system_window_); } // Create/initialize the display using the system window
diff --git a/src/cobalt/renderer/test/png_utils/png_decode_test.cc b/src/cobalt/renderer/test/png_utils/png_decode_test.cc index 54b323d..d2a4194 100644 --- a/src/cobalt/renderer/test/png_utils/png_decode_test.cc +++ b/src/cobalt/renderer/test/png_utils/png_decode_test.cc
@@ -24,7 +24,7 @@ namespace { FilePath GetPremultipliedAlphaTestImagePath() { FilePath data_directory; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_directory)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &data_directory)); return data_directory.Append(FILE_PATH_LITERAL("test")) .Append(FILE_PATH_LITERAL("png_utils")) .Append(FILE_PATH_LITERAL("png_premultiplied_alpha_test_image.png"));
diff --git a/src/cobalt/renderer/test/png_utils/png_utils.gyp b/src/cobalt/renderer/test/png_utils/png_utils.gyp index 719ab86..8251a1c 100644 --- a/src/cobalt/renderer/test/png_utils/png_utils.gyp +++ b/src/cobalt/renderer/test/png_utils/png_utils.gyp
@@ -45,18 +45,7 @@ '<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gtest.gyp:gtest', 'png_utils', - ], - 'actions': [ - { - 'action_name': 'copy_data', - 'variables': { - 'input_files': [ - 'png_premultiplied_alpha_test_image.png', - ], - 'output_dir': 'test/png_utils', - }, - 'includes': ['../../../build/copy_test_data.gypi'], - }, + 'png_utils_copy_test_data', ], }, { @@ -69,19 +58,21 @@ '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/cobalt/trace_event/trace_event.gyp:run_all_benchmarks', 'png_utils', + 'png_utils_copy_test_data', ], - 'actions': [ - { - 'action_name': 'copy_data', - 'variables': { - 'input_files': [ - 'png_benchmark_image.png', - ], - 'output_dir': 'test/png_utils', - }, - 'includes': ['../../../build/copy_test_data.gypi'], - }, - ], + }, + + { + 'target_name': 'png_utils_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + 'png_benchmark_image.png', + 'png_premultiplied_alpha_test_image.png', + ], + 'content_test_output_subdir': 'test/png_utils', + }, + 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], }, ], }
diff --git a/src/cobalt/renderer/test/png_utils/png_utils_benchmark.cc b/src/cobalt/renderer/test/png_utils/png_utils_benchmark.cc index 02a119a..b123821 100644 --- a/src/cobalt/renderer/test/png_utils/png_utils_benchmark.cc +++ b/src/cobalt/renderer/test/png_utils/png_utils_benchmark.cc
@@ -21,7 +21,7 @@ namespace { FilePath GetBenchmarkImagePath() { FilePath data_directory; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_directory)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &data_directory)); return data_directory.Append(FILE_PATH_LITERAL("test")) .Append(FILE_PATH_LITERAL("png_utils")) .Append(FILE_PATH_LITERAL("png_benchmark_image.png")); @@ -52,7 +52,7 @@ "PNGFileReadContext::DecodeImageTo()", cobalt::trace_event::IN_SCOPE_DURATION) { FilePath data_directory; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_directory)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &data_directory)); const int kIterationCount = 20; for (int i = 0; i < kIterationCount; ++i) {
diff --git a/src/cobalt/renderer/test/scenes/image_wrap_scene.cc b/src/cobalt/renderer/test/scenes/image_wrap_scene.cc index 4cbbadc..c363ffc 100644 --- a/src/cobalt/renderer/test/scenes/image_wrap_scene.cc +++ b/src/cobalt/renderer/test/scenes/image_wrap_scene.cc
@@ -77,7 +77,7 @@ scoped_refptr<Image> GetTestImage(ResourceProvider* resource_provider) { // Load a test image from disk. FilePath data_directory; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_directory)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &data_directory)); return DecodePNGToRenderTreeImage( data_directory.Append(FILE_PATH_LITERAL("test")) .Append(FILE_PATH_LITERAL("scenes"))
diff --git a/src/cobalt/renderer/test/scenes/scenes.gyp b/src/cobalt/renderer/test/scenes/scenes.gyp index 164e371..d7c1ffb 100644 --- a/src/cobalt/renderer/test/scenes/scenes.gyp +++ b/src/cobalt/renderer/test/scenes/scenes.gyp
@@ -44,19 +44,19 @@ '<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree', '<(DEPTH)/cobalt/renderer/test/png_utils/png_utils.gyp:png_utils', '<(DEPTH)/cobalt/trace_event/trace_event.gyp:trace_event', + 'scenes_copy_test_data', ], - 'actions': [ - { - 'action_name': 'copy_data', - 'variables': { - 'input_files': [ - 'demo_image.png', - ], - 'output_dir': 'test/scenes', - }, - 'includes': ['../../../build/copy_test_data.gypi'], - }, - ], + }, + { + 'target_name': 'scenes_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + 'demo_image.png', + ], + 'content_test_output_subdir': 'test/scenes', + }, + 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], }, ], }
diff --git a/src/cobalt/renderer/test/scenes/spinning_sprites_scene.cc b/src/cobalt/renderer/test/scenes/spinning_sprites_scene.cc index 25a9766..6376e91 100644 --- a/src/cobalt/renderer/test/scenes/spinning_sprites_scene.cc +++ b/src/cobalt/renderer/test/scenes/spinning_sprites_scene.cc
@@ -106,7 +106,7 @@ scoped_refptr<Image> GetTestImage(ResourceProvider* resource_provider) { // Load a test image from disk. FilePath data_directory; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &data_directory)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &data_directory)); return DecodePNGToRenderTreeImage( data_directory.Append(FILE_PATH_LITERAL("test")) .Append(FILE_PATH_LITERAL("scenes"))
diff --git a/src/cobalt/samples/simple_example/simple_example.gyp b/src/cobalt/samples/simple_example/simple_example.gyp index f1660fb..feec6c2 100644 --- a/src/cobalt/samples/simple_example/simple_example.gyp +++ b/src/cobalt/samples/simple_example/simple_example.gyp
@@ -94,22 +94,22 @@ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests', '<(DEPTH)/testing/gtest.gyp:gtest', 'simple_example_lib', + 'simple_example_copy_test_data', ], + }, - # This section is optional and is only needed if tests are using - # external data. - 'actions': [ - { - 'action_name': 'copy_test_data', - 'variables': { - 'input_files': [ - 'testdata', - ], - 'output_dir': 'cobalt/samples', - }, - 'includes': [ '../../build/copy_test_data.gypi' ], - }, - ], + # This target is optional and is only needed if tests are using + # external data. + { + 'target_name': 'simple_example_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + 'testdata', + ], + 'content_test_output_subdir': 'cobalt/samples', + }, + 'includes': [ '<(DEPTH)/starboard/build/copy_test_data.gypi' ], }, # This target will deploy the test and its data to a platform specific
diff --git a/src/cobalt/samples/simple_example/simple_example_test.cc b/src/cobalt/samples/simple_example/simple_example_test.cc index 377c436..a8f8d42 100644 --- a/src/cobalt/samples/simple_example/simple_example_test.cc +++ b/src/cobalt/samples/simple_example/simple_example_test.cc
@@ -71,7 +71,7 @@ TEST_F(SimpleExampleTest, MultiplyAndAddDataFromFile) { FilePath data_dir; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); + ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir)); data_dir = data_dir.Append(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("samples")) .Append(FILE_PATH_LITERAL("testdata")) @@ -119,7 +119,7 @@ TEST_F(SimpleExampleTest, PrintData) { FilePath data_dir; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); + ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir)); data_dir = data_dir.Append(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("samples")) .Append(FILE_PATH_LITERAL("testdata"));
diff --git a/src/cobalt/script/javascript_engine.h b/src/cobalt/script/javascript_engine.h index e9388fa..b96a432 100644 --- a/src/cobalt/script/javascript_engine.h +++ b/src/cobalt/script/javascript_engine.h
@@ -27,6 +27,12 @@ class GlobalEnvironment; +// https://webplatform.github.io/docs/apis/timing/properties/memory/ +struct HeapStatistics { + size_t total_heap_size; + size_t used_heap_size; +}; + class JavaScriptEngine { public: struct Options { @@ -51,12 +57,6 @@ static scoped_ptr<JavaScriptEngine> CreateEngine( const Options& options = Options()); - // Updates the memory usage and returns the total memory that is reserved - // across all of the engines. This includes the part that is actually - // occupied by JavaScript objects, and the part that is not yet. - // This function is defined per-implementation. - static size_t UpdateMemoryStatsAndReturnReserved(); - // Create a new JavaScript global object proxy. virtual scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() = 0; @@ -64,8 +64,9 @@ virtual void CollectGarbage() = 0; // Indicate to the JavaScript heap that extra bytes have been allocated by - // some Javascript object. This may mean collection needs to happen sooner. - virtual void ReportExtraMemoryCost(size_t bytes) = 0; + // some Javascript object. The engine will take advantage of this + // information to the best of its ability. + virtual void AdjustAmountOfExternalAllocatedMemory(int64_t bytes) = 0; // Installs an ErrorHandler for listening to JavaScript errors. // Returns true if the error handler could be installed. False otherwise. @@ -74,6 +75,11 @@ // Adjusts the memory threshold to force garbage collection. virtual void SetGcThreshold(int64_t bytes) = 0; + // Get the current |HeapStatistics| measurements for this engine. Note that + // engines will implement this to the best of their ability, but will likely + // be unable to provide perfectly accurate values. + virtual HeapStatistics GetHeapStatistics() = 0; + protected: virtual ~JavaScriptEngine() {} friend class scoped_ptr<JavaScriptEngine>;
diff --git a/src/cobalt/script/mozjs-45/conversion_helpers.h b/src/cobalt/script/mozjs-45/conversion_helpers.h index f72ba96..e5e57a2 100644 --- a/src/cobalt/script/mozjs-45/conversion_helpers.h +++ b/src/cobalt/script/mozjs-45/conversion_helpers.h
@@ -519,7 +519,9 @@ exception_state->SetSimpleException(kDoesNotImplementInterface); } -// CallbackInterface -> JSValue +// ScriptValue<T> (where T is a CallbackInterface) -> JSValue +// Note that there is currently no implementation for ScriptValue<T> where T +// is not a CallbackInterface. template <typename T> inline void ToJSValue(JSContext* context, const ScriptValue<T>* callback_interface, @@ -544,7 +546,7 @@ // can get the implementing object. const MozjsCallbackInterfaceClass* mozjs_callback_interface = base::polymorphic_downcast<const MozjsCallbackInterfaceClass*>( - user_object_holder->GetScriptValue()); + user_object_holder->GetValue()); DCHECK(mozjs_callback_interface); out_value.setObjectOrNull(mozjs_callback_interface->handle()); } @@ -688,6 +690,23 @@ return; } +template <typename T> +void ToJSValue(JSContext* context, + const ScriptValue<Promise<T>>* promise_holder, + JS::MutableHandleValue out_value); + +template <typename T> +void ToJSValue(JSContext* context, ScriptValue<Promise<T>>* promise_holder, + JS::MutableHandleValue out_value); + +// script::Handle<T> -> JSValue +template <typename T> +void ToJSValue(JSContext* context, const Handle<T>& handle, + JS::MutableHandleValue out_value) { + TRACK_MEMORY_SCOPE("Javascript"); + ToJSValue(context, handle.GetScriptValue(), out_value); +} + } // namespace mozjs } // namespace script } // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs-45.gyp b/src/cobalt/script/mozjs-45/mozjs-45.gyp index 8eddeb6..51afe27 100644 --- a/src/cobalt/script/mozjs-45/mozjs-45.gyp +++ b/src/cobalt/script/mozjs-45/mozjs-45.gyp
@@ -43,6 +43,7 @@ 'mozjs_tracer.h', 'mozjs_user_object_holder.h', 'mozjs_value_handle.h', + 'mozjs_value_handle.cc', 'mozjs_wrapper_handle.h', 'native_promise.h', 'promise_wrapper.cc',
diff --git a/src/cobalt/script/mozjs-45/mozjs.cc b/src/cobalt/script/mozjs-45/mozjs.cc index dc8e290..f54f449 100644 --- a/src/cobalt/script/mozjs-45/mozjs.cc +++ b/src/cobalt/script/mozjs-45/mozjs.cc
@@ -89,7 +89,7 @@ // Execute the script and get the results of execution. std::string result; bool success = global_environment->EvaluateScript( - source, &result, false /*mute_errors*/); + source, false /*mute_errors*/, &result); // Echo the results to stdout. if (!success) { std::cout << "Exception: ";
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.cc b/src/cobalt/script/mozjs-45/mozjs_engine.cc index c56dfed..5825d73 100644 --- a/src/cobalt/script/mozjs-45/mozjs_engine.cc +++ b/src/cobalt/script/mozjs-45/mozjs_engine.cc
@@ -66,45 +66,6 @@ return js::DoesntShadow; } -class EngineStats { - public: - EngineStats(); - - static EngineStats* GetInstance() { - return Singleton<EngineStats, - StaticMemorySingletonTraits<EngineStats> >::get(); - } - - void EngineCreated() { ++engine_count_; } - void EngineDestroyed() { --engine_count_; } - - size_t UpdateMemoryStatsAndReturnReserved() { - // Accessing CVals triggers a lock, so rely on local variables when - // possible to avoid unecessary locking. - size_t allocated_memory = - MemoryAllocatorReporter::Get()->GetCurrentBytesAllocated(); - size_t mapped_memory = - MemoryAllocatorReporter::Get()->GetCurrentBytesMapped(); - - allocated_memory_ = allocated_memory; - mapped_memory_ = mapped_memory; - - return allocated_memory + mapped_memory; - } - - private: - base::CVal<int> engine_count_; - base::CVal<base::cval::SizeInBytes, base::CValPublic> allocated_memory_; - base::CVal<base::cval::SizeInBytes, base::CValPublic> mapped_memory_; -}; - -EngineStats::EngineStats() - : engine_count_("Count.JS.Engine", 0, - "Total JavaScript engine registered."), - allocated_memory_("Memory.JS.AllocatedMemory", 0, - "JS memory occupied by the Mozjs allocator."), - mapped_memory_("Memory.JS.MappedMemory", 0, "JS mapped memory.") {} - // Pretend we always preserve wrappers since we never call // SetPreserveWrapperCallback anywhere else. This is necessary for // TryPreserveReflector called by WeakMap to not crash. Disabling @@ -135,11 +96,18 @@ } // namespace -MozjsEngine::MozjsEngine(const Options& options) - : context_(nullptr), accumulated_extra_memory_cost_(0), options_(options) { +MozjsEngine::MozjsEngine(const Options& options) : options_(options) { TRACE_EVENT0("cobalt::script", "MozjsEngine::MozjsEngine()"); SbOnce(&g_js_init_once_control, CallInitAndRegisterShutDownOnce); - runtime_ = JS_NewRuntime(options_.gc_threshold_bytes); + + // Set the nursery size to half of the GC threshold size. Analysis has shown + // that allocating less than this does not reduce the total amount of JS + // memory used, and allocating more does not provide performance improvements. + constexpr size_t kMinMaxNurseryBytes = 1 * 1024 * 1024; + uint32_t max_nursery_bytes = + std::max(options_.gc_threshold_bytes / 2, kMinMaxNurseryBytes); + + runtime_ = JS_NewRuntime(options_.gc_threshold_bytes, max_nursery_bytes); CHECK(runtime_); // Sets the size of the native stack that should not be exceeded. @@ -185,13 +153,10 @@ js::SetPreserveWrapperCallback(runtime_, DummyPreserveWrapperCallback); JS_SetErrorReporter(runtime_, ReportErrorHandler); - - EngineStats::GetInstance()->EngineCreated(); } MozjsEngine::~MozjsEngine() { DCHECK(thread_checker_.CalledOnValidThread()); - EngineStats::GetInstance()->EngineDestroyed(); JS_DestroyRuntime(runtime_); } @@ -207,27 +172,37 @@ JS_GC(runtime_); } -void MozjsEngine::ReportExtraMemoryCost(size_t bytes) { +void MozjsEngine::AdjustAmountOfExternalAllocatedMemory(int64_t bytes) { DCHECK(thread_checker_.CalledOnValidThread()); - accumulated_extra_memory_cost_ += bytes; - - const bool do_collect_garbage = - accumulated_extra_memory_cost_ > options_.gc_threshold_bytes; - if (do_collect_garbage) { - accumulated_extra_memory_cost_ = 0; + // |force_gc_heuristic_| is only incremented, never decremented. See its + // declaration in the header for details. + force_gc_heuristic_ += (bytes > 0) ? bytes : 0; + if (force_gc_heuristic_ > options_.gc_threshold_bytes) { + force_gc_heuristic_ = 0; CollectGarbage(); } } -bool MozjsEngine::RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) { +bool MozjsEngine::RegisterErrorHandler(ErrorHandler handler) { + DCHECK(thread_checker_.CalledOnValidThread()); error_handler_ = handler; return true; } void MozjsEngine::SetGcThreshold(int64_t bytes) { + DCHECK(thread_checker_.CalledOnValidThread()); runtime_->gc.setMaxMallocBytes(static_cast<size_t>(bytes)); } +HeapStatistics MozjsEngine::GetHeapStatistics() { + DCHECK(thread_checker_.CalledOnValidThread()); + // There is unfortunately no easy way to get used vs total in SpiderMonkey, + // so just return total bytes allocated for both. + size_t total_heap_size = + MemoryAllocatorReporter::Get()->GetTotalHeapSize(); + return {total_heap_size, total_heap_size}; +} + // static bool MozjsEngine::ContextCallback(JSContext* context, unsigned context_op, void* data) { @@ -249,7 +224,7 @@ void* data) { MozjsEngine* engine = reinterpret_cast<MozjsEngine*>(data); if (status == JSGC_END) { - engine->accumulated_extra_memory_cost_ = 0; + engine->force_gc_heuristic_ = 0; } if (!engine->context_) { return; @@ -287,11 +262,5 @@ return make_scoped_ptr<JavaScriptEngine>(new mozjs::MozjsEngine(options)); } -// static -size_t JavaScriptEngine::UpdateMemoryStatsAndReturnReserved() { - return mozjs::EngineStats::GetInstance() - ->UpdateMemoryStatsAndReturnReserved(); -} - } // namespace script } // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs_engine.h b/src/cobalt/script/mozjs-45/mozjs_engine.h index 9e27846..b1d01ad 100644 --- a/src/cobalt/script/mozjs-45/mozjs_engine.h +++ b/src/cobalt/script/mozjs-45/mozjs_engine.h
@@ -33,9 +33,10 @@ scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() override; void CollectGarbage() override; - void ReportExtraMemoryCost(size_t bytes) override; - bool RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) override; + void AdjustAmountOfExternalAllocatedMemory(int64_t bytes) override; + bool RegisterErrorHandler(ErrorHandler handler) override; void SetGcThreshold(int64_t bytes) override; + HeapStatistics GetHeapStatistics() override; private: static bool ContextCallback(JSContext* context, unsigned context_op, @@ -46,20 +47,25 @@ base::ThreadChecker thread_checker_; - // Top-level object that represents the Javascript engine. Typically there is - // one per process, but it's allowed to have multiple. + // Top-level object that represents the JavaScript engine. JSRuntime* runtime_; // The sole context created for this JSRuntime. - JSContext* context_; + JSContext* context_ = nullptr; - // The amount of externally allocated memory since last forced GC. - size_t accumulated_extra_memory_cost_; + // The amount of externally allocated memory since the last GC, regardless of + // whether it was forced by us. Once this value exceeds + // |options_.gc_threshold_bytes|, we will force a garbage collection, and set + // this value back to 0. Note that there is no guarantee that the allocations + // that incremented this value will have actually been collected. Also note + // that this value is only incremented, never decremented, as it makes for a + // better force garbage collection heuristic. + int64_t force_gc_heuristic_ = 0; - // Used to handle javascript errors. + // Used to handle JavaScript errors. ErrorHandler error_handler_; - JavaScriptEngine::Options options_; + Options options_; }; } // namespace mozjs } // namespace script
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc index 68c1832..0bb5c7e 100644 --- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc +++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -32,6 +32,7 @@ #include "cobalt/script/mozjs-45/util/stack_trace_helpers.h" #include "nb/memory_scope.h" #include "third_party/mozjs-45/js/public/Initialization.h" +#include "third_party/mozjs-45/js/src/jsapi.h" #include "third_party/mozjs-45/js/src/jsfriendapi.h" #include "third_party/mozjs-45/js/src/jsfun.h" #include "third_party/mozjs-45/js/src/jsobj.h" @@ -57,6 +58,7 @@ namespace cobalt { namespace script { namespace mozjs { + namespace { // Class definition for global object with no bindings. @@ -119,8 +121,29 @@ }; static base::LazyInstance<MozjsStubHandler> proxy_handler; + } // namespace +// static +MozjsGlobalEnvironment* MozjsGlobalEnvironment::GetFromContext( + JSContext* context) { + MozjsGlobalEnvironment* global_proxy = + static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); + DCHECK(global_proxy); + return global_proxy; +} + +// static +bool MozjsGlobalEnvironment::CheckEval(JSContext* context) { + TRACK_MEMORY_SCOPE("Javascript"); + MozjsGlobalEnvironment* global_environment = GetFromContext(context); + DCHECK(global_environment); + if (!global_environment->report_eval_.is_null()) { + global_environment->report_eval_.Run(); + } + return global_environment->eval_enabled_; +} + MozjsGlobalEnvironment::MozjsGlobalEnvironment(JSRuntime* runtime) : context_(NULL), garbage_collection_count_(0), @@ -324,7 +347,7 @@ auto it = kept_alive_objects_.find(wrappable.get()); DCHECK(it != kept_alive_objects_.end()); it->second.count--; - DCHECK(it->second.count >= 0); + DCHECK_GE(it->second.count, 0); if (it->second.count == 0) { kept_alive_objects_.erase(it); } @@ -389,11 +412,60 @@ return script_value_factory_.get(); } +// static +void MozjsGlobalEnvironment::TraceFunction(JSTracer* tracer, void* data) { + MozjsGlobalEnvironment* global_environment = + static_cast<MozjsGlobalEnvironment*>(data); + if (global_environment->global_object_proxy_) { + JS_CallObjectTracer(tracer, &global_environment->global_object_proxy_, + "MozjsGlobalEnvironment"); + } + + for (auto& interface_data : global_environment->cached_interface_data_) { + if (interface_data.prototype) { + JS_CallObjectTracer(tracer, &interface_data.prototype, + "MozjsGlobalEnvironment"); + } + if (interface_data.interface_object) { + JS_CallObjectTracer(tracer, &interface_data.interface_object, + "MozjsGlobalEnvironment"); + } + } + + auto& kept_alive_objects_ = global_environment->kept_alive_objects_; + for (auto& pair : kept_alive_objects_) { + auto& counted_heap_object = pair.second; + DCHECK_GT(counted_heap_object.count, 0); + JS_CallObjectTracer(tracer, &counted_heap_object.heap_object, + "MozjsGlobalEnvironment"); + } +} + void MozjsGlobalEnvironment::EvaluateAutomatics() { EvaluateEmbeddedScript( MozjsEmbeddedResources::promise_min_js, sizeof(MozjsEmbeddedResources::promise_min_js), "promise.min.js"); + // Store the |Promise| (currently the only polyfill that needs to be + // accessed from native code) implemented there in our own handle (as + // opposed to fetching it from the global object everytime we need it), in + // order to defend ourselves from application JavaScript modifying + // |window.Promise| later. + { + DCHECK(!stored_promise_constructor_); + JS::RootedObject global_object( + context_, js::GetProxyTargetObject(global_object_proxy_)); + JSAutoRequest auto_request(context_); + JSAutoCompartment auto_compartment(context_, global_object); + JS::RootedValue promise_constructor_property(context_); + bool result = JS_GetProperty(context_, global_object, "Promise", + &promise_constructor_property); + DCHECK(result); + DCHECK(promise_constructor_property.isObject()); + stored_promise_constructor_ = JS::PersistentRootedObject( + context_, &promise_constructor_property.toObject()); + DCHECK(stored_promise_constructor_); + } EvaluateEmbeddedScript( MozjsEmbeddedResources::byte_length_queuing_strategy_js, sizeof(MozjsEmbeddedResources::byte_length_queuing_strategy_js), @@ -448,14 +520,6 @@ } } -MozjsGlobalEnvironment* MozjsGlobalEnvironment::GetFromContext( - JSContext* context) { - MozjsGlobalEnvironment* global_proxy = - static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context)); - DCHECK(global_proxy); - return global_proxy; -} - void MozjsGlobalEnvironment::SetGlobalObjectProxyAndWrapper( JS::HandleObject global_object_proxy, const scoped_refptr<Wrappable>& wrappable) { @@ -531,44 +595,6 @@ } } -void MozjsGlobalEnvironment::TraceFunction(JSTracer* tracer, void* data) { - MozjsGlobalEnvironment* global_environment = - static_cast<MozjsGlobalEnvironment*>(data); - if (global_environment->global_object_proxy_) { - JS_CallObjectTracer(tracer, &global_environment->global_object_proxy_, - "MozjsGlobalEnvironment"); - } - - for (auto& interface_data : global_environment->cached_interface_data_) { - if (interface_data.prototype) { - JS_CallObjectTracer(tracer, &interface_data.prototype, - "MozjsGlobalEnvironment"); - } - if (interface_data.interface_object) { - JS_CallObjectTracer(tracer, &interface_data.interface_object, - "MozjsGlobalEnvironment"); - } - } - - auto& kept_alive_objects_ = global_environment->kept_alive_objects_; - for (auto& pair : kept_alive_objects_) { - auto& counted_heap_object = pair.second; - DCHECK(counted_heap_object.count > 0); - JS_CallObjectTracer(tracer, &counted_heap_object.heap_object, - "MozjsGlobalEnvironment"); - } -} - -bool MozjsGlobalEnvironment::CheckEval(JSContext* context) { - TRACK_MEMORY_SCOPE("Javascript"); - MozjsGlobalEnvironment* global_environment = GetFromContext(context); - DCHECK(global_environment); - if (!global_environment->report_eval_.is_null()) { - global_environment->report_eval_.Run(); - } - return global_environment->eval_enabled_; -} - } // namespace mozjs } // namespace script } // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h index d4c64c3..da75983 100644 --- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h +++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -47,6 +47,13 @@ class MozjsGlobalEnvironment : public GlobalEnvironment, public Wrappable::CachedWrapperAccessor { public: + static MozjsGlobalEnvironment* GetFromContext(JSContext* context); + + // This will be called every time an attempt is made to use eval() and + // friends. If it returns false, then the ReportErrorHandler will be fired + // with an error that eval() is disabled. + static bool CheckEval(JSContext* context); + explicit MozjsGlobalEnvironment(JSRuntime* runtime); ~MozjsGlobalEnvironment() override; @@ -114,6 +121,13 @@ return environment_settings_; } + void GetStoredPromiseConstructor( + JS::MutableHandleObject out_promise_constructor) { + DCHECK(stored_promise_constructor_); + out_promise_constructor.set(*stored_promise_constructor_); + DCHECK(out_promise_constructor); + } + void SetGlobalObjectProxyAndWrapper( JS::HandleObject global_object_proxy, const scoped_refptr<Wrappable>& wrappable); @@ -131,13 +145,6 @@ void BeginGarbageCollection(); void EndGarbageCollection(); - static MozjsGlobalEnvironment* GetFromContext(JSContext* context); - - // This will be called every time an attempt is made to use eval() and - // friends. If it returns false, then the ReportErrorHandler will be fired - // with an error that eval() is disabled. - static bool CheckEval(JSContext* context); - void ReportError(const char* message, JSErrorReport* report); private: @@ -156,7 +163,9 @@ int count; }; - // Evaluates any automatically included Javascript for the environment. + static void TraceFunction(JSTracer* trace, void* data); + + // Evaluates any automatically included JavaScript for the environment. void EvaluateAutomatics(); bool EvaluateScriptInternal(const scoped_refptr<SourceCode>& source_code, @@ -166,8 +175,6 @@ void EvaluateEmbeddedScript(const unsigned char* data, size_t size, const char* filename); - static void TraceFunction(JSTracer* trace, void* data); - base::ThreadChecker thread_checker_; JSContext* context_; int garbage_collection_count_; @@ -184,6 +191,13 @@ // TODO: Should be |std::unordered_set| once C++11 is enabled. base::hash_set<Traceable*> visited_traceables_; + // Store the result of "Promise" immediately after evaluating the + // promise polyfill in order to defend against application JavaScript + // changing it to something else later. Note that this should be removed if + // we ever rebase to a SpiderMonkey version >= 50, as that is when native + // promises were added to it. + base::optional<JS::PersistentRootedObject> stored_promise_constructor_; + // If non-NULL, the error message from the ReportErrorHandler will get // assigned to this instead of being printed. std::string* last_error_message_;
diff --git a/src/cobalt/script/mozjs-45/mozjs_script_value_factory.cc b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.cc index 626106e..87070e3 100644 --- a/src/cobalt/script/mozjs-45/mozjs_script_value_factory.cc +++ b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.cc
@@ -23,11 +23,12 @@ MozjsScriptValueFactory::MozjsScriptValueFactory( MozjsGlobalEnvironment* global_environment) : global_environment_(global_environment) {} + } // namespace mozjs // Implementation of template function declared in the base class. template <typename T> -scoped_ptr<ScriptValue<Promise<T> > > ScriptValueFactory::CreatePromise() { +Handle<Promise<T>> ScriptValueFactory::CreatePromise() { mozjs::MozjsScriptValueFactory* mozjs_this = base::polymorphic_downcast<mozjs::MozjsScriptValueFactory*>(this); return mozjs_this->CreatePromise<T>();
diff --git a/src/cobalt/script/mozjs-45/mozjs_script_value_factory.h b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.h index a7b33cf..2bfb00b 100644 --- a/src/cobalt/script/mozjs-45/mozjs_script_value_factory.h +++ b/src/cobalt/script/mozjs-45/mozjs_script_value_factory.h
@@ -28,12 +28,10 @@ class MozjsScriptValueFactory : public ScriptValueFactory { public: explicit MozjsScriptValueFactory(MozjsGlobalEnvironment* global_environment); - ~MozjsScriptValueFactory() override {} template <typename T> - scoped_ptr<ScriptValue<Promise<T> > > CreatePromise() { - typedef ScriptValue<Promise<T> > ScriptPromiseType; - typedef MozjsUserObjectHolder<NativePromise<T> > MozjsPromiseHolderType; + Handle<Promise<T>> CreatePromise() { + using MozjsPromiseHolderType = MozjsUserObjectHolder<NativePromise<T>>; JSContext* context = global_environment_->context(); JS::RootedObject global_object(context, @@ -42,11 +40,9 @@ JSAutoCompartment auto_compartment(context, global_object); JS::RootedValue promise_wrapper(context); - promise_wrapper.setObjectOrNull( - PromiseWrapper::Create(context, global_object)); - scoped_ptr<ScriptPromiseType> promise( + promise_wrapper.setObjectOrNull(PromiseWrapper::Create(context)); + return Handle<Promise<T>>( new MozjsPromiseHolderType(context, promise_wrapper)); - return promise.Pass(); } private:
diff --git a/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h b/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h index a33ff7a..4168da3 100644 --- a/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h +++ b/src/cobalt/script/mozjs-45/mozjs_user_object_holder.h
@@ -74,14 +74,6 @@ return util::IsSameGcThing(context_, value1, value2); } - void TraceMembers(Tracer* tracer) override { - if (handle_) { - MozjsTracer* mozjs_tracer = - base::polymorphic_downcast<MozjsTracer*>(tracer); - handle_->Trace(mozjs_tracer->js_tracer()); - } - } - void RegisterOwner(Wrappable* owner) override { JSAutoRequest auto_request(context_); JS::RootedValue owned_value(context_, js_value()); @@ -123,8 +115,12 @@ } } - const typename MozjsUserObjectType::BaseType* GetScriptValue() - const override { + typename MozjsUserObjectType::BaseType* GetValue() override { + return const_cast<typename MozjsUserObjectType::BaseType*>( + static_cast<const MozjsUserObjectHolder*>(this)->GetValue()); + } + + const typename MozjsUserObjectType::BaseType* GetValue() const override { return handle_ ? &handle_.value() : NULL; } @@ -147,11 +143,16 @@ return handle_->handle(); } + JSContext* context() const { + DCHECK(context_); + return context_; + } + private: JSContext* context_; base::optional<MozjsUserObjectType> handle_; int prevent_garbage_collection_count_; - base::optional<JS::Value> persistent_root_; + base::optional<JS::PersistentRootedValue> persistent_root_; }; } // namespace mozjs
diff --git a/src/cobalt/script/mozjs-45/mozjs_value_handle.cc b/src/cobalt/script/mozjs-45/mozjs_value_handle.cc new file mode 100644 index 0000000..33f20b4 --- /dev/null +++ b/src/cobalt/script/mozjs-45/mozjs_value_handle.cc
@@ -0,0 +1,93 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <string> + +#include "cobalt/script/mozjs-45/mozjs_value_handle.h" + +#include "cobalt/script/mozjs-45/conversion_helpers.h" + +namespace cobalt { +namespace script { +namespace mozjs { + +std::unordered_map<std::string, std::string> ConvertSimpleObjectToMap( + const ValueHandleHolder& value, script::ExceptionState* exception_state) { + const MozjsValueHandleHolder* mozjs_value_handle_holder = + base::polymorphic_downcast<const MozjsValueHandleHolder*>(&value); + + const ValueHandle* value_handle = mozjs_value_handle_holder->GetValue(); + + const MozjsValueHandle* mozjs_value_handle = + base::polymorphic_downcast<const MozjsValueHandle*>(value_handle); + + if (!mozjs_value_handle->value().isObject()) { + exception_state->SetSimpleException(kTypeError, + "The value must be an object"); + return std::unordered_map<std::string, std::string>{}; + } + + JSContext* context = mozjs_value_handle_holder->context(); + + JS::RootedObject js_object(context, mozjs_value_handle->handle()); + + JS::Rooted<JS::IdVector> property_ids(context, JS::IdVector(context)); + JS_Enumerate(context, js_object, &property_ids); + + std::unordered_map<std::string, std::string> object_properties_map; + + // Create a map of the object's properties. + for (size_t i = 0; i < property_ids.length(); i++) { + JS::RootedId id(context, property_ids[i]); + JS::RootedValue property(context); + JS_GetPropertyById(context, js_object, id, &property); + + JS::RootedValue property_id(context); + JS_IdToValue(context, id, &property_id); + if (!property_id.isString()) { + exception_state->SetSimpleException( + kTypeError, "Object property ids must be strings"); + return std::unordered_map<std::string, std::string>{}; + } + + if (!property.isNumber() && !property.isString() && !property.isBoolean()) { + exception_state->SetSimpleException( + kTypeError, + "Object property values must be a number, string or boolean"); + return std::unordered_map<std::string, std::string>{}; + } + + std::string property_id_string; + FromJSValue(context, property_id, kNoConversionFlags, exception_state, + &property_id_string); + std::string property_string; + FromJSValue(context, property, kNoConversionFlags, exception_state, + &property_string); + + object_properties_map.insert( + std::make_pair(property_id_string, property_string)); + } + + return object_properties_map; +} + +} // namespace mozjs + +std::unordered_map<std::string, std::string> ConvertSimpleObjectToMap( + const ValueHandleHolder& value, script::ExceptionState* exception_state) { + return mozjs::ConvertSimpleObjectToMap(value, exception_state); +} + +} // namespace script +} // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/native_promise.h b/src/cobalt/script/mozjs-45/native_promise.h index f5f09d6..a5be3f7 100644 --- a/src/cobalt/script/mozjs-45/native_promise.h +++ b/src/cobalt/script/mozjs-45/native_promise.h
@@ -15,6 +15,7 @@ #ifndef COBALT_SCRIPT_MOZJS_45_NATIVE_PROMISE_H_ #define COBALT_SCRIPT_MOZJS_45_NATIVE_PROMISE_H_ +#include "base/logging.h" #include "cobalt/script/mozjs-45/conversion_helpers.h" #include "cobalt/script/mozjs-45/mozjs_exception_state.h" #include "cobalt/script/mozjs-45/mozjs_user_object_holder.h" @@ -38,8 +39,6 @@ out_value.setUndefined(); } -// Shared functionality for NativePromise<T>. Does not implement the Resolve -// function, since that needs to be specialized for Promise<T>. template <typename T> class NativePromise : public Promise<T> { public: @@ -75,13 +74,11 @@ JSObject* promise() const { return promise_resolver_->GetPromise(); } void Resolve(const ResolveType& value) const override { - JS::RootedObject promise_wrapper(context_, - promise_resolver_->get().GetObject()); - if (!promise_wrapper) { - return; - } + JS::RootedObject promise_resolver(context_, + promise_resolver_->get().GetObject()); + DCHECK(promise_resolver); JSAutoRequest auto_request(context_); - JSAutoCompartment auto_compartment(context_, promise_wrapper); + JSAutoCompartment auto_compartment(context_, promise_resolver); JS::RootedValue converted_value(context_); ToJSValue(context_, value, &converted_value); promise_resolver_->Resolve(converted_value); @@ -90,9 +87,7 @@ void Reject() const override { JS::RootedObject promise_resolver(context_, promise_resolver_->get().GetObject()); - if (!promise_resolver) { - return; - } + DCHECK(promise_resolver); JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, promise_resolver); promise_resolver_->Reject(JS::UndefinedHandleValue); @@ -101,9 +96,7 @@ void Reject(SimpleExceptionType exception) const override { JS::RootedObject promise_resolver(context_, promise_resolver_->get().GetObject()); - if (!promise_resolver) { - return; - } + DCHECK(promise_resolver); JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, promise_resolver); JS::RootedValue error_result(context_); @@ -115,9 +108,7 @@ void Reject(const scoped_refptr<ScriptException>& result) const override { JS::RootedObject promise_resolver(context_, promise_resolver_->get().GetObject()); - if (!promise_resolver) { - return; - } + DCHECK(promise_resolver); JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, promise_resolver); JS::RootedValue converted_result(context_); @@ -125,6 +116,15 @@ promise_resolver_->Reject(converted_result); } + PromiseState State() const { + JS::RootedObject promise_resolver(context_, + promise_resolver_->get().GetObject()); + DCHECK(promise_resolver); + JSAutoRequest auto_request(context_); + JSAutoCompartment auto_compartment(context_, promise_resolver); + return promise_resolver_->State(); + } + private: JSContext* context_; base::optional<PromiseWrapper> promise_resolver_; @@ -153,7 +153,7 @@ const NativePromise<T>* native_promise = base::polymorphic_downcast<const NativePromise<T>*>( - user_object_holder->GetScriptValue()); + user_object_holder->GetValue()); DCHECK(native_promise); out_value.setObjectOrNull(native_promise->promise()); @@ -171,16 +171,6 @@ out_value); } -// Destroys |promise_holder| as soon as the conversion is done. -// This is useful when a wrappable is not interested in retaining a reference -// to a promise, typically when a promise is resolved or rejected synchronously. -template <typename T> -inline void ToJSValue(JSContext* context, - scoped_ptr<ScriptValue<Promise<T>>> promise_holder, - JS::MutableHandleValue out_value) { - ToJSValue(context, promise_holder.get(), out_value); -} - } // namespace mozjs } // namespace script } // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/promise_wrapper.cc b/src/cobalt/script/mozjs-45/promise_wrapper.cc index c972ed1..9f264ba 100644 --- a/src/cobalt/script/mozjs-45/promise_wrapper.cc +++ b/src/cobalt/script/mozjs-45/promise_wrapper.cc
@@ -15,12 +15,15 @@ #include "cobalt/script/mozjs-45/promise_wrapper.h" #include "base/logging.h" +#include "cobalt/script/mozjs-45/mozjs_global_environment.h" #include "third_party/mozjs-45/js/src/jsfun.h" namespace cobalt { namespace script { namespace mozjs { + namespace { + enum ReservedSlots { kResolveFunction, kRejectFunction, @@ -106,19 +109,11 @@ } // Get the Promise constructor from the global object. -JSObject* GetPromiseConstructor(JSContext* context, - JS::HandleObject global_object) { - JS::RootedValue promise_constructor_property(context); - bool result = JS_GetProperty(context, global_object, "Promise", - &promise_constructor_property); - DCHECK(result); - if (!promise_constructor_property.isObject() || - !JS_ObjectIsFunction(context, &promise_constructor_property.toObject())) { - DLOG(ERROR) << "\"Promise\" property is not a function."; - return NULL; - } - - return &promise_constructor_property.toObject(); +void GetPromiseConstructor(JSContext* context, + JS::MutableHandleObject out_promise_constructor) { + MozjsGlobalEnvironment* global_environment = + MozjsGlobalEnvironment::GetFromContext(context); + global_environment->GetStoredPromiseConstructor(out_promise_constructor); } void Settle(JSContext* context, JS::HandleValue result, @@ -140,15 +135,12 @@ } } // namespace -JSObject* PromiseWrapper::Create(JSContext* context, - JS::HandleObject global_object) { +JSObject* PromiseWrapper::Create(JSContext* context) { // Get the Promise constructor. - JS::RootedObject constructor(context, - GetPromiseConstructor(context, global_object)); - if (!constructor) { - DLOG(ERROR) << "Failed to find Promise constructor."; - return NULL; - } + JS::RootedObject constructor(context); + GetPromiseConstructor(context, &constructor); + DCHECK(constructor); + // Create a new NativePromise JS object, and bind it to the NativeExecutor // function. JS::RootedObject promise_wrapper(context, CreateNativePromise(context)); @@ -163,10 +155,8 @@ args[0].set(JS::ObjectOrNullValue(executor)); JS::RootedObject promise_object(context); promise_object = JS_New(context, constructor, args); - if (!promise_object) { - DLOG(ERROR) << "Failed to create a new Promise."; - return NULL; - } + DCHECK(promise_object); + // Maintain a handle to the promise object on the NativePromise. JS::RootedValue promise_value(context); promise_value.setObject(*promise_object); @@ -201,6 +191,26 @@ } } +PromiseState PromiseWrapper::State() const { + JS::RootedObject promise(context_, GetPromise()); + DCHECK(promise); + JS::RootedValue state_value(context_); + JS_GetProperty(context_, promise, "_state", &state_value); + DCHECK(state_value.isInt32()); + int state = state_value.toInt32(); + switch (state) { + case 0: + return PromiseState::kPending; + case 1: + return PromiseState::kFulfilled; + case 2: + return PromiseState::kRejected; + default: + NOTREACHED(); + } + return PromiseState::kRejected; +} + PromiseWrapper::PromiseWrapper(JSContext* context, JS::HandleValue promise_wrapper) : context_(context), weak_promise_wrapper_(context, promise_wrapper) {
diff --git a/src/cobalt/script/mozjs-45/promise_wrapper.h b/src/cobalt/script/mozjs-45/promise_wrapper.h index cd34140..9404e16 100644 --- a/src/cobalt/script/mozjs-45/promise_wrapper.h +++ b/src/cobalt/script/mozjs-45/promise_wrapper.h
@@ -17,6 +17,7 @@ #include "base/memory/scoped_ptr.h" #include "cobalt/script/mozjs-45/weak_heap_object.h" +#include "cobalt/script/promise.h" #include "third_party/mozjs-45/js/src/jsapi.h" namespace cobalt { @@ -30,7 +31,7 @@ public: // Creates a new JS object that wraps a new Promise, created using the // Promise constructor on |global_object|. Returns NULL on failure. - static JSObject* Create(JSContext* context, JS::HandleObject global_object); + static JSObject* Create(JSContext* context); PromiseWrapper(JSContext* context, JS::HandleValue promise_wrapper); @@ -39,6 +40,7 @@ JSObject* GetPromise() const; void Resolve(JS::HandleValue value) const; void Reject(JS::HandleValue value) const; + PromiseState State() const; private: JSContext* context_;
diff --git a/src/cobalt/script/mozjs-45/referenced_object_map.cc b/src/cobalt/script/mozjs-45/referenced_object_map.cc index 38312fe..a9f665c 100644 --- a/src/cobalt/script/mozjs-45/referenced_object_map.cc +++ b/src/cobalt/script/mozjs-45/referenced_object_map.cc
@@ -16,7 +16,9 @@ #include <utility> +#include "cobalt/script/mozjs-45/mozjs_global_environment.h" #include "cobalt/script/mozjs-45/util/algorithm_helpers.h" +#include "cobalt/script/mozjs-45/wrapper_private.h" #include "nb/memory_scope.h" #include "third_party/mozjs-45/js/src/jsapi.h" @@ -28,17 +30,24 @@ : context_(context) {} // Add/Remove a reference from a WrapperPrivate to a JSValue. -void ReferencedObjectMap::AddReferencedObject(const Wrappable* wrappable, +void ReferencedObjectMap::AddReferencedObject(Wrappable* wrappable, JS::HandleValue referee) { TRACK_MEMORY_SCOPE("Javascript"); DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!referee.isNullOrUndefined()); DCHECK(referee.isGCThing()); + + // Force a wrapper to get created for |wrappable| in order to ensure it will + // get traced by SpiderMonkey, allowing us to find |referee| later. + WrapperPrivate* wrapper_private = WrapperPrivate::GetFromWrappable( + wrappable, context_, + MozjsGlobalEnvironment::GetFromContext(context_)->wrapper_factory()); + referenced_objects_.insert( std::make_pair(wrappable, WeakHeapObject(context_, referee))); } -void ReferencedObjectMap::RemoveReferencedObject(const Wrappable* wrappable, +void ReferencedObjectMap::RemoveReferencedObject(Wrappable* wrappable, JS::HandleValue referee) { DCHECK(thread_checker_.CalledOnValidThread()); auto pair_range = referenced_objects_.equal_range(wrappable); @@ -55,7 +64,7 @@ } void ReferencedObjectMap::TraceReferencedObjects(JSTracer* trace, - const Wrappable* wrappable) { + Wrappable* wrappable) { DCHECK(thread_checker_.CalledOnValidThread()); auto pair_range = referenced_objects_.equal_range(wrappable); for (auto it = pair_range.first; it != pair_range.second; ++it) {
diff --git a/src/cobalt/script/mozjs-45/referenced_object_map.h b/src/cobalt/script/mozjs-45/referenced_object_map.h index 7ae3281..7962be5 100644 --- a/src/cobalt/script/mozjs-45/referenced_object_map.h +++ b/src/cobalt/script/mozjs-45/referenced_object_map.h
@@ -33,12 +33,11 @@ public: explicit ReferencedObjectMap(JSContext* context); - void AddReferencedObject(const Wrappable* wrappable, JS::HandleValue referee); - void RemoveReferencedObject(const Wrappable* wrappable, - JS::HandleValue referee); + void AddReferencedObject(Wrappable* wrappable, JS::HandleValue referee); + void RemoveReferencedObject(Wrappable* wrappable, JS::HandleValue referee); // Trace all objects referenced from this |wrappable|. - void TraceReferencedObjects(JSTracer* trace, const Wrappable* wrappable); + void TraceReferencedObjects(JSTracer* trace, Wrappable* wrappable); // Remove any referenced objects that are NULL. It may be the case that a // weak reference to an object was garbage collected, so remove it from the
diff --git a/src/cobalt/script/promise.h b/src/cobalt/script/promise.h index 6497f0d..5c6a1fb 100644 --- a/src/cobalt/script/promise.h +++ b/src/cobalt/script/promise.h
@@ -27,6 +27,14 @@ // exposed to code outside of engine specific script implementations. struct PromiseResultUndefined {}; +// See [[PromiseState]] in +// https://tc39.github.io/ecma262/#sec-properties-of-promise-instances +enum class PromiseState { + kPending, + kFulfilled, + kRejected, +}; + // Interface for interacting with a JavaScript Promise object that is resolved // or rejected from native code. template <typename T> @@ -41,6 +49,10 @@ virtual void Reject() const = 0; virtual void Reject(SimpleExceptionType exception) const = 0; virtual void Reject(const scoped_refptr<ScriptException>& result) const = 0; + + // Returns the value of the [[PromiseState]] field. + virtual PromiseState State() const = 0; + virtual ~Promise() {} };
diff --git a/src/cobalt/script/scope.h b/src/cobalt/script/scope.h index 606dd89..c21e4b6 100644 --- a/src/cobalt/script/scope.h +++ b/src/cobalt/script/scope.h
@@ -72,11 +72,9 @@ return kLocalString; case kWith: return kWithString; - default: { - NOTREACHED(); - return kLocalString; - } } + NOTREACHED(); + return kLocalString; } };
diff --git a/src/cobalt/script/script_runner.cc b/src/cobalt/script/script_runner.cc index 3734048..436ad49 100644 --- a/src/cobalt/script/script_runner.cc +++ b/src/cobalt/script/script_runner.cc
@@ -93,7 +93,7 @@ } std::string result; if (!global_environment_->EvaluateScript(source_code, mute_errors, &result)) { - DLOG(WARNING) << "Failed to execute JavaScript: " << result; + LOG(WARNING) << "Failed to execute JavaScript: " << result; #if defined(HANDLE_CORE_DUMP) script_runner_log.Get().IncrementFailCount(); #endif
diff --git a/src/cobalt/script/script_value.h b/src/cobalt/script/script_value.h index 09bd016..2a9af9d 100644 --- a/src/cobalt/script/script_value.h +++ b/src/cobalt/script/script_value.h
@@ -15,6 +15,8 @@ #ifndef COBALT_SCRIPT_SCRIPT_VALUE_H_ #define COBALT_SCRIPT_SCRIPT_VALUE_H_ +#include <algorithm> + #include "base/basictypes.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -23,6 +25,9 @@ namespace cobalt { namespace script { +template <typename T> +class Handle; + class Wrappable; // ScriptValue is a wrapper around raw JavaScript values that are being passed @@ -42,70 +47,35 @@ // detached from the rest of the graph of JavaScript GC things, and can safely // be garbage collected. template <class T> -class ScriptValue : public Traceable { +class ScriptValue { public: - // A reference type for when a |Traceable| owns a |ScriptValue|, and only - // wants to keep it alive if the owning object continues to get traced. This - // is the preferred reference type in that if you can use it, you should (as - // opposed to legacy types |Reference| or |StrongReference|, which predate - // the TraceMembers system). - class TracedReference : public Traceable { - public: - explicit TracedReference(scoped_ptr<ScriptValue> script_value) - : referenced_value_(script_value.Pass()) { - DCHECK(referenced_value_); - } - - explicit TracedReference(const ScriptValue& script_value) - : referenced_value_(script_value.MakeCopy()) { - DCHECK(referenced_value_); - } - - const T& value() const { return *(referenced_value_->GetScriptValue()); } - - // Return the referenced ScriptValue. This ScriptValue can - // be passed back into the JavaScript bindings layer where the referenced - // JavaScript object can be extracted from the ScriptValue. - const ScriptValue<T>& referenced_value() const { - return *(referenced_value_.get()); - } - - void TraceMembers(Tracer* tracer) override { - tracer->Trace(referenced_value_.get()); - } - - private: - scoped_ptr<ScriptValue> referenced_value_; - - DISALLOW_COPY_AND_ASSIGN(TracedReference); - }; - // The Reference class maintains the ownership relationship between a // Wrappable and the JavaScript value wrapped by a ScriptValue. This is an // RAII object in that creation of a Reference instance will mark the - // underlying value as owned by this wrappable, and the underlying object will - // be unmarked when this Reference is destructed. The lifetime of a Reference - // must be at least as long as the Wrappable that has been passed into the - // constructor. + // underlying value as owned by this wrappable, and the underlying value + // will be unmarked when this Reference is destructed. The lifetime of a + // Reference must be at least as long as the Wrappable that has been passed + // into the constructor. class Reference { public: - Reference(Wrappable* wrappable, scoped_ptr<ScriptValue> script_value) - : owner_(wrappable), referenced_value_(script_value.Pass()) { - DCHECK(referenced_value_); - referenced_value_->RegisterOwner(owner_); - } - Reference(Wrappable* wrappable, const ScriptValue& script_value) : owner_(wrappable), referenced_value_(script_value.MakeCopy()) { DCHECK(referenced_value_); referenced_value_->RegisterOwner(owner_); } - const T& value() const { return *(referenced_value_->GetScriptValue()); } + Reference(Wrappable* wrappable, const Handle<T>& local) + : owner_(wrappable), + referenced_value_(local.GetScriptValue()->MakeCopy()) { + DCHECK(referenced_value_); + referenced_value_->RegisterOwner(owner_); + } - // Return the referenced ScriptValue. This ScriptValue can - // be passed back into the JavaScript bindings layer where the referenced - // JavaScript object can be extracted from the ScriptValue. + const T& value() const { return *(referenced_value_->GetValue()); } + + // Return the referenced ScriptValue. This ScriptValue can be passed back + // into the JavaScript bindings layer where the referenced JavaScript + // value can be extracted from the ScriptValue. const ScriptValue<T>& referenced_value() const { return *(referenced_value_.get()); } @@ -119,50 +89,15 @@ DISALLOW_COPY_AND_ASSIGN(Reference); }; - // Prevent garbage collection of the ScriptValue. This should be used with - // care as it can result in resource leaks if not managed appropriately. - // A common use case is to create a StrongReference on the stack when a - // ScriptValue is passed into a function, but a reference to the ScriptValue - // doesn't need to be retained past the scope of the function. - class StrongReference { - public: - explicit StrongReference(scoped_ptr<ScriptValue> script_value) - : referenced_value_(script_value.Pass()) { - DCHECK(referenced_value_); - referenced_value_->PreventGarbageCollection(); - } - - explicit StrongReference(const ScriptValue& script_value) - : referenced_value_(script_value.MakeCopy()) { - DCHECK(referenced_value_); - referenced_value_->PreventGarbageCollection(); - } - - const T& value() const { return *(referenced_value_->GetScriptValue()); } - - // Return the referenced ScriptValue. This ScriptValue can - // be passed back into the JavaScript bindings layer where the referenced - // JavaScript object can be extracted from the ScriptValue. - const ScriptValue<T>& referenced_value() const { - return *(referenced_value_.get()); - } - - ~StrongReference() { referenced_value_->AllowGarbageCollection(); } - - private: - scoped_ptr<ScriptValue> referenced_value_; - - DISALLOW_COPY_AND_ASSIGN(StrongReference); - }; - - // Return true iff |other| refers to the same underlying JavaScript object. + // Return true if and only if |other| refers to the same underlying + // JavaScript value. virtual bool EqualTo(const ScriptValue& other) const = 0; - // Returns true if this ScriptValue is referring to a NULL JavaScript object. - bool IsNull() const { return GetScriptValue() == NULL; } + // Returns true if this ScriptValue is referring to a NULL JavaScript value. + bool IsNull() const { return GetValue() == NULL; } // Creates a new ScriptValue that contains a weak reference to the same - // underlying JavaScript object. Note that this will not prevent the object + // underlying JavaScript value. Note that this will not prevent the value // from being garbage collected, one must create a Reference to do that. scoped_ptr<ScriptValue> MakeWeakCopy() const { return MakeCopy().Pass(); @@ -172,27 +107,110 @@ virtual ~ScriptValue() {} private: - // Mark/unmark this Wrappable as owning a handle to the underlying JavaScript - // object. + // Register this Wrappable as owning a handle to the underlying JavaScript + // value. virtual void RegisterOwner(Wrappable* owner) = 0; virtual void DeregisterOwner(Wrappable* owner) = 0; - // Prevent/Allow garbage collection of the underlying ScriptValue. Calls must - // be balanced and are not idempodent. While the number of calls to |Prevent| - // are greater than the number of calls to |Allow|, the underlying object - // will never be garbage collected. + // Prevent/Allow garbage collection of the underlying ScriptValue. Calls + // must be balanced and are not idempodent. While the number of calls to + // |Prevent| are greater than the number of calls to |Allow|, the underlying + // value will never be garbage collected. virtual void PreventGarbageCollection() = 0; virtual void AllowGarbageCollection() = 0; - // Return a pointer to the object that wraps the underlying JavaScript object. - virtual const T* GetScriptValue() const = 0; + // Return a pointer to the value that wraps the underlying JavaScript + // value. + virtual T* GetValue() = 0; + virtual const T* GetValue() const = 0; - // Make a new ScriptValue instance that holds a handle to the same underlying - // JavaScript object. This should not be called for a ScriptValue that has a - // NULL script object (that is, GetScriptValue() returns NULL). + // Make a new ScriptValue instance that holds a handle to the same + // underlying JavaScript value. This should not be called for a ScriptValue + // that has a NULL script value (that is, GetScriptValue() returns NULL). virtual scoped_ptr<ScriptValue> MakeCopy() const = 0; + int reference_count_ = 0; + friend class scoped_ptr<ScriptValue>; + + template <typename F> + friend class Handle; +}; + +// A handle that references a |ScriptValue|, and manages its garbage +// collection state via reference counting. This is the preferred type for +// receiving, returning, and manipulating |ScriptValue|s. +template <typename T> +class Handle { + public: + // This should only be used by internals, next to where engine specific + // |ScriptValue| implementations are constructed. Calling this from common + // code is a usage error. If you want a new |Local<T>|, then you should + // build it from an existing reference, rather than working directly with + // the |ScriptValue|. + explicit Handle(ScriptValue<T>* script_value) : script_value_(script_value) { + DCHECK(script_value_); + DCHECK_EQ(script_value_->reference_count_, 0); + script_value_->PreventGarbageCollection(); + script_value_->reference_count_++; + } + // Intentionally not explicit because bindings' usage of ToJSValue relies on + // implicit conversion. + Handle(const ScriptValue<T>* script_value) // NOLINT + : Handle(script_value->MakeWeakCopy().release()) {} + Handle(const ScriptValue<T>& script_value) // NOLINT + : Handle(script_value.MakeWeakCopy().release()) {} + Handle(const typename ScriptValue<T>::Reference& reference) // NOLINT + : Handle(reference.referenced_value().MakeWeakCopy().release()) {} + + Handle(const Handle& other) : script_value_(other.script_value_) { + script_value_->PreventGarbageCollection(); + script_value_->reference_count_++; + } + Handle& operator=(const Handle& other) { + // Increment |other|'s value first to allow for self assignment. + if (other.script_value_) { + other.script_value_->PreventGarbageCollection(); + other.script_value_->reference_count_++; + } + Clear(); + script_value_ = other.script_value_; + return *this; + } + + Handle(Handle&& other) : script_value_(other.script_value_) { + other.script_value_ = nullptr; + } + Handle& operator=(Handle&& other) { + std::swap(script_value_, other.script_value_); + return *this; + } + + ~Handle() { Clear(); } + + T* operator*() { return script_value_->GetValue(); } + T* operator->() { return script_value_->GetValue(); } + + // These are primarly exposed for internals. In most cases you don't need + // to work with the ScriptValue directly. + ScriptValue<T>* GetScriptValue() { return script_value_; } + const ScriptValue<T>* GetScriptValue() const { return script_value_; } + + bool IsEmpty() const { return script_value_ == nullptr; } + + private: + void Clear() { + if (script_value_) { + script_value_->reference_count_--; + script_value_->AllowGarbageCollection(); + if (script_value_->reference_count_ == 0) { + delete script_value_; + } + } + script_value_ = nullptr; + } + + ScriptValue<T>* script_value_; }; } // namespace script
diff --git a/src/cobalt/script/script_value_factory.h b/src/cobalt/script/script_value_factory.h index 79437ef..06f09ec 100644 --- a/src/cobalt/script/script_value_factory.h +++ b/src/cobalt/script/script_value_factory.h
@@ -21,14 +21,16 @@ namespace cobalt { namespace script { + class ScriptValueFactory { public: - typedef ScriptValue<Promise<scoped_refptr<Wrappable> > > - WrappablePromiseValue; + using WrappablePromise = scoped_refptr<Wrappable>; + + virtual ~ScriptValueFactory() {} // Create a Promise<T> where T is void or a primitive type. template <typename T> - scoped_ptr<ScriptValue<Promise<T> > > CreateBasicPromise() { + Handle<Promise<T>> CreateBasicPromise() { return CreatePromise<T>(); } @@ -40,17 +42,15 @@ // defined in Cobalt. // TODO: Figure out how to make it so only a scoped_refptr<T> can be accepted. template <typename T> - scoped_ptr<WrappablePromiseValue> CreateInterfacePromise() { - return CreatePromise<scoped_refptr<Wrappable> >(); + Handle<Promise<WrappablePromise>> CreateInterfacePromise() { + return CreatePromise<WrappablePromise>(); } - virtual ~ScriptValueFactory() {} - private: // Non-virtual template function that will be implemented in the // engine-specific implementation for ScriptValueFactory. template <typename T> - scoped_ptr<ScriptValue<Promise<T> > > CreatePromise(); + Handle<Promise<T>> CreatePromise(); }; } // namespace script
diff --git a/src/cobalt/script/script_value_factory_instantiations.h b/src/cobalt/script/script_value_factory_instantiations.h index 0862f1b..39365df 100644 --- a/src/cobalt/script/script_value_factory_instantiations.h +++ b/src/cobalt/script/script_value_factory_instantiations.h
@@ -26,9 +26,8 @@ // Explicit template instantiations for all Promise types that will be used // in Cobalt. -#define PROMISE_TEMPLATE_INSTANTIATION(TYPE) \ - template scoped_ptr<ScriptValue<Promise<TYPE> > > \ - ScriptValueFactory::CreatePromise<TYPE>(); +#define PROMISE_TEMPLATE_INSTANTIATION(TYPE) \ + template Handle<Promise<TYPE>> ScriptValueFactory::CreatePromise<TYPE>(); PROMISE_TEMPLATE_INSTANTIATION(void); PROMISE_TEMPLATE_INSTANTIATION(bool);
diff --git a/src/cobalt/script/testing/fake_script_value.h b/src/cobalt/script/testing/fake_script_value.h index 9ecf42e..e6e667c 100644 --- a/src/cobalt/script/testing/fake_script_value.h +++ b/src/cobalt/script/testing/fake_script_value.h
@@ -27,8 +27,7 @@ public: typedef cobalt::script::ScriptValue<T> BaseClass; - explicit FakeScriptValue(const T* listener) - : value_(listener) {} + explicit FakeScriptValue(T* listener) : value_(listener) {} bool EqualTo(const BaseClass& other) const override { const FakeScriptValue* other_script_object = @@ -36,19 +35,18 @@ return value_ == other_script_object->value_; } - void TraceMembers(Tracer*) override {}; - void RegisterOwner(script::Wrappable*) override {} void DeregisterOwner(script::Wrappable*) override {} void PreventGarbageCollection() override {} void AllowGarbageCollection() override {} - const T* GetScriptValue() const override { return value_; } + T* GetValue() override { return value_; } + const T* GetValue() const override { return value_; } scoped_ptr<BaseClass> MakeCopy() const override { return make_scoped_ptr<BaseClass>(new FakeScriptValue(value_)); } private: - const T* value_; + T* value_; }; } // namespace testing
diff --git a/src/cobalt/script/v8c/algorithm_helpers.cc b/src/cobalt/script/v8c/algorithm_helpers.cc index d8578df..50184d9 100644 --- a/src/cobalt/script/v8c/algorithm_helpers.cc +++ b/src/cobalt/script/v8c/algorithm_helpers.cc
@@ -26,12 +26,10 @@ v8::MaybeLocal<v8::Object> GetIterator(v8::Isolate* isolate, v8::Local<v8::Object> object, V8cExceptionState* exception_state) { - v8::TryCatch try_catch(isolate); v8::Local<v8::Context> context = isolate->GetCurrentContext(); v8::Local<v8::Value> iterator_getter; if (!object->Get(context, v8::Symbol::GetIterator(isolate)) .ToLocal(&iterator_getter)) { - exception_state->ReThrow(&try_catch); return {}; } if (!iterator_getter->IsFunction()) { @@ -42,7 +40,6 @@ v8::Local<v8::Value> iterator; if (!MaybeCallAsFunction(context, iterator_getter, object) .ToLocal(&iterator)) { - exception_state->ReThrow(&try_catch); return {}; } if (!iterator->IsObject()) {
diff --git a/src/cobalt/script/v8c/conversion_helpers.h b/src/cobalt/script/v8c/conversion_helpers.h index 0dea922..004e573 100644 --- a/src/cobalt/script/v8c/conversion_helpers.h +++ b/src/cobalt/script/v8c/conversion_helpers.h
@@ -537,7 +537,7 @@ // can get the implementing object. const V8cCallbackInterfaceClass* v8c_callback_interface = base::polymorphic_downcast<const V8cCallbackInterfaceClass*>( - user_object_holder->GetScriptValue()); + user_object_holder->GetValue()); DCHECK(v8c_callback_interface); *out_value = v8c_callback_interface->NewLocal(isolate); } @@ -612,8 +612,6 @@ // JS -> IDL type conversion procedure described here: // https://heycam.github.io/webidl/#es-sequence - v8::TryCatch try_catch(isolate); - // 1. If Type(V) is not Object, throw a TypeError. if (!value->IsObject()) { exception_state->SetSimpleException(kNotObjectType); @@ -643,7 +641,7 @@ // Let next be ? IteratorStep(iter). v8::Local<v8::Value> next; if (!iterator->Get(context, next_key).ToLocal(&next)) { - v8c_exception_state->ReThrow(&try_catch); + exception_state->SetSimpleException(kTypeError, ""); return; } if (!next->IsFunction()) { @@ -656,7 +654,6 @@ if (!next.As<v8::Function>() ->Call(context, iterator, 0, nullptr) .ToLocal(&next_result)) { - v8c_exception_state->ReThrow(&try_catch); return; } if (!next_result->IsObject()) { @@ -671,13 +668,11 @@ v8::Local<v8::Value> done; if (!result_object->Get(context, value_key).ToLocal(&next_item) || !result_object->Get(context, done_key).ToLocal(&done)) { - v8c_exception_state->ReThrow(&try_catch); return; } bool done_as_bool; if (!done->BooleanValue(context).To(&done_as_bool)) { - v8c_exception_state->ReThrow(&try_catch); return; } if (done_as_bool) { @@ -696,6 +691,23 @@ } } +template <typename T> +void ToJSValue(v8::Isolate* isolate, + const ScriptValue<Promise<T>>* promise_holder, + v8::Local<v8::Value>* out_value); + +template <typename T> +void ToJSValue(v8::Isolate* isolate, ScriptValue<Promise<T>>* promise_holder, + v8::Local<v8::Value>* out_value); + +// script::Handle<T> -> JSValue +template <typename T> +void ToJSValue(v8::Isolate* isolate, const Handle<T>& local, + v8::Local<v8::Value>* out_value) { + TRACK_MEMORY_SCOPE("Javascript"); + ToJSValue(isolate, local.GetScriptValue(), out_value); +} + } // namespace v8c } // namespace script } // namespace cobalt
diff --git a/src/cobalt/script/v8c/isolate_fellowship.cc b/src/cobalt/script/v8c/isolate_fellowship.cc new file mode 100644 index 0000000..4711c8b --- /dev/null +++ b/src/cobalt/script/v8c/isolate_fellowship.cc
@@ -0,0 +1,190 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cobalt/script/v8c/isolate_fellowship.h" + +#include <string> + +#include "base/debug/trace_event.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "starboard/file.h" +#include "v8/include/libplatform/libplatform.h" + +namespace cobalt { +namespace script { +namespace v8c { + +IsolateFellowship::IsolateFellowship() { + TRACE_EVENT0("cobalt::script", "IsolateFellowship::IsolateFellowship"); + // TODO: Initialize V8 ICU stuff here as well. + platform = v8::platform::CreateDefaultPlatform(); + v8::V8::InitializePlatform(platform); + v8::V8::Initialize(); + array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + + InitializeStartupData(); +} + +IsolateFellowship::~IsolateFellowship() { + TRACE_EVENT0("cobalt::script", "IsolateFellowship::~IsolateFellowship"); + v8::V8::Dispose(); + v8::V8::ShutdownPlatform(); + + DCHECK(platform); + delete platform; + platform = nullptr; + + DCHECK(array_buffer_allocator); + delete array_buffer_allocator; + array_buffer_allocator = nullptr; + + // Note that both us and V8 will have created this with new[], see + // "snapshot-common.cc". Also note that both startup data creation failure + // from V8 is possible, and deleting a null pointer is safe, so there is no + // DCHECK here. + delete[] startup_data.data; + startup_data = {nullptr, 0}; +} + +void IsolateFellowship::InitializeStartupData() { + TRACE_EVENT0("cobalt::script", "IsolateFellowship::InitializeStartupData"); + DCHECK(startup_data.data == nullptr); + + char cache_path[SB_FILE_MAX_PATH] = {}; + char executable_path[SB_FILE_MAX_PATH] = {}; + SbFileInfo executable_info; + if (!SbSystemGetPath(kSbSystemPathCacheDirectory, cache_path, + SB_ARRAY_SIZE_INT(cache_path)) || + !SbSystemGetPath(kSbSystemPathExecutableFile, executable_path, + SB_ARRAY_SIZE_INT(executable_path)) || + !SbFileGetPathInfo(executable_path, &executable_info)) { + // If either of these conditions fail (there is no cache directory or we + // can't stat our own executable), then just save the startup data in + // memory. + LOG(WARNING) << "Unable to read/write V8 startup snapshot data to file."; + startup_data = v8::V8::CreateSnapshotDataBlob(); + return; + } + + // Whether or not we should attempt to remove the existing cache file due to + // it being in an invalid state. We will do so in the case that it either + // was of size 0 (but existed), or we failed to write it. + bool should_remove_cache_file = false; + + // Attempt to read the cache file. + std::string full_path = std::string(cache_path) + SB_FILE_SEP_STRING + + V8C_INTERNAL_STARTUP_DATA_CACHE_FILE_NAME; + bool read_file = ([&]() { + starboard::ScopedFile scoped_file(full_path.c_str(), + kSbFileOpenOnly | kSbFileRead); + if (!scoped_file.IsValid()) { + return false; + } + + SbFileInfo cache_info; + if (!scoped_file.GetInfo(&cache_info)) { + LOG(WARNING) + << "Failed to get info for cache file that we successfully opened."; + should_remove_cache_file = true; + return false; + } + + if (executable_info.creation_time > cache_info.creation_time) { + LOG(INFO) << "Running executable newer than V8 startup snapshot cache " + "file, creating a new one."; + should_remove_cache_file = true; + return false; + } + + int64_t size = scoped_file.GetSize(); + if (size == -1) { + LOG(ERROR) << "Received size of -1 for file that was valid."; + return false; + } + + if (size == 0) { + LOG(ERROR) << "Read V8 snapshot file of size 0."; + should_remove_cache_file = true; + return false; + } + + scoped_array<char> data(new char[size]); + int read = scoped_file.ReadAll(data.get(), size); + // Logically, this could be collapsed to just "read != size", but this + // should be read as "if the platform explicitly told us reading failed, + // or the platform told us we read less than we expected". + if (read == -1 || read != size) { + LOG(ERROR) << "Reading V8 startup snapshot cache file failed for some " + "unknown reason."; + return false; + } + + LOG(INFO) << "Successfully read V8 startup snapshot cache file."; + startup_data.data = data.release(); + startup_data.raw_size = size; + return true; + })(); + + auto maybe_remove_cache_file = [&]() { + if (should_remove_cache_file) { + if (!SbFileDelete(full_path.c_str())) { + LOG(ERROR) << "Failed to delete V8 startup snapshot cache file."; + } + should_remove_cache_file = false; + } + }; + maybe_remove_cache_file(); + + // If we failed to read the file, then create the snapshot data and attempt + // to write it. + if (!read_file) { + ([&]() { + startup_data = v8::V8::CreateSnapshotDataBlob(); + if (startup_data.data == nullptr) { + // Trust the V8 API, but verify. |raw_size| should also be 0. + DCHECK_EQ(startup_data.raw_size, 0); + // This is technically legal w.r.t. to the API documentation, but + // *probably* indicates a serious problem (are you hacking V8 + // internals or something?). + LOG(WARNING) << "Failed to create V8 startup snapshot."; + return; + } + + starboard::ScopedFile scoped_file(full_path.c_str(), + kSbFileCreateOnly | kSbFileWrite); + if (!scoped_file.IsValid()) { + LOG(ERROR) + << "Failed to open V8 startup snapshot cache file for writing."; + return; + } + + int written = + scoped_file.WriteAll(startup_data.data, startup_data.raw_size); + if (written < startup_data.raw_size) { + LOG(ERROR) << "Failed to write entire V8 startup snapshot."; + should_remove_cache_file = true; + return; + } + + LOG(INFO) << "Successfully wrote V8 startup snapshot cache file."; + })(); + } + + maybe_remove_cache_file(); +} + +} // namespace v8c +} // namespace script +} // namespace cobalt
diff --git a/src/cobalt/script/v8c/isolate_fellowship.h b/src/cobalt/script/v8c/isolate_fellowship.h new file mode 100644 index 0000000..5815d6f --- /dev/null +++ b/src/cobalt/script/v8c/isolate_fellowship.h
@@ -0,0 +1,58 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COBALT_SCRIPT_V8C_ISOLATE_FELLOWSHIP_H_ +#define COBALT_SCRIPT_V8C_ISOLATE_FELLOWSHIP_H_ + +#include "base/memory/singleton.h" +#include "v8/include/v8-platform.h" +#include "v8/include/v8.h" + +namespace cobalt { +namespace script { +namespace v8c { + +// A helper singleton to hold global state required by all V8 isolates across +// the entire process (the fellowship). Initialization logic for this data is +// also handled here, most notably, the possible reading/writing of +// |startup_data| from/to a cache file. +struct IsolateFellowship { + public: + static IsolateFellowship* GetInstance() { + return Singleton<IsolateFellowship, + StaticMemorySingletonTraits<IsolateFellowship>>::get(); + } + + v8::Platform* platform = nullptr; + v8::ArrayBuffer::Allocator* array_buffer_allocator = nullptr; + v8::StartupData startup_data = {nullptr, 0}; + + friend struct StaticMemorySingletonTraits<IsolateFellowship>; + + private: + IsolateFellowship(); + ~IsolateFellowship(); + + // Initialize |startup_data| by either reading it from a cache file or + // creating it. If creating it for the first time, then when appropriate + // (i.e. the platform has a suitable directory) attempt to write it to a cache + // file. + void InitializeStartupData(); +}; + +} // namespace v8c +} // namespace script +} // namespace cobalt + +#endif // COBALT_SCRIPT_V8C_ISOLATE_FELLOWSHIP_H_
diff --git a/src/cobalt/script/v8c/native_promise.h b/src/cobalt/script/v8c/native_promise.h index a2fe980..fca24a9 100644 --- a/src/cobalt/script/v8c/native_promise.h +++ b/src/cobalt/script/v8c/native_promise.h
@@ -15,6 +15,7 @@ #ifndef COBALT_SCRIPT_V8C_NATIVE_PROMISE_H_ #define COBALT_SCRIPT_V8C_NATIVE_PROMISE_H_ +#include "base/logging.h" #include "cobalt/script/promise.h" #include "cobalt/script/v8c/conversion_helpers.h" #include "cobalt/script/v8c/entry_scope.h" @@ -61,18 +62,16 @@ PromiseResultUndefined, T>::type; NativePromise(v8::Isolate* isolate, v8::Local<v8::Value> resolver) - : isolate_(isolate), ScopedPersistent(isolate, resolver) {} + : isolate_(isolate), ScopedPersistent(isolate, resolver) { + DCHECK(resolver->IsPromise()); + } void Resolve(const ResolveType& value) const override { - if (this->IsEmpty()) { - return; - } - + DCHECK(!this->IsEmpty()); EntryScope entry_scope(isolate_); v8::Local<v8::Context> context = isolate_->GetCurrentContext(); - v8::Local<v8::Promise::Resolver> promise_resolver = - v8::Local<v8::Promise::Resolver>::Cast(this->Get().Get(isolate_)); + v8::Local<v8::Promise::Resolver> promise_resolver = this->resolver(); v8::Local<v8::Value> converted_value; ToJSValue(isolate_, value, &converted_value); v8::Maybe<bool> reject_result = @@ -81,30 +80,22 @@ } void Reject() const override { - if (this->IsEmpty()) { - return; - } - + DCHECK(!this->IsEmpty()); EntryScope entry_scope(isolate_); v8::Local<v8::Context> context = isolate_->GetCurrentContext(); - v8::Local<v8::Promise::Resolver> promise_resolver = - v8::Local<v8::Promise::Resolver>::Cast(this->Get().Get(isolate_)); + v8::Local<v8::Promise::Resolver> promise_resolver = this->resolver(); v8::Maybe<bool> reject_result = promise_resolver->Reject(context, v8::Undefined(isolate_)); DCHECK(reject_result.FromJust()); } void Reject(SimpleExceptionType exception) const override { - if (this->IsEmpty()) { - return; - } - + DCHECK(!this->IsEmpty()); EntryScope entry_scope(isolate_); v8::Local<v8::Context> context = isolate_->GetCurrentContext(); - v8::Local<v8::Promise::Resolver> promise_resolver = - v8::Local<v8::Promise::Resolver>::Cast(this->Get().Get(isolate_)); + v8::Local<v8::Promise::Resolver> promise_resolver = this->resolver(); v8::Local<v8::Value> error_result = CreateErrorObject(isolate_, exception); v8::Maybe<bool> reject_result = promise_resolver->Reject(context, error_result); @@ -112,15 +103,11 @@ } void Reject(const scoped_refptr<ScriptException>& result) const override { - if (this->IsEmpty()) { - return; - } - + DCHECK(!this->IsEmpty()); EntryScope entry_scope(isolate_); v8::Local<v8::Context> context = isolate_->GetCurrentContext(); - v8::Local<v8::Promise::Resolver> promise_resolver = - v8::Local<v8::Promise::Resolver>::Cast(this->Get().Get(isolate_)); + v8::Local<v8::Promise::Resolver> promise_resolver = this->resolver(); v8::Local<v8::Value> converted_result; ToJSValue(isolate_, result, &converted_result); v8::Maybe<bool> reject_result = @@ -128,13 +115,35 @@ DCHECK(reject_result.FromJust()); } + PromiseState State() const override { + DCHECK(!this->IsEmpty()); + EntryScope entry_scope(isolate_); + + v8::Promise::PromiseState v8_promise_state = this->promise()->State(); + switch (v8_promise_state) { + case v8::Promise::kPending: + return PromiseState::kPending; + case v8::Promise::kFulfilled: + return PromiseState::kFulfilled; + case v8::Promise::kRejected: + return PromiseState::kRejected; + } + NOTREACHED(); + return PromiseState::kRejected; + } + v8::Local<v8::Promise> promise() const { - return v8::Local<v8::Promise::Resolver>::Cast(this->Get().Get(isolate_)) - ->GetPromise(); + DCHECK(!this->IsEmpty()); + return resolver()->GetPromise(); } private: v8::Isolate* isolate_; + + v8::Local<v8::Promise::Resolver> resolver() const { + DCHECK(!this->IsEmpty()); + return this->Get().Get(isolate_).template As<v8::Promise::Resolver>(); + } }; template <typename T> @@ -159,7 +168,7 @@ promise_holder); const NativePromise<T>* native_promise = base::polymorphic_downcast<const NativePromise<T>*>( - user_object_holder->GetScriptValue()); + user_object_holder->GetValue()); DCHECK(native_promise); *out_value = native_promise->promise(); } @@ -175,16 +184,6 @@ out_value); } -// Destroys |promise_holder| as soon as the conversion is done. -// This is useful when a wrappable is not interested in retaining a reference -// to a promise, typically when a promise is resolved or rejected synchronously. -template <typename T> -inline void ToJSValue(v8::Isolate* isolate, - scoped_ptr<ScriptValue<Promise<T>>> promise_holder, - v8::Local<v8::Value>* out_value) { - ToJSValue(isolate, promise_holder.get(), out_value); -} - } // namespace v8c } // namespace script } // namespace cobalt
diff --git a/src/cobalt/script/v8c/v8c.gyp b/src/cobalt/script/v8c/v8c.gyp index ab51b262..c063add 100644 --- a/src/cobalt/script/v8c/v8c.gyp +++ b/src/cobalt/script/v8c/v8c.gyp
@@ -25,6 +25,8 @@ 'conversion_helpers.h', 'entry_scope.h', 'interface_data.h', + 'isolate_fellowship.cc', + 'isolate_fellowship.h', 'native_promise.h', 'scoped_persistent.h', 'stack_trace_helpers.cc', @@ -50,6 +52,7 @@ 'v8c_source_code.h', 'v8c_user_object_holder.h', 'v8c_value_handle.h', + 'v8c_value_handle.cc', 'v8c_wrapper_handle.h', 'wrapper_factory.cc', 'wrapper_factory.h', @@ -60,17 +63,25 @@ '<(DEPTH)/cobalt/script/script.gyp:script', '<(DEPTH)/v8/src/v8.gyp:v8', '<(DEPTH)/v8/src/v8.gyp:v8_libplatform', + 'embed_v8c_resources_as_header_files', ], 'defines': [ 'ENGINE_SUPPORTS_INT64', 'ENGINE_SUPPORTS_JIT', + # The file name to store our V8 startup snapshot file at. This is a + # serialized representation of a |v8::Isolate| after completing all + # tasks prior to creation of the global object (e.g., executing self + # hosted JavaScript to implement ECMAScript level features). This + # state is architecture dependent, and in fact, dependent on anything + # that could affect JavaScript execution (such as #defines), and thus + # must be unique with respect to binary, which is why we build it out + # of platform name and configuration. + 'V8C_INTERNAL_STARTUP_DATA_CACHE_FILE_NAME="<(starboard_platform_name)_<(cobalt_config)_v8_startup_snapshot.bin"', ], 'all_dependent_settings': { 'defines': [ 'ENGINE_SUPPORTS_INDEXED_DELETERS', 'ENGINE_SUPPORTS_INT64', - # TODO: Remove this when exact rooting and generational GC is enabled. - 'ENGINE_USES_CONSERVATIVE_ROOTING', ], }, # V8 always requires JIT. |cobalt_enable_jit| must be set to true to @@ -97,5 +108,44 @@ '<(DEPTH)/v8/src/v8.gyp:v8_libplatform', ], }, + + { + # This target takes specified files and embeds them as header files for + # inclusion into the binary. + 'target_name': 'embed_v8c_resources_as_header_files', + 'type': 'none', + # Because we generate a header, we must set the hard_dependency flag. + 'hard_dependency': 1, + 'variables': { + 'script_path': '<(DEPTH)/cobalt/build/generate_data_header.py', + 'output_path': '<(SHARED_INTERMEDIATE_DIR)/cobalt/script/v8c/embedded_resources.h', + }, + 'sources': [ + '<(DEPTH)/cobalt/fetch/embedded_scripts/fetch.js', + '<(DEPTH)/cobalt/streams/embedded_scripts/byte_length_queuing_strategy.js', + '<(DEPTH)/cobalt/streams/embedded_scripts/count_queuing_strategy.js', + '<(DEPTH)/cobalt/streams/embedded_scripts/readable_stream.js', + ], + 'actions': [ + { + 'action_name': 'embed_v8c_resources_as_header_files', + 'inputs': [ + '<(script_path)', + '<@(_sources)', + ], + 'outputs': [ + '<(output_path)', + ], + 'action': ['python', '<(script_path)', 'V8cEmbeddedResources', '<(output_path)', '<@(_sources)' ], + 'message': 'Embedding v8c resources in into header file, "<(output_path)".', + }, + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + }, + }, + ], }
diff --git a/src/cobalt/script/v8c/v8c_engine.cc b/src/cobalt/script/v8c/v8c_engine.cc index 9bf36b2..a8c956a 100644 --- a/src/cobalt/script/v8c/v8c_engine.cc +++ b/src/cobalt/script/v8c/v8c_engine.cc
@@ -22,59 +22,105 @@ #include "base/message_loop.h" #include "cobalt/base/c_val.h" #include "cobalt/browser/stack_size_constants.h" +#include "cobalt/script/v8c/isolate_fellowship.h" #include "cobalt/script/v8c/v8c_global_environment.h" -#include "starboard/once.h" -#include "v8/include/libplatform/libplatform.h" -#include "v8/include/v8.h" namespace cobalt { namespace script { namespace v8c { + namespace { -SbOnceControl g_js_init_once_control = SB_ONCE_INITIALIZER; -v8::Platform* g_platform = nullptr; -v8::ArrayBuffer::Allocator* g_array_buffer_allocator = nullptr; - -void ShutDown(void*) { - v8::V8::Dispose(); - v8::V8::ShutdownPlatform(); - - DCHECK(g_platform); - delete g_platform; - g_platform = nullptr; - - DCHECK(g_array_buffer_allocator); - delete g_array_buffer_allocator; - g_array_buffer_allocator = nullptr; +void VisitWeakHandlesForMinorGC(v8::Isolate* isolate) { + class V8cPersistentHandleVisitor : public v8::PersistentHandleVisitor { + public: + void VisitPersistentHandle(v8::Persistent<v8::Value>* value, + uint16_t class_id) override { + DCHECK(value); + value->MarkActive(); + } + } visitor; + isolate->VisitWeakHandles(&visitor); } -void InitializeAndRegisterShutDownOnce() { - // TODO: Initialize V8 ICU stuff here as well. - g_platform = v8::platform::CreateDefaultPlatform(); - v8::V8::InitializePlatform(g_platform); - v8::V8::Initialize(); - g_array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); +size_t UsedHeapSize(v8::Isolate* isolate) { + v8::HeapStatistics heap_statistics; + isolate->GetHeapStatistics(&heap_statistics); + return heap_statistics.used_heap_size(); +} - base::AtExitManager::RegisterCallback(ShutDown, nullptr); +void GCPrologueCallback(v8::Isolate* isolate, v8::GCType type, + v8::GCCallbackFlags) { + switch (type) { + case v8::kGCTypeScavenge: + TRACE_EVENT_BEGIN1("cobalt::script", "MinorGC", "usedHeapSizeBefore", + UsedHeapSize(isolate)); + VisitWeakHandlesForMinorGC(isolate); + break; + case v8::kGCTypeMarkSweepCompact: + TRACE_EVENT_BEGIN2("cobalt::script", "MajorGC", "usedHeapSizeBefore", + UsedHeapSize(isolate), "type", "atomic pause"); + break; + case v8::kGCTypeIncrementalMarking: + TRACE_EVENT_BEGIN2("cobalt::script", "MajorGC", "usedHeapSizeBefore", + UsedHeapSize(isolate), "type", "incremental marking"); + break; + case v8::kGCTypeProcessWeakCallbacks: + TRACE_EVENT_BEGIN2("cobalt::script", "MajorGC", "usedHeapSizeBefore", + UsedHeapSize(isolate), "type", "weak processing"); + break; + default: + NOTREACHED(); + } +} + +void GCEpilogueCallback(v8::Isolate* isolate, v8::GCType type, + v8::GCCallbackFlags) { + switch (type) { + case v8::kGCTypeScavenge: + TRACE_EVENT_END1("cobalt::script", "MinorGC", "usedHeapSizeAfter", + UsedHeapSize(isolate)); + break; + case v8::kGCTypeMarkSweepCompact: + TRACE_EVENT_END1("cobalt::script", "MajorGC", "usedHeapSizeAfter", + UsedHeapSize(isolate)); + break; + case v8::kGCTypeIncrementalMarking: + TRACE_EVENT_END1("cobalt::script", "MajorGC", "usedHeapSizeAfter", + UsedHeapSize(isolate)); + break; + case v8::kGCTypeProcessWeakCallbacks: + TRACE_EVENT_END1("cobalt::script", "MajorGC", "usedHeapSizeAfter", + UsedHeapSize(isolate)); + break; + default: + NOTREACHED(); + } } } // namespace -v8::Platform* GetPlatform() { return g_platform; } - -V8cEngine::V8cEngine(const Options& options) - : accumulated_extra_memory_cost_(0), options_(options) { +V8cEngine::V8cEngine(const Options& options) : options_(options) { TRACE_EVENT0("cobalt::script", "V8cEngine::V8cEngine()"); - SbOnce(&g_js_init_once_control, InitializeAndRegisterShutDownOnce); - DCHECK(g_platform); - DCHECK(g_array_buffer_allocator); - v8::Isolate::CreateParams params; - params.array_buffer_allocator = g_array_buffer_allocator; + auto* isolate_fellowship = IsolateFellowship::GetInstance(); - isolate_ = v8::Isolate::New(params); + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = + isolate_fellowship->array_buffer_allocator; + auto* startup_data = &isolate_fellowship->startup_data; + if (startup_data->data != nullptr) { + create_params.snapshot_blob = startup_data; + } else { + // Technically possible to attempt to recover here, but hitting this + // indicates that something is probably seriously wrong. + LOG(WARNING) << "Isolate fellowship startup data was null, this will " + "significantly slow down startup time."; + } + + isolate_ = v8::Isolate::New(create_params); CHECK(isolate_); + // There are 2 total isolate data slots, one for the sole |V8cEngine| (us), // and one for the |V8cGlobalEnvironment|. const int kTotalIsolateDataSlots = 2; @@ -83,16 +129,15 @@ v8c_heap_tracer_.reset(new V8cHeapTracer(isolate_)); isolate_->SetEmbedderHeapTracer(v8c_heap_tracer_.get()); + + isolate_->AddGCPrologueCallback(GCPrologueCallback); + isolate_->AddGCEpilogueCallback(GCEpilogueCallback); } V8cEngine::~V8cEngine() { TRACE_EVENT0("cobalt::script", "V8cEngine::~V8cEngine"); DCHECK(thread_checker_.CalledOnValidThread()); - // This next GC is to GC everything (mostly for ASan), so we actually don't - // want our wrappers and wrappables to stay alive. - isolate_->SetEmbedderHeapTracer(nullptr); - // Send a low memory notification to V8 in order to force a garbage // collection before shut down. This is required to run weak callbacks that // are responsible for freeing native objects that live in the internal @@ -116,16 +161,9 @@ isolate_->LowMemoryNotification(); } -void V8cEngine::ReportExtraMemoryCost(size_t bytes) { +void V8cEngine::AdjustAmountOfExternalAllocatedMemory(int64_t bytes) { DCHECK(thread_checker_.CalledOnValidThread()); - accumulated_extra_memory_cost_ += bytes; - - const bool do_collect_garbage = - accumulated_extra_memory_cost_ > options_.gc_threshold_bytes; - if (do_collect_garbage) { - accumulated_extra_memory_cost_ = 0; - CollectGarbage(); - } + isolate_->AdjustAmountOfExternalAllocatedMemory(bytes); } bool V8cEngine::RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) { @@ -135,6 +173,13 @@ void V8cEngine::SetGcThreshold(int64_t bytes) { NOTIMPLEMENTED(); } +HeapStatistics V8cEngine::GetHeapStatistics() { + v8::HeapStatistics v8_heap_statistics; + isolate_->GetHeapStatistics(&v8_heap_statistics); + return {v8_heap_statistics.total_heap_size(), + v8_heap_statistics.used_heap_size()}; +} + } // namespace v8c // static @@ -144,11 +189,5 @@ return make_scoped_ptr<JavaScriptEngine>(new v8c::V8cEngine(options)); } -// static -size_t JavaScriptEngine::UpdateMemoryStatsAndReturnReserved() { - NOTIMPLEMENTED(); - return 0; -} - } // namespace script } // namespace cobalt
diff --git a/src/cobalt/script/v8c/v8c_engine.h b/src/cobalt/script/v8c/v8c_engine.h index 8b36a35..1acfae9 100644 --- a/src/cobalt/script/v8c/v8c_engine.h +++ b/src/cobalt/script/v8c/v8c_engine.h
@@ -28,8 +28,6 @@ namespace script { namespace v8c { -v8::Platform* GetPlatform(); - class V8cEngine : public JavaScriptEngine { public: // Helper function to allow others to retrieve us (a |V8cEngine|) from our @@ -43,9 +41,10 @@ scoped_refptr<GlobalEnvironment> CreateGlobalEnvironment() override; void CollectGarbage() override; - void ReportExtraMemoryCost(size_t bytes) override; + void AdjustAmountOfExternalAllocatedMemory(int64_t bytes) override; bool RegisterErrorHandler(JavaScriptEngine::ErrorHandler handler) override; void SetGcThreshold(int64_t bytes) override; + HeapStatistics GetHeapStatistics() override; v8::Isolate* isolate() const { return isolate_; } V8cHeapTracer* heap_tracer() const { return v8c_heap_tracer_.get(); } @@ -62,9 +61,6 @@ scoped_ptr<V8cHeapTracer> v8c_heap_tracer_; - // The amount of externally allocated memory since last forced GC. - size_t accumulated_extra_memory_cost_; - // Used to handle javascript errors. ErrorHandler error_handler_;
diff --git a/src/cobalt/script/v8c/v8c_exception_state.cc b/src/cobalt/script/v8c/v8c_exception_state.cc index 896dafb..876b683 100644 --- a/src/cobalt/script/v8c/v8c_exception_state.cc +++ b/src/cobalt/script/v8c/v8c_exception_state.cc
@@ -89,16 +89,6 @@ is_exception_set_ = true; } -void V8cExceptionState::ReThrow(v8::TryCatch* try_catch) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(!is_exception_set_); - DCHECK(try_catch); - DCHECK(try_catch->HasCaught()); - - is_exception_set_ = true; - try_catch->ReThrow(); -} - } // namespace v8c } // namespace script } // namespace cobalt
diff --git a/src/cobalt/script/v8c/v8c_exception_state.h b/src/cobalt/script/v8c/v8c_exception_state.h index ba062ed..7e8efd2 100644 --- a/src/cobalt/script/v8c/v8c_exception_state.h +++ b/src/cobalt/script/v8c/v8c_exception_state.h
@@ -42,8 +42,6 @@ bool is_exception_set() const { return is_exception_set_; } - void ReThrow(v8::TryCatch* try_catch); - private: base::ThreadChecker thread_checker_;
diff --git a/src/cobalt/script/v8c/v8c_global_environment.cc b/src/cobalt/script/v8c/v8c_global_environment.cc index a5eaa7d..7f4ab04 100644 --- a/src/cobalt/script/v8c/v8c_global_environment.cc +++ b/src/cobalt/script/v8c/v8c_global_environment.cc
@@ -21,6 +21,7 @@ #include "base/lazy_instance.h" #include "base/stringprintf.h" #include "cobalt/base/polymorphic_downcast.h" +#include "cobalt/script/v8c/embedded_resources.h" #include "cobalt/script/v8c/entry_scope.h" #include "cobalt/script/v8c/v8c_script_value_factory.h" #include "cobalt/script/v8c/v8c_source_code.h" @@ -31,6 +32,7 @@ namespace cobalt { namespace script { namespace v8c { + namespace { std::string ExceptionToString(const v8::TryCatch& try_catch) { @@ -56,11 +58,10 @@ } // namespace V8cGlobalEnvironment::V8cGlobalEnvironment(v8::Isolate* isolate) - : garbage_collection_count_(0), - environment_settings_(nullptr), - last_error_message_(nullptr), - eval_enabled_(false), - isolate_(isolate) { + : isolate_(isolate), + destruction_helper_(isolate), + wrapper_factory_(new WrapperFactory(isolate)), + script_value_factory_(new V8cScriptValueFactory(isolate)) { TRACK_MEMORY_SCOPE("Javascript"); TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::V8cGlobalEnvironment()"); @@ -68,23 +69,14 @@ isolate_->SetData(kIsolateDataIndex, this); DCHECK(isolate_->GetData(kIsolateDataIndex) == this); - // TODO: Change type of this member to scoped_ptr - script_value_factory_ = new V8cScriptValueFactory(isolate); + isolate_->SetAllowCodeGenerationFromStringsCallback( + AllowCodeGenerationFromStringsCallback); } V8cGlobalEnvironment::~V8cGlobalEnvironment() { TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::~V8cGlobalEnvironment()"); DCHECK(thread_checker_.CalledOnValidThread()); - - // TODO: Change type of this member to scoped_ptr - delete script_value_factory_; - script_value_factory_ = nullptr; - - // Run garbage collection right before we die, in order to give callbacks - // that depend on us being alive one more chance to run. - isolate_->LowMemoryNotification(); - isolate_->SetData(kIsolateDataIndex, nullptr); } void V8cGlobalEnvironment::CreateGlobalObject() { @@ -192,16 +184,7 @@ v8::HandleScope handle_scope(isolate_); v8::Local<v8::Object> wrapper = wrapper_factory_->GetWrapper(wrappable); - DCHECK(WrapperPrivate::HasWrapperPrivate(wrapper)); - - // Attempt to insert a |Wrappable*| -> wrapper mapping into - // |kept_alive_objects_|... - auto insert_result = - kept_alive_objects_.insert({wrappable.get(), {{isolate_, wrapper}, 1}}); - // ...and if it was already there, just increment the count. - if (!insert_result.second) { - insert_result.first->second.count++; - } + WrapperPrivate::GetFromWrapperObject(wrapper)->IncrementRefCount(); } void V8cGlobalEnvironment::AllowGarbageCollection( @@ -209,32 +192,35 @@ TRACK_MEMORY_SCOPE("Javascript"); DCHECK(thread_checker_.CalledOnValidThread()); - auto it = kept_alive_objects_.find(wrappable.get()); - DCHECK(it != kept_alive_objects_.end()); - it->second.count--; - DCHECK_GE(it->second.count, 0); - if (it->second.count == 0) { - kept_alive_objects_.erase(it); - } + v8::HandleScope handle_scope(isolate_); + v8::Local<v8::Object> wrapper = wrapper_factory_->GetWrapper(wrappable); + WrapperPrivate::GetFromWrapperObject(wrapper)->DecrementRefCount(); } void V8cGlobalEnvironment::DisableEval(const std::string& message) { DCHECK(thread_checker_.CalledOnValidThread()); - eval_disabled_message_.emplace(message); - eval_enabled_ = false; + + EntryScope entry_scope(isolate_); + v8::Local<v8::Context> context = isolate_->GetCurrentContext(); + context->AllowCodeGenerationFromStrings(false); + context->SetErrorMessageForCodeGenerationFromStrings( + v8::String::NewFromUtf8(isolate_, message.c_str(), + v8::NewStringType::kNormal) + .ToLocalChecked()); } void V8cGlobalEnvironment::EnableEval() { DCHECK(thread_checker_.CalledOnValidThread()); - eval_disabled_message_ = base::nullopt; - eval_enabled_ = true; + + EntryScope entry_scope(isolate_); + v8::Local<v8::Context> context = isolate_->GetCurrentContext(); + context->AllowCodeGenerationFromStrings(true); } void V8cGlobalEnvironment::DisableJit() { DCHECK(thread_checker_.CalledOnValidThread()); - SB_DLOG(INFO) - << "V8 version " << V8_MAJOR_VERSION << '.' << V8_MINOR_VERSION - << "can only be run with JIT enabled, ignoring |DisableJit| call."; + LOG(INFO) << "V8 version " << V8_MAJOR_VERSION << '.' << V8_MINOR_VERSION + << "can only be run with JIT enabled, ignoring |DisableJit| call."; } void V8cGlobalEnvironment::SetReportEvalCallback( @@ -272,7 +258,58 @@ ScriptValueFactory* V8cGlobalEnvironment::script_value_factory() { DCHECK(script_value_factory_); - return script_value_factory_; + return script_value_factory_.get(); +} + +V8cGlobalEnvironment::DestructionHelper::~DestructionHelper() { + class ForceWeakVisitor : public v8::PersistentHandleVisitor { + public: + explicit ForceWeakVisitor(v8::Isolate* isolate) : isolate_(isolate) {} + void VisitPersistentHandle(v8::Persistent<v8::Value>* value, + uint16_t class_id) override { + if (class_id == WrapperPrivate::kClassId) { + v8::Local<v8::Value> v = value->Get(isolate_); + DCHECK(v->IsObject()); + WrapperPrivate* wrapper_private = + WrapperPrivate::GetFromWrapperObject(v.As<v8::Object>()); + wrapper_private->ForceWeakForShutDown(); + } + } + + private: + v8::Isolate* isolate_; + }; + + TRACE_EVENT0("cobalt::script", + "V8cGlobalEnvironment::DestructionHelper::~DestructionHelper()"); + + { + TRACE_EVENT0("cobalt::script", + "V8cGlobalEnvironment::DestructionHelper::~DestructionHelper::" + "ForceWeakVisitor"); + v8::HandleScope handle_scope(isolate_); + ForceWeakVisitor force_weak_visitor(isolate_); + isolate_->VisitHandlesWithClassIds(&force_weak_visitor); + } + + isolate_->SetEmbedderHeapTracer(nullptr); + isolate_->SetData(kIsolateDataIndex, nullptr); + isolate_->LowMemoryNotification(); +} + +// static +bool V8cGlobalEnvironment::AllowCodeGenerationFromStringsCallback( + v8::Local<v8::Context> context, v8::Local<v8::String> source) { + V8cGlobalEnvironment* global_environment = + V8cGlobalEnvironment::GetFromIsolate(context->GetIsolate()); + DCHECK(global_environment); + if (!global_environment->report_eval_.is_null()) { + global_environment->report_eval_.Run(); + } + // This callback should only be called while code generation from strings is + // not allowed from within V8, so this should always be false. + DCHECK_EQ(context->IsCodeGenerationFromStringsAllowed(), false); + return context->IsCodeGenerationFromStringsAllowed(); } v8::MaybeLocal<v8::Value> V8cGlobalEnvironment::EvaluateScriptInternal( @@ -318,25 +355,58 @@ v8::Local<v8::Context> context = isolate_->GetCurrentContext(); v8::Local<v8::Script> script; - if (!v8::Script::Compile(context, source, &script_origin).ToLocal(&script)) { - LOG(WARNING) << "Failed to compile script."; - return {}; + { + TRACE_EVENT0("cobalt::script", "v8::Script::Compile()"); + if (!v8::Script::Compile(context, source, &script_origin) + .ToLocal(&script)) { + LOG(WARNING) << "Failed to compile script."; + return {}; + } } v8::Local<v8::Value> result; - if (!script->Run(context).ToLocal(&result)) { - LOG(WARNING) << "Failed to run script."; - return {}; + { + TRACE_EVENT0("cobalt::script", "v8::Script::Run()"); + if (!script->Run(context).ToLocal(&result)) { + LOG(WARNING) << "Failed to run script."; + return {}; + } } return result; } +void V8cGlobalEnvironment::EvaluateEmbeddedScript(const unsigned char* data, + size_t size, + const char* filename) { + TRACE_EVENT1("cobalt::script", "V8cGlobalEnvironment::EvaluateEmbeddedScript", + "filename", filename); + TRACK_MEMORY_SCOPE("Javascript"); + std::string source(reinterpret_cast<const char*>(data), size); + scoped_refptr<SourceCode> source_code = + new V8cSourceCode(source, base::SourceLocation(filename, 1, 1)); + std::string result; + bool success = EvaluateScript(source_code, false /*mute_errors*/, &result); + if (!success) { + DLOG(FATAL) << result; + } +} + void V8cGlobalEnvironment::EvaluateAutomatics() { TRACE_EVENT0("cobalt::script", "V8cGlobalEnvironment::EvaluateAutomatics()"); - // TODO: Maybe add fetch and stream polyfills. Investigate what V8 has to - // natively offer first. - NOTIMPLEMENTED(); + EvaluateEmbeddedScript( + V8cEmbeddedResources::byte_length_queuing_strategy_js, + sizeof(V8cEmbeddedResources::byte_length_queuing_strategy_js), + "byte_length_queuing_strategy.js"); + EvaluateEmbeddedScript( + V8cEmbeddedResources::count_queuing_strategy_js, + sizeof(V8cEmbeddedResources::count_queuing_strategy_js), + "count_queuing_strategy.js"); + EvaluateEmbeddedScript(V8cEmbeddedResources::readable_stream_js, + sizeof(V8cEmbeddedResources::readable_stream_js), + "readable_stream.js"); + EvaluateEmbeddedScript(V8cEmbeddedResources::fetch_js, + sizeof(V8cEmbeddedResources::fetch_js), "fetch.js"); } bool V8cGlobalEnvironment::HasInterfaceData(int key) const {
diff --git a/src/cobalt/script/v8c/v8c_global_environment.h b/src/cobalt/script/v8c/v8c_global_environment.h index 6428f79..251599d 100644 --- a/src/cobalt/script/v8c/v8c_global_environment.h +++ b/src/cobalt/script/v8c/v8c_global_environment.h
@@ -111,42 +111,36 @@ void AddInterfaceData(int key, v8::Local<v8::FunctionTemplate> function_template); - WrapperFactory* wrapper_factory() { return wrapper_factory_.get(); } + WrapperFactory* wrapper_factory() const { return wrapper_factory_.get(); } + + Wrappable* global_wrappable() const { return global_wrappable_; } EnvironmentSettings* GetEnvironmentSettings() const { return environment_settings_; } - void AddReferencedObject(Wrappable* owner, v8::Local<v8::Value> value) { - auto it = referenced_object_map_.insert({owner, {isolate_, value}}); - // TODO: Make this weak. - } - - void RemoveReferencedObject(Wrappable* owner, v8::Local<v8::Value> value) { - auto pair_range = referenced_object_map_.equal_range(owner); - auto it = std::find_if(pair_range.first, pair_range.second, - [this, &value](decltype(*pair_range.first) handle) { - return handle.second.Get(isolate_) == value; - }); - if (it != pair_range.second) { - referenced_object_map_.erase(it); - } else { - DLOG(WARNING) << "No reference to the specified object found."; - } - } - private: - // Helper struct to store a V8 persistent handle as well as a count. We - // need to make it the less common copyable variant because we plan on - // storing it in an STL container. - struct CountedHandle { - v8::Persistent<v8::Value, v8::CopyablePersistentTraits<v8::Value>> handle; - int count; + // A helper class to trigger a final necessary garbage collection to run + // finalizer callbacks precisely in between |global_wrappable_| getting + // propped up and |context_| + |global_object_| getting reset. + class DestructionHelper { + public: + explicit DestructionHelper(v8::Isolate* isolate) : isolate_(isolate) {} + ~DestructionHelper(); + + private: + v8::Isolate* isolate_; }; + static bool AllowCodeGenerationFromStringsCallback( + v8::Local<v8::Context> context, v8::Local<v8::String> source); + v8::MaybeLocal<v8::Value> EvaluateScriptInternal( const scoped_refptr<SourceCode>& source_code, bool mute_errors); + void EvaluateEmbeddedScript(const unsigned char* data, size_t size, + const char* filename); + // Evaluates any automatically included Javascript for the environment. void EvaluateAutomatics(); @@ -156,38 +150,31 @@ base::ThreadChecker thread_checker_; v8::Isolate* isolate_; - v8::Global<v8::Context> context_; - int garbage_collection_count_; + // Hold an extra reference to the global wrappable in order to properly + // destruct. Were we to not do this, finalizers can run in the order (e.g.) + // document window, which the Cobalt DOM does not support. + scoped_refptr<Wrappable> global_wrappable_; + DestructionHelper destruction_helper_; + v8::Global<v8::Context> context_; v8::Global<v8::Object> global_object_; + scoped_ptr<WrapperFactory> wrapper_factory_; + scoped_ptr<V8cScriptValueFactory> script_value_factory_; // Data that is cached on a per-interface basis. Note that we can get to // everything (the function instance, the prototype template, and the // instance template) from just the function template. std::vector<v8::Eternal<v8::FunctionTemplate>> cached_interface_data_; - // TODO: Should be scoped_ptr, fix headers/sources template mess. - V8cScriptValueFactory* script_value_factory_; - - EnvironmentSettings* environment_settings_; - - std::unordered_map<Wrappable*, CountedHandle> kept_alive_objects_; - - // TODO: We might need to make all of these weak, and only visit them when - // we get asked to trace them. - std::unordered_multimap< - Wrappable*, - v8::Persistent<v8::Value, v8::CopyablePersistentTraits<v8::Value>>> - referenced_object_map_; + EnvironmentSettings* environment_settings_ = nullptr; // If non-NULL, the error message from the ReportErrorHandler will get // assigned to this instead of being printed. - std::string* last_error_message_; + std::string* last_error_message_ = nullptr; - bool eval_enabled_; - base::optional<std::string> eval_disabled_message_; base::Closure report_eval_; + ReportErrorCallback report_error_callback_; };
diff --git a/src/cobalt/script/v8c/v8c_heap_tracer.cc b/src/cobalt/script/v8c/v8c_heap_tracer.cc index 0c19377..927603d 100644 --- a/src/cobalt/script/v8c/v8c_heap_tracer.cc +++ b/src/cobalt/script/v8c/v8c_heap_tracer.cc
@@ -14,6 +14,9 @@ #include "cobalt/script/v8c/v8c_heap_tracer.h" +#include <algorithm> + +#include "base/debug/trace_event.h" #include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/wrapper_private.h" @@ -27,12 +30,8 @@ for (const auto& embedder_field : embedder_fields) { WrapperPrivate* wrapper_private = static_cast<WrapperPrivate*>(embedder_field.first); - wrapper_private->Mark(); - Wrappable* wrappable = wrapper_private->raw_wrappable(); - if (visited_.insert(wrappable).second) { - frontier_.push_back(wrappable); - } + MaybeAddToFrontier(wrappable); // We expect this field to always be null, since we only have it as a // workaround for V8. See "wrapper_private.h" for details. @@ -40,8 +39,22 @@ } } +void V8cHeapTracer::TracePrologue() { + TRACE_EVENT0("cobalt::script", "V8cHeapTracer::TracePrologue"); + + DCHECK_EQ(frontier_.size(), 0); + DCHECK_EQ(visited_.size(), 0); + + // This feels a bit weird, but as far as I can tell, we're expected to + // manually decide to trace the from the global object. + MaybeAddToFrontier( + V8cGlobalEnvironment::GetFromIsolate(isolate_)->global_wrappable()); +} + bool V8cHeapTracer::AdvanceTracing(double deadline_in_ms, AdvanceTracingActions actions) { + TRACE_EVENT0("cobalt::script", "V8cHeapTracer::AdvanceTracing"); + while (actions.force_completion == v8::EmbedderHeapTracer::ForceCompletionAction::FORCE_COMPLETION || platform_->MonotonicallyIncreasingTime() < deadline_in_ms) { @@ -51,44 +64,77 @@ Traceable* traceable = frontier_.back(); frontier_.pop_back(); + + if (traceable->IsWrappable()) { + Wrappable* wrappable = base::polymorphic_downcast<Wrappable*>(traceable); + auto pair_range = reference_map_.equal_range(wrappable); + for (auto it = pair_range.first; it != pair_range.second; ++it) { + it->second->Get().RegisterExternalReference(isolate_); + } + WrapperFactory* wrapper_factory = + V8cGlobalEnvironment::GetFromIsolate(isolate_)->wrapper_factory(); + WrapperPrivate* maybe_wrapper_private = + wrapper_factory->MaybeGetWrapperPrivate( + static_cast<Wrappable*>(traceable)); + if (maybe_wrapper_private) { + maybe_wrapper_private->Mark(); + } + } + traceable->TraceMembers(this); } return true; } +void V8cHeapTracer::TraceEpilogue() { + TRACE_EVENT0("cobalt::script", "V8cHeapTracer::TraceEpilogue"); + + DCHECK(frontier_.empty()); + visited_.clear(); +} + +void V8cHeapTracer::EnterFinalPause() { + TRACE_EVENT0("cobalt::script", "V8cHeapTracer::EnterFinalPause"); +} + +void V8cHeapTracer::AbortTracing() { + TRACE_EVENT0("cobalt::script", "V8cHeapTracer::AbortTracing"); + + LOG(WARNING) << "Tracing aborted."; + frontier_.clear(); + visited_.clear(); +} + +size_t V8cHeapTracer::NumberOfWrappersToTrace() { return frontier_.size(); } + void V8cHeapTracer::Trace(Traceable* traceable) { if (!traceable) { return; } + MaybeAddToFrontier(traceable); +} - // Attempt to add |traceable| to |visited_|. Don't do any additional work - // if we find that it has already been traced. +void V8cHeapTracer::AddReferencedObject(Wrappable* owner, + ScopedPersistent<v8::Value>* value) { + auto it = reference_map_.insert({owner, value}); +} + +void V8cHeapTracer::RemoveReferencedObject(Wrappable* owner, + ScopedPersistent<v8::Value>* value) { + auto pair_range = reference_map_.equal_range(owner); + auto it = std::find_if( + pair_range.first, pair_range.second, + [&](decltype(*pair_range.first) it) { return it.second == value; }); + DCHECK(it != pair_range.second); + reference_map_.erase(it); +} + +void V8cHeapTracer::MaybeAddToFrontier(Traceable* traceable) { if (!visited_.insert(traceable).second) { return; } - - // A |Traceable| that isn't a |Wrappable| cannot be traced by V8, so we will - // add it to |frontier_| and trace it ourselves later. - if (!traceable->IsWrappable()) { - frontier_.push_back(traceable); - return; - } - - // Now that we know |Traceable| is a |Wrappable|, check and see if it has a - // wrapper. If it does, then let V8 know that it's reacheable, and wait for - // V8 to give it back to us later via |RegisterV8References|. If not, add - // it to the frontier to trace ourselves later. - Wrappable* wrappable = base::polymorphic_downcast<Wrappable*>(traceable); - WrapperFactory* wrapper_factory = - V8cGlobalEnvironment::GetFromIsolate(isolate_)->wrapper_factory(); - WrapperPrivate* maybe_wrapper_private = - wrapper_factory->MaybeGetWrapperPrivate(wrappable); - if (maybe_wrapper_private) { - maybe_wrapper_private->Mark(); - } else { - frontier_.push_back(wrappable); - } + frontier_.push_back(traceable); } } // namespace v8c
diff --git a/src/cobalt/script/v8c/v8c_heap_tracer.h b/src/cobalt/script/v8c/v8c_heap_tracer.h index e081277..32c799c 100644 --- a/src/cobalt/script/v8c/v8c_heap_tracer.h +++ b/src/cobalt/script/v8c/v8c_heap_tracer.h
@@ -15,22 +15,21 @@ #ifndef COBALT_SCRIPT_V8C_V8C_HEAP_TRACER_H_ #define COBALT_SCRIPT_V8C_V8C_HEAP_TRACER_H_ +#include <unordered_map> #include <unordered_set> #include <utility> #include <vector> +#include "cobalt/script/v8c/isolate_fellowship.h" +#include "cobalt/script/v8c/scoped_persistent.h" #include "cobalt/script/wrappable.h" -#include "v8/include/v8.h" #include "v8/include/v8-platform.h" +#include "v8/include/v8.h" namespace cobalt { namespace script { namespace v8c { -// We need to re-forward declare this because |V8cEngine| needs us to be -// defined to have us as a member inside of a |scoped_ptr|. -v8::Platform* GetPlatform(); - class V8cHeapTracer final : public v8::EmbedderHeapTracer, public ::cobalt::script::Tracer { public: @@ -38,28 +37,31 @@ void RegisterV8References( const std::vector<std::pair<void*, void*>>& embedder_fields) override; - void TracePrologue() override {} + void TracePrologue() override; bool AdvanceTracing(double deadline_in_ms, AdvanceTracingActions actions) override; - void TraceEpilogue() override { - DCHECK(frontier_.empty()); - visited_.clear(); - } - void EnterFinalPause() override {} - void AbortTracing() override { - LOG(WARNING) << "Tracing aborted."; - frontier_.clear(); - visited_.clear(); - } - size_t NumberOfWrappersToTrace() override { return frontier_.size(); } + void TraceEpilogue() override; + void EnterFinalPause() override; + void AbortTracing() override; + size_t NumberOfWrappersToTrace() override; void Trace(Traceable* traceable) override; + void AddReferencedObject(Wrappable* owner, + ScopedPersistent<v8::Value>* value); + void RemoveReferencedObject(Wrappable* owner, + ScopedPersistent<v8::Value>* value); + private: + void MaybeAddToFrontier(Traceable* traceable); + v8::Isolate* const isolate_; - v8::Platform* const platform_ = GetPlatform(); + v8::Platform* const platform_ = IsolateFellowship::GetInstance()->platform; + std::vector<Traceable*> frontier_; std::unordered_set<Traceable*> visited_; + std::unordered_multimap<Wrappable*, ScopedPersistent<v8::Value>*> + reference_map_; }; } // namespace v8c
diff --git a/src/cobalt/script/v8c/v8c_script_value_factory.cc b/src/cobalt/script/v8c/v8c_script_value_factory.cc index a80eb19..1ac190d 100644 --- a/src/cobalt/script/v8c/v8c_script_value_factory.cc +++ b/src/cobalt/script/v8c/v8c_script_value_factory.cc
@@ -21,7 +21,7 @@ // Implementation of template function declared in the base class. template <typename T> -scoped_ptr<ScriptValue<Promise<T>>> ScriptValueFactory::CreatePromise() { +Handle<Promise<T>> ScriptValueFactory::CreatePromise() { v8c::V8cScriptValueFactory* v8c_this = base::polymorphic_downcast<v8c::V8cScriptValueFactory*>(this); return v8c_this->CreatePromise<T>();
diff --git a/src/cobalt/script/v8c/v8c_script_value_factory.h b/src/cobalt/script/v8c/v8c_script_value_factory.h index dda8fab..64cba74 100644 --- a/src/cobalt/script/v8c/v8c_script_value_factory.h +++ b/src/cobalt/script/v8c/v8c_script_value_factory.h
@@ -18,7 +18,6 @@ #include "cobalt/script/script_value_factory.h" #include "cobalt/script/v8c/entry_scope.h" #include "cobalt/script/v8c/native_promise.h" -#include "cobalt/script/v8c/v8c_global_environment.h" #include "v8/include/v8.h" namespace cobalt { @@ -30,9 +29,8 @@ explicit V8cScriptValueFactory(v8::Isolate* isolate) : isolate_(isolate) {} template <typename T> - scoped_ptr<ScriptValue<Promise<T>>> CreatePromise() { - typedef ScriptValue<Promise<T>> ScriptPromiseType; - typedef V8cUserObjectHolder<NativePromise<T>> V8cPromiseHolderType; + Handle<Promise<T>> CreatePromise() { + using V8cPromiseHolderType = V8cUserObjectHolder<NativePromise<T>>; EntryScope entry_scope(isolate_); v8::Local<v8::Context> context = isolate_->GetCurrentContext(); @@ -41,11 +39,11 @@ v8::Promise::Resolver::New(context); v8::Local<v8::Promise::Resolver> resolver; if (!maybe_resolver.ToLocal(&resolver)) { - return make_scoped_ptr<ScriptPromiseType>(nullptr); + return Handle<Promise<T>>( + new V8cPromiseHolderType(isolate_, v8::Null(isolate_))); } - return make_scoped_ptr<ScriptPromiseType>( - new V8cPromiseHolderType(isolate_, resolver)); + return Handle<Promise<T>>(new V8cPromiseHolderType(isolate_, resolver)); } private:
diff --git a/src/cobalt/script/v8c/v8c_user_object_holder.h b/src/cobalt/script/v8c/v8c_user_object_holder.h index 29d6e33..532a874 100644 --- a/src/cobalt/script/v8c/v8c_user_object_holder.h +++ b/src/cobalt/script/v8c/v8c_user_object_holder.h
@@ -16,6 +16,7 @@ #define COBALT_SCRIPT_V8C_V8C_USER_OBJECT_HOLDER_H_ #include "cobalt/script/script_value.h" +#include "cobalt/script/v8c/v8c_engine.h" #include "cobalt/script/v8c/v8c_global_environment.h" #include "cobalt/script/v8c/wrapper_private.h" #include "v8/include/v8.h" @@ -79,33 +80,19 @@ return v8_value() == v8c_other->v8_value(); } - void TraceMembers(Tracer* tracer) override { - if (!handle_.IsEmpty()) { - handle_.Get().RegisterExternalReference(isolate_); - } - } - void RegisterOwner(Wrappable* owner) override { - V8cGlobalEnvironment* global_environment = - V8cGlobalEnvironment::GetFromIsolate(isolate_); - v8::HandleScope handle_scope(isolate_); - global_environment->AddReferencedObject(owner, v8_value()); + V8cEngine::GetFromIsolate(isolate_)->heap_tracer()->AddReferencedObject( + owner, &handle_); } void DeregisterOwner(Wrappable* owner) override { - V8cGlobalEnvironment* global_environment = - V8cGlobalEnvironment::GetFromIsolate(isolate_); - if (!global_environment) { - // TODO: This will get hit when finalization callbacks get run during - // shut down, in between the time in which an isolate and context exist. - // It might be safe to just no-op, but we might have to do something - // different, such as stopping early in the callback if some flag that - // lives on the isolate is set. - LOG(WARNING) << "DeregisterOwner after global environment destroyed."; - return; + // This will get called in destructors caused by finalizers caused by the + // final shutdown GC. In this case, the entire heap tracer will have + // already been removed, so we don't need to bother removing ourselves. + V8cHeapTracer* tracer = V8cEngine::GetFromIsolate(isolate_)->heap_tracer(); + if (tracer) { + tracer->RemoveReferencedObject(owner, &handle_); } - v8::HandleScope handle_scope(isolate_); - global_environment->RemoveReferencedObject(owner, v8_value()); } void PreventGarbageCollection() override { @@ -121,7 +108,12 @@ } } - const typename V8cUserObjectType::BaseType* GetScriptValue() const override { + typename V8cUserObjectType::BaseType* GetValue() override { + return const_cast<typename V8cUserObjectType::BaseType*>( + static_cast<const V8cUserObjectHolder&>(*this).GetValue()); + } + + const typename V8cUserObjectType::BaseType* GetValue() const override { return handle_.IsEmpty() ? nullptr : &handle_; } @@ -133,6 +125,8 @@ v8::Local<v8::Value> v8_value() const { return handle_.NewLocal(isolate_); } + v8::Isolate* isolate() const { return isolate_; } + private: v8::Isolate* isolate_ = nullptr; V8cUserObjectType handle_{};
diff --git a/src/cobalt/script/v8c/v8c_value_handle.cc b/src/cobalt/script/v8c/v8c_value_handle.cc new file mode 100644 index 0000000..953cd51 --- /dev/null +++ b/src/cobalt/script/v8c/v8c_value_handle.cc
@@ -0,0 +1,107 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <string> + +#include "cobalt/script/v8c/v8c_value_handle.h" + +#include "cobalt/script/v8c/conversion_helpers.h" +#include "cobalt/script/v8c/entry_scope.h" + +namespace cobalt { +namespace script { +namespace v8c { + +std::unordered_map<std::string, std::string> ConvertSimpleObjectToMap( + const ValueHandleHolder& value, script::ExceptionState* exception_state) { + const V8cValueHandleHolder* v8_value_handle_holder = + base::polymorphic_downcast<const V8cValueHandleHolder*>(&value); + + v8::Isolate* isolate = v8_value_handle_holder->isolate(); + EntryScope entry_scope(isolate); + + v8::Local<v8::Value> v8_value = v8_value_handle_holder->v8_value(); + + if (!v8_value->IsObject()) { + exception_state->SetSimpleException(kTypeError, + "The value must be an object"); + return std::unordered_map<std::string, std::string>{}; + } + + v8::Local<v8::Object> js_object = v8_value.As<v8::Object>(); + + v8::Local<v8::Context> context = isolate->GetCurrentContext(); + + v8::Local<v8::Array> property_names; + if (!js_object->GetPropertyNames(context).ToLocal(&property_names)) { + exception_state->SetSimpleException( + kError, "Unexpected failure getting object property names"); + return std::unordered_map<std::string, std::string>{}; + } + + std::unordered_map<std::string, std::string> object_properties_map; + + // Create a map of the object's properties. + for (int i = 0; i < property_names->Length(); i++) { + v8::Local<v8::Value> property_name; + if (!property_names->Get(context, i).ToLocal(&property_name)) { + exception_state->SetSimpleException( + kError, "Unexpected failure getting object property name"); + return std::unordered_map<std::string, std::string>{}; + } + + if (!property_name->IsString()) { + exception_state->SetSimpleException( + kTypeError, "Object property names must be strings"); + return std::unordered_map<std::string, std::string>{}; + } + + v8::Local<v8::Value> property; + if (!js_object->Get(context, property_name).ToLocal(&property)) { + exception_state->SetSimpleException( + kError, "Unexpected failure getting object property"); + return std::unordered_map<std::string, std::string>{}; + } + + if (!property->IsNumber() && !property->IsString() && + !property->IsBoolean()) { + exception_state->SetSimpleException( + kTypeError, + "Object property values must be a number, string or boolean"); + return std::unordered_map<std::string, std::string>{}; + } + + std::string property_name_string; + FromJSValue(isolate, property_name, kNoConversionFlags, exception_state, + &property_name_string); + std::string property_string; + FromJSValue(isolate, property, kNoConversionFlags, exception_state, + &property_string); + + object_properties_map.insert( + std::make_pair(property_name_string, property_string)); + } + + return object_properties_map; +} + +} // namespace v8c + +std::unordered_map<std::string, std::string> ConvertSimpleObjectToMap( + const ValueHandleHolder& value, script::ExceptionState* exception_state) { + return v8c::ConvertSimpleObjectToMap(value, exception_state); +} + +} // namespace script +} // namespace cobalt
diff --git a/src/cobalt/script/v8c/wrapper_private.h b/src/cobalt/script/v8c/wrapper_private.h index 646213c..0a6a075 100644 --- a/src/cobalt/script/v8c/wrapper_private.h +++ b/src/cobalt/script/v8c/wrapper_private.h
@@ -28,7 +28,7 @@ // one mapping of such v8::Object and WrapperPrivate instances, and the // corresponding |WrapperPrivate| must be destroyed when its |v8::Object| is // garbage collected. -struct WrapperPrivate : public base::SupportsWeakPtr<WrapperPrivate> { +class WrapperPrivate : public base::SupportsWeakPtr<WrapperPrivate> { public: // The callback that V8 will run when the |v8::Object| that we live inside // of dies. @@ -54,6 +54,10 @@ // further information. static const int kInternalFieldCount = 2; + // Start at 1009 out of paranoia that we will collide with V8 looking for + // Blink specific class ids in the future. + static const int kClassId = 1009; + WrapperPrivate() = delete; WrapperPrivate(v8::Isolate* isolate, const scoped_refptr<Wrappable>& wrappable, @@ -64,9 +68,11 @@ nullptr); wrapper_.SetWeak(this, &WrapperPrivate::Callback, v8::WeakCallbackType::kParameter); + wrapper_.SetWrapperClassId(kClassId); } ~WrapperPrivate() { DCHECK(wrapper_.IsNearDeath()); + DCHECK_EQ(ref_count_, 0); wrapper_.ClearWeak(); wrapper_.Reset(); } @@ -84,6 +90,28 @@ v8::Local<v8::Object> wrapper() const { return wrapper_.Get(isolate_); } + void IncrementRefCount() { + DCHECK_GE(ref_count_, 0); + ref_count_++; + wrapper_.ClearWeak(); + } + + void DecrementRefCount() { + DCHECK_GT(ref_count_, 0); + if (--ref_count_ == 0) { + wrapper_.SetWeak(this, &WrapperPrivate::Callback, + v8::WeakCallbackType::kParameter); + } + } + + // This should only be called for the special case of shutdown, where we + // want to keep nothing alive and run all finalizers. + void ForceWeakForShutDown() { + ref_count_ = 0; + wrapper_.SetWeak(this, &WrapperPrivate::Callback, + v8::WeakCallbackType::kParameter); + } + private: // For the time being, we only use a single internal field, which stores a // pointer back to us (us being the |WrapperPrivate|). @@ -104,6 +132,7 @@ v8::Isolate* isolate_; scoped_refptr<Wrappable> wrappable_; v8::Global<v8::Object> wrapper_; + int ref_count_ = 0; DISALLOW_COPY_AND_ASSIGN(WrapperPrivate); };
diff --git a/src/cobalt/script/value_handle.h b/src/cobalt/script/value_handle.h index bb94c60..ebde710 100644 --- a/src/cobalt/script/value_handle.h +++ b/src/cobalt/script/value_handle.h
@@ -15,6 +15,10 @@ #ifndef COBALT_SCRIPT_VALUE_HANDLE_H_ #define COBALT_SCRIPT_VALUE_HANDLE_H_ +#include <string> +#include <unordered_map> + +#include "cobalt/script/exception_state.h" #include "cobalt/script/script_value.h" namespace cobalt { @@ -31,6 +35,18 @@ typedef ScriptValue<ValueHandle> ValueHandleHolder; +// Converts a "simple" object to a map of the object's properties. "Simple" +// means that the object's property names are strings and its property values +// must be a boolean, number or string. Note that this is implemented on a per +// engine basis. The use of this function should be avoided if possible. +// Eventually, JavaScript values will be exposed, making this function obsolete. +// Example "simple" object: +// {'countryCode': 'US'} +// Example non-"simple" object: +// {'countryCode': null} +std::unordered_map<std::string, std::string> ConvertSimpleObjectToMap( + const ValueHandleHolder& value, ExceptionState* exception_state); + } // namespace script } // namespace cobalt
diff --git a/src/cobalt/speech/microphone_fake.cc b/src/cobalt/speech/microphone_fake.cc index 50ec338..c54d36c 100644 --- a/src/cobalt/speech/microphone_fake.cc +++ b/src/cobalt/speech/microphone_fake.cc
@@ -74,7 +74,7 @@ file_paths_.push_back(options.file_path.value()); } else { FilePath audio_files_path; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &audio_files_path)); + CHECK(PathService::Get(base::DIR_TEST_DATA, &audio_files_path)); audio_files_path = audio_files_path.Append(FILE_PATH_LITERAL("cobalt")) .Append(FILE_PATH_LITERAL("speech")) .Append(FILE_PATH_LITERAL("testdata"));
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp index 8b3e15b..318f044 100644 --- a/src/cobalt/speech/speech.gyp +++ b/src/cobalt/speech/speech.gyp
@@ -109,21 +109,14 @@ }, }], ['cobalt_copy_test_data == 1', { - 'actions': [ - { - 'action_name': 'speech_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/speech/testdata/', - ], - 'output_dir': 'cobalt/speech/testdata/', - }, - 'includes': [ '../build/copy_test_data.gypi' ], - }, - ], - } - - ], + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/cobalt/speech/testdata/', + ], + 'content_test_output_subdir': 'cobalt/speech/testdata/', + }, + 'includes': [ '<(DEPTH)/starboard/build/copy_test_data.gypi' ], + }], ], }, ],
diff --git a/src/cobalt/speech/starboard_speech_recognizer.cc b/src/cobalt/speech/starboard_speech_recognizer.cc index 14fdfd5..ec30c3e 100644 --- a/src/cobalt/speech/starboard_speech_recognizer.cc +++ b/src/cobalt/speech/starboard_speech_recognizer.cc
@@ -20,6 +20,7 @@ #include "cobalt/speech/speech_recognition_error.h" #include "cobalt/speech/speech_recognition_event.h" #include "starboard/log.h" +#include "starboard/types.h" namespace cobalt { namespace speech { @@ -70,8 +71,10 @@ } void StarboardSpeechRecognizer::Start(const SpeechRecognitionConfig& config) { + SB_DCHECK(config.max_alternatives < INT_MAX); SbSpeechConfiguration configuration = { - config.continuous, config.interim_results, config.max_alternatives}; + config.continuous, config.interim_results, + static_cast<int>(config.max_alternatives)}; if (SbSpeechRecognizerIsValid(speech_recognizer_)) { SbSpeechRecognizerStart(speech_recognizer_, &configuration); }
diff --git a/src/cobalt/storage/storage.gyp b/src/cobalt/storage/storage.gyp index 6f18bc9..fbf2ce9 100644 --- a/src/cobalt/storage/storage.gyp +++ b/src/cobalt/storage/storage.gyp
@@ -79,18 +79,13 @@ { 'target_name': 'storage_upgrade_copy_test_data', 'type': 'none', - 'actions': [ - { - 'action_name': 'storage_upgrade_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/storage/upgrade/testdata/', - ], - 'output_dir': 'cobalt/storage/upgrade/testdata/', - }, - 'includes': [ '../build/copy_test_data.gypi' ], - }, - ], + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/cobalt/storage/upgrade/testdata/', + ], + 'content_test_output_subdir': 'cobalt/storage/upgrade/testdata/', + }, + 'includes': [ '<(DEPTH)/starboard/build/copy_test_data.gypi' ], }, ], }
diff --git a/src/cobalt/storage/upgrade/storage_upgrade_test.cc b/src/cobalt/storage/upgrade/storage_upgrade_test.cc index 56710a2..d6a7c7f 100644 --- a/src/cobalt/storage/upgrade/storage_upgrade_test.cc +++ b/src/cobalt/storage/upgrade/storage_upgrade_test.cc
@@ -43,7 +43,7 @@ EXPECT_TRUE(pathname); EXPECT_TRUE(string_out); FilePath file_path; - EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); + EXPECT_TRUE(PathService::Get(base::DIR_TEST_DATA, &file_path)); file_path = file_path.Append(pathname); EXPECT_TRUE(file_util::ReadFileToString(file_path, string_out)); const char* data = string_out->c_str();
diff --git a/src/cobalt/tools/automated_testing/c_val_names.py b/src/cobalt/tools/automated_testing/c_val_names.py index 226ee9a..4c0c69f 100644 --- a/src/cobalt/tools/automated_testing/c_val_names.py +++ b/src/cobalt/tools/automated_testing/c_val_names.py
@@ -9,14 +9,14 @@ return 'Count.DOM.ActiveJavaScriptEvents' -def count_dom_html_elements_document(): +def count_dom_html_element(): + return 'Count.MainWebModule.DOM.HtmlElement' + + +def count_dom_html_element_document(): return 'Count.MainWebModule.DOM.HtmlElement.Document' -def count_dom_html_elements_total(): - return 'Count.MainWebModule.DOM.HtmlElement.Total' - - def count_dom_html_script_element_execute(): return 'Count.MainWebModule.DOM.HtmlScriptElement.Execute' @@ -57,12 +57,6 @@ return 'Event.Duration.MainWebModule.DOM.VideoStartDelay' -def event_duration_renderer_rasterize_render_tree_delay(event_type): - c_val_prefix = 'Event.Duration.MainWebModule' - c_val_suffix = 'Renderer.Rasterize.RenderTreeDelay' - return '{}.{}.{}'.format(c_val_prefix, event_type, c_val_suffix) - - def event_is_processing(): return 'Event.MainWebModule.IsProcessing' @@ -87,6 +81,10 @@ return 'Memory.MainWebModule.ImageCache.Resource.Loaded' +def memory_dom_html_script_element_execute(): + return 'Memory.MainWebModule.DOM.HtmlScriptElement.Execute' + + def rasterize_animations_entry_list(): return 'Renderer.Rasterize.Animations.EntryList'
diff --git a/src/cobalt/tools/automated_testing/cobalt_runner.py b/src/cobalt/tools/automated_testing/cobalt_runner.py index 21766c7..46ab679 100644 --- a/src/cobalt/tools/automated_testing/cobalt_runner.py +++ b/src/cobalt/tools/automated_testing/cobalt_runner.py
@@ -4,18 +4,19 @@ from __future__ import division from __future__ import print_function +import json import os import re import sys import thread import threading +import time import _env # pylint: disable=unused-import +from cobalt.tools.automated_testing import c_val_names +from cobalt.tools.automated_testing import webdriver_utils from starboard.tools import abstract_launcher - -# pylint: disable=C6204 -sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) -import webdriver_utils +from starboard.tools import command_line # Pattern to match Cobalt log line for when the WebDriver port has been # opened. @@ -27,9 +28,18 @@ RE_WEBMODULE_LOADED = re.compile( r'^\[\d+/\d+:INFO:browser_module\.cc\(\d+\)\] Loaded WebModule') +# selenium imports +# pylint: disable=C0103 +ActionChains = webdriver_utils.import_selenium_module( + submodule='webdriver.common.action_chains').ActionChains +keys = webdriver_utils.import_selenium_module('webdriver.common.keys') + DEFAULT_STARTUP_TIMEOUT_SECONDS = 2 * 60 WEBDRIVER_HTTP_TIMEOUT_SECONDS = 2 * 60 COBALT_EXIT_TIMEOUT_SECONDS = 5 +PAGE_LOAD_WAIT_SECONDS = 30 +WINDOWDRIVER_CREATED_TIMEOUT_SECONDS = 30 +WEBMODULE_LOADED_TIMEOUT_SECONDS = 30 COBALT_WEBDRIVER_CAPABILITIES = { 'browserName': 'cobalt', @@ -37,84 +47,106 @@ 'platform': 'LINUX' } -_launcher = None -_webdriver = None -_windowdriver_created = threading.Event() -_webmodule_loaded = threading.Event() - - -def SendResume(): - """Sends a resume signal to start Cobalt from preload.""" - _launcher.SendResume() - - -def SendSuspend(): - """Sends a system signal to put Cobalt into suspend state.""" - _launcher.SendSuspend() - - -def GetWebDriver(): - """Returns the active connect WebDriver instance.""" - return _webdriver - - -def GetWindowDriverCreated(): - """Returns the WindowDriver created instance.""" - return _windowdriver_created - - -def GetWebModuleLoaded(): - """Returns the WebModule loaded instance.""" - return _webmodule_loaded - class TimeoutException(Exception): + """General timeout exception.""" pass class CobaltRunner(object): - """Runs a Cobalt browser w/ a WebDriver client attached.""" - test_script_started = threading.Event() - selenium_webdriver_module = None - launcher = None - webdriver = None - thread = None - failed = False - should_exit = threading.Event() + """Wrapper around a Starboard applauncher object specialized to launch Cobalt. - def __init__(self, platform, config, device_id, target_params, out_directory, - url, log_file, success_message): - global _launcher + In addition to launching Cobalt, this class also includes logic to attach (via + Selenium) a webdriver client to the Cobalt process, and wait for some common + events to occur such as when the initial URL finishes loading. Additional + functionality is provided for common operations such as querying from the + Cobalt process the value of a CVal. + """ + + class DeviceParams(object): + """A struct to store runner's device info.""" + platform = None + device_id = None + config = None + out_directory = None + + class WindowDriverCreatedTimeoutException(Exception): + """Exception thrown when WindowDriver was not created in time.""" + + class WebModuleLoadedTimeoutException(Exception): + """Exception thrown when WebModule was not loaded in time.""" + + class AssertException(Exception): + """Raised when assert condition fails.""" + + def __init__(self, + device_params, + url, + log_file=None, + target_params=None, + success_message=None): + """CobaltRunner constructor. + + Args: + device_params: A DeviceParams object storing all device specific info. + url: The intial URL to launch Cobalt on. + log_file: The log file's name string. + target_params: An array of command line arguments to launch Cobalt + with. + success_message: Optional success message to be printed on successful + exit. + """ + + self.test_script_started = threading.Event() + self.launcher = None + self.webdriver = None + self.failed = False + self.should_exit = threading.Event() + self.launcher_is_running = False + self.windowdriver_created = threading.Event() + self.webmodule_loaded = threading.Event() self.selenium_webdriver_module = webdriver_utils.import_selenium_module( 'webdriver') - command_line_args = [] - if target_params is not None: - split_target_params = target_params.split(' ') - for param in split_target_params: - command_line_args.append(param) - command_line_args.append('--debug_console=off') - command_line_args.append('--null_savegame') - command_line_args.append('--url=' + url) - - self.log_file = log_file + self.platform = device_params.platform + self.config = device_params.config + self.device_id = device_params.device_id + self.out_directory = device_params.out_directory + if log_file: + self.log_file = open(log_file) + else: + self.log_file = sys.stdout + self.url = url + self.target_params = target_params self.success_message = success_message + url_string = '--url=' + self.url + if not self.target_params: + self.target_params = [url_string] + else: + self.target_params.append(url_string) - read_fd, write_fd = os.pipe() + def SendResume(self): + """Sends a resume signal to start Cobalt from preload.""" + self.launcher.SendResume() - self.launcher_read_pipe = os.fdopen(read_fd, 'r') - self.launcher_write_pipe = os.fdopen(write_fd, 'w') + def SendSuspend(self): + """Sends a system signal to put Cobalt into suspend state.""" + self.launcher.SendSuspend() - self.launcher = abstract_launcher.LauncherFactory( - platform, - 'cobalt', - config, - device_id=device_id, - target_params=command_line_args, - output_file=self.launcher_write_pipe, - out_directory=out_directory) - _launcher = self.launcher + def GetURL(self): + return self.url + + def GetLogFile(self): + return self.log_file + + def GetWindowDriverCreated(self): + """Returns the WindowDriver created instance.""" + return self.windowdriver_created + + def GetWebModuleLoaded(self): + """Returns the WebModule loaded instance.""" + return self.webmodule_loaded def _HandleLine(self): """Reads log lines to determine when cobalt/webdriver server start.""" @@ -122,15 +154,17 @@ line = self.launcher_read_pipe.readline() if line: self.log_file.write(line) + # Calling flush() to ensure the logs are delievered timely. + self.log_file.flush() else: break if RE_WINDOWDRIVER_CREATED.search(line): - _windowdriver_created.set() + self.windowdriver_created.set() continue if RE_WEBMODULE_LOADED.search(line): - _webmodule_loaded.set() + self.webmodule_loaded.set() continue # Wait for WebDriver port here then connect @@ -147,13 +181,38 @@ def __enter__(self): - self.runner_thread = threading.Thread(target=self.Run) + self.Run() + return self + + def Run(self): + """Construct and run app launcher.""" + if self.launcher_is_running: + return + + # Behavior to restart a killed app launcher is not clearly defined. + # Let's get a new launcher for every launch. + read_fd, write_fd = os.pipe() + + self.launcher_read_pipe = os.fdopen(read_fd, 'r') + self.launcher_write_pipe = os.fdopen(write_fd, 'w') + + self.launcher = abstract_launcher.LauncherFactory( + self.platform, + 'cobalt', + self.config, + device_id=self.device_id, + target_params=self.target_params, + output_file=self.launcher_write_pipe, + out_directory=self.out_directory) + + self.runner_thread = threading.Thread(target=self._RunLauncher) self.runner_thread.start() self.reader_thread = threading.Thread(target=self._HandleLine) # Make this thread daemonic so that it always exits self.reader_thread.daemon = True self.reader_thread.start() + self.launcher_is_running = True try: self.WaitForStart() except KeyboardInterrupt: @@ -162,7 +221,6 @@ self.Exit(should_fail=True) raise TimeoutException - return self def __exit__(self, exc_type, exc_value, exc_traceback): # The unittest module terminates with a SystemExit @@ -180,6 +238,10 @@ self.failed = failed self.should_exit.set() + self._KillLauncher() + + def _KillLauncher(self): + """Kills the launcher and its attached Cobalt instance.""" try: self.launcher.Kill() except Exception as e: # pylint: disable=broad-except @@ -201,14 +263,12 @@ pass def _StartWebdriver(self, port): - global _webdriver host, webdriver_port = self.launcher.GetHostAndPortGivenPort(port) url = 'http://{}:{}/'.format(host, webdriver_port) self.webdriver = self.selenium_webdriver_module.Remote( url, COBALT_WEBDRIVER_CAPABILITIES) self.webdriver.command_executor.set_timeout(WEBDRIVER_HTTP_TIMEOUT_SECONDS) print('Selenium Connected\n', file=self.log_file) - _webdriver = self.webdriver self.test_script_started.set() def WaitForStart(self): @@ -222,14 +282,13 @@ raise TimeoutException print('Cobalt started', file=self.log_file) - def Run(self): + def _RunLauncher(self): """Thread run routine.""" try: print('Running launcher', file=self.log_file) self.launcher.Run() - print( - 'Cobalt terminated. failed: ' + str(self.failed), file=self.log_file) - if not self.failed: + print('Cobalt terminated.', file=self.log_file) + if not self.failed and self.success_message: print('{}\n'.format(self.success_message)) # pylint: disable=broad-except except Exception as ex: @@ -241,3 +300,139 @@ # we must interrupt it. thread.interrupt_main() return 0 + + def GetCval(self, cval_name): + """Returns the Python object represented by a JSON cval string. + + Args: + cval_name: Name of the cval. + Returns: + Python object represented by the JSON cval string + """ + javascript_code = 'return h5vcc.cVal.getValue(\'{}\')'.format(cval_name) + json_result = self.webdriver.execute_script(javascript_code) + if json_result is None: + return None + else: + return json.loads(json_result) + + def PollUntilFound(self, css_selector, expected_num=None): + """Polls until an element is found. + + Args: + css_selector: A CSS selector + expected_num: The expected number of the selector type to be found. + Raises: + Underlying WebDriver exceptions + """ + start_time = time.time() + while ((not self.FindElements(css_selector)) and + (time.time() - start_time < PAGE_LOAD_WAIT_SECONDS)): + time.sleep(1) + if expected_num: + self.FindElements(css_selector, expected_num) + + def UniqueFind(self, unique_selector): + """Finds and returns a uniquely selected element. + + Args: + unique_selector: A CSS selector that will select only one element + Raises: + AssertException: the element isn't unique + Returns: + Element + """ + return self.FindElements(unique_selector, expected_num=1)[0] + + def AssertDisplayed(self, css_selector): + """Asserts that an element is displayed. + + Args: + css_selector: A CSS selector + Raises: + AssertException: the element isn't found + """ + # TODO does not actually assert that it's visible, like webdriver.py + # probably does. + if not self.UniqueFind(css_selector): + raise CobaltRunner.AssertException( + 'Did not find selector: {}'.format(css_selector)) + + def FindElements(self, css_selector, expected_num=None): + """Finds elements based on a selector. + + Args: + css_selector: A CSS selector + expected_num: Expected number of matching elements + Raises: + AssertException: expected_num isn't met + Returns: + Array of selected elements + """ + elements = self.webdriver.find_elements_by_css_selector(css_selector) + if expected_num is not None and len(elements) != expected_num: + raise CobaltRunner.AssertException( + 'Expected number of element {} is: {}, got {}'.format( + css_selector, expected_num, len(elements))) + return elements + + def SendKeys(self, key_events): + """Sends keys to whichever element currently has focus. + + Args: + key_events: key events + + Raises: + Underlying WebDriver exceptions + """ + ActionChains(self.webdriver).send_keys(key_events).perform() + + def ClearUrlLoadedEvents(self): + """Clear the events that indicate that Cobalt finished loading a URL.""" + + self.GetWindowDriverCreated().clear() + self.GetWebModuleLoaded().clear() + + def WaitForUrlLoadedEvents(self): + """Wait for the events indicating that Cobalt finished loading a URL.""" + if not self.windowdriver_created.wait(WINDOWDRIVER_CREATED_TIMEOUT_SECONDS): + raise CobaltRunner.WindowDriverCreatedTimeoutException() + + if not self.webmodule_loaded.wait(WEBMODULE_LOADED_TIMEOUT_SECONDS): + raise CobaltRunner.WebModuleLoadedTimeoutException() + + def LoadUrl(self, url): + """Loads about:blank and waits for it to finish. + + Args: + url: URL string to be loaded by Cobalt. + Raises: + Underlying WebDriver exceptions + """ + self.ClearUrlLoadedEvents() + self.webdriver.get(url) + self.WaitForUrlLoadedEvents() + + def IsInPreload(self): + """We assume Cobalt is in preload mode if no render tree is generated.""" + render_tree_count = self.GetCval( + c_val_names.count_rasterize_new_render_tree()) + if render_tree_count is not None: + return False + return True + + +def GetDeviceParamsFromCommandLine(): + """Provide a commanline parser for all CobaltRunner inputs.""" + + arg_parser = command_line.CreateParser() + + args, _ = arg_parser.parse_known_args() + + device_params = CobaltRunner.DeviceParams() + device_params.platform = args.platform + device_params.config = args.config + device_params.device_id = args.device_id + device_params.out_directory = args.out_directory + + return device_params
diff --git a/src/cobalt/tools/automated_testing/cobalt_test.py b/src/cobalt/tools/automated_testing/cobalt_test.py deleted file mode 100644 index f135f30..0000000 --- a/src/cobalt/tools/automated_testing/cobalt_test.py +++ /dev/null
@@ -1,332 +0,0 @@ -"""Base class for tests that need to launch Cobalt.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import json -import os -import sys -import time -import traceback -import unittest - -# pylint: disable=C6204 -import _env # pylint: disable=unused-import -from cobalt.tools.automated_testing import c_val_names -from cobalt.tools.automated_testing import cobalt_runner -from cobalt.tools.automated_testing import webdriver_utils -from starboard.tools import command_line - -# selenium imports -# pylint: disable=C0103 -ActionChains = webdriver_utils.import_selenium_module( - submodule='webdriver.common.action_chains').ActionChains -keys = webdriver_utils.import_selenium_module('webdriver.common.keys') - -WINDOWDRIVER_CREATED_TIMEOUT_SECONDS = 30 -WEBMODULE_LOADED_TIMEOUT_SECONDS = 30 -PAGE_LOAD_WAIT_SECONDS = 30 -PROCESSING_TIMEOUT_SECONDS = 180 -HTML_SCRIPT_ELEMENT_EXECUTE_TIMEOUT_SECONDS = 30 -MEDIA_TIMEOUT_SECONDS = 30 -DEFAULT_TEST_SUCCESS_MESSAGE = 'Cobalt Test Case passed.' -DEFAULT_URL = 'https://www.youtube.com/tv' - -_platform = 'unknown' - - -class CobaltTestCase(unittest.TestCase): - - class WindowDriverCreatedTimeoutException(Exception): - """Exception thrown when WindowDriver was not created in time.""" - - class WebModuleLoadedTimeoutException(Exception): - """Exception thrown when WebModule was not loaded in time.""" - - class ProcessingTimeoutException(Exception): - """Exception thrown when processing did not complete in time.""" - - class HtmlScriptElementExecuteTimeoutException(Exception): - """Exception thrown when processing did not complete in time.""" - - def setUp(self): - pass - - @classmethod - def setUpClass(cls): - print('Running ' + cls.__name__) - - @classmethod - def tearDownClass(cls): - print('Done ' + cls.__name__) - - def get_platform(self): - return _platform - - def get_webdriver(self): - return cobalt_runner.GetWebDriver() - - def send_resume(self): - return cobalt_runner.SendResume() - - def send_suspend(self): - return cobalt_runner.SendSuspend() - - def get_cval(self, cval_name): - """Returns the Python object represented by a JSON cval string. - - Args: - cval_name: Name of the cval. - Returns: - Python object represented by the JSON cval string - """ - javascript_code = 'return h5vcc.cVal.getValue(\'{}\')'.format(cval_name) - json_result = self.get_webdriver().execute_script(javascript_code) - if json_result is None: - return None - else: - return json.loads(json_result) - - def poll_until_found(self, css_selector, expected_num=None): - """Polls until an element is found. - - Args: - css_selector: A CSS selector - expected_num: The expected number of the selector type to be found. - Raises: - Underlying WebDriver exceptions - """ - start_time = time.time() - while ((not self.find_elements(css_selector)) and - (time.time() - start_time < PAGE_LOAD_WAIT_SECONDS)): - time.sleep(1) - if expected_num: - self.find_elements(css_selector, expected_num) - - def unique_find(self, unique_selector): - """Finds and returns a uniquely selected element. - - Args: - unique_selector: A CSS selector that will select only one element - Raises: - Underlying WebDriver exceptions - AssertError: the element isn't unique - Returns: - Element - """ - return self.find_elements(unique_selector, expected_num=1)[0] - - def assert_displayed(self, css_selector): - """Asserts that an element is displayed. - - Args: - css_selector: A CSS selector - Raises: - Underlying WebDriver exceptions - AssertError: the element isn't found - """ - # TODO does not actually assert that it's visible, like webdriver.py - # probably does. - self.assertTrue(self.unique_find(css_selector)) - - def find_elements(self, css_selector, expected_num=None): - """Finds elements based on a selector. - - Args: - css_selector: A CSS selector - expected_num: Expected number of matching elements - Raises: - Underlying WebDriver exceptions - AssertError: expected_num isn't met - Returns: - Array of selected elements - """ - elements = self.get_webdriver().find_elements_by_css_selector(css_selector) - if expected_num is not None: - self.assertEqual(len(elements), expected_num) - return elements - - def send_keys(self, key_events): - """Sends keys to whichever element currently has focus. - - Args: - key_events: key events - - Raises: - Underlying WebDriver exceptions - """ - ActionChains(self.get_webdriver()).send_keys(key_events).perform() - - def clear_url_loaded_events(self): - """Clear the events that indicate that Cobalt finished loading a URL.""" - cobalt_runner.GetWindowDriverCreated().clear() - cobalt_runner.GetWebModuleLoaded().clear() - - def load_blank(self): - """Loads about:blank and waits for it to finish. - - Raises: - Underlying WebDriver exceptions - """ - self.clear_url_loaded_events() - self.get_webdriver().get('about:blank') - self.wait_for_url_loaded_events() - - def wait_for_url_loaded_events(self): - """Wait for the events indicating that Cobalt finished loading a URL.""" - windowdriver_created = cobalt_runner.GetWindowDriverCreated() - if not windowdriver_created.wait(WINDOWDRIVER_CREATED_TIMEOUT_SECONDS): - raise CobaltTestCase.WindowDriverCreatedTimeoutException() - - webmodule_loaded = cobalt_runner.GetWebModuleLoaded() - if not webmodule_loaded.wait(WEBMODULE_LOADED_TIMEOUT_SECONDS): - raise CobaltTestCase.WebModuleLoadedTimeoutException() - - def wait_for_processing_complete(self, check_animations=True): - """Waits for Cobalt to complete processing. - - This method requires two consecutive iterations through its loop where - Cobalt is not processing before treating processing as complete. This - protects against a brief window between two different processing sections - being mistaken as completed processing. - - Args: - check_animations: Whether or not animations should be checked when - determining if processing is complete. - - Raises: - ProcessingTimeoutException: Processing is not complete within the - required time. - """ - start_time = time.time() - - # First simply check for whether or not the event is still processing. - # There's no need to check anything else while the event is still going on. - # Once it is done processing, it won't get re-set, so there's no need to - # re-check it. - while self.get_cval(c_val_names.event_is_processing()): - if time.time() - start_time > PROCESSING_TIMEOUT_SECONDS: - raise CobaltTestCase.ProcessingTimeoutException() - - time.sleep(0.1) - - # Now wait for all processing to complete in Cobalt. - count = 0 - while count < 2: - if self.is_processing(check_animations): - count = 0 - else: - count += 1 - - if time.time() - start_time > PROCESSING_TIMEOUT_SECONDS: - raise CobaltTestCase.ProcessingTimeoutException() - - time.sleep(0.1) - - def is_processing(self, check_animations): - """Checks to see if Cobalt is currently processing.""" - return (self.get_cval(c_val_names.count_dom_active_java_script_events()) or - self.get_cval(c_val_names.is_render_tree_generation_pending()) or - self.get_cval(c_val_names.is_render_tree_rasterization_pending()) or - (check_animations and - self.get_cval(c_val_names.renderer_has_active_animations())) or - self.get_cval(c_val_names.count_image_cache_resource_loading())) - - def wait_for_media_element_playing(self): - """Waits for a video to begin playing. - - Returns: - Whether or not the video started. - """ - start_time = time.time() - while self.get_cval( - c_val_names.event_duration_dom_video_start_delay()) == 0: - if time.time() - start_time > MEDIA_TIMEOUT_SECONDS: - return False - time.sleep(0.1) - - return True - - def wait_for_html_script_element_execute_count(self, required_count): - """Waits for specified number of html script element Execute() calls. - - Args: - required_count: the number of executions that must occur - - Raises: - HtmlScriptElementExecuteTimeoutException: The required html script element - executions did not occur within the required time. - """ - start_time = time.time() - while self.get_cval( - c_val_names.count_dom_html_script_element_execute()) < required_count: - if time.time() - start_time > HTML_SCRIPT_ELEMENT_EXECUTE_TIMEOUT_SECONDS: - raise CobaltTestCase.HtmlScriptElementExecuteTimeoutException() - time.sleep(0.1) - - def is_in_preload(self): - """We assume Cobalt is in preload mode if no render tree is generated.""" - render_tree_count = self.get_cval( - c_val_names.count_rasterize_new_render_tree()) - if render_tree_count is not None: - return False - return True - - -def main(url): - """Launch Cobalt and run python unit test main function.""" - global _platform - - arg_parser = command_line.CreateParser() - arg_parser.add_argument( - '-l', '--log_file', help='Logfile pathname. stdout if absent.') - arg_parser.add_argument( - '--success_message', help='Custom message to be printed on test success.') - - args, _ = arg_parser.parse_known_args() - # Keep unittest module from seeing these args - sys.argv = sys.argv[:1] - - success_message = DEFAULT_TEST_SUCCESS_MESSAGE - if args.success_message is not None: - success_message = args.success_message - - _platform = args.platform - if _platform is None: - try: - _platform = os.environ['BUILD_PLATFORM'] - except KeyError: - sys.stderr.write('Must specify --platform\n') - sys.exit(1) - - if not args.log_file: - log_file = sys.stdout - else: - log_file = open(args.log_file) - - if not url: - url = DEFAULT_URL - - try: - with cobalt_runner.CobaltRunner( - platform=_platform, - config=args.config, - device_id=args.device_id, - target_params=args.target_params, - out_directory=args.out_directory, - url=url, - log_file=log_file, - success_message=success_message): - unittest.main( - testRunner=unittest.TextTestRunner(verbosity=0, stream=log_file)) - except cobalt_runner.TimeoutException: - print('Timeout waiting for Cobalt to start', file=sys.stderr) - sys.exit(1) - # User pressed Ctrl + C, or an error in the launcher caused a shutdown. - except KeyboardInterrupt: - sys.exit(1) - except Exception: # pylint: disable=W0703 - sys.stderr.write('Exception while running test:\n') - traceback.print_exc(file=sys.stderr) - sys.exit(1)
diff --git a/src/cobalt/webdriver/algorithms.cc b/src/cobalt/webdriver/algorithms.cc index 7d36c60..f085038 100644 --- a/src/cobalt/webdriver/algorithms.cc +++ b/src/cobalt/webdriver/algorithms.cc
@@ -19,10 +19,7 @@ #include <functional> #include <vector> -#include "base/i18n/case_conversion.h" -#include "base/string16.h" #include "base/string_util.h" -#include "base/utf_string_conversions.h" #include "cobalt/cssom/css_computed_style_data.h" #include "cobalt/cssom/string_value.h" #include "cobalt/dom/document.h" @@ -32,6 +29,7 @@ #include "cobalt/dom/node.h" #include "cobalt/dom/node_list.h" #include "cobalt/math/rect.h" +#include "third_party/icu/source/common/unicode/unistr.h" namespace cobalt { namespace webdriver { @@ -40,29 +38,87 @@ // Characters that match \s in ECMAScript regular expressions. // Note that non-breaking space is at the beginning to simplify definition of // kWhitespaceCharsExcludingNonBreakingSpace below. -const char kWhitespaceChars[] = - u8"\u00a0 " - u8"\f\n\r\t\v\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006" - u8"\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\ufeff"; -const char* kWhitespaceCharsExcludingNonBreakingSpace = kWhitespaceChars + 1; +const char32_t kWhitespaceChars[] = + U"\u00a0 " + U"\f\n\r\t\v\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006" + U"\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\ufeff"; +const char32_t* kWhitespaceCharsExcludingNonBreakingSpace = + kWhitespaceChars + 1; // Defined in https://www.w3.org/TR/webdriver/#text.horizontal -const char kHorizontalWhitespaceChars[] = u8" \f\t\v\u2028\u2029"; +const char32_t kHorizontalWhitespaceChars[] = U" \f\t\v\u2028\u2029"; // Defined in step 2.1 of the getElementText() algorithm in // https://www.w3.org/TR/webdriver/#get-element-text -const char kZeroWidthSpacesAndFeeds[] = u8"\f\v\u200b\u200e\u200f"; +const char32_t kZeroWidthSpacesAndFeeds[] = U"\f\v\u200b\u200e\u200f"; -const char kNonBreakingSpace = '\xa0'; +const char32_t kNonBreakingSpace[] = U"\u00a0"; -bool IsHorizontalWhitespace(char c) { - DCHECK_NE(c, '\0'); - return strchr(kHorizontalWhitespaceChars, c) != NULL; +const char32_t kIcuEndOfString = 0xffff; + +bool StringContains(const char32_t* str, char32_t ch) { + for (size_t i = 0; str[i] != 0; ++i) { + if (ch == str[i]) { + return true; + } + } + return false; } -bool IsZeroWidthSpaceOrFeed(char c) { - DCHECK_NE(c, '\0'); - return strchr(kZeroWidthSpacesAndFeeds, c) != NULL; +void RemoveCharacters(icu::UnicodeString* text, const char32_t* characters) { + for (int32_t text_offset = 0;;) { + char32_t ch = text->char32At(text_offset); + if (ch == kIcuEndOfString) { + return; + } + if (StringContains(characters, ch)) { + text->remove(text_offset, 1); + } else { + ++text_offset; + } + } +} + +void ReplaceCharacters(icu::UnicodeString* text, const char32_t* characters, + char32_t new_character) { + for (int32_t text_offset = 0;; ++text_offset) { + char32_t ch = text->char32At(text_offset); + if (ch == kIcuEndOfString) { + return; + } + if (StringContains(characters, ch)) { + text->replace(text_offset, 1, UChar32(new_character)); + } + } +} + +void TrimUnicodeString(icu::UnicodeString* text, const char32_t* characters) { + // Trim characters at the end. + int32_t original_length = text->length(); + for (int32_t text_offset = original_length - 1;; --text_offset) { + if (text_offset < 0 && original_length > 0) { + text->remove(); + } + char32_t ch = text->char32At(text_offset); + if (!StringContains(characters, ch)) { + ++text_offset; + if (text_offset < original_length) { + text->remove(text_offset, original_length - text_offset); + } + break; + } + } + + // Trim characters at the beginning. + for (int32_t text_offset = 0;; ++text_offset) { + char32_t ch = text->char32At(text_offset); + if (ch == kIcuEndOfString || !StringContains(characters, ch)) { + if (text_offset > 0) { + text->remove(0, text_offset); + } + break; + } + } } bool IsInHeadElement(dom::Element* element) { @@ -77,44 +133,24 @@ return IsInHeadElement(parent.get()); } -// Helper class that can be used as a predicate to std::remove_if. -// When more than one instance of the character |c| occurs consecutively, the -// functor will return true for each occurrence of |c| after the first one. -class MatchConsecutiveCharactersPredicate { - public: - explicit MatchConsecutiveCharactersPredicate(char c) - : character_to_match_(c), last_('\0') {} - bool operator()(char c) { - DCHECK_NE(c, '\0'); - bool same_char = c == last_; - last_ = c; - return same_char && c == character_to_match_; - } - - private: - char character_to_match_; - char last_; -}; - void CanonicalizeText(const base::optional<std::string>& whitespace_style, const base::optional<std::string>& text_transform, - std::string* text) { - // std::remove_if will not resize the std::string, but will return a new end - // of the string. Use this iterator instead of text->end() in each step - // below, and erase the end of the string at the end. - std::string::iterator end = text->end(); - + icu::UnicodeString* text) { // https://www.w3.org/TR/webdriver/#get-element-text // 2.1 Remove any zero-width spaces (\u200b, \u200e, \u200f), form feeds (\f) // or vertical tab feeds (\v) from text. - end = std::remove_if(text->begin(), end, IsZeroWidthSpaceOrFeed); + RemoveCharacters(text, kZeroWidthSpacesAndFeeds); // Consecutive sequences of new lines should be compressed to a single new // line. Accomplish this by converting all \r chars to \n chars, and then // converting sequences of \n chars to a single \n char. - std::replace(text->begin(), end, '\r', '\n'); - MatchConsecutiveCharactersPredicate consecutive_newline_predicate('\n'); - end = std::remove_if(text->begin(), end, consecutive_newline_predicate); + ReplaceCharacters(text, U"\r", '\n'); + + const icu::UnicodeString consecutive_newline = UNICODE_STRING_SIMPLE("\n\n"); + for (int32_t index = text->indexOf(consecutive_newline); index >= 0; + index = text->indexOf(consecutive_newline, index)) { + text->remove(index, 1); + } // https://www.w3.org/TR/webdriver/#get-element-text // 2.3 @@ -123,42 +159,37 @@ // replace each newline (\n) in text with a single space character (\x20). if (*whitespace_style == cssom::kNormalKeywordName || *whitespace_style == cssom::kNoWrapKeywordName) { - std::replace(text->begin(), end, '\n', ' '); + ReplaceCharacters(text, U"\n", ' '); } // If the parent's effective CSS whitespace style is 'pre' or 'pre-wrap' // replace each horizontal whitespace character with a non-breaking space - // character (\xa0). + // character (\u00a0). // Otherwise replace each sequence of horizontal whitespace characters - // except non-breaking spaces (\xa0) with a single space character. + // except non-breaking spaces (\u00a0) with a single space character. // // Cobalt does not have 'pre-wrap' style, so just check for 'pre'. if (*whitespace_style == cssom::kPreKeywordName) { - std::replace_if(text->begin(), end, IsHorizontalWhitespace, - kNonBreakingSpace); + ReplaceCharacters(text, kHorizontalWhitespaceChars, kNonBreakingSpace[0]); } else { // Replace all horizontal whitespace characters with ' '. - std::replace_if(text->begin(), end, IsHorizontalWhitespace, ' '); + ReplaceCharacters(text, kHorizontalWhitespaceChars, ' '); // Convert consecutive ' ' characters to a single ' '. - MatchConsecutiveCharactersPredicate consecutive_space_predicate(' '); - end = std::remove_if(text->begin(), end, consecutive_space_predicate); + const icu::UnicodeString consecutive_space = UNICODE_STRING_SIMPLE(" "); + for (int32_t index = text->indexOf(consecutive_space); index >= 0; + index = text->indexOf(consecutive_space, index)) { + text->remove(index, 1); + } } } - // Trim the original string, since several characters may have been removed. - text->erase(end, text->end()); - // https://www.w3.org/TR/webdriver/#get-element-text // 2.4 Apply the parent's effective CSS text-transform style as per the // CSS 2.1 specification ([CSS21]) if (text_transform) { // Cobalt does not support 'capitalize' and 'lowercase' keywords. if (*text_transform == cssom::kUppercaseKeywordName) { - // Convert to UTF16 to do i18n safe upper-case conversion. - string16 utf16_string; - UTF8ToUTF16(text->c_str(), text->length(), &utf16_string); - utf16_string = base::i18n::ToUpper(utf16_string.c_str()); - UTF16ToUTF8(utf16_string.c_str(), utf16_string.length(), text); + text->toUpper(); } } } @@ -198,17 +229,17 @@ // Return true if there is at least one string in the vector and the last one // is non-empty. -bool LastLineIsNonEmpty(const std::vector<std::string>& lines) { +bool LastLineIsNonEmpty(const std::vector<icu::UnicodeString>& lines) { if (lines.empty()) { return false; } - return !lines.back().empty(); + return !lines.back().isEmpty(); } // Recursive function to build the vector of lines for the text representation // of an element. void GetElementTextInternal(dom::Element* element, - std::vector<std::string>* lines) { + std::vector<icu::UnicodeString>* lines) { // If the element is a: BR element: Push '' to lines and continue. if (element->AsHTMLElement() && element->AsHTMLElement()->AsHTMLBRElement()) { lines->push_back(""); @@ -239,7 +270,8 @@ if (child->IsText() && is_displayed) { // If descendent is a [DOM] text node let text equal the nodeValue // property of descendent. - std::string text = child->node_value().value_or(""); + icu::UnicodeString text = icu::UnicodeString::fromUTF8( + child->node_value().value_or("")); CanonicalizeText(whitespace_style, text_transform_style, &text); // 2.5 If last(lines) ends with a space character and text starts with a @@ -248,9 +280,8 @@ if (lines->empty()) { lines->push_back(text); } else { - if (!lines->back().empty() && *(lines->back().rbegin()) == ' ' && - !text.empty() && *(text.begin()) == ' ') { - text.erase(0); + if (lines->back().endsWith(' ') && text.startsWith(' ')) { + text.remove(0, 1); } lines->back().append(text); } @@ -397,20 +428,27 @@ // Recursively visit this element and its children to create a vector of lines // of text. - std::vector<std::string> lines; + std::vector<icu::UnicodeString> lines; GetElementTextInternal(element, &lines); // Trim leading and trailing non-breaking space characters in each line in - // place. + // place. Also replace non-breaking spaces with regular spaces. for (size_t i = 0; i < lines.size(); ++i) { - TrimString(lines[0], kWhitespaceCharsExcludingNonBreakingSpace, - &(lines[0])); + TrimUnicodeString(&lines[i], kWhitespaceCharsExcludingNonBreakingSpace); + ReplaceCharacters(&lines[i], kNonBreakingSpace, ' '); } + // Join the lines, and trim any leading/trailing newlines. - std::string joined = JoinString(lines, '\n'); - TrimString(joined, "\n", &joined); - // Convert non-breaking spaces to regular spaces. - std::replace(joined.begin(), joined.end(), kNonBreakingSpace, ' '); + std::string joined; + if (!lines.empty()) { + lines[0].toUTF8String(joined); + for (size_t i = 1; i < lines.size(); ++i) { + joined.append("\n"); + lines[i].toUTF8String(joined); + } + TrimString(joined, "\n", &joined); + } + return joined; }
diff --git a/src/cobalt/webdriver/get_element_text_test.cc b/src/cobalt/webdriver/get_element_text_test.cc index 5b50e0d..bb50bcb 100644 --- a/src/cobalt/webdriver/get_element_text_test.cc +++ b/src/cobalt/webdriver/get_element_text_test.cc
@@ -83,7 +83,7 @@ } // namespace TEST_F(GetElementTextTest, ZeroSpaceWidthIsRemoved) { - AppendText(u8"a\u200bb\u200ec\u200fd"); + AppendText(u8"a\u200bb\u200ec\u200f\u200bd"); EXPECT_STREQ("abcd", algorithms::GetElementText(div_.get()).c_str()); } @@ -94,7 +94,7 @@ TEST_F(GetElementTextTest, NoWrapStyle) { div_->style()->set_white_space("nowrap", NULL); - AppendText(u8"a\n\nb\nc\td\u2028e\u2029f\xa0g"); + AppendText(u8"a\n\nb\nc\td\u2028e\u2029f\u00a0g"); EXPECT_STREQ("a b c d e f g", algorithms::GetElementText(div_.get()).c_str()); } @@ -127,5 +127,23 @@ EXPECT_STREQ("a\nb\nc", algorithms::GetElementText(div_.get()).c_str()); } +TEST_F(GetElementTextTest, LinesAreTrimmed) { + AppendText(" a \n"); + AppendBR(); + AppendText(" b \n\n"); + AppendBR(); + AppendText("\nc "); + AppendBR(); + AppendText(" "); + + EXPECT_STREQ("a\nb\nc", algorithms::GetElementText(div_.get()).c_str()); +} + +TEST_F(GetElementTextTest, WholeCodePointsAreProcessed) { + AppendText(u8"a\u200b\u2020\u2029\u2022\U0002070Eb"); + EXPECT_STREQ(u8"a\u2020 \u2022\U0002070Eb", + algorithms::GetElementText(div_.get()).c_str()); +} + } // namespace webdriver } // namespace cobalt
diff --git a/src/cobalt/webdriver/search.h b/src/cobalt/webdriver/search.h index ab7e97e..27b6bef 100644 --- a/src/cobalt/webdriver/search.h +++ b/src/cobalt/webdriver/search.h
@@ -54,7 +54,11 @@ NodeListToElementVector(node_list, &found_elements); break; } - default: + case protocol::SearchStrategy::kId: + case protocol::SearchStrategy::kLinkText: + case protocol::SearchStrategy::kName: + case protocol::SearchStrategy::kPartialLinkText: + case protocol::SearchStrategy::kXPath: NOTIMPLEMENTED(); } return PopulateFindResults<T>(found_elements, element_mapping);
diff --git a/src/cobalt/webdriver/server.cc b/src/cobalt/webdriver/server.cc index e9489d6..74ef7d1 100644 --- a/src/cobalt/webdriver/server.cc +++ b/src/cobalt/webdriver/server.cc
@@ -53,9 +53,10 @@ case WebDriverServer::kDelete: return "DELETE"; case WebDriverServer::kUnknownMethod: - default: return "UNKNOWN"; } + NOTREACHED(); + return ""; } // Implementation of the ResponseHandler interface.
diff --git a/src/cobalt/webdriver/webdriver.gyp b/src/cobalt/webdriver/webdriver.gyp index 6408c48..5d35681 100644 --- a/src/cobalt/webdriver/webdriver.gyp +++ b/src/cobalt/webdriver/webdriver.gyp
@@ -79,6 +79,7 @@ 'window_driver.cc', 'window_driver.h', ], + 'dependencies': [ 'copy_webdriver_data', ], 'defines': [ 'ENABLE_WEBDRIVER', ], 'all_dependent_settings': { 'defines': [ 'ENABLE_WEBDRIVER', ], @@ -96,7 +97,7 @@ '<(DEPTH)/cobalt/dom/dom.gyp:dom_testing', '<(DEPTH)/cobalt/speech/speech.gyp:speech', '<(DEPTH)/net/net.gyp:http_server', - 'copy_webdriver_data', + '<(DEPTH)/third_party/icu/icu.gyp:icuuc', ], }, { @@ -105,15 +106,14 @@ 'copies': [ { 'destination': '<(sb_static_contents_output_data_dir)/webdriver', - 'conditions': [ - ['enable_webdriver==1', { - 'files': ['content/webdriver-init.js'], - }, { - 'files': [], - }], - ], + 'files': ['content/webdriver-init.js'], }, ], + 'all_dependent_settings': { + 'variables': { + 'content_deploy_subdirs': [ 'webdriver' ] + } + }, }, ], }
diff --git a/src/cobalt/webdriver/webdriver_test.gyp b/src/cobalt/webdriver/webdriver_test.gyp index c95ddb7..a71648a 100644 --- a/src/cobalt/webdriver/webdriver_test.gyp +++ b/src/cobalt/webdriver/webdriver_test.gyp
@@ -18,19 +18,20 @@ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests', '<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gtest.gyp:gtest', + 'webdriver_copy_test_data', ], - 'actions': [ - { - 'action_name': 'webdriver_test_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/webdriver/testdata/', - ], - 'output_dir': 'cobalt/webdriver_test', - }, - 'includes': ['../build/copy_test_data.gypi'], - } - ], + }, + + { + 'target_name': 'webdriver_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/cobalt/webdriver/testdata/', + ], + 'content_test_output_subdir': 'cobalt/webdriver_test', + }, + 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], }, {
diff --git a/src/cobalt/xhr/xhr.gyp b/src/cobalt/xhr/xhr.gyp index 34203b8..d055477 100644 --- a/src/cobalt/xhr/xhr.gyp +++ b/src/cobalt/xhr/xhr.gyp
@@ -67,7 +67,6 @@ '<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gtest.gyp:gtest', 'xhr', - 'xhr_copy_test_data', # TODO: Remove the dependency below, it works around the fact that # ScriptValueFactory has non-virtual method CreatePromise(). @@ -85,22 +84,5 @@ }, 'includes': [ '../../starboard/build/deploy.gypi' ], }, - - { - 'target_name': 'xhr_copy_test_data', - 'type': 'none', - 'actions': [ - { - 'action_name': 'xhr_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/cobalt/xhr/testdata/', - ], - 'output_dir': 'cobalt/xhr/testdata/', - }, - 'includes': [ '../build/copy_test_data.gypi' ], - }, - ], - }, ], }
diff --git a/src/cobalt/xhr/xhr_response_data.cc b/src/cobalt/xhr/xhr_response_data.cc index ac4ab88..aa617f9 100644 --- a/src/cobalt/xhr/xhr_response_data.cc +++ b/src/cobalt/xhr/xhr_response_data.cc
@@ -36,36 +36,32 @@ } // namespace -XhrResponseData::XhrResponseData() { - dom::GlobalStats::GetInstance()->IncreaseXHRMemoryUsage(capacity()); -} +XhrResponseData::XhrResponseData() { IncreaseMemoryUsage(); } -XhrResponseData::~XhrResponseData() { - dom::GlobalStats::GetInstance()->DecreaseXHRMemoryUsage(capacity()); -} +XhrResponseData::~XhrResponseData() { DecreaseMemoryUsage(); } void XhrResponseData::Clear() { - dom::GlobalStats::GetInstance()->DecreaseXHRMemoryUsage(capacity()); + DecreaseMemoryUsage(); // Use swap to force free the memory allocated. std::string dummy; data_.swap(dummy); - dom::GlobalStats::GetInstance()->IncreaseXHRMemoryUsage(capacity()); + IncreaseMemoryUsage(); } void XhrResponseData::Reserve(size_t new_capacity_bytes) { - dom::GlobalStats::GetInstance()->DecreaseXHRMemoryUsage(capacity()); + DecreaseMemoryUsage(); data_.reserve(new_capacity_bytes); - dom::GlobalStats::GetInstance()->IncreaseXHRMemoryUsage(capacity()); + IncreaseMemoryUsage(); } void XhrResponseData::Append(const uint8* source_data, size_t size_bytes) { if (size_bytes == 0) { return; } - dom::GlobalStats::GetInstance()->DecreaseXHRMemoryUsage(capacity()); + DecreaseMemoryUsage(); data_.resize(data_.size() + size_bytes); memcpy(&data_[data_.size() - size_bytes], source_data, size_bytes); - dom::GlobalStats::GetInstance()->IncreaseXHRMemoryUsage(capacity()); + IncreaseMemoryUsage(); } const uint8* XhrResponseData::data() const { @@ -76,5 +72,13 @@ return data_.empty() ? &s_dummy : reinterpret_cast<uint8*>(&data_[0]); } +void XhrResponseData::IncreaseMemoryUsage() { + dom::GlobalStats::GetInstance()->IncreaseXHRMemoryUsage(capacity()); +} + +void XhrResponseData::DecreaseMemoryUsage() { + dom::GlobalStats::GetInstance()->DecreaseXHRMemoryUsage(capacity()); +} + } // namespace xhr } // namespace cobalt
diff --git a/src/cobalt/xhr/xhr_response_data.h b/src/cobalt/xhr/xhr_response_data.h index b0efd69..b383c7e 100644 --- a/src/cobalt/xhr/xhr_response_data.h +++ b/src/cobalt/xhr/xhr_response_data.h
@@ -45,6 +45,9 @@ size_t capacity() const { return data_.capacity(); } private: + void IncreaseMemoryUsage(); + void DecreaseMemoryUsage(); + std::string data_; };
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc index 310a787..e3506b0 100644 --- a/src/cobalt/xhr/xml_http_request.cc +++ b/src/cobalt/xhr/xml_http_request.cc
@@ -507,11 +507,10 @@ case kDocument: case kBlob: case kResponseTypeCodeMax: - default: NOTIMPLEMENTED() << "Unsupported response_type_ " << response_type(exception_state); - return base::nullopt; } + return base::nullopt; } int XMLHttpRequest::status() const { @@ -704,10 +703,8 @@ if (fetch_callback_) { scoped_refptr<dom::Uint8Array> data = new dom::Uint8Array( - settings_, - reinterpret_cast<const uint8*>(download_data->data()), - static_cast<uint32>(download_data->size()), - NULL); + settings_, reinterpret_cast<const uint8*>(download_data->data()), + static_cast<uint32>(download_data->size()), NULL); fetch_callback_->value().Run(data); } @@ -1059,8 +1056,6 @@ DCHECK_EQ((is_active && has_event_listeners), false); - settings_->javascript_engine()->ReportExtraMemoryCost( - response_body_.capacity()); settings_->global_environment()->AllowGarbageCollection( make_scoped_refptr(this)); }
diff --git a/src/cobalt/xhr/xml_http_request.h b/src/cobalt/xhr/xml_http_request.h index 6b17342..c7b786a 100644 --- a/src/cobalt/xhr/xml_http_request.h +++ b/src/cobalt/xhr/xml_http_request.h
@@ -30,6 +30,7 @@ #include "cobalt/dom/uint8_array.h" #include "cobalt/loader/cors_preflight.h" #include "cobalt/loader/net_fetcher.h" +#include "cobalt/script/environment_settings.h" #include "cobalt/script/union_type.h" #include "cobalt/xhr/xhr_response_data.h" #include "cobalt/xhr/xml_http_request_event_target.h" @@ -56,7 +57,7 @@ public: // Note: This is expected to be a DOMSettings object, but we declare it as // EnvironmentSettings so that JSC doesn't need to know about dom. - explicit XMLHttpRequest(script::EnvironmentSettings*); + explicit XMLHttpRequest(script::EnvironmentSettings* settings); typedef script::UnionType2<std::string, scoped_refptr<dom::ArrayBuffer> > ResponseType;
diff --git a/src/cobalt/xhr/xml_http_request.idl b/src/cobalt/xhr/xml_http_request.idl index 176e6c2..6e08550 100644 --- a/src/cobalt/xhr/xml_http_request.idl +++ b/src/cobalt/xhr/xml_http_request.idl
@@ -42,20 +42,20 @@ // TODO: Upgrade to void send(optional (Document or BodyInit)? body = null); // See https://xhr.spec.whatwg.org/#interface-xmlhttprequest. [RaisesException] void send( - optional (DOMString or ArrayBufferView or ArrayBuffer)? request_body); + optional (DOMString or ArrayBufferView or ArrayBuffer)? requestBody); void abort(); // FetchAPI: replacement for "send" when fetch functionality is needed. [RaisesException] void fetch( - FetchUpdateCallback fetch_callback, FetchModeCallback mode_callback, - (DOMString or ArrayBufferView or ArrayBuffer)? request_body); + FetchUpdateCallback fetchCallback, FetchModeCallback modeCallback, + (DOMString or ArrayBufferView or ArrayBuffer)? requestBody); // response readonly attribute unsigned short status; readonly attribute DOMString statusText; DOMString? getResponseHeader(DOMString header); DOMString getAllResponseHeaders(); - [RaisesException] void overrideMimeType(DOMString mime_type); + [RaisesException] void overrideMimeType(DOMString mimeType); [RaisesException=Setter] attribute DOMString responseType; // TODO: Use a union type for all the possible response types. [RaisesException] readonly attribute (DOMString or ArrayBuffer) response; @@ -74,4 +74,4 @@ // FetchAPI: custom callback to allow progressive updates. callback FetchUpdateCallback = void(Uint8Array data); // FetchAPI: custom callback to distinguish between mode. -callback FetchModeCallback = void(boolean is_cors_mode); +callback FetchModeCallback = void(boolean isCorsMode);
diff --git a/src/glimp/gles/convert_pixel_data.cc b/src/glimp/gles/convert_pixel_data.cc index 7609baa..3aaf764 100644 --- a/src/glimp/gles/convert_pixel_data.cc +++ b/src/glimp/gles/convert_pixel_data.cc
@@ -36,7 +36,7 @@ // If the pitches are equal, we can do the entire copy in one memcpy(). SbMemoryCopy(destination, source, destination_pitch * num_rows); } else { - // If the pitches are not equal, we must memcpy each row seperately. + // If the pitches are not equal, we must memcpy each row separately. for (int i = 0; i < num_rows; ++i) { SbMemoryCopy(destination + i * destination_pitch, source + i * source_pitch, bytes_per_row);
diff --git a/src/googleurl/src/url_canon_relative.cc b/src/googleurl/src/url_canon_relative.cc index 63630b4..a00beee 100644 --- a/src/googleurl/src/url_canon_relative.cc +++ b/src/googleurl/src/url_canon_relative.cc
@@ -397,7 +397,7 @@ // Parse the relative URL, just like we would for anything following a // scheme. url_parse::Parsed relative_parsed; // Everything but the scheme is valid. - url_parse::ParseAfterScheme(&relative_url[relative_component.begin], + url_parse::ParseAfterScheme(relative_url, relative_component.len, relative_component.begin, &relative_parsed);
diff --git a/src/nb/analytics/memory_tracker_helpers.h b/src/nb/analytics/memory_tracker_helpers.h index 8b846dd..e8c8afe 100644 --- a/src/nb/analytics/memory_tracker_helpers.h +++ b/src/nb/analytics/memory_tracker_helpers.h
@@ -21,16 +21,16 @@ #include <vector> #include "nb/analytics/memory_tracker.h" -#include "nb/atomic.h" #include "nb/simple_thread.h" #include "nb/std_allocator.h" #include "nb/thread_local_boolean.h" #include "nb/thread_local_pointer.h" +#include "starboard/atomic.h" +#include "starboard/log.h" +#include "starboard/memory.h" #include "starboard/mutex.h" #include "starboard/thread.h" #include "starboard/types.h" -#include "starboard/log.h" -#include "starboard/memory.h" namespace nb { namespace analytics { @@ -70,8 +70,8 @@ private: const std::string name_; - nb::atomic_int64_t allocation_bytes_; - nb::atomic_int32_t num_allocations_; + starboard::atomic_int64_t allocation_bytes_; + starboard::atomic_int32_t num_allocations_; SB_DISALLOW_COPY_AND_ASSIGN(AllocationGroup); };
diff --git a/src/nb/analytics/memory_tracker_impl.cc b/src/nb/analytics/memory_tracker_impl.cc index 8f4392b..aefc1f7 100644 --- a/src/nb/analytics/memory_tracker_impl.cc +++ b/src/nb/analytics/memory_tracker_impl.cc
@@ -21,7 +21,6 @@ #include <iterator> #include <sstream> -#include "nb/atomic.h" #include "starboard/atomic.h" #include "starboard/log.h" #include "starboard/time.h"
diff --git a/src/nb/analytics/memory_tracker_impl.h b/src/nb/analytics/memory_tracker_impl.h index 9f8c630..b8d1e10 100644 --- a/src/nb/analytics/memory_tracker_impl.h +++ b/src/nb/analytics/memory_tracker_impl.h
@@ -171,7 +171,7 @@ AllocationMapType atomic_allocation_map_; AtomicStringAllocationGroupMap alloc_group_map_; - atomic_int64_t total_bytes_allocated_; + starboard::atomic_int64_t total_bytes_allocated_; ConcurrentPtr<MemoryTrackerDebugCallback> debug_callback_; // THREAD LOCAL SECTION.
diff --git a/src/nb/atomic.h b/src/nb/atomic.h deleted file mode 100644 index 8c3c999..0000000 --- a/src/nb/atomic.h +++ /dev/null
@@ -1,349 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef NB_ATOMIC_H_ -#define NB_ATOMIC_H_ - -#include "starboard/atomic.h" -#include "starboard/mutex.h" -#include "starboard/types.h" - -namespace nb { - -// Provides atomic types like integer and float. Some types like atomic_int32_t -// are likely to be hardware accelerated for your platform. -// -// Never use the parent types like atomic<T>, atomic_number<T> or -// atomic_integral<T> and instead use the fully qualified classes like -// atomic_int32_t, atomic_pointer<T*>, etc. -// -// Note on template instantiation, avoid using the parent type and instead -// use the fully qualified type. -// BAD: -// template<typename T> -// void Foo(const atomic<T>& value); -// GOOD: -// template<typename atomic_t> -// void Foo(const atomic_t& vlaue); - -// Atomic Pointer class. Instantiate as atomic_pointer<void*> -// for void* pointer types. -template<typename T> -class atomic_pointer; - -// Atomic bool class. -class atomic_bool; - -// Atomic int32 class -class atomic_int32_t; - -// Atomic int64 class. -class atomic_int64_t; - -// Atomic float class. -class atomic_float; - -// Atomic double class. -class atomic_double; - -/////////////////////////////////////////////////////////////////////////////// -// Class hiearchy. -/////////////////////////////////////////////////////////////////////////////// - -// Base functionality for atomic types. Defines exchange(), load(), -// store(), compare_exhange_weak(), compare_exchange_strong() -template <typename T> -class atomic; - -// Subtype of atomic<T> for numbers likes float and integer types but not bool. -// Adds fetch_add() and fetch_sub(). -template <typename T> -class atomic_number; - -// Subtype of atomic_number<T> for integer types like int32 and int64. Adds -// increment and decrement. -template <typename T> -class atomic_integral; - -/////////////////////////////////////////////////////////////////////////////// -// Implimentation. -/////////////////////////////////////////////////////////////////////////////// - -// Similar to C++11 std::atomic<T>. -// atomic<T> may be instantiated with any TriviallyCopyable type T. -// atomic<T> is neither copyable nor movable. -template <typename T> -class atomic { - public: - typedef T ValueType; - - // C++11 forbids a copy constructor for std::atomic<T>, it also forbids - // a move operation. - atomic() : value_() {} - explicit atomic(T v) : value_(v) {} - - // Checks whether the atomic operations on all objects of this type - // are lock-free. - // Returns true if the atomic operations on the objects of this type - // are lock-free, false otherwise. - // - // All atomic types may be implemented using mutexes or other locking - // operations, rather than using the lock-free atomic CPU instructions. - // atomic types are also allowed to be sometimes lock-free, e.g. if only - // aligned memory accesses are naturally atomic on a given architecture, - // misaligned objects of the same type have to use locks. - // - // See also std::atomic<T>::is_lock_free(). - bool is_lock_free() const { return false; } - bool is_lock_free() const volatile { return false; } - - // Atomically replaces the value of the atomic object - // and returns the value held previously. - // See also std::atomic<T>::exchange(). - T exchange(T new_val) { - T old_value; - { - starboard::ScopedLock lock(mutex_); - old_value = value_; - value_ = new_val; - } - return old_value; - } - - // Atomically obtains the value of the atomic object. - // See also std::atomic<T>::load(). - T load() const { - starboard::ScopedLock lock(mutex_); - return value_; - } - - // Stores the value. See std::atomic<T>::store(...) - void store(T val) { - starboard::ScopedLock lock(mutex_); - value_ = val; - } - - // compare_exchange_strong(...) sets the new value if and only if - // *expected_value matches what is stored internally. - // If this succeeds then true is returned and *expected_value == new_value. - // Otherwise If there is a mismatch then false is returned and - // *expected_value is set to the internal value. - // Inputs: - // new_value: Attempt to set the value to this new value. - // expected_value: A test condition for success. If the actual value - // matches the expected_value then the swap will succeed. - // - // See also std::atomic<T>::compare_exchange_strong(...). - bool compare_exchange_strong(T* expected_value, T new_value) { - // Save original value so that its local. This hints to the compiler - // that test_val doesn't have aliasing issues and should result in - // more optimal code inside of the lock. - const T test_val = *expected_value; - starboard::ScopedLock lock(mutex_); - if (test_val == value_) { - value_ = new_value; - return true; - } else { - *expected_value = value_; - return false; - } - } - - // Weak version of this function is documented to be faster, but has allows - // weaker memory ordering and therefore will sometimes have a false negative: - // The value compared will actually be equal but the return value from this - // function indicates otherwise. - // By default, the function delegates to compare_exchange_strong(...). - // - // See also std::atomic<T>::compare_exchange_weak(...). - bool compare_exchange_weak(T* expected_value, T new_value) { - return compare_exchange_strong(expected_value, new_value); - } - - protected: - T value_; - starboard::Mutex mutex_; -}; - -// A subclass of atomic<T> that adds fetch_add(...) and fetch_sub(...). -template <typename T> -class atomic_number : public atomic<T> { - public: - typedef atomic<T> Super; - typedef T ValueType; - - atomic_number() : Super() {} - explicit atomic_number(T v) : Super(v) {} - - // Returns the previous value before the input value was added. - // See also std::atomic<T>::fetch_add(...). - T fetch_add(T val) { - T old_val; - { - starboard::ScopedLock lock(this->mutex_); - old_val = this->value_; - this->value_ += val; - } - return old_val; - } - - // Returns the value before the operation was applied. - // See also std::atomic<T>::fetch_sub(...). - T fetch_sub(T val) { - T old_val; - { - starboard::ScopedLock lock(this->mutex_); - old_val = this->value_; - this->value_ -= val; - } - return old_val; - } -}; - -// A subclass to classify Atomic Integers. Adds increment and decrement -// functions. -template <typename T> -class atomic_integral : public atomic_number<T> { - public: - typedef atomic_number<T> Super; - - atomic_integral() : Super() {} - explicit atomic_integral(T v) : Super(v) {} - - T increment() { return this->fetch_add(T(1)); } - T decrement() { return this->fetch_sub(T(1)); } -}; - -// atomic_pointer class. -template <typename T> -class atomic_pointer : public atomic<T> { - public: - typedef atomic<T> Super; - atomic_pointer() : Super() {} - explicit atomic_pointer(T initial_val) : Super(initial_val) {} -}; - -// Simple atomic bool class. This could be optimized for speed using -// compiler intrinsics for concurrent integer modification. -class atomic_bool : public atomic<bool> { - public: - typedef atomic<bool> Super; - atomic_bool() : Super() {} - explicit atomic_bool(bool initial_val) : Super(initial_val) {} -}; - -// Lockfree atomic int class. -class atomic_int32_t { - public: - typedef int32_t ValueType; - atomic_int32_t() : value_(0) {} - atomic_int32_t(SbAtomic32 value) : value_(value) {} - - bool is_lock_free() const { return true; } - bool is_lock_free() const volatile { return true; } - - int32_t increment() { - return fetch_add(1); - } - int32_t decrement() { - return fetch_add(-1); - } - - int32_t fetch_add(int32_t val) { - // fetch_add is a post-increment operation, while SbAtomicBarrier_Increment - // is a pre-increment operation. Therefore subtract the value to match - // the expected interface. - return SbAtomicBarrier_Increment(volatile_ptr(), val) - val; - } - - int32_t fetch_sub(int32_t val) { - return fetch_add(-val); - } - - // Atomically replaces the value of the atomic object - // and returns the value held previously. - // See also std::atomic<T>::exchange(). - int32_t exchange(int32_t new_val) { - return SbAtomicNoBarrier_Exchange(volatile_ptr(), new_val); - } - - // Atomically obtains the value of the atomic object. - // See also std::atomic<T>::load(). - int32_t load() const { - return SbAtomicAcquire_Load(volatile_const_ptr()); - } - - // Stores the value. See std::atomic<T>::store(...) - void store(int32_t val) { - SbAtomicRelease_Store(volatile_ptr(), val); - } - - bool compare_exchange_strong(int32_t* expected_value, int32_t new_value) { - int32_t prev_value = *expected_value; - SbAtomicMemoryBarrier(); - int32_t value_written = SbAtomicRelease_CompareAndSwap(volatile_ptr(), - prev_value, - new_value); - const bool write_ok = (prev_value == value_written); - if (!write_ok) { - *expected_value = value_written; - } - return write_ok; - } - - // Weak version of this function is documented to be faster, but has allows - // weaker memory ordering and therefore will sometimes have a false negative: - // The value compared will actually be equal but the return value from this - // function indicates otherwise. - // By default, the function delegates to compare_exchange_strong(...). - // - // See also std::atomic<T>::compare_exchange_weak(...). - bool compare_exchange_weak(int32_t* expected_value, int32_t new_value) { - return compare_exchange_strong(expected_value, new_value); - } - - private: - volatile int32_t* volatile_ptr() { return &value_; } - volatile const int32_t* volatile_const_ptr() const { return &value_; } - int32_t value_; -}; - -// Simple atomic int class. This could be optimized for speed using -// compiler intrinsics for concurrent integer modification. -class atomic_int64_t : public atomic_integral<int64_t> { - public: - typedef atomic_integral<int64_t> Super; - atomic_int64_t() : Super() {} - explicit atomic_int64_t(int64_t initial_val) : Super(initial_val) {} -}; - -class atomic_float : public atomic_number<float> { - public: - typedef atomic_number<float> Super; - atomic_float() : Super() {} - explicit atomic_float(float initial_val) : Super(initial_val) {} -}; - -class atomic_double : public atomic_number<double> { - public: - typedef atomic_number<double> Super; - atomic_double() : Super() {} - explicit atomic_double(double initial_val) : Super(initial_val) {} -}; - -} // namespace nb - -#endif // NB_ATOMIC_H_
diff --git a/src/nb/atomic_test.cc b/src/nb/atomic_test.cc deleted file mode 100644 index 222965d..0000000 --- a/src/nb/atomic_test.cc +++ /dev/null
@@ -1,487 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "nb/atomic.h" - -#include <algorithm> -#include <numeric> -#include <vector> - -#include "nb/test_thread.h" -#include "starboard/configuration.h" -#include "starboard/mutex.h" -#include "testing/gtest/include/gtest/gtest.h" - -#define NUM_THREADS 4 - -namespace nb { -namespace { - -/////////////////////////////////////////////////////////////////////////////// -// Boilerplate for test setup. -/////////////////////////////////////////////////////////////////////////////// - -// Defines a typelist for all atomic types. -typedef ::testing::Types<atomic_int32_t, atomic_int64_t, - atomic_float, atomic_double, - atomic_bool, - atomic_pointer<int*> > AllAtomicTypes; - -// Defines a typelist for just atomic number types. -typedef ::testing::Types<atomic_int32_t, atomic_int64_t, - atomic_float, atomic_double> AtomicNumberTypes; - -// Defines a typelist for just atomic number types. -typedef ::testing::Types<atomic_int32_t, atomic_int64_t> AtomicIntegralTypes; - -// Defines test type that will be instantiated using each type in -// AllAtomicTypes type list. -template <typename T> -class AtomicTest : public ::testing::Test {}; -TYPED_TEST_CASE(AtomicTest, AllAtomicTypes); // Registration. - -// Defines test type that will be instantiated using each type in -// AtomicNumberTypes type list. -template <typename T> -class AtomicNumberTest : public ::testing::Test {}; -TYPED_TEST_CASE(AtomicNumberTest, AtomicNumberTypes); // Registration. - -template <typename T> -class AtomicIntegralTest : public ::testing::Test {}; -TYPED_TEST_CASE(AtomicIntegralTest, AtomicIntegralTypes); // Registration. - - -/////////////////////////////////////////////////////////////////////////////// -// Singlethreaded tests. -/////////////////////////////////////////////////////////////////////////////// - -// Tests default constructor and single-argument constructor. -TYPED_TEST(AtomicTest, Constructors) { - typedef TypeParam AtomicT; - typedef typename AtomicT::ValueType T; - - const T zero(0); - const T one = zero + 1; // Allows AtomicPointer<T*>. - - AtomicT atomic_default; - - // Tests that default value is zero. - ASSERT_EQ(atomic_default.load(), zero); - AtomicT atomic_val(one); - ASSERT_EQ(one, atomic_val.load()); -} - -// Tests load() and exchange(). -TYPED_TEST(AtomicTest, Load_Exchange_SingleThread) { - typedef TypeParam AtomicT; - typedef typename AtomicT::ValueType T; - - const T zero(0); - const T one = zero + 1; // Allows AtomicPointer<T*>. - - AtomicT atomic; - ASSERT_EQ(atomic.load(), zero); // Default is 0. - ASSERT_EQ(zero, atomic.exchange(one)); // Old value was 0. - - // Tests that AtomicType has const get function. - const AtomicT& const_atomic = atomic; - ASSERT_EQ(one, const_atomic.load()); -} - -// Tests compare_exchange_strong(). -TYPED_TEST(AtomicNumberTest, CompareExchangeStrong_SingleThread) { - typedef TypeParam AtomicT; - typedef typename AtomicT::ValueType T; - - const T zero(0); - const T one = zero + 1; // Allows AtomicPointer<T*>. - - AtomicT atomic; - ASSERT_EQ(atomic.load(), zero); // Default is 0. - T expected_value = zero; - // Should succeed. - ASSERT_TRUE(atomic.compare_exchange_strong(&expected_value, - one)); // New value. - - ASSERT_EQ(zero, expected_value); - ASSERT_EQ(one, atomic.load()); // Expect that value was set. - - expected_value = zero; - // Asserts that when the expected and actual value is mismatched that the - // compare_exchange_strong() fails. - ASSERT_FALSE(atomic.compare_exchange_strong(&expected_value, // Mismatched. - zero)); // New value. - - // Failed and this means that expected_value should be set to what the - // internal value was. In this case, one. - ASSERT_EQ(expected_value, one); - ASSERT_EQ(one, atomic.load()); - - ASSERT_TRUE(atomic.compare_exchange_strong(&expected_value, // Matches. - zero)); - ASSERT_EQ(expected_value, one); -} - -// Tests atomic fetching and adding. -TYPED_TEST(AtomicNumberTest, FetchAdd_SingleThread) { - typedef TypeParam AtomicT; - typedef typename AtomicT::ValueType T; - - const T zero(0); - const T one = zero + 1; // Allows atomic_pointer<T*>. - const T two = zero + 2; - - AtomicT atomic; - ASSERT_EQ(atomic.load(), zero); // Default is 0. - ASSERT_EQ(zero, atomic.fetch_add(one)); // Prev value was 0. - ASSERT_EQ(one, atomic.load()); // Now value is this. - ASSERT_EQ(one, atomic.fetch_add(one)); // Prev value was 1. - ASSERT_EQ(two, atomic.exchange(one)); // Old value was 2. -} - -// Tests atomic fetching and subtracting. -TYPED_TEST(AtomicNumberTest, FetchSub_SingleThread) { - typedef TypeParam AtomicT; - typedef typename AtomicT::ValueType T; - - const T zero(0); - const T one = zero + 1; // Allows AtomicPointer<T*>. - const T two = zero + 2; - const T neg_two(zero-2); - - AtomicT atomic; - ASSERT_EQ(atomic.load(), zero); // Default is 0. - atomic.exchange(two); - ASSERT_EQ(two, atomic.fetch_sub(one)); // Prev value was 2. - ASSERT_EQ(one, atomic.load()); // New value. - ASSERT_EQ(one, atomic.fetch_sub(one)); // Prev value was one. - ASSERT_EQ(zero, atomic.load()); // New 0. - ASSERT_EQ(zero, atomic.fetch_sub(two)); - ASSERT_EQ(neg_two, atomic.load()); // 0-2 = -2 -} - -TYPED_TEST(AtomicIntegralTest, IncrementAndDecrement_SingleThread) { - typedef TypeParam AtomicT; - typedef typename AtomicT::ValueType T; - - const T zero(0); - const T one = zero + 1; // Allows AtomicPointer<T*>. - - AtomicT atomic; - ASSERT_EQ(atomic.load(), zero); // Default is 0. - ASSERT_EQ(zero, atomic.increment()); // Tests for post-increment operation. - ASSERT_EQ(one, atomic.decrement()); // Tests for post-decrement operation. -} - -/////////////////////////////////////////////////////////////////////////////// -// Multithreaded tests. -/////////////////////////////////////////////////////////////////////////////// - -// A thread that will execute compare_exhange_strong() and write out a result -// to a shared output. -template <typename AtomicT> -class CompareExchangeThread : public TestThread { - public: - typedef typename AtomicT::ValueType T; - CompareExchangeThread(int start_num, - int end_num, - AtomicT* atomic_value, - std::vector<T>* output, - starboard::Mutex* output_mutex) - : start_num_(start_num), end_num_(end_num), - atomic_value_(atomic_value), output_(output), - output_mutex_(output_mutex) { - } - - virtual void Run() { - std::vector<T> output_buffer; - output_buffer.reserve(end_num_ - start_num_); - for (int i = start_num_; i < end_num_; ++i) { - T new_value = T(i); - while (true) { - if (std::rand() % 3 == 0) { - // 1 in 3 chance of yielding. - // Attempt to cause more contention by giving other threads a chance - // to run. - SbThreadYield(); - } - - const T prev_value = atomic_value_->load(); - T expected_value = prev_value; - const bool success = - atomic_value_->compare_exchange_strong(&expected_value, - new_value); - if (success) { - output_buffer.push_back(prev_value); - break; - } - } - } - - // Lock the output to append this output buffer. - starboard::ScopedLock lock(*output_mutex_); - output_->insert(output_->end(), - output_buffer.begin(), - output_buffer.end()); - } - private: - const int start_num_; - const int end_num_; - AtomicT*const atomic_value_; - std::vector<T>*const output_; - starboard::Mutex*const output_mutex_; -}; - -// Tests Atomic<T>::compare_exchange_strong(). Each thread has a unique -// sequential range [0,1,2,3 ... ), [5,6,8, ...) that it will generate. -// The numbers are sequentially written to the shared Atomic type and then -// exposed to other threads: -// -// Generates [0,1,2,...,n/2) -// +------+ Thread A <--------+ (Write Exchanged Value) -// | | -// | compare_exchange() +---> exchanged? ---+ -// +----> +------------+ +----+ v -// | AtomicType | Output vector -// +----> +------------+ +----+ ^ -// | compare_exchange() +---> exchanged? ---+ -// | | -// +------+ Thread B <--------+ -// Generates [n/2,n/2+1,...,n) -// -// By repeatedly calling atomic<T>::compare_exchange_strong() by each of the -// threads, each will see the previous value of the shared variable when their -// exchange (atomic swap) operation is successful. If all of the swapped out -// values are recombined then it will form the original generated sequence from -// all threads. -// -// TEST PHASE -// sort( output vector ) AND TEST THAT -// output vector Contains [0,1,2,...,n) -// -// The test passes when the output array is tested that it contains every -// expected generated number from all threads. If compare_exchange_strong() is -// written incorrectly for an atomic type then the output array will have -// duplicates or otherwise not be equal to the expected natural number set. -TYPED_TEST(AtomicNumberTest, Test_CompareExchange_MultiThreaded) { - typedef TypeParam AtomicT; - typedef typename AtomicT::ValueType T; - - static const int kNumElements = 1000; - - AtomicT atomic_value(T(-1)); - std::vector<TestThread*> threads; - std::vector<T> output_values; - starboard::Mutex output_mutex; - - for (int i = 0; i < NUM_THREADS; ++i) { - const int start_num = (kNumElements * i) / NUM_THREADS; - const int end_num = (kNumElements * (i + 1)) / NUM_THREADS; - threads.push_back( - new CompareExchangeThread<AtomicT>( - start_num, // defines the number range to generate. - end_num, - &atomic_value, - &output_values, - &output_mutex)); - } - - // These threads will generate unique numbers in their range and then - // write them to the output array. - for (int i = 0; i < NUM_THREADS; ++i) { - threads[i]->Start(); - } - - for (int i = 0; i < NUM_THREADS; ++i) { - threads[i]->Join(); - } - // Cleanup threads. - for (int i = 0; i < NUM_THREADS; ++i) { - delete threads[i]; - } - threads.clear(); - - // Final value needs to be written out. The last thread to join doesn't - // know it's the last and therefore the final value in the shared - // has not be pushed to the output array. - output_values.push_back(atomic_value.load()); - std::sort(output_values.begin(), output_values.end()); - - // We expect the -1 value because it was the explicit initial value of the - // shared atomic. - ASSERT_EQ(T(-1), output_values[0]); - ASSERT_EQ(T(0), output_values[1]); - output_values.erase(output_values.begin()); // Chop off the -1 at the front. - - // Finally, assert that the output array is equal to the natural numbers - // after it has been sorted. - ASSERT_EQ(output_values.size(), kNumElements); - // All of the elements should be equal too. - for (int i = 0; i < output_values.size(); ++i) { - ASSERT_EQ(output_values[i], T(i)); - } -} - -// A thread that will invoke increment() and decrement() and equal number -// of times to atomic_value. The value after this is done should be equal to -// 0. -template <typename AtomicT> -class IncrementAndDecrementThread : public TestThread { - public: - typedef typename AtomicT::ValueType T; - IncrementAndDecrementThread(size_t half_number_of_operations, - AtomicT* atomic_value) - : atomic_value_(atomic_value) { - for (size_t i = 0; i < half_number_of_operations; ++i) { - operation_sequence_.push_back(true); - } - for (size_t i = 0; i < half_number_of_operations; ++i) { - operation_sequence_.push_back(false); - } - std::random_shuffle(operation_sequence_.begin(), - operation_sequence_.end()); - } - - virtual void Run() { - for (size_t i = 0; i < operation_sequence_.size(); ++i) { - if (std::rand() % 3 == 0) { - // 1 in 3 chance of yielding. - // Attempt to cause more contention by giving other threads a chance - // to run. - SbThreadYield(); - } - T prev_value = 0; - if (operation_sequence_[i]) { - prev_value = atomic_value_->increment(); - } else { - prev_value = atomic_value_->decrement(); - } - } - } - private: - // Used purely for true/false values. Note that we don't - // use std::vector<bool> because some platforms won't support - // swapping elements of std::vector<bool>, which is required for - // std::random_shuffle(). - std::vector<uint8_t> operation_sequence_; - AtomicT*const atomic_value_; -}; - -TYPED_TEST(AtomicIntegralTest, Test_IncrementAndDecrement_MultiThreaded) { - typedef TypeParam AtomicT; - typedef typename AtomicT::ValueType T; - - static const int kNumOperations = 10000; - - AtomicT atomic_value(T(0)); - std::vector<TestThread*> threads; - - for (int i = 0; i < NUM_THREADS; ++i) { - threads.push_back( - new IncrementAndDecrementThread<AtomicT>( - kNumOperations, - &atomic_value)); - } - - for (int i = 0; i < NUM_THREADS; ++i) { - threads[i]->Start(); - } - - for (int i = 0; i < NUM_THREADS; ++i) { - threads[i]->Join(); - } - // Cleanup threads. - for (int i = 0; i < NUM_THREADS; ++i) { - delete threads[i]; - } - threads.clear(); - - // After an equal number of decrements and increments, the final value should - // be 0. - ASSERT_EQ(0, atomic_value.load()); -} - -template <typename AtomicT> -class FetchAddSubThread : public TestThread { - public: - typedef typename AtomicT::ValueType T; - FetchAddSubThread(const int32_t start_value, - const int32_t end_value, - AtomicT* atomic_value) - : start_value_(start_value), - end_value_(end_value), - atomic_value_(atomic_value) { - } - - virtual void Run() { - for (int32_t i = start_value_; i < end_value_; ++i) { - if (std::rand() % 3 == 0) { - // 1 in 3 chance of yielding. - // Attempt to cause more contention by giving other threads a chance - // to run.s - SbThreadYield(); - } - - if (std::rand() % 2 == 0) { - atomic_value_->fetch_add(i); - } else { - atomic_value_->fetch_sub(-i); - } - } - } - private: - int32_t start_value_; - int32_t end_value_; - AtomicT*const atomic_value_; -}; - -TYPED_TEST(AtomicIntegralTest, Test_FetchAdd_MultiThreaded) { - typedef TypeParam AtomicT; - typedef typename AtomicT::ValueType T; - - static const int kNumOperations = 10000; - - AtomicT atomic_value(T(0)); - std::vector<TestThread*> threads; - - // First value is inclusive, second is exclusive. - threads.push_back( - new FetchAddSubThread<AtomicT>(-kNumOperations, 0, &atomic_value)); - - threads.push_back( - new FetchAddSubThread<AtomicT>(1, kNumOperations+1, &atomic_value)); - - for (int i = 0; i < threads.size(); ++i) { - threads[i]->Start(); - } - - for (int i = 0; i < threads.size(); ++i) { - threads[i]->Join(); - } - // Cleanup threads. - for (int i = 0; i < threads.size(); ++i) { - delete threads[i]; - } - threads.clear(); - - // After an equal number of decrements and increments, the final value should - // be 0. - ASSERT_EQ(0, atomic_value.load()); -} - - -} // namespace -} // namespace nb
diff --git a/src/nb/fixed_no_free_allocator.cc b/src/nb/fixed_no_free_allocator.cc index 53a615f..3ac6302 100644 --- a/src/nb/fixed_no_free_allocator.cc +++ b/src/nb/fixed_no_free_allocator.cc
@@ -15,6 +15,9 @@ */ #include "nb/fixed_no_free_allocator.h" + +#include <algorithm> + #include "nb/pointer_arithmetic.h" #include "starboard/log.h" @@ -29,8 +32,11 @@ void FixedNoFreeAllocator::Free(void* memory) { // Nothing to do here besides ensure that the freed memory belongs to us. - SB_DCHECK(memory >= memory_start_); - SB_DCHECK(memory < memory_end_); + if (memory < memory_start_ || memory >= memory_end_) { + SB_NOTREACHED() << "Invalid block to free: |memory| is " << memory + << ", start is " << memory_start_ << ", and end is " + << memory_end_; + } } std::size_t FixedNoFreeAllocator::GetCapacity() const { @@ -48,6 +54,8 @@ void* FixedNoFreeAllocator::Allocate(std::size_t* size, std::size_t alignment, bool align_pointer) { + *size = std::max<std::size_t>(*size, 1); + // Find the next aligned memory available. uint8_t* aligned_next_memory = AsPointer(AlignUp(AsInteger(next_memory_), alignment));
diff --git a/src/nb/lexical_cast.h b/src/nb/lexical_cast.h deleted file mode 100644 index 6ded009..0000000 --- a/src/nb/lexical_cast.h +++ /dev/null
@@ -1,135 +0,0 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef NB_LEXICAL_CAST_H_ -#define NB_LEXICAL_CAST_H_ - -#include <limits> -#include <sstream> - -#include "starboard/types.h" - -namespace nb { - -// Converts a string into a value. This function should not be used in -// performance sensitive code. -// -// Note: -// * All strings are assumed to represent numbers in base 10. -// * Casting will parse until a non-numerical character is encountered: -// * Numbers like "128M" will drop "M" and cast to a value of 128. -// * Numbers like "12M8" will cast to value 12. -// * Numbers like "M128" will fail to cast. -// -// Returns the value of the result after the lexical cast. If the lexical -// cast fails then the default value of the parameterized type is returned. -// |cast_ok| is an optional parameter which will be |true| if the cast -// succeeds, otherwise |false|. -// Example: -// int value = lexical_cast<int>("1234"); -// EXPECT_EQ(value, 1234); -// bool ok = true; -// value = lexical_cast<int>("not a number", &ok); -// EXPECT_FALSE(ok); -// EXPECT_EQ(0, value); -template <typename T> -inline T lexical_cast(const char* s, bool* cast_ok = NULL) { - if (!s) { // Handle NULL case of input string. - if (cast_ok) { - *cast_ok = false; - } - return T(); - } - std::stringstream ss; - ss << s; - T value; - ss >> value; - if (cast_ok) { - *cast_ok = !ss.fail(); - } - if (ss.fail()) { - value = T(); - } - return value; -} - -template <typename T> -inline T lexical_cast(const std::string& s, bool* cast_ok = NULL) { - return lexical_cast<T>(s.c_str(), cast_ok); -} - -// int8_t and uint8_t will normally be interpreted as a char, which will -// result in only the first character being parsed. This is obviously not -// what we want. Therefore we provide specializations for lexical_cast for -// these types. -template <> -inline int8_t lexical_cast<int8_t>(const char* s, bool* cast_ok) { - int16_t value_i16 = lexical_cast<int16_t>(s, cast_ok); - if (value_i16 < std::numeric_limits<int8_t>::min() || - value_i16 > std::numeric_limits<int8_t>::max()) { - value_i16 = 0; - if (cast_ok) { - *cast_ok = false; - } - } - return static_cast<int8_t>(value_i16); -} - -template <typename SmallInt> -inline SmallInt NarrowingLexicalCast(const char* s, bool* cast_ok) { - int64_t value = lexical_cast<int64_t>(s, cast_ok); - if ((value > std::numeric_limits<SmallInt>::max()) || - (value < std::numeric_limits<SmallInt>::min())) { - if (cast_ok) { - *cast_ok = false; - } - return static_cast<SmallInt>(0); - } - return static_cast<SmallInt>(value); -} - -template <> -inline uint8_t lexical_cast<uint8_t>(const char* s, bool* cast_ok) { - return NarrowingLexicalCast<uint8_t>(s, cast_ok); -} - -template <> -inline uint16_t lexical_cast<uint16_t>(const char* s, bool* cast_ok) { - return NarrowingLexicalCast<uint16_t>(s, cast_ok); -} - -template <> -inline uint32_t lexical_cast<uint32_t>(const char* s, bool* cast_ok) { - return NarrowingLexicalCast<uint32_t>(s, cast_ok); -} - -// uint64_t types will have a max value of int64_t. But this is acceptable. -template <> -inline uint64_t lexical_cast<uint64_t>(const char* s, bool* cast_ok) { - int64_t val_i64 = lexical_cast<int64_t>(s, cast_ok); - // Handle failure condition for negative values. - if (val_i64 < 0) { - val_i64 = 0; - if (cast_ok) { - *cast_ok = false; - } - } - return static_cast<uint64_t>(val_i64); -} - -} // namespace nb - -#endif // NB_LEXICAL_CAST_H_
diff --git a/src/nb/lexical_cast_test.cc b/src/nb/lexical_cast_test.cc deleted file mode 100644 index 1108024..0000000 --- a/src/nb/lexical_cast_test.cc +++ /dev/null
@@ -1,199 +0,0 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "nb/lexical_cast.h" -#include "starboard/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace nb { -namespace { - -TEST(lexical_cast, OptionalParameterOmitted) { - EXPECT_EQ(123, lexical_cast<int>("123")); - EXPECT_EQ(0, lexical_cast<int>("not a number")); - EXPECT_EQ(-123, lexical_cast<int8_t>("-123")); - EXPECT_EQ(123, lexical_cast<uint8_t>("123")); -} - -TEST(lexical_cast, PositiveBasicTypes) { - bool cast_ok = false; - EXPECT_EQ(123, lexical_cast<int>("123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(123, lexical_cast<int8_t>("123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(123, lexical_cast<int16_t>("123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(123, lexical_cast<int32_t>("123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(123, lexical_cast<int64_t>("123", &cast_ok)); - EXPECT_TRUE(cast_ok); - - EXPECT_EQ(123, lexical_cast<uint8_t>("123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(123, lexical_cast<uint16_t>("123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(123, lexical_cast<uint32_t>("123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(123, lexical_cast<uint64_t>("123", &cast_ok)); - EXPECT_TRUE(cast_ok); - - EXPECT_FLOAT_EQ(1234.5f, lexical_cast<float>("1234.5", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_FLOAT_EQ(1234.5f, lexical_cast<float>("1234.5f", &cast_ok)); - EXPECT_TRUE(cast_ok); - - EXPECT_DOUBLE_EQ(1234.5, lexical_cast<double>("1234.5", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_DOUBLE_EQ(1234.5, lexical_cast<double>("1234.5f", &cast_ok)); - EXPECT_TRUE(cast_ok); -} - -TEST(lexical_cast, NegativeBasicTypes) { - bool cast_ok = false; - EXPECT_EQ(-123, lexical_cast<int>("-123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(-123, lexical_cast<int8_t>("-123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(-123, lexical_cast<int16_t>("-123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(-123, lexical_cast<int32_t>("-123", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(-123, lexical_cast<int64_t>("-123", &cast_ok)); - EXPECT_TRUE(cast_ok); - - EXPECT_EQ(0, lexical_cast<uint8_t>("-123", &cast_ok)); - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, lexical_cast<uint16_t>("-123", &cast_ok)); - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, lexical_cast<uint32_t>("-123", &cast_ok)); - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, lexical_cast<uint64_t>("-123", &cast_ok)); - EXPECT_FALSE(cast_ok); - - EXPECT_FLOAT_EQ(-1234.5f, lexical_cast<float>("-1234.5", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_FLOAT_EQ(-1234.5f, lexical_cast<float>("-1234.5f", &cast_ok)); - EXPECT_TRUE(cast_ok); - - EXPECT_DOUBLE_EQ(-1234.5f, lexical_cast<double>("-1234.5", &cast_ok)); - EXPECT_TRUE(cast_ok); - EXPECT_DOUBLE_EQ(-1234.5f, lexical_cast<double>("-1234.5f", &cast_ok)); - EXPECT_TRUE(cast_ok); -} - -TEST(lexical_cast, StringIsNonNumerical) { - bool cast_ok = false; - EXPECT_EQ(0, lexical_cast<int>("not a number", &cast_ok)); - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, lexical_cast<int8_t>("not a number", &cast_ok)); - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, lexical_cast<int16_t>("not a number", &cast_ok)); - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, lexical_cast<int32_t>("not a number", &cast_ok)); - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, lexical_cast<int64_t>("not a number", &cast_ok)); - EXPECT_FALSE(cast_ok); - - EXPECT_FLOAT_EQ(0.f, lexical_cast<float>("not a number", &cast_ok)); - EXPECT_FALSE(cast_ok); - EXPECT_DOUBLE_EQ(0.0, lexical_cast<double>("not a number", &cast_ok)); - EXPECT_FALSE(cast_ok); -} - -TEST(lexical_cast, StringIsEmpty) { - bool cast_ok = false; - int value = lexical_cast<int>(""); - EXPECT_EQ(value, 0); - EXPECT_FALSE(cast_ok); -} - -TEST(lexical_cast, StringIsNull) { - bool cast_ok = false; - int value = lexical_cast<int>(NULL); - EXPECT_EQ(value, 0); - EXPECT_FALSE(cast_ok); -} - -TEST(lexical_cast, IntegerOverflow_int8) { - bool cast_ok = false; - int8_t value = lexical_cast<int8_t>("128", &cast_ok); - - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, value); -} - -TEST(lexical_cast, IntegerOverflow_uint8) { - bool cast_ok = false; - uint8_t value = lexical_cast<uint8_t>("256", &cast_ok); - - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, value); -} - -TEST(lexical_cast, IntegerOverflow_int16) { - bool cast_ok = false; - int16_t value = lexical_cast<int16_t>("65535", &cast_ok); - - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, value); -} - -TEST(lexical_cast, IntegerOverflow_uint16) { - bool cast_ok = false; - int16_t value = lexical_cast<int16_t>("65536", &cast_ok); - - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, value); -} - -TEST(lexical_cast, LetterBeforeNumber) { - bool cast_ok = false; - int value = lexical_cast<int>("M128", &cast_ok); - EXPECT_FALSE(cast_ok); - EXPECT_EQ(0, value); -} - -TEST(lexical_cast, LetterInNumber) { - bool cast_ok = false; - int value = lexical_cast<int>("12M8", &cast_ok); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(12, value); -} - -TEST(lexical_cast, LetterAfterNumber) { - bool cast_ok = false; - int value = lexical_cast<int>("128M", &cast_ok); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(128, value); -} - -TEST(lexical_cast, OnlyBase10) { - bool cast_ok = false; - // Expect that the "100000" part of "100000ff" will be parsed. The "ff" part - // is dropped. - uint32_t value = lexical_cast<uint32_t>("100000ff", &cast_ok); - EXPECT_TRUE(cast_ok); - EXPECT_EQ(100000, value); // -} - -TEST(lexical_cast, StdString) { - bool cast_ok = false; - uint32_t value = lexical_cast<uint32_t>(std::string("100000"), &cast_ok); - EXPECT_EQ(100000, value); -} - -} // namespace -} // namespace nb
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp index b33d946..c62ffb6 100644 --- a/src/nb/nb.gyp +++ b/src/nb/nb.gyp
@@ -42,7 +42,6 @@ 'fixed_no_free_allocator.h', 'hash.cc', 'hash.h', - 'lexical_cast.h', 'memory_pool.h', 'memory_scope.cc', 'memory_scope.h', @@ -95,13 +94,11 @@ 'analytics/memory_tracker_helpers_test.cc', 'analytics/memory_tracker_impl_test.cc', 'analytics/memory_tracker_test.cc', - 'atomic_test.cc', 'bidirectional_fit_reuse_allocator_test.cc', 'concurrent_map_test.cc', 'concurrent_ptr_test.cc', 'first_fit_reuse_allocator_test.cc', 'fixed_no_free_allocator_test.cc', - 'lexical_cast_test.cc', 'memory_scope_test.cc', 'multipart_allocator_test.cc', 'rewindable_vector_test.cc', @@ -140,18 +137,7 @@ ], 'dependencies': [ 'nb', - ], - 'actions': [ - { - 'action_name': 'reuse_allocator_benchmark_copy_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/nb/testdata/', - ], - 'output_dir': 'nb/testdata', - }, - 'includes': ['../starboard/build/copy_test_data.gypi'], - } + 'nb_copy_test_data', ], }, { @@ -164,5 +150,18 @@ 'executable_name': 'reuse_allocator_benchmark', }, }, + + { + 'target_name': 'nb_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/nb/testdata/', + ], + 'content_test_output_subdir': 'nb/testdata', + }, + 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], + }, + ], }
diff --git a/src/nb/reuse_allocator_benchmark.cc b/src/nb/reuse_allocator_benchmark.cc index 5acf3b1..4fef17f 100644 --- a/src/nb/reuse_allocator_benchmark.cc +++ b/src/nb/reuse_allocator_benchmark.cc
@@ -68,11 +68,13 @@ void LoadAllocationPlayback(std::vector<AllocationCommand>* commands, const std::string& filename) { char buffer[SB_FILE_MAX_NAME * 16]; - bool result = SbSystemGetPath(kSbSystemPathSourceDirectory, buffer, + bool result = SbSystemGetPath(kSbSystemPathContentDirectory, buffer, SB_ARRAY_SIZE(buffer)); SB_DCHECK(result); std::string path_name = buffer; path_name += SB_FILE_SEP_CHAR; + path_name += "test"; + path_name += SB_FILE_SEP_CHAR; path_name += "nb"; path_name += SB_FILE_SEP_CHAR; path_name += "testdata";
diff --git a/src/nb/simple_thread.h b/src/nb/simple_thread.h index 6bc2d97..b01a58c 100644 --- a/src/nb/simple_thread.h +++ b/src/nb/simple_thread.h
@@ -19,7 +19,7 @@ #include <string> -#include "nb/atomic.h" +#include "starboard/atomic.h" #include "starboard/thread.h" #include "starboard/time.h" #include "starboard/types.h" @@ -54,7 +54,7 @@ const std::string name_; SbThread thread_; - atomic_bool join_called_; + starboard::atomic_bool join_called_; SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread); };
diff --git a/src/nb/thread_local_object_test.cc b/src/nb/thread_local_object_test.cc index 7a00c54..3709573 100644 --- a/src/nb/thread_local_object_test.cc +++ b/src/nb/thread_local_object_test.cc
@@ -16,9 +16,9 @@ #include <string> #include "nb/test_thread.h" -#include "nb/atomic.h" #include "nb/thread_local_object.h" #include "nb/scoped_ptr.h" +#include "starboard/atomic.h" #include "starboard/mutex.h" #include "starboard/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -30,11 +30,11 @@ struct CountsInstances { CountsInstances() { s_instances_.increment(); } ~CountsInstances() { s_instances_.decrement(); } - static atomic_int32_t s_instances_; + static starboard::atomic_int32_t s_instances_; static int NumInstances() { return s_instances_.load(); } static void ResetNumInstances() { s_instances_.exchange(0); } }; -nb::atomic_int32_t CountsInstances::s_instances_(0); +starboard::atomic_int32_t CountsInstances::s_instances_(0); // A simple thread that just creates the an object from the supplied // ThreadLocalObject<T> and then exits.
diff --git a/src/net/base/directory_lister_unittest.cc b/src/net/base/directory_lister_unittest.cc index 7a34e65..c4006fd 100644 --- a/src/net/base/directory_lister_unittest.cc +++ b/src/net/base/directory_lister_unittest.cc
@@ -88,7 +88,7 @@ TEST(DirectoryListerTest, BigDirTest) { FilePath path; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &path)); + ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &path)); ListerDelegate delegate(false, false); DirectoryLister lister(path, &delegate); @@ -114,7 +114,7 @@ TEST(DirectoryListerTest, CancelTest) { FilePath path; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &path)); + ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &path)); ListerDelegate delegate(false, true); DirectoryLister lister(path, &delegate);
diff --git a/src/net/base/gzip_filter_unittest.cc b/src/net/base/gzip_filter_unittest.cc index df25571..b28fa5a 100644 --- a/src/net/base/gzip_filter_unittest.cc +++ b/src/net/base/gzip_filter_unittest.cc
@@ -62,7 +62,7 @@ // Get the path of source data file. FilePath file_path; - PathService::Get(base::DIR_SOURCE_ROOT, &file_path); + PathService::Get(base::DIR_TEST_DATA, &file_path); file_path = file_path.AppendASCII("net"); file_path = file_path.AppendASCII("data"); file_path = file_path.AppendASCII("filter_unittests");
diff --git a/src/net/base/test_data_directory.cc b/src/net/base/test_data_directory.cc index 8e609c9..08f4492 100644 --- a/src/net/base/test_data_directory.cc +++ b/src/net/base/test_data_directory.cc
@@ -11,7 +11,7 @@ FilePath GetTestCertsDirectory() { FilePath certs_dir; - PathService::Get(base::DIR_SOURCE_ROOT, &certs_dir); + PathService::Get(base::DIR_TEST_DATA, &certs_dir); return certs_dir.Append(FILE_PATH_LITERAL("net/data/ssl/certificates")); }
diff --git a/src/net/dial/dial_system_config_starboard.cc b/src/net/dial/dial_system_config_starboard.cc index c1f7add..921f3ff 100644 --- a/src/net/dial/dial_system_config_starboard.cc +++ b/src/net/dial/dial_system_config_starboard.cc
@@ -14,8 +14,14 @@ #include "net/dial/dial_system_config.h" +#include "starboard/file.h" #include "starboard/system.h" +namespace { +const char kInAppDialUuidFilename[] = "upnp_udn"; +const size_t kUuidSizeBytes = 16; +} + namespace net { // static @@ -50,15 +56,56 @@ } } +namespace { +std::string GenerateRandomUuid() { + char uuid_buffer[kUuidSizeBytes]; + SbSystemGetRandomData(uuid_buffer, kUuidSizeBytes); + return std::string(uuid_buffer, kUuidSizeBytes); +} +} // namespace + // static std::string DialSystemConfig::GeneratePlatformUuid() { - char buffer[kMaxNameSize]; - if (SbSystemGetProperty(kSbSystemPropertyPlatformUuid, buffer, - sizeof(buffer))) { - return std::string(buffer); - } else { - return std::string(); + char path_buffer[SB_FILE_MAX_PATH]; + bool success = SbSystemGetPath(kSbSystemPathCacheDirectory, + path_buffer, + sizeof(path_buffer)); + + DCHECK(success) << "kSbSystemPathCacheDirectory not implemented"; + + std::string path(path_buffer); + path.append(SB_FILE_SEP_STRING); + path.append(kInAppDialUuidFilename); + + bool created; + SbFileError error; + starboard::ScopedFile file(path.c_str(), + kSbFileOpenAlways | kSbFileRead | kSbFileWrite, + &created, + &error); + if (error != kSbFileOk) { + LOG(ERROR) << "Unable to open or create " << path; + return GenerateRandomUuid(); } + + char uuid_buffer[kUuidSizeBytes]; + int bytes_read = file.ReadAll(uuid_buffer, kUuidSizeBytes); + + if (bytes_read == kUuidSizeBytes) { + return std::string(uuid_buffer, bytes_read); + } + + file.Truncate(0); + + std::string uuid = GenerateRandomUuid(); + + int bytes_written = file.WriteAll(uuid.data(), uuid.size()); + + if (bytes_written != uuid.size()) { + LOG(ERROR) << "Unable to store device UUID to " << path; + } + + return uuid; } } // namespace net
diff --git a/src/net/net.gyp b/src/net/net.gyp index 2813dc8..c02cbf1 100644 --- a/src/net/net.gyp +++ b/src/net/net.gyp
@@ -819,6 +819,7 @@ '<(DEPTH)/third_party/openssl/openssl.gyp:openssl', 'http_server', # This is needed by dial_http_server in net 'net', + 'net_copy_test_data', 'net_test_support', ], 'sources': [ @@ -1057,18 +1058,6 @@ 'websockets/websocket_net_log_params_unittest.cc', 'websockets/websocket_throttle_unittest.cc', ], - 'actions': [ - { - 'action_name': 'copy_test_data', - 'variables': { - 'input_files': [ - 'data', - ], - 'output_dir': 'net', - }, - 'includes': [ '../cobalt/build/copy_test_data.gypi' ], - }, - ], 'conditions': [ ['enable_spdy == 0', { 'sources/': [ @@ -1114,6 +1103,17 @@ ], }, { + 'target_name': 'net_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + 'data', + ], + 'content_test_output_subdir': 'net', + }, + 'includes': [ '<(DEPTH)/starboard/build/copy_test_data.gypi' ], + }, + { 'target_name': 'net_test_support', 'type': 'static_library', 'dependencies': [
diff --git a/src/net/proxy/proxy_resolver_perftest.cc b/src/net/proxy/proxy_resolver_perftest.cc index 17c1448..48d17bd 100644 --- a/src/net/proxy/proxy_resolver_perftest.cc +++ b/src/net/proxy/proxy_resolver_perftest.cc
@@ -167,7 +167,7 @@ // Read the PAC script from disk and initialize the proxy resolver with it. void LoadPacScriptIntoResolver(const std::string& script_name) { FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); + PathService::Get(base::DIR_TEST_DATA, &path); path = path.AppendASCII("net"); path = path.AppendASCII("data"); path = path.AppendASCII("proxy_resolver_perftest");
diff --git a/src/net/proxy/proxy_script_fetcher_impl_unittest.cc b/src/net/proxy/proxy_script_fetcher_impl_unittest.cc index e20e837..5bad81f 100644 --- a/src/net/proxy/proxy_script_fetcher_impl_unittest.cc +++ b/src/net/proxy/proxy_script_fetcher_impl_unittest.cc
@@ -107,7 +107,7 @@ // Get a file:// url relative to net/data/proxy/proxy_script_fetcher_unittest. GURL GetTestFileUrl(const std::string& relpath) { FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); + PathService::Get(base::DIR_TEST_DATA, &path); path = path.AppendASCII("net"); path = path.AppendASCII("data"); path = path.AppendASCII("proxy_script_fetcher_unittest");
diff --git a/src/net/url_request/url_fetcher_impl_unittest.cc b/src/net/url_request/url_fetcher_impl_unittest.cc index e03f2b7..3fd29b5 100644 --- a/src/net/url_request/url_fetcher_impl_unittest.cc +++ b/src/net/url_request/url_fetcher_impl_unittest.cc
@@ -712,7 +712,7 @@ URLFetcherBadHTTPSTest::URLFetcherBadHTTPSTest() { - PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_); + PathService::Get(base::DIR_TEST_DATA, &cert_dir_); cert_dir_ = cert_dir_.AppendASCII("chrome"); cert_dir_ = cert_dir_.AppendASCII("test"); cert_dir_ = cert_dir_.AppendASCII("data");
diff --git a/src/net/url_request/url_request_unittest.cc b/src/net/url_request/url_request_unittest.cc index 4a9ecec..dce8735 100644 --- a/src/net/url_request/url_request_unittest.cc +++ b/src/net/url_request/url_request_unittest.cc
@@ -717,7 +717,7 @@ #if defined(OS_WIN) TEST_F(URLRequestTest, ResolveShortcutTest) { FilePath app_path; - PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + PathService::Get(base::DIR_TEST_DATA, &app_path); app_path = app_path.AppendASCII("net"); app_path = app_path.AppendASCII("data"); app_path = app_path.AppendASCII("url_request_unittest"); @@ -784,7 +784,7 @@ TestDelegate d; { FilePath file_path; - PathService::Get(base::DIR_SOURCE_ROOT, &file_path); + PathService::Get(base::DIR_TEST_DATA, &file_path); file_path = file_path.Append(FILE_PATH_LITERAL("net")); file_path = file_path.Append(FILE_PATH_LITERAL("data")); @@ -807,7 +807,7 @@ // redirects does not crash. See http://crbug.com/18686. FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); + PathService::Get(base::DIR_TEST_DATA, &path); path = path.Append(FILE_PATH_LITERAL("net")); path = path.Append(FILE_PATH_LITERAL("data")); path = path.Append(FILE_PATH_LITERAL("url_request_unittest")); @@ -3151,7 +3151,7 @@ ScopedVector<UploadElementReader> element_readers; FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); + PathService::Get(base::DIR_TEST_DATA, &path); path = path.Append(FILE_PATH_LITERAL("net")); path = path.Append(FILE_PATH_LITERAL("data")); path = path.Append(FILE_PATH_LITERAL("url_request_unittest")); @@ -3423,7 +3423,7 @@ EXPECT_EQ(URLRequestStatus::SUCCESS, req.status().status()); FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); + PathService::Get(base::DIR_TEST_DATA, &path); path = path.Append(FILE_PATH_LITERAL("net")); path = path.Append(FILE_PATH_LITERAL("data")); path = path.Append(FILE_PATH_LITERAL("url_request_unittest")); @@ -4906,7 +4906,7 @@ ASSERT_TRUE(test_server_.Start()); FilePath app_path; - PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + PathService::Get(base::DIR_TEST_DATA, &app_path); app_path = app_path.AppendASCII("LICENSE"); TestDelegate d; { @@ -4935,7 +4935,7 @@ ASSERT_TRUE(test_server_.Start()); FilePath app_path; - PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + PathService::Get(base::DIR_TEST_DATA, &app_path); app_path = app_path.AppendASCII("LICENSE"); TestDelegate d; { @@ -4967,7 +4967,7 @@ ASSERT_TRUE(test_server_.Start()); FilePath app_path; - PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + PathService::Get(base::DIR_TEST_DATA, &app_path); app_path = app_path.AppendASCII("LICENSE"); TestDelegate d; { @@ -4997,7 +4997,7 @@ ASSERT_TRUE(test_server_.Start()); FilePath app_path; - PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + PathService::Get(base::DIR_TEST_DATA, &app_path); app_path = app_path.AppendASCII("LICENSE"); TestDelegate d; // Set correct login credentials. The delegate will be asked for them when @@ -5030,7 +5030,7 @@ ASSERT_TRUE(test_server_.Start()); FilePath app_path; - PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + PathService::Get(base::DIR_TEST_DATA, &app_path); app_path = app_path.AppendASCII("LICENSE"); TestDelegate d; { @@ -5060,7 +5060,7 @@ ASSERT_TRUE(test_server_.Start()); FilePath app_path; - PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + PathService::Get(base::DIR_TEST_DATA, &app_path); app_path = app_path.AppendASCII("LICENSE"); TestDelegate d; // Set correct login credentials. The delegate will be asked for them when @@ -5093,7 +5093,7 @@ ASSERT_TRUE(test_server_.Start()); FilePath app_path; - PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + PathService::Get(base::DIR_TEST_DATA, &app_path); app_path = app_path.AppendASCII("LICENSE"); scoped_ptr<TestDelegate> d(new TestDelegate); @@ -5143,7 +5143,7 @@ ASSERT_TRUE(test_server_.Start()); FilePath app_path; - PathService::Get(base::DIR_SOURCE_ROOT, &app_path); + PathService::Get(base::DIR_TEST_DATA, &app_path); app_path = app_path.AppendASCII("LICENSE"); scoped_ptr<TestDelegate> d(new TestDelegate);
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md index d30a2c0..5fd84c7 100644 --- a/src/starboard/CHANGELOG.md +++ b/src/starboard/CHANGELOG.md
@@ -10,6 +10,30 @@ ## Version 10 +### Add support for multiple versions of ffmpeg + +An extra version agnostic ffmpeg dynamic dispatch layer is added in order to +support multiple different versions of ffmpeg as may appear on user systems. + +### Make linux-x64x11 builds use GLX (via Angle) instead of EGL by default + +While common Cobalt code still targets EGL/GLES2, we now use Angle on +linux-x64x11 builds to translate those calls to GLX/GL calls. Thus, from +the perspective of the system, linux-x64x11 builds now appear to use +GLX/GL. This change was made because GLX/GL was generally found to have +better desktop support than EGL/GLES2. The logic for this is added in the +Starboard [enable_glx_via_angle.gypi](linux/x64x11/enable_glx_via_angle.gypi) +file. + +### Split `base.gypi` into `cobalt_configuration.gypi` and `base_configuration.gypi` + +Up until now, both Cobalt-specific build configuration options as well as +application-independent Starboard build configuration options were mixed +together within base.gypi. They have now been split apart, and the application +independent options have been moved into Starboard under +[base_configuration.gypi](build/base_configuration.gypi). The Cobalt-specific +options have been left in Cobalt, though renamed to `cobalt_configuration.gypi`. + ### Moved `tizen` to `contrib/tizen`. Please see [contrib/README.md](contrib/README.md) for description of
diff --git a/src/starboard/build/collect_deploy_content.gypi b/src/starboard/build/collect_deploy_content.gypi new file mode 100644 index 0000000..3f5a08b --- /dev/null +++ b/src/starboard/build/collect_deploy_content.gypi
@@ -0,0 +1,54 @@ +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file is meant to be included into a platform deploy gypi to provide an +# action to collect static content that should be deployed with a given target. +# +# The action builds a symlink farm in the content/deploy/<executable_name> +# directory, pointing to the subset of data in content/data that was populated +# by some dependency of the target being deployed. +{ + 'variables': { + # Root of the content tree that should be deployed with a given target. + 'content_deploy_dir': '<(sb_static_contents_output_base_dir)/deploy/<(executable_name)', + + # Stamp file that will be updated after the symlink farm is built. + 'content_deploy_stamp_file': '<(content_deploy_dir).stamp', + + # This is a list of relative paths within both input_dir and output_dir, + # and is named such that GYP does not relativize it. Values are merged in + # from the all_dependent_settings blocks of gypi files that copy static data + # into content/data. + 'content_deploy_subdirs': [], + }, + 'actions': [{ + 'action_name': 'collect_deploy_content', + 'variables': { + 'input_dir': '<(sb_static_contents_output_data_dir)', + 'output_dir': '<(content_deploy_dir)', + }, + # Re-collect the content whenever the executable is rebuilt. + 'inputs': [ '<(executable_file)'], + 'outputs': [ '<(content_deploy_stamp_file)' ], + 'action': [ + 'python', + '<(DEPTH)/starboard/build/collect_deploy_content.py', + '-i', '<(input_dir)', + '-o', '<(output_dir)', + '-s', '<(content_deploy_stamp_file)', + '>@(content_deploy_subdirs)', + ], + 'message': 'Collect content: <(executable_name)', + }], +}
diff --git a/src/starboard/build/collect_deploy_content.py b/src/starboard/build/collect_deploy_content.py new file mode 100644 index 0000000..0cbd0c9 --- /dev/null +++ b/src/starboard/build/collect_deploy_content.py
@@ -0,0 +1,84 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Builds a symlink farm pointing to specified subdirectories of the input dir. +""" + +import argparse +import logging +import os +import shutil +import sys + +# The name of an environment variable that when set to |'1'|, signals to us that +# we should log all output directories that we have populated. +_SHOULD_LOG_ENV_KEY = 'STARBOARD_GYP_SHOULD_LOG_COPIES' + + +def EscapePath(path): + """Returns a path with spaces escaped.""" + return path.replace(' ', '\\ ') + + +def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument( + '-i', dest='input_dir', required=True, help='input directory') + parser.add_argument( + '-o', dest='output_dir', required=True, help='output directory') + parser.add_argument( + '-s', dest='stamp_file', required=True, + help='stamp file to update after the output directory is populated') + parser.add_argument( + 'subdirs', metavar='subdirs', nargs='*', + help='subdirectories within both the input and output directories') + options = parser.parse_args(argv[1:]) + + if os.environ.get(_SHOULD_LOG_ENV_KEY, None) == '1': + log_level = logging.INFO + else: + log_level = logging.WARNING + logging.basicConfig(level=log_level, format='COLLECT CONTENT: %(message)s') + + logging.info('< %s', options.input_dir) + logging.info('> %s', options.output_dir) + for subdir in options.subdirs: + logging.info('+ %s', subdir) + + if os.path.exists(options.output_dir): + shutil.rmtree(options.output_dir) + + for subdir in options.subdirs: + src_path = os.path.abspath( + EscapePath(os.path.join(options.input_dir, subdir))) + dst_path = os.path.abspath( + EscapePath(os.path.join(options.output_dir, subdir))) + + dst_dir = os.path.dirname(dst_path) + rel_path = os.path.relpath(src_path, dst_dir) + + logging.info('%s => %s', dst_path, rel_path) + + # TODO: Add an alternate implementation for win32. + if not os.path.exists(dst_dir): + os.makedirs(dst_dir) + os.symlink(rel_path, dst_path) + + if options.stamp_file: + with open(options.stamp_file, 'w') as stamp_file: + stamp_file.write('\n'.join(options.subdirs)) + +if __name__ == '__main__': + sys.exit(main(sys.argv))
diff --git a/src/starboard/build/copy_test_data.gypi b/src/starboard/build/copy_test_data.gypi index 92ef910..19abaff 100644 --- a/src/starboard/build/copy_test_data.gypi +++ b/src/starboard/build/copy_test_data.gypi
@@ -12,53 +12,62 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This file is meant to be included into an action to copy test data files into -# the DIR_SOURCE_ROOT directory, e.g. out/PS3_Debug/content/dir_source_root/. +# This file is meant to be included into a target to add an action +# that copies test data files into the DIR_TEST_DATA directory, +# e.g. out/linux-x64x11_devel/content/data/test/. # # To use this, create a gyp target with the following form: # { -# 'target_name': 'copy_data_target_name', +# 'target_name': 'target_name_copy_test_data', # 'type': 'none', -# 'actions': [ -# { -# 'action_name': 'copy_data_target_name', -# 'variables': { -# 'input_files': [ -# 'path/to/datafile.txt', # The file will be copied. -# 'path/to/data/directory', # The directory and its content will be copied. -# 'path/to/data/directory/', # The directory's content will be copied. -# ], -# 'output_dir' : 'path/to/output/directory', -# }, -# 'includes': [ 'path/to/this/gypi/file' ], -# }, -# ], +# 'variables': { +# 'content_test_input_files': [ +# 'path/to/datafile.txt', # The file will be copied. +# 'path/to/data/directory', # The directory and its content will be copied. +# 'path/to/data/directory/', # The directory's content will be copied. +# ], +# 'content_test_output_subdir' : 'path/to/output/directory', +# }, +# 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], # }, # # Meaning of the variables: -# input_files: list: paths to data files or directories. When an item is a -# directory, without the final "/", the directory (along -# with the content) will be copied, otherwise only the -# content will be copied. -# output_dir: string: The directory that all input files will be copied to. -# Generally, this should be the directory of the gypi file -# containing the target (e.g. it should be "base" for the -# target in base/base.gyp). -# It is recommended that input_files and output_dir have similar path, so the -# directory structure in dir_source_root/ will reflect that in the source -# folder. +# content_test_input_files: list: +# Paths to data files or directories. When an item is a directory, +# without the final "/", the directory (along with the content) will be +# copied, otherwise only the content will be copied. +# +# content_test_output_subdir: string: +# Directory within the test directory that all input files will be +# copied to. Generally, this should be the directory of the gypi file +# containing the target (e.g. it should be "base" for the target in +# base/base.gyp). +# +# It's recommended that content_test_input_files and content_test_output_subdir +# have similar paths, so the directory structure in data/test/ will reflect +# that in the source folder. { - 'inputs': [ - '<!@pymod_do_main(starboard.build.copy_data --inputs <(input_files))', + 'actions': [ + { + 'action_name': 'copy_test', + 'inputs': [ + '<!@pymod_do_main(starboard.build.copy_data --inputs <(content_test_input_files))', + ], + 'outputs': [ + '<!@pymod_do_main(starboard.build.copy_data -o <(sb_static_contents_output_data_dir)/test/<(content_test_output_subdir) --outputs <(content_test_input_files))', + ], + 'action': [ + 'python', + '<(DEPTH)/starboard/build/copy_data.py', + '-o', '<(sb_static_contents_output_data_dir)/test/<(content_test_output_subdir)', + '<@(content_test_input_files)', + ], + }, ], - 'outputs': [ - '<!@pymod_do_main(starboard.build.copy_data -o <(sb_static_contents_output_base_dir)/dir_source_root/<(output_dir) --outputs <(input_files))', - ], - 'action': [ - 'python', - '<(DEPTH)/starboard/build/copy_data.py', - '-o', '<(sb_static_contents_output_base_dir)/dir_source_root/<(output_dir)', - '<@(input_files)', - ], + 'all_dependent_settings': { + 'variables': { + 'content_deploy_subdirs': [ 'test/<(content_test_output_subdir)' ] + } + }, }
diff --git a/src/starboard/build/gyp_runner.py b/src/starboard/build/gyp_runner.py index 56b4a6f..940693d 100644 --- a/src/starboard/build/gyp_runner.py +++ b/src/starboard/build/gyp_runner.py
@@ -199,6 +199,7 @@ 'host_os': _GetHostOS(), 'starboard_path': os.path.relpath(platform.Get(platform_name).path, source_tree_dir), + 'starboard_platform_name': platform_name, } _AppendVariables(common_variables, self.common_args)
diff --git a/src/starboard/build/platform_configuration.py b/src/starboard/build/platform_configuration.py index de71164..43c1c1a 100644 --- a/src/starboard/build/platform_configuration.py +++ b/src/starboard/build/platform_configuration.py
@@ -55,7 +55,9 @@ Should be derived by platform specific configurations. """ - def __init__(self, platform_name, asan_enabled_by_default=False, + def __init__(self, + platform_name, + asan_enabled_by_default=False, directory=None): self._platform_name = platform_name if directory: @@ -258,8 +260,8 @@ def GetLauncher(self): """Gets the module used to launch applications on this platform.""" - module_path = os.path.abspath(os.path.join(self.GetLauncherPath(), - 'launcher.py')) + module_path = os.path.abspath( + os.path.join(self.GetLauncherPath(), 'launcher.py')) try: return imp.load_source('launcher', module_path) except (IOError, ImportError, RuntimeError):
diff --git a/src/starboard/common/common.gyp b/src/starboard/common/common.gyp index f85fbb5..c3cd9b6 100644 --- a/src/starboard/common/common.gyp +++ b/src/starboard/common/common.gyp
@@ -40,6 +40,8 @@ 'state_machine.h', 'thread_collision_warner.cc', 'thread_collision_warner.h', + 'thread.cc', + 'thread.h', ], 'defines': [ # This must be defined when building Starboard, and must not when
diff --git a/src/starboard/common/thread.cc b/src/starboard/common/thread.cc new file mode 100644 index 0000000..d41976f --- /dev/null +++ b/src/starboard/common/thread.cc
@@ -0,0 +1,115 @@ +/* + * Copyright 2018 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "starboard/common/thread.h" + +#include "starboard/atomic.h" +#include "starboard/common/optional.h" +#include "starboard/common/semaphore.h" +#include "starboard/log.h" +#include "starboard/mutex.h" +#include "starboard/thread.h" +#include "starboard/time.h" +#include "starboard/types.h" + +namespace starboard { + +struct Thread::Data { + std::string name_; + SbThread thread_ = kSbThreadInvalid; + atomic_bool started_; + atomic_bool join_called_; + Semaphore join_sema_; + optional<Thread::Options> options_; +}; + +Thread::Thread(const std::string& name) { + d_.reset(new Thread::Data); + d_->name_ = name; +} + +Thread::~Thread() { + SB_DCHECK(d_->join_called_.load()) << "Join not called on thread."; +} + +void Thread::Start(const Options& options) { + SbThreadEntryPoint entry_point = ThreadEntryPoint; + + SB_DCHECK(!d_->started_.load()); + SB_DCHECK(!d_->options_.has_engaged()); + d_->started_.store(true); + d_->options_ = options; + + d_->thread_ = SbThreadCreate(options.stack_size, + options.priority_, + kSbThreadNoAffinity, // default affinity. + options.joinable, + d_->name_.c_str(), + entry_point, + this); + + // SbThreadCreate() above produced an invalid thread handle. + SB_DCHECK(d_->thread_ != kSbThreadInvalid); +} + +void Thread::Sleep(SbTime microseconds) { + SbThreadSleep(microseconds); +} + +void Thread::SleepMilliseconds(int value) { + return Sleep(value * kSbTimeMillisecond); +} + +bool Thread::WaitForJoin(SbTime timeout) { + bool joined = d_->join_sema_.TakeWait(timeout); + if (joined) { + SB_DCHECK(d_->join_called_.load()); + } + return joined; +} + +starboard::Semaphore* Thread::join_sema() { + return &d_->join_sema_; +} + +starboard::atomic_bool* Thread::joined_bool() { + return &d_->join_called_; +} + +void* Thread::ThreadEntryPoint(void* context) { + Thread* this_ptr = static_cast<Thread*>(context); + this_ptr->Run(); + return NULL; +} + +void Thread::Join() { + SB_DCHECK(d_->join_called_.load() == false); + SB_DCHECK(d_->options_->joinable) + << "Detached thread should not be joined."; + + d_->join_called_.store(true); + d_->join_sema_.Put(); + + if (!SbThreadJoin(d_->thread_, NULL)) { + SB_DCHECK(false) << "Could not join thread."; + } +} + +bool Thread::join_called() const { + return d_->join_called_.load(); +} + +} // namespace starboard
diff --git a/src/starboard/common/thread.h b/src/starboard/common/thread.h new file mode 100644 index 0000000..6b4afca --- /dev/null +++ b/src/starboard/common/thread.h
@@ -0,0 +1,81 @@ +/* + * Copyright 2018 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STARBOARD_COMMON_THREAD_H_ +#define STARBOARD_COMMON_THREAD_H_ + +#include <functional> +#include <string> + +#include "starboard/common/scoped_ptr.h" +#include "starboard/configuration.h" +#include "starboard/thread.h" +#include "starboard/types.h" + +namespace starboard { + +class Semaphore; +class atomic_bool; + +class Thread { + public: + struct Options { + Options() : stack_size(0), // Signal for default stack size. + priority_(kSbThreadNoPriority), + joinable(true) {} + int64_t stack_size; + SbThreadPriority priority_; + bool joinable = true; + }; + + explicit Thread(const std::string& name); + virtual ~Thread(); + + // Subclasses should override the Run method. + // Example: + // void Run() { + // while (!WaitForJoin(kWaitTime)) { + // ... do work ... + // } + // } + virtual void Run() = 0; + + // Called by the main thread, this will cause Run() to be invoked + // on another thread. + virtual void Start(const Options& options = Options()); + virtual void Join(); + bool join_called() const; + + protected: + static void* ThreadEntryPoint(void* context); + static void Sleep(SbTime microseconds); + static void SleepMilliseconds(int value); + + // Waits at most |timeout| microseconds for Join() to be called. If + // Join() was called then return |true|, else |false|. + bool WaitForJoin(SbTime timeout); + Semaphore* join_sema(); + atomic_bool* joined_bool(); + + struct Data; + scoped_ptr<Data> d_; + + SB_DISALLOW_COPY_AND_ASSIGN(Thread); +}; + +} // namespace starboard + +#endif // STARBOARD_COMMON_THREAD_H_
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h index 3b3da18..fb8598a 100644 --- a/src/starboard/configuration.h +++ b/src/starboard/configuration.h
@@ -68,12 +68,37 @@ // // exposes functionality for my new feature. // #define SB_MY_EXPERIMENTAL_FEATURE_VERSION SB_EXPERIMENTAL_API_VERSION +// Minimum API version where supporting player error messages is required. +#define SB_PLAYER_ERROR_MESSAGE_API_VERSION SB_EXPERIMENTAL_API_VERSION + // Minimum API version for supporting system-level closed caption settings. #define SB_ACCESSIBILITY_CAPTIONS_API_VERSION SB_EXPERIMENTAL_API_VERSION -// Minimum API version for supporting audioless video playback. +// Minimum API version where supporting audioless video playback is required. #define SB_AUDIOLESS_VIDEO_API_VERSION SB_EXPERIMENTAL_API_VERSION +// API version where DRM session closed callback is required. +// Add a callback to SbDrmCreateSystem that allows a DRM system to +// signal that a DRM session has closed from the Starboard layer. +// Previously, DRM sessions could only be closed from the application layer. +#define SB_DRM_SESSION_CLOSED_API_VERSION SB_EXPERIMENTAL_API_VERSION + +// API version where kSbSystemPathSourceDirectory is removed. +// Test code looking for its static input files should instead use the `test` +// subdirectory in kSbSystemPathContentDirectory. +#define SB_PATH_SOURCE_DIR_REMOVED_API_VERSION SB_EXPERIMENTAL_API_VERSION + +// API version where kSbSystemPropertyPlatformUuid is removed. +// This property was only ever used in platforms using `in_app_dial`. +// The only usage of this system property was replaced with a +// self-contained mechanism. +#define SB_PROPERTY_UUID_REMOVED_API_VERSION SB_EXPERIMENTAL_API_VERSION + +// API version where kSbMediaAudioSampleTypeInt16 is deprecated. +// SB_HAS_QUIRK_SUPPORT_INT16_AUDIO_SAMPLES has to be defined to continue +// support int16 audio samples after this version. +#define SB_DEPRECATE_INT16_AUDIO_SAMPLE_VERSION SB_EXPERIMENTAL_API_VERSION + // --- Release Candidate Feature Defines ------------------------------------- // --- Common Detected Features ---------------------------------------------- @@ -552,12 +577,31 @@ #endif // defined(SB_HAS_DRM_KEY_STATUSES) #endif // SB_API_VERSION >= 6 +#if SB_API_VERSION >= SB_DRM_SESSION_CLOSED_API_VERSION +#if defined(SB_HAS_DRM_SESSION_CLOSED) +#if !SB_HAS(DRM_SESSION_CLOSED) +#error "SB_HAS_DRM_SESSION_CLOSED is required in this API version." +#endif // !SB_HAS(DRM_SESSION_CLOSED) +#else // defined(SB_HAS_DRM_SESSION_CLOSED) +#define SB_HAS_DRM_SESSION_CLOSED 1 +#endif // defined(SB_HAS_DRM_SESSION_CLOSED) +#endif // SB_API_VERSION >= SB_DRM_SESSION_CLOSED_API_VERSION + #if SB_API_VERSION >= 5 #if !defined(SB_HAS_SPEECH_RECOGNIZER) #error "Your platform must define SB_HAS_SPEECH_RECOGNIZER." #endif // !defined(SB_HAS_SPEECH_RECOGNIZER) #endif // SB_API_VERSION >= 5 +#if SB_API_VERSION >= 8 +#if !defined(SB_HAS_ON_SCREEN_KEYBOARD) +#error "Your platform must define SB_HAS_ON_SCREEN_KEYBOARD." +#endif // !defined(SB_HAS_ON_SCREEN_KEYBOARD) +#if !defined(SB_HAS_PLAYER_WITH_URL) +#error "Your platform must define SB_HAS_PLAYER_WITH_URL." +#endif // !defined(SB_HAS_PLAYER_WITH_URL) +#endif // SB_API_VERSION >= 8 + #if SB_HAS(ON_SCREEN_KEYBOARD) && (SB_API_VERSION < 8) #error "SB_HAS_ON_SCREEN_KEYBOARD not supported in this API version." #endif @@ -567,6 +611,20 @@ #error "SB_HAS_CAPTIONS not supported in this API version." #endif +#if SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION +#define SB_HAS_AUDIOLESS_VIDEO 1 +#endif + +#if SB_API_VERSION >= SB_PLAYER_ERROR_MESSAGE_API_VERSION +#define SB_HAS_PLAYER_ERROR_MESSAGE 1 +#endif + +#if SB_API_VERSION < SB_DEPRECATE_INT16_AUDIO_SAMPLE_VERSION +#if !SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES) +#define SB_HAS_QUIRK_SUPPORT_INT16_AUDIO_SAMPLES 1 +#endif // !SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES) +#endif // SB_API_VERSION < SB_DEPRECATE_INT16_AUDIO_SAMPLE_VERSION + // --- Derived Configuration ------------------------------------------------- // Whether the current platform is little endian.
diff --git a/src/starboard/contrib/creator/ci20x11/egl_workaround.cc b/src/starboard/contrib/creator/ci20x11/egl_workaround.cc index bc350e8..e6f83e2 100644 --- a/src/starboard/contrib/creator/ci20x11/egl_workaround.cc +++ b/src/starboard/contrib/creator/ci20x11/egl_workaround.cc
@@ -22,7 +22,8 @@ NativeDisplayType native_display_; -extern "C" EGLDisplay __wrap_eglGetDisplay(EGLNativeDisplayType native_display) { +extern "C" EGLDisplay + __wrap_eglGetDisplay(EGLNativeDisplayType native_display) { native_display_ = XOpenDisplay(0); return __real_eglGetDisplay((EGLNativeDisplayType) native_display_); } @@ -32,4 +33,3 @@ XCloseDisplay((NativeDisplayType) native_display_); return result; } -
diff --git a/src/starboard/contrib/creator/ci20x11/gcc/4.9/gyp_configuration.py b/src/starboard/contrib/creator/ci20x11/gcc/4.9/gyp_configuration.py index be4a5d9..9559624 100644 --- a/src/starboard/contrib/creator/ci20x11/gcc/4.9/gyp_configuration.py +++ b/src/starboard/contrib/creator/ci20x11/gcc/4.9/gyp_configuration.py
@@ -13,11 +13,13 @@ # limitations under the License. """Starboard Creator CI20 X11 gcc 4.9 platform configuration.""" +import logging import os from starboard.contrib.creator.shared import gyp_configuration as shared_configuration from starboard.tools.testing import test_filter + class PlatformConfig(shared_configuration.CreatorConfiguration): """Starboard Creator platform configuration.""" @@ -26,9 +28,13 @@ def GetVariables(self, configuration): variables = super(PlatformConfig, self).GetVariables(configuration) - variables.update({'clang': 0,}) + variables.update({ + 'clang': 0, + }) toolchain_lib_path = os.path.join(self.toolchain_dir, 'lib') - variables.update({'toolchain_lib_path': toolchain_lib_path,}) + variables.update({ + 'toolchain_lib_path': toolchain_lib_path, + }) return variables def GetEnvironmentVariables(self): @@ -49,21 +55,20 @@ def GetTestFilters(self): filters = super(PlatformConfig, self).GetTestFilters() filters.extend([ - # test fails when built with GCC 4.9, issue was fixed in later versions of GCC - test_filter.TestFilter( - 'nplb', 'SbAlignTest.AlignAsStackVariable'), + # test fails when built with GCC 4.9, issue was fixed + # in later versions of GCC + test_filter.TestFilter('nplb', 'SbAlignTest.AlignAsStackVariable'), # tests fail also on x86 - test_filter.TestFilter( - 'nplb', 'SbSystemSymbolizeTest.SunnyDay'), - test_filter.TestFilter( - 'nplb', 'SbSystemGetStackTest.SunnyDayStackDirection'), - test_filter.TestFilter( - 'nplb', 'SbSystemGetStackTest.SunnyDay'), - test_filter.TestFilter( - 'nplb', 'SbSystemGetStackTest.SunnyDayShortStack'), + test_filter.TestFilter('nplb', 'SbSystemSymbolizeTest.SunnyDay'), + test_filter.TestFilter('nplb', + 'SbSystemGetStackTest.SunnyDayStackDirection'), + test_filter.TestFilter('nplb', 'SbSystemGetStackTest.SunnyDay'), + test_filter.TestFilter('nplb', + 'SbSystemGetStackTest.SunnyDayShortStack'), ]) return filters + def CreatePlatformConfig(): try: return PlatformConfig('creator-ci20x11-gcc-4-9')
diff --git a/src/starboard/contrib/creator/ci20x11/gyp_configuration.py b/src/starboard/contrib/creator/ci20x11/gyp_configuration.py index 9268eeb..9ba9c39 100644 --- a/src/starboard/contrib/creator/ci20x11/gyp_configuration.py +++ b/src/starboard/contrib/creator/ci20x11/gyp_configuration.py
@@ -21,10 +21,11 @@ from starboard.tools.toolchain import cp from starboard.tools.toolchain import touch + class CreatorCI20X11Configuration(shared_configuration.CreatorConfiguration): """Starboard Creator X11 platform configuration.""" - def __init__(self, platform_name="creator-ci20x11"): + def __init__(self, platform_name='creator-ci20x11'): super(CreatorCI20X11Configuration, self).__init__(platform_name) def GetTargetToolchain(self): @@ -51,5 +52,6 @@ filters = super(CreatorCI20X11Configuration, self).GetTestFilters() return filters + def CreatePlatformConfig(): return CreatorCI20X11Configuration()
diff --git a/src/starboard/contrib/creator/ci20x11/system_get_property.cc b/src/starboard/contrib/creator/ci20x11/system_get_property.cc index 2e88d56..cb834af 100644 --- a/src/starboard/contrib/creator/ci20x11/system_get_property.cc +++ b/src/starboard/contrib/creator/ci20x11/system_get_property.cc
@@ -14,8 +14,8 @@ #include "starboard/system.h" -#include <netdb.h> #include <linux/if.h> // NOLINT(build/include_alpha) +#include <netdb.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -53,13 +53,12 @@ } struct ifreq* cur_interface = config.ifc_req; - const struct ifreq* const end = cur_interface + - (config.ifc_len / sizeof(struct ifreq)); + const struct ifreq* const end = + cur_interface + (config.ifc_len / sizeof(struct ifreq)); for (; cur_interface != end; ++cur_interface) { - SbStringCopy(interface.ifr_name, - cur_interface->ifr_name, - sizeof(cur_interface->ifr_name)); + SbStringCopy(interface.ifr_name, cur_interface->ifr_name, + sizeof(cur_interface->ifr_name)); if (ioctl(fd, SIOCGIFFLAGS, &interface) == -1) { continue; } @@ -69,7 +68,8 @@ if (ioctl(fd, SIOCGIFHWADDR, &interface) == -1) { continue; } - SbStringFormatF(out_value, value_length, "%x:%x:%x:%x:%x:%x", + SbStringFormatF( + out_value, value_length, "%x:%x:%x:%x:%x:%x", interface.ifr_addr.sa_data[0], interface.ifr_addr.sa_data[1], interface.ifr_addr.sa_data[2], interface.ifr_addr.sa_data[3], interface.ifr_addr.sa_data[4], interface.ifr_addr.sa_data[5]);
diff --git a/src/starboard/contrib/creator/shared/gyp_configuration.gypi b/src/starboard/contrib/creator/shared/gyp_configuration.gypi index aa25e94..b853967 100644 --- a/src/starboard/contrib/creator/shared/gyp_configuration.gypi +++ b/src/starboard/contrib/creator/shared/gyp_configuration.gypi
@@ -24,10 +24,7 @@ 'platform_libraries': [ '-lasound', - '-lavcodec', - '-lavformat', - '-lavresample', - '-lavutil', + '-ldl', '-lm', '-lpthread', '-lrt',
diff --git a/src/starboard/contrib/creator/shared/gyp_configuration.py b/src/starboard/contrib/creator/shared/gyp_configuration.py index 6bc7db9..84dfa65 100644 --- a/src/starboard/contrib/creator/shared/gyp_configuration.py +++ b/src/starboard/contrib/creator/shared/gyp_configuration.py
@@ -48,8 +48,8 @@ 'ci20 builds require $CI20_HOME/%s to be a valid directory.', relative_sysroot) sys.exit(1) - variables = super(CreatorConfiguration, self).GetVariables(config_name, - use_clang=1) + variables = super(CreatorConfiguration, self).GetVariables( + config_name, use_clang=1) variables.update({ 'sysroot': sysroot, }) @@ -65,22 +65,24 @@ def GetGeneratorVariables(self, config_name): del config_name - generator_variables = {'qtcreator_session_name_prefix': 'cobalt',} + generator_variables = { + 'qtcreator_session_name_prefix': 'cobalt', + } return generator_variables def GetEnvironmentVariables(self): self.ci20_home = self._GetCi20Home() - self.host_compiler_environment = build.GetHostCompilerEnvironment( + self.host_compiler_environment = build.GetHostCompilerEnvironment( clang.GetClangSpecification(), False) env_variables = self.host_compiler_environment env_variables = { - 'CC': self.host_compiler_environment['CC_host'], - 'CXX': self.host_compiler_environment['CXX_host'], - 'CC_host': 'gcc', - 'CXX_host': 'g++', - 'LD_host': 'g++', - 'ARFLAGS_host': 'rcs', - 'ARTHINFLAGS_host': 'rcsT', + 'CC': self.host_compiler_environment['CC_host'], + 'CXX': self.host_compiler_environment['CXX_host'], + 'CC_host': 'gcc', + 'CXX_host': 'g++', + 'LD_host': 'g++', + 'ARFLAGS_host': 'rcs', + 'ARTHINFLAGS_host': 'rcsT', } return env_variables @@ -89,47 +91,49 @@ filters = super(CreatorConfiguration, self).GetTestFilters() filters.extend([ # test is disabled on x64 - test_filter.TestFilter( - 'bindings_test', ('GlobalInterfaceBindingsTest.' - 'PropertiesAndOperationsAreOwnProperties')), + test_filter.TestFilter('bindings_test', + ('GlobalInterfaceBindingsTest.' + 'PropertiesAndOperationsAreOwnProperties')), # tests miss the defined delay - test_filter.TestFilter( - 'nplb', 'SbConditionVariableWaitTimedTest.SunnyDay'), + test_filter.TestFilter('nplb', + 'SbConditionVariableWaitTimedTest.SunnyDay'), test_filter.TestFilter( 'nplb', 'SbConditionVariableWaitTimedTest.SunnyDayAutoInit'), # tests sometimes miss the threshold of 10ms test_filter.TestFilter( 'nplb', 'Semaphore.ThreadTakesWait_PutBeforeTimeExpires'), - test_filter.TestFilter( - 'nplb', 'RWLock.HoldsLockForTime'), + test_filter.TestFilter('nplb', 'RWLock.HoldsLockForTime'), # tests sometimes miss the threshold of 5ms - test_filter.TestFilter( - 'nplb', 'Semaphore.ThreadTakesWait_TimeExpires'), - test_filter.TestFilter( - 'nplb', 'SbWindowCreateTest.SunnyDayDefault'), - test_filter.TestFilter( - 'nplb', 'SbWindowCreateTest.SunnyDayDefaultSet'), - test_filter.TestFilter( - 'nplb', 'SbWindowGetPlatformHandleTest.SunnyDay'), - test_filter.TestFilter( - 'nplb', 'SbWindowGetSizeTest.SunnyDay'), - test_filter.TestFilter( - 'nplb', 'SbPlayerTest.SunnyDay'), + test_filter.TestFilter('nplb', 'Semaphore.ThreadTakesWait_TimeExpires'), + test_filter.TestFilter('nplb', 'SbWindowCreateTest.SunnyDayDefault'), + test_filter.TestFilter('nplb', 'SbWindowCreateTest.SunnyDayDefaultSet'), + test_filter.TestFilter('nplb', + 'SbWindowGetPlatformHandleTest.SunnyDay'), + test_filter.TestFilter('nplb', 'SbWindowGetSizeTest.SunnyDay'), + test_filter.TestFilter('nplb', 'SbPlayerTest.SunnyDay'), # tests fail also on x86 test_filter.TestFilter( - 'nplb', 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDayDestination/1'), + 'nplb', + ('SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.' + 'SunnyDayDestination/1') + ), test_filter.TestFilter( - 'nplb', 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceForDestination/1'), + 'nplb', + ('SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.' + 'SunnyDaySourceForDestination/1') + ), test_filter.TestFilter( - 'nplb', 'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceNotLoopback/1'), + 'nplb', + ('SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.' + 'SunnyDaySourceNotLoopback/1') + ), # there are no test cases in this test - test_filter.TestFilter( - 'nplb_blitter_pixel_tests', test_filter.FILTER_ALL), + test_filter.TestFilter('nplb_blitter_pixel_tests', + test_filter.FILTER_ALL), # test fails on x64 also - test_filter.TestFilter( - 'net_unittests', 'HostResolverImplDnsTest.DnsTaskUnspec'), + test_filter.TestFilter('net_unittests', + 'HostResolverImplDnsTest.DnsTaskUnspec'), # we don't have proper procedure for running this test - test_filter.TestFilter( - 'web_platform_tests', test_filter.FILTER_ALL), + test_filter.TestFilter('web_platform_tests', test_filter.FILTER_ALL), ]) return filters
diff --git a/src/starboard/contrib/creator/shared/player_components_impl.cc b/src/starboard/contrib/creator/shared/player_components_impl.cc index 51c9de1..3195d3a 100644 --- a/src/starboard/contrib/creator/shared/player_components_impl.cc +++ b/src/starboard/contrib/creator/shared/player_components_impl.cc
@@ -84,9 +84,18 @@ *video_renderer_sink = new PunchoutVideoRendererSink( video_parameters.player, kVideoSinkRenderInterval); } + + void GetAudioRendererParams(int* max_cached_frames, + int* max_frames_per_append) const override { + SB_DCHECK(max_cached_frames); + SB_DCHECK(max_frames_per_append); + + *max_cached_frames = 256 * 1024; + *max_frames_per_append = 16384; + } }; -} // namespace +} // namespace // static scoped_ptr<PlayerComponents> PlayerComponents::Create() {
diff --git a/src/starboard/contrib/creator/shared/starboard_platform.gypi b/src/starboard/contrib/creator/shared/starboard_platform.gypi index be6b570..f64dd2b 100644 --- a/src/starboard/contrib/creator/shared/starboard_platform.gypi +++ b/src/starboard/contrib/creator/shared/starboard_platform.gypi
@@ -37,14 +37,6 @@ '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc', '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc', '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h', '<(DEPTH)/starboard/shared/gcc/atomic_gcc_public.h', '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc', '<(DEPTH)/starboard/shared/iso/character_is_digit.cc', @@ -252,6 +244,7 @@ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink.h', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h', + '<(DEPTH)/starboard/shared/starboard/player/filter/audio_resampler_impl.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/cpu_video_frame.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/cpu_video_frame.h', '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.h', @@ -333,6 +326,7 @@ 'starboard_platform_dependencies': [ '<(DEPTH)/starboard/common/common.gyp:common', '<(DEPTH)/starboard/linux/shared/starboard_base_symbolize.gyp:starboard_base_symbolize', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg.gyp:ffmpeg_linked', '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc', '<(DEPTH)/third_party/libevent/libevent.gyp:libevent', ],
diff --git a/src/starboard/contrib/tizen/armv7l/starboard_platform.gyp b/src/starboard/contrib/tizen/armv7l/starboard_platform.gyp index ac9a851..d940f42 100644 --- a/src/starboard/contrib/tizen/armv7l/starboard_platform.gyp +++ b/src/starboard/contrib/tizen/armv7l/starboard_platform.gyp
@@ -49,6 +49,7 @@ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink.h', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h', + '<(DEPTH)/starboard/shared/starboard/player/filter/audio_resampler_impl.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.h', '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.cc', @@ -107,8 +108,6 @@ '<(DEPTH)/starboard/shared/wayland/application_wayland.cc', '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.cc', '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_audio_decoder.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h', '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.cc', '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_common.h', '<(DEPTH)/starboard/contrib/tizen/shared/ffmpeg/ffmpeg_video_decoder.cc',
diff --git a/src/starboard/drm.h b/src/starboard/drm.h index fbc6ea1..4bdf4aa 100644 --- a/src/starboard/drm.h +++ b/src/starboard/drm.h
@@ -132,6 +132,15 @@ const SbDrmKeyStatus* key_statuses); #endif // SB_HAS(DRM_KEY_STATUSES) +// A callback for signalling that a session has been closed by the SbDrmSystem +#if SB_HAS(DRM_SESSION_CLOSED) +typedef void (*SbDrmSessionClosedFunc)( + SbDrmSystem drm_system, + void* context, + const void* session_id, + int session_id_size); +#endif // SB_HAS(DRM_SESSION_CLOSED)) + // --- Constants ------------------------------------------------------------- // An invalid SbDrmSystem. @@ -171,7 +180,26 @@ // SbDrmGenerateSessionUpdateRequest() is called. // |session_updated_callback|: A function that is called every time after // SbDrmUpdateSession() is called. -#if SB_HAS(DRM_KEY_STATUSES) +// |key_statuses_changed_callback|: A function that can be called to indicate +// that key statuses have changed. +// |session_closed_callback|: A function that can be called to indicate that +// a session has closed. +#if SB_HAS(DRM_SESSION_CLOSED) + +#if !SB_HAS(DRM_KEY_STATUSES) +#error "Platforms with SB_HAS_DRM_SESSION_CLOSED must also set " + "SB_HAS_DRM_KEY_STATUSES" +#endif // !SB_HAS(DRM_KEY_STATUSES) + +SB_EXPORT SbDrmSystem SbDrmCreateSystem( + const char* key_system, + void* context, + SbDrmSessionUpdateRequestFunc update_request_callback, + SbDrmSessionUpdatedFunc session_updated_callback, + SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback, + SbDrmSessionClosedFunc session_closed_callback); + +#elif SB_HAS(DRM_KEY_STATUSES) SB_EXPORT SbDrmSystem SbDrmCreateSystem( const char* key_system,
diff --git a/src/starboard/linux/shared/BUILD.gn b/src/starboard/linux/shared/BUILD.gn index 80ced09..79bdf4e 100644 --- a/src/starboard/linux/shared/BUILD.gn +++ b/src/starboard/linux/shared/BUILD.gn
@@ -285,8 +285,6 @@ "//starboard/shared/dlmalloc/memory_unmap.cc", "//starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc", "//starboard/shared/ffmpeg/ffmpeg_audio_decoder.h", - "//starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc", - "//starboard/shared/ffmpeg/ffmpeg_audio_resampler.h", "//starboard/shared/ffmpeg/ffmpeg_common.cc", "//starboard/shared/ffmpeg/ffmpeg_common.h", "//starboard/shared/ffmpeg/ffmpeg_video_decoder.cc", @@ -488,6 +486,7 @@ "//starboard/shared/starboard/player/filter/audio_renderer_sink.h", "//starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc", "//starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h", + "//starboard/shared/starboard/player/filter/audio_resampler_impl.cc', "//starboard/shared/starboard/player/filter/audio_time_stretcher.cc", "//starboard/shared/starboard/player/filter/audio_time_stretcher.h", "//starboard/shared/starboard/player/filter/cpu_video_frame.cc",
diff --git a/src/starboard/linux/shared/cobalt/configuration.gypi b/src/starboard/linux/shared/cobalt/configuration.gypi index 46601e1..5ba01c4 100644 --- a/src/starboard/linux/shared/cobalt/configuration.gypi +++ b/src/starboard/linux/shared/cobalt/configuration.gypi
@@ -18,5 +18,11 @@ 'variables': { 'in_app_dial%': 1, 'cobalt_media_source_2016': 1, + # The maximum amount of memory that will be used to store media buffers when + # video resolution is no larger than 1080p. + 'cobalt_media_buffer_max_capacity_1080p': 36 * 1024 * 1024, + # The maximum amount of memory that will be used to store media buffers when + # video resolution is 4k. + 'cobalt_media_buffer_max_capacity_4k': 65 * 1024 * 1024, }, # end of variables }
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h index 573d4ca..bbe079c 100644 --- a/src/starboard/linux/shared/configuration_public.h +++ b/src/starboard/linux/shared/configuration_public.h
@@ -278,6 +278,9 @@ // stack size for media stack threads. #define SB_MEDIA_THREAD_STACK_SIZE 0U +// Allow playing audioless video. +#define SB_HAS_AUDIOLESS_VIDEO 1 + // --- Decoder-only Params --- // Specifies how media buffers must be aligned on this platform as some
diff --git a/src/starboard/linux/shared/gyp_configuration.gypi b/src/starboard/linux/shared/gyp_configuration.gypi index dd144ef..a98a1ba 100644 --- a/src/starboard/linux/shared/gyp_configuration.gypi +++ b/src/starboard/linux/shared/gyp_configuration.gypi
@@ -20,12 +20,12 @@ 'target_arch%': 'x64', 'target_os': 'linux', + 'javascript_engine%': 'v8', + 'cobalt_enable_jit%': 1, + 'platform_libraries': [ '-lasound', - '-lavcodec', - '-lavformat', - '-lavresample', - '-lavutil', + '-ldl', '-lpthread', '-lrt', ], @@ -41,6 +41,9 @@ 'variables': { 'use_dlmalloc_allocator%': 0, }, + 'linker_flags': [ + '-static-libstdc++' + ], 'conditions': [ ['use_dlmalloc_allocator==1 and use_asan==0', {
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py index b05ca59..d23d1e7 100644 --- a/src/starboard/linux/shared/gyp_configuration.py +++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -28,10 +28,9 @@ platform, asan_enabled_by_default=True, goma_supports_compiler=True): + self.goma_supports_compiler = goma_supports_compiler super(LinuxConfiguration, self).__init__(platform, asan_enabled_by_default) self.AppendApplicationConfigurationPath(os.path.dirname(__file__)) - self.host_compiler_environment = build.GetHostCompilerEnvironment( - clang.GetClangSpecification(), goma_supports_compiler) def GetBuildFormat(self): """Returns the desired build format.""" @@ -54,6 +53,10 @@ return generator_variables def GetEnvironmentVariables(self): + if not hasattr(self, 'host_compiler_environment'): + self.host_compiler_environment = build.GetHostCompilerEnvironment( + clang.GetClangSpecification(), self.goma_supports_compiler) + env_variables = self.host_compiler_environment env_variables.update({ 'CC': self.host_compiler_environment['CC_host'],
diff --git a/src/starboard/linux/shared/launcher.py b/src/starboard/linux/shared/launcher.py index 0305ee0..ace2767 100644 --- a/src/starboard/linux/shared/launcher.py +++ b/src/starboard/linux/shared/launcher.py
@@ -19,11 +19,25 @@ import socket import subprocess import sys +import time import traceback import _env # pylint: disable=unused-import from starboard.tools import abstract_launcher +STATUS_CHANGE_TIMEOUT = 15 + + +def GetProcessStatus(pid): + """Returns process running status given its pid, or empty string if not found. + + Args: + pid: process id of specified cobalt instance. + """ + output = subprocess.check_output( + ["ps -o state= -p {}".format(pid)], shell=True) + return output + class Launcher(abstract_launcher.AbstractLauncher): """Class for launching Cobalt/tools on Linux.""" @@ -35,7 +49,7 @@ if socket.has_ipv6: # If the device supports IPv6: self.device_id = "::1" # Use the only IPv6 loopback address else: - self.device_id = socket.gethostbyname("localhost") + self.device_id = socket.gethostbyname("localhost") # pylint: disable=W6503 self.executable = self.GetTargetPath() @@ -78,6 +92,8 @@ sys.stderr.write("\n***Sending continue signal to executable***\n") if self.proc: self.proc.send_signal(signal.SIGCONT) + # Wait for process status change in Linux system. + self.WaitForProcessStatus("R", STATUS_CHANGE_TIMEOUT) else: sys.stderr.write("Cannot send continue to executable; it is closed.\n") @@ -86,5 +102,25 @@ sys.stderr.write("\n***Sending suspend signal to executable***\n") if self.proc: self.proc.send_signal(signal.SIGUSR1) + # Wait for process status change in Linux system. + self.WaitForProcessStatus("T", STATUS_CHANGE_TIMEOUT) else: sys.stderr.write("Cannot send suspend to executable; it is closed.\n") + + def WaitForProcessStatus(self, target_status, timeout): + """Wait for Cobalt to turn to target status within specified timeout limit. + + Args: + target_status: A character representing application status: + R-running; + T-stopped/suspended; + S-sleep/paused; + timeout: Time limit in unit of seconds. + """ + elapsed_time = 0 + while not GetProcessStatus(pid=self.pid).startswith(target_status): + if elapsed_time >= timeout: + return + else: + elapsed_time += .005 + time.sleep(.005)
diff --git a/src/starboard/linux/shared/player_components_impl.cc b/src/starboard/linux/shared/player_components_impl.cc index c8b745e..3548a17 100644 --- a/src/starboard/linux/shared/player_components_impl.cc +++ b/src/starboard/linux/shared/player_components_impl.cc
@@ -28,7 +28,6 @@ #include "starboard/shared/starboard/player/filter/video_render_algorithm.h" #include "starboard/shared/starboard/player/filter/video_render_algorithm_impl.h" #include "starboard/shared/starboard/player/filter/video_renderer_sink.h" -#include "starboard/time.h" namespace starboard { namespace shared { @@ -48,9 +47,9 @@ SB_DCHECK(audio_decoder); SB_DCHECK(audio_renderer_sink); - scoped_ptr<AudioDecoderImpl> audio_decoder_impl(new AudioDecoderImpl( + scoped_ptr<AudioDecoderImpl> audio_decoder_impl(AudioDecoderImpl::Create( audio_parameters.audio_codec, audio_parameters.audio_header)); - if (audio_decoder_impl->is_valid()) { + if (audio_decoder_impl && audio_decoder_impl->is_valid()) { audio_decoder->reset(audio_decoder_impl.release()); } else { audio_decoder->reset(); @@ -80,10 +79,10 @@ video_parameters.decode_target_graphics_context_provider)); } else { scoped_ptr<FfmpegVideoDecoderImpl> ffmpeg_video_decoder( - new FfmpegVideoDecoderImpl( + FfmpegVideoDecoderImpl::Create( video_parameters.video_codec, video_parameters.output_mode, video_parameters.decode_target_graphics_context_provider)); - if (ffmpeg_video_decoder->is_valid()) { + if (ffmpeg_video_decoder && ffmpeg_video_decoder->is_valid()) { video_decoder->reset(ffmpeg_video_decoder.release()); } } @@ -92,6 +91,15 @@ *video_renderer_sink = new PunchoutVideoRendererSink( video_parameters.player, kVideoSinkRenderInterval); } + + void GetAudioRendererParams(int* max_cached_frames, + int* max_frames_per_append) const override { + SB_DCHECK(max_cached_frames); + SB_DCHECK(max_frames_per_append); + + *max_cached_frames = 256 * 1024; + *max_frames_per_append = 16384; + } }; } // namespace
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi index 9ff79a9..c73b15c 100644 --- a/src/starboard/linux/shared/starboard_platform.gypi +++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -36,14 +36,6 @@ '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc', '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc', '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h', '<(DEPTH)/starboard/shared/gcc/atomic_gcc_public.h', '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc', '<(DEPTH)/starboard/shared/iso/character_is_digit.cc', @@ -243,6 +235,7 @@ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink.h', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h', + '<(DEPTH)/starboard/shared/starboard/player/filter/audio_resampler_impl.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.h', '<(DEPTH)/starboard/shared/starboard/player/filter/cpu_video_frame.cc', @@ -355,9 +348,19 @@ 'starboard_platform_dependencies': [ '<(DEPTH)/starboard/common/common.gyp:common', '<(DEPTH)/starboard/linux/shared/starboard_base_symbolize.gyp:starboard_base_symbolize', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg.gyp:ffmpeg_dynamic_load', '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc', '<(DEPTH)/third_party/libevent/libevent.gyp:libevent', '<(DEPTH)/third_party/libvpx/libvpx.gyp:libvpx', ], }, + 'conditions': [ + ['gl_type != "none"', { + 'target_defaults': { + 'include_dirs': [ + '<(DEPTH)/third_party/khronos', + ], + } + }] + ], }
diff --git a/src/starboard/linux/shared/system_get_path.cc b/src/starboard/linux/shared/system_get_path.cc index 223a204..ee2d846 100644 --- a/src/starboard/linux/shared/system_get_path.cc +++ b/src/starboard/linux/shared/system_get_path.cc
@@ -138,6 +138,7 @@ return false; } break; + case kSbSystemPathCacheDirectory: if (!GetCacheDirectory(path, kPathSize)) { return false; @@ -149,6 +150,7 @@ return false; } break; + case kSbSystemPathDebugOutputDirectory: if (!SbSystemGetPath(kSbSystemPathTempDirectory, path, kPathSize)) { return false; @@ -156,34 +158,26 @@ if (SbStringConcat(path, "/log", kPathSize) >= kPathSize) { return false; } - SbDirectoryCreate(path); break; - case kSbSystemPathSourceDirectory: - if (!GetExecutableDirectory(path, kPathSize)) { - return false; - } - if (SbStringConcat(path, "/content/dir_source_root", kPathSize) >= - kPathSize) { - return false; - } - break; + case kSbSystemPathTempDirectory: if (!GetTemporaryDirectory(path, kPathSize)) { return false; } - SbDirectoryCreate(path); break; + case kSbSystemPathTestOutputDirectory: return SbSystemGetPath(kSbSystemPathDebugOutputDirectory, out_path, path_size); + case kSbSystemPathExecutableFile: return GetExecutablePath(out_path, path_size); - case kSbSystemPathFontConfigurationDirectory: - case kSbSystemPathFontDirectory: - return false; + case kSbSystemPathFontConfigurationDirectory: + case kSbSystemPathFontDirectory: + return false; default: SB_NOTIMPLEMENTED() << "SbSystemGetPath not implemented for "
diff --git a/src/starboard/linux/x64directfb/future/configuration_public.h b/src/starboard/linux/x64directfb/future/configuration_public.h index ec08c92..59042fe 100644 --- a/src/starboard/linux/x64directfb/future/configuration_public.h +++ b/src/starboard/linux/x64directfb/future/configuration_public.h
@@ -26,6 +26,12 @@ #undef SB_IS_PLAYER_COMPOSITED #undef SB_IS_PLAYER_PUNCHED_OUT +// Whether the current platform implements the on screen keyboard interface. +#define SB_HAS_ON_SCREEN_KEYBOARD 0 + +// Whether the current platform uses a media player that relies on a URL. +#define SB_HAS_PLAYER_WITH_URL 0 + // Include the X64DIRECTFB Linux configuration. #include "starboard/linux/x64directfb/configuration_public.h"
diff --git a/src/starboard/linux/x64directfb/system_get_property.cc b/src/starboard/linux/x64directfb/system_get_property.cc index 1d20f7f..c3d338f 100644 --- a/src/starboard/linux/x64directfb/system_get_property.cc +++ b/src/starboard/linux/x64directfb/system_get_property.cc
@@ -56,9 +56,11 @@ case kSbSystemPropertyPlatformName: return CopyStringAndTestIfSuccess(out_value, value_length, kPlatformName); +#if SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION case kSbSystemPropertyPlatformUuid: SB_NOTIMPLEMENTED(); return CopyStringAndTestIfSuccess(out_value, value_length, "N/A"); +#endif // SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION default: SB_DLOG(WARNING) << __FUNCTION__
diff --git a/src/starboard/linux/x64x11/clang/3.6/download_clang.sh b/src/starboard/linux/x64x11/clang/3.6/download_clang.sh new file mode 100755 index 0000000..b77102c --- /dev/null +++ b/src/starboard/linux/x64x11/clang/3.6/download_clang.sh
@@ -0,0 +1,48 @@ +#!/bin/bash +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script downloads and compiles clang version 3.6. + +set -e + +toolchain_name="clang" +version="3.6" +toolchain_folder="x86_64-linux-gnu-${toolchain_name}-${version}" +branch="release_36" + +binary_path="llvm/Release+Asserts/bin/clang++" +build_duration="about 20 minutes" + +cd $(dirname $0) +source ../../toolchain_paths.sh + +( + git clone --branch ${branch} https://git.llvm.org/git/llvm.git/ + cd llvm/tools/ + git clone --branch ${branch} https://git.llvm.org/git/clang.git/ + cd ../projects/ + git clone --branch ${branch} https://git.llvm.org/git/compiler-rt.git/ + cd ${toolchain_path} + + cd llvm + # Specify a bootstrap compiler that is known to be available. + CC=gcc CXX=g++ \ + ./configure --enable-optimized --disable-doxygen --prefix=${PWD}/bin + make -j"$(nproc)" + cd ${toolchain_path} + + ls -l ${toolchain_binary} + ${toolchain_binary} --version +) >${logfile} 2>&1
diff --git a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py index 13a88b6..82e3e7b 100644 --- a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py +++ b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
@@ -13,7 +13,12 @@ # limitations under the License. """Starboard Linux X64 X11 Clang 3.6 platform configuration.""" +import logging +import os +import subprocess + from starboard.linux.shared import gyp_configuration as shared_configuration +from starboard.tools import build class PlatformConfig(shared_configuration.LinuxConfiguration): @@ -21,15 +26,29 @@ def __init__(self, platform, asan_enabled_by_default=True): super(PlatformConfig, self).__init__( - platform, goma_supports_compiler=False) + platform, asan_enabled_by_default, goma_supports_compiler=False) + + script_path = os.path.dirname(os.path.realpath(__file__)) + # Run the script that ensures clang 3.6 is installed. + subprocess.call( + os.path.join(script_path, 'download_clang.sh'), cwd=script_path) + self.toolchain_dir = os.path.join(build.GetToolchainsDir(), + 'x86_64-linux-gnu-clang-3.6', 'llvm', + 'Release+Asserts') def GetEnvironmentVariables(self): - env_variables = { - 'CC': 'clang-3.6', - 'CXX': 'clang++-3.6', - } + env_variables = super(PlatformConfig, self).GetEnvironmentVariables() + toolchain_bin_dir = os.path.join(self.toolchain_dir, 'bin') + env_variables.update({ + 'CC': os.path.join(toolchain_bin_dir, 'clang'), + 'CXX': os.path.join(toolchain_bin_dir, 'clang++'), + }) return env_variables def CreatePlatformConfig(): - return PlatformConfig('linux-x64x11-clang-3-6') + try: + return PlatformConfig('linux-x64x11-clang-3-6') + except RuntimeError as e: + logging.critical(e) + return None
diff --git a/src/starboard/linux/x64x11/configuration_public.h b/src/starboard/linux/x64x11/configuration_public.h index b1cff4c..caa1787 100644 --- a/src/starboard/linux/x64x11/configuration_public.h +++ b/src/starboard/linux/x64x11/configuration_public.h
@@ -116,4 +116,12 @@ #undef SB_HAS_MICROPHONE #define SB_HAS_MICROPHONE 1 +#if SB_API_VERSION >= 8 +// Whether the current platform implements the on screen keyboard interface. +#define SB_HAS_ON_SCREEN_KEYBOARD 0 + +// Whether the current platform uses a media player that relies on a URL. +#define SB_HAS_PLAYER_WITH_URL 0 +#endif // SB_API_VERSION >= 8 + #endif // STARBOARD_LINUX_X64X11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/gcc/6.3/download_gcc.sh b/src/starboard/linux/x64x11/gcc/6.3/download_gcc.sh index e98ef29..08683fd 100755 --- a/src/starboard/linux/x64x11/gcc/6.3/download_gcc.sh +++ b/src/starboard/linux/x64x11/gcc/6.3/download_gcc.sh
@@ -24,6 +24,7 @@ binary_path="gcc/bin/g++" build_duration="about 40 minutes" +cd $(dirname $0) source ../../toolchain_paths.sh (
diff --git a/src/starboard/linux/x64x11/mock/gyp_configuration.py b/src/starboard/linux/x64x11/mock/gyp_configuration.py index a3d0402..f3d5ded 100644 --- a/src/starboard/linux/x64x11/mock/gyp_configuration.py +++ b/src/starboard/linux/x64x11/mock/gyp_configuration.py
@@ -24,9 +24,6 @@ def __init__(self, platform): super(PlatformConfig, self).__init__(platform) - goma_supports_compiler = True - self.host_compiler_environment = build.GetHostCompilerEnvironment( - clang.GetClangSpecification(), goma_supports_compiler) def GetBuildFormat(self): return 'ninja,qtcreator_ninja' @@ -42,6 +39,11 @@ return generator_variables def GetEnvironmentVariables(self): + if not hasattr(self, 'host_compiler_environment'): + goma_supports_compiler = True + self.host_compiler_environment = build.GetHostCompilerEnvironment( + clang.GetClangSpecification(), goma_supports_compiler) + env_variables = self.host_compiler_environment env_variables.update({ 'CC': self.host_compiler_environment['CC_host'],
diff --git a/src/starboard/linux/x64x11/mozjs/atomic_public.h b/src/starboard/linux/x64x11/mozjs/atomic_public.h new file mode 100644 index 0000000..693a374 --- /dev/null +++ b/src/starboard/linux/x64x11/mozjs/atomic_public.h
@@ -0,0 +1,20 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_LINUX_X64X11_MOZJS_ATOMIC_PUBLIC_H_ +#define STARBOARD_LINUX_X64X11_MOZJS_ATOMIC_PUBLIC_H_ + +#include "starboard/linux/shared/atomic_public.h" + +#endif // STARBOARD_LINUX_X64X11_MOZJS_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/mozjs/configuration_public.h b/src/starboard/linux/x64x11/mozjs/configuration_public.h new file mode 100644 index 0000000..1d91170 --- /dev/null +++ b/src/starboard/linux/x64x11/mozjs/configuration_public.h
@@ -0,0 +1,21 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_LINUX_X64X11_MOZJS_CONFIGURATION_PUBLIC_H_ +#define STARBOARD_LINUX_X64X11_MOZJS_CONFIGURATION_PUBLIC_H_ + +// Include the X64X11 Linux configuration. +#include "starboard/linux/x64x11/configuration_public.h" + +#endif // STARBOARD_LINUX_X64X11_MOZJS_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/mozjs/gyp_configuration.gypi b/src/starboard/linux/x64x11/mozjs/gyp_configuration.gypi new file mode 100644 index 0000000..fdfced2 --- /dev/null +++ b/src/starboard/linux/x64x11/mozjs/gyp_configuration.gypi
@@ -0,0 +1,41 @@ +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +{ + 'variables': { + 'javascript_engine': 'mozjs-45', + 'cobalt_enable_jit': 0, + }, + 'target_defaults': { + 'default_configuration': 'linux-x64x11-mozjs_debug', + 'configurations': { + 'linux-x64x11-mozjs_debug': { + 'inherit_from': ['debug_base'], + }, + 'linux-x64x11-mozjs_devel': { + 'inherit_from': ['devel_base'], + }, + 'linux-x64x11-mozjs_qa': { + 'inherit_from': ['qa_base'], + }, + 'linux-x64x11-mozjs_gold': { + 'inherit_from': ['gold_base'], + }, + }, # end of configurations + }, + + 'includes': [ + '../gyp_configuration.gypi', + ], +}
diff --git a/src/starboard/linux/x64x11/mozjs/gyp_configuration.py b/src/starboard/linux/x64x11/mozjs/gyp_configuration.py new file mode 100644 index 0000000..f1b50e9 --- /dev/null +++ b/src/starboard/linux/x64x11/mozjs/gyp_configuration.py
@@ -0,0 +1,20 @@ +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Starboard Linux X64 X11 future platform configuration.""" + +from starboard.linux.x64x11 import gyp_configuration as linux_configuration + + +def CreatePlatformConfig(): + return linux_configuration.LinuxX64X11Configuration('linux-x64x11-mozjs')
diff --git a/src/starboard/linux/x64x11/mozjs/starboard_platform.gyp b/src/starboard/linux/x64x11/mozjs/starboard_platform.gyp new file mode 100644 index 0000000..db7c468 --- /dev/null +++ b/src/starboard/linux/x64x11/mozjs/starboard_platform.gyp
@@ -0,0 +1,33 @@ +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{ + 'includes': [ + '../starboard_platform.gypi' + ], + 'targets': [ + { + 'target_name': 'starboard_platform', + 'type': 'static_library', + 'sources': ['<@(starboard_platform_sources)'], + 'defines': [ + # This must be defined when building Starboard, and must not when + # building Starboard client code. + 'STARBOARD_IMPLEMENTATION', + ], + 'dependencies': [ + '<@(starboard_platform_dependencies)', + ], + }, + ], +}
diff --git a/src/starboard/linux/x64x11/mozjs/thread_types_public.h b/src/starboard/linux/x64x11/mozjs/thread_types_public.h new file mode 100644 index 0000000..a46ec87 --- /dev/null +++ b/src/starboard/linux/x64x11/mozjs/thread_types_public.h
@@ -0,0 +1,20 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_LINUX_X64X11_MOZJS_THREAD_TYPES_PUBLIC_H_ +#define STARBOARD_LINUX_X64X11_MOZJS_THREAD_TYPES_PUBLIC_H_ + +#include "starboard/linux/shared/thread_types_public.h" + +#endif // STARBOARD_LINUX_X64X11_MOZJS_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/system_get_property.cc b/src/starboard/linux/x64x11/system_get_property.cc index 4337e27..8211f06 100644 --- a/src/starboard/linux/x64x11/system_get_property.cc +++ b/src/starboard/linux/x64x11/system_get_property.cc
@@ -36,6 +36,8 @@ return true; } +#if SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION + bool GetPlatformUuid(char* out_value, int value_length) { struct ifreq interface; struct ifconf config; @@ -78,6 +80,8 @@ return false; } +#endif // SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION + } // namespace bool SbSystemGetProperty(SbSystemPropertyId property_id, @@ -103,8 +107,10 @@ case kSbSystemPropertyPlatformName: return CopyStringAndTestIfSuccess(out_value, value_length, kPlatformName); +#if SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION case kSbSystemPropertyPlatformUuid: return GetPlatformUuid(out_value, value_length); +#endif // SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION default: SB_DLOG(WARNING) << __FUNCTION__
diff --git a/src/starboard/media.h b/src/starboard/media.h index 0e3178a..51f9c0d 100644 --- a/src/starboard/media.h +++ b/src/starboard/media.h
@@ -34,6 +34,9 @@ // Time represented in 90KHz ticks. typedef int64_t SbMediaTime; +#define SB_MEDIA_TIME_TO_SB_TIME(media) (media * 100 / 9) +#define SB_TIME_TO_SB_MEDIA_TIME(time) (time * 9 / 100) + // Types of media component streams. typedef enum SbMediaType { // Value used for audio streams. @@ -111,8 +114,11 @@ // Possible audio sample types. typedef enum SbMediaAudioSampleType { - kSbMediaAudioSampleTypeInt16, + kSbMediaAudioSampleTypeInt16Deprecated, kSbMediaAudioSampleTypeFloat32, +#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES) + kSbMediaAudioSampleTypeInt16 = kSbMediaAudioSampleTypeInt16Deprecated, +#endif // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES) } SbMediaAudioSampleType; // Possible audio frame storage types. @@ -471,12 +477,10 @@ // --- Constants ------------------------------------------------------------- +// TODO: remove kSbMediaTimeSecond. // One second in SbMediaTime (90KHz ticks). #define kSbMediaTimeSecond ((SbMediaTime)(90000)) -// Use the minimum value of int64_t to indicate an invalid media time. -#define kSbMediaTimeInvalid ((SbMediaTime)kSbInt64Min) - // --- Functions ------------------------------------------------------------- // Indicates whether this platform supports decoding |video_codec| and
diff --git a/src/starboard/mutex.h b/src/starboard/mutex.h index 2d0e20c..8063ed8 100644 --- a/src/starboard/mutex.h +++ b/src/starboard/mutex.h
@@ -102,6 +102,7 @@ ~Mutex() { SbMutexDestroy(&mutex_); } void Acquire() const { + debugPreAcquire(); SbMutexAcquire(&mutex_); debugSetAcquired(); } @@ -132,6 +133,11 @@ SB_DCHECK(currrent_thread_acquired_ == current_thread); currrent_thread_acquired_ = kSbThreadInvalid; } + void debugPreAcquire() const { + // Check that the mutex is not held by the current thread. + SbThread current_thread = SbThreadGetCurrent(); + SB_DCHECK(currrent_thread_acquired_ != current_thread); + } void debugSetAcquired() const { // Check that the thread has already not been held. SB_DCHECK(currrent_thread_acquired_ == kSbThreadInvalid); @@ -141,6 +147,7 @@ #else void debugInit() {} void debugSetReleased() const {} + void debugPreAcquire() const {} void debugSetAcquired() const {} #endif
diff --git a/src/starboard/nplb/BUILD.gn b/src/starboard/nplb/BUILD.gn index c6d2869..6729ec2 100644 --- a/src/starboard/nplb/BUILD.gn +++ b/src/starboard/nplb/BUILD.gn
@@ -153,7 +153,6 @@ "mutex_destroy_test.cc", "once_test.cc", "optional_test.cc", - "player_create_with_url_test.cc", "random_helpers.cc", "rwlock_test.cc", "semaphore_test.cc", @@ -267,7 +266,10 @@ # TODO: find a way to remove this dependence on Cobalt. if (sb_media_platform == "starboard") { sources += [ + "//starboard/testing/fake_graphics_context_provider.cc", + "//starboard/testing/fake_graphics_context_provider.h", "player_create_test.cc", + "player_create_with_url_test.cc", "player_output_mode_supported_test.cc", ] }
diff --git a/src/starboard/nplb/atomic_base_test.cc b/src/starboard/nplb/atomic_base_test.cc new file mode 100644 index 0000000..608311f --- /dev/null +++ b/src/starboard/nplb/atomic_base_test.cc
@@ -0,0 +1,524 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <algorithm> +#include <numeric> +#include <vector> + +#include "starboard/atomic.h" +#include "starboard/configuration.h" +#include "starboard/mutex.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define NUM_THREADS 4 + +namespace starboard { +namespace nplb { +namespace { + +// TestThread that is a bare bones class wrapper around Starboard +// thread. Subclasses must override Run(). +class TestThread { + public: + TestThread() : thread_(kSbThreadInvalid) {} + virtual ~TestThread() {} + + // Subclasses should override the Run method. + virtual void Run() = 0; + + // Calls SbThreadCreate() with default parameters. + void Start() { + SbThreadEntryPoint entry_point = ThreadEntryPoint; + + thread_ = SbThreadCreate(0, // default stack_size. + kSbThreadNoPriority, // default priority. + kSbThreadNoAffinity, // default affinity. + true, // joinable. + "TestThread", entry_point, this); + + if (kSbThreadInvalid == thread_) { + ADD_FAILURE_AT(__FILE__, __LINE__) << "Invalid thread."; + } + return; + } + + void Join() { + if (!SbThreadJoin(thread_, NULL)) { + ADD_FAILURE_AT(__FILE__, __LINE__) << "Could not join thread."; + } + } + + private: + static void* ThreadEntryPoint(void* ptr) { + TestThread* this_ptr = static_cast<TestThread*>(ptr); + this_ptr->Run(); + return NULL; + } + + SbThread thread_; + + SB_DISALLOW_COPY_AND_ASSIGN(TestThread); +}; + +/////////////////////////////////////////////////////////////////////////////// +// Boilerplate for test setup. +/////////////////////////////////////////////////////////////////////////////// + +// Defines a typelist for all atomic types. +typedef ::testing::Types<atomic_int32_t, + atomic_int64_t, + atomic_float, + atomic_double, + atomic_bool, + atomic_pointer<int*> > + AllAtomicTypes; + +// Defines a typelist for just atomic number types. +typedef ::testing:: + Types<atomic_int32_t, atomic_int64_t, atomic_float, atomic_double> + AtomicNumberTypes; + +// Defines a typelist for just atomic number types. +typedef ::testing::Types<atomic_int32_t, atomic_int64_t> AtomicIntegralTypes; + +// Defines test type that will be instantiated using each type in +// AllAtomicTypes type list. +template <typename T> +class AtomicBaseTest : public ::testing::Test {}; +TYPED_TEST_CASE(AtomicBaseTest, AllAtomicTypes); // Registration. + +// Defines test type that will be instantiated using each type in +// AtomicNumberTypes type list. +template <typename T> +class AtomicNumberTest : public ::testing::Test {}; +TYPED_TEST_CASE(AtomicNumberTest, AtomicNumberTypes); // Registration. + +template <typename T> +class AtomicIntegralTest : public ::testing::Test {}; +TYPED_TEST_CASE(AtomicIntegralTest, AtomicIntegralTypes); // Registration. + +/////////////////////////////////////////////////////////////////////////////// +// Singlethreaded tests. +/////////////////////////////////////////////////////////////////////////////// + +// Tests default constructor and single-argument constructor. +TYPED_TEST(AtomicBaseTest, Constructors) { + typedef TypeParam AtomicT; + typedef typename AtomicT::ValueType T; + + const T zero(0); + const T one = zero + 1; // Allows AtomicPointer<T*>. + + AtomicT atomic_default; + + // Tests that default value is zero. + ASSERT_EQ(atomic_default.load(), zero); + AtomicT atomic_val(one); + ASSERT_EQ(one, atomic_val.load()); +} + +// Tests load() and exchange(). +TYPED_TEST(AtomicBaseTest, Load_Exchange_SingleThread) { + typedef TypeParam AtomicT; + typedef typename AtomicT::ValueType T; + + const T zero(0); + const T one = zero + 1; // Allows AtomicPointer<T*>. + + AtomicT atomic; + ASSERT_EQ(atomic.load(), zero); // Default is 0. + ASSERT_EQ(zero, atomic.exchange(one)); // Old value was 0. + + // Tests that AtomicType has const get function. + const AtomicT& const_atomic = atomic; + ASSERT_EQ(one, const_atomic.load()); +} + +// Tests compare_exchange_strong(). +TYPED_TEST(AtomicNumberTest, CompareExchangeStrong_SingleThread) { + typedef TypeParam AtomicT; + typedef typename AtomicT::ValueType T; + + const T zero(0); + const T one = zero + 1; // Allows AtomicPointer<T*>. + + AtomicT atomic; + ASSERT_EQ(atomic.load(), zero); // Default is 0. + T expected_value = zero; + // Should succeed. + ASSERT_TRUE(atomic.compare_exchange_strong(&expected_value, + one)); // New value. + + ASSERT_EQ(zero, expected_value); + ASSERT_EQ(one, atomic.load()); // Expect that value was set. + + expected_value = zero; + // Asserts that when the expected and actual value is mismatched that the + // compare_exchange_strong() fails. + ASSERT_FALSE(atomic.compare_exchange_strong(&expected_value, // Mismatched. + zero)); // New value. + + // Failed and this means that expected_value should be set to what the + // internal value was. In this case, one. + ASSERT_EQ(expected_value, one); + ASSERT_EQ(one, atomic.load()); + + ASSERT_TRUE(atomic.compare_exchange_strong(&expected_value, // Matches. + zero)); + ASSERT_EQ(expected_value, one); +} + +// Tests atomic fetching and adding. +TYPED_TEST(AtomicNumberTest, FetchAdd_SingleThread) { + typedef TypeParam AtomicT; + typedef typename AtomicT::ValueType T; + + const T zero(0); + const T one = zero + 1; // Allows atomic_pointer<T*>. + const T two = zero + 2; + + AtomicT atomic; + ASSERT_EQ(atomic.load(), zero); // Default is 0. + ASSERT_EQ(zero, atomic.fetch_add(one)); // Prev value was 0. + ASSERT_EQ(one, atomic.load()); // Now value is this. + ASSERT_EQ(one, atomic.fetch_add(one)); // Prev value was 1. + ASSERT_EQ(two, atomic.exchange(one)); // Old value was 2. +} + +// Tests atomic fetching and subtracting. +TYPED_TEST(AtomicNumberTest, FetchSub_SingleThread) { + typedef TypeParam AtomicT; + typedef typename AtomicT::ValueType T; + + const T zero(0); + const T one = zero + 1; // Allows AtomicPointer<T*>. + const T two = zero + 2; + const T neg_two(zero - 2); + + AtomicT atomic; + ASSERT_EQ(atomic.load(), zero); // Default is 0. + atomic.exchange(two); + ASSERT_EQ(two, atomic.fetch_sub(one)); // Prev value was 2. + ASSERT_EQ(one, atomic.load()); // New value. + ASSERT_EQ(one, atomic.fetch_sub(one)); // Prev value was one. + ASSERT_EQ(zero, atomic.load()); // New 0. + ASSERT_EQ(zero, atomic.fetch_sub(two)); + ASSERT_EQ(neg_two, atomic.load()); // 0-2 = -2 +} + +TYPED_TEST(AtomicIntegralTest, IncrementAndDecrement_SingleThread) { + typedef TypeParam AtomicT; + typedef typename AtomicT::ValueType T; + + const T zero(0); + const T one = zero + 1; // Allows AtomicPointer<T*>. + + AtomicT atomic; + ASSERT_EQ(atomic.load(), zero); // Default is 0. + ASSERT_EQ(zero, atomic.increment()); // Tests for post-increment operation. + ASSERT_EQ(one, atomic.decrement()); // Tests for post-decrement operation. +} + +/////////////////////////////////////////////////////////////////////////////// +// Multithreaded tests. +/////////////////////////////////////////////////////////////////////////////// + +// A thread that will execute compare_exhange_strong() and write out a result +// to a shared output. +template <typename AtomicT> +class CompareExchangeThread : public TestThread { + public: + typedef typename AtomicT::ValueType T; + CompareExchangeThread(int start_num, + int end_num, + AtomicT* atomic_value, + std::vector<T>* output, + starboard::Mutex* output_mutex) + : start_num_(start_num), + end_num_(end_num), + atomic_value_(atomic_value), + output_(output), + output_mutex_(output_mutex) {} + + virtual void Run() { + std::vector<T> output_buffer; + output_buffer.reserve(end_num_ - start_num_); + for (int i = start_num_; i < end_num_; ++i) { + T new_value = T(i); + while (true) { + if (std::rand() % 3 == 0) { + // 1 in 3 chance of yielding. + // Attempt to cause more contention by giving other threads a chance + // to run. + SbThreadYield(); + } + + const T prev_value = atomic_value_->load(); + T expected_value = prev_value; + const bool success = + atomic_value_->compare_exchange_strong(&expected_value, new_value); + if (success) { + output_buffer.push_back(prev_value); + break; + } + } + } + + // Lock the output to append this output buffer. + starboard::ScopedLock lock(*output_mutex_); + output_->insert(output_->end(), output_buffer.begin(), output_buffer.end()); + } + + private: + const int start_num_; + const int end_num_; + AtomicT* const atomic_value_; + std::vector<T>* const output_; + starboard::Mutex* const output_mutex_; +}; + +// Tests Atomic<T>::compare_exchange_strong(). Each thread has a unique +// sequential range [0,1,2,3 ... ), [5,6,8, ...) that it will generate. +// The numbers are sequentially written to the shared Atomic type and then +// exposed to other threads: +// +// Generates [0,1,2,...,n/2) +// +------+ Thread A <--------+ (Write Exchanged Value) +// | | +// | compare_exchange() +---> exchanged? ---+ +// +----> +------------+ +----+ v +// | AtomicType | Output vector +// +----> +------------+ +----+ ^ +// | compare_exchange() +---> exchanged? ---+ +// | | +// +------+ Thread B <--------+ +// Generates [n/2,n/2+1,...,n) +// +// By repeatedly calling atomic<T>::compare_exchange_strong() by each of the +// threads, each will see the previous value of the shared variable when their +// exchange (atomic swap) operation is successful. If all of the swapped out +// values are recombined then it will form the original generated sequence from +// all threads. +// +// TEST PHASE +// sort( output vector ) AND TEST THAT +// output vector Contains [0,1,2,...,n) +// +// The test passes when the output array is tested that it contains every +// expected generated number from all threads. If compare_exchange_strong() is +// written incorrectly for an atomic type then the output array will have +// duplicates or otherwise not be equal to the expected natural number set. +TYPED_TEST(AtomicNumberTest, Test_CompareExchange_MultiThreaded) { + typedef TypeParam AtomicT; + typedef typename AtomicT::ValueType T; + + static const int kNumElements = 1000; + + AtomicT atomic_value(T(-1)); + std::vector<TestThread*> threads; + std::vector<T> output_values; + starboard::Mutex output_mutex; + + for (int i = 0; i < NUM_THREADS; ++i) { + const int start_num = (kNumElements * i) / NUM_THREADS; + const int end_num = (kNumElements * (i + 1)) / NUM_THREADS; + threads.push_back(new CompareExchangeThread<AtomicT>( + start_num, // defines the number range to generate. + end_num, &atomic_value, &output_values, &output_mutex)); + } + + // These threads will generate unique numbers in their range and then + // write them to the output array. + for (int i = 0; i < NUM_THREADS; ++i) { + threads[i]->Start(); + } + + for (int i = 0; i < NUM_THREADS; ++i) { + threads[i]->Join(); + } + // Cleanup threads. + for (int i = 0; i < NUM_THREADS; ++i) { + delete threads[i]; + } + threads.clear(); + + // Final value needs to be written out. The last thread to join doesn't + // know it's the last and therefore the final value in the shared + // has not be pushed to the output array. + output_values.push_back(atomic_value.load()); + std::sort(output_values.begin(), output_values.end()); + + // We expect the -1 value because it was the explicit initial value of the + // shared atomic. + ASSERT_EQ(T(-1), output_values[0]); + ASSERT_EQ(T(0), output_values[1]); + output_values.erase(output_values.begin()); // Chop off the -1 at the front. + + // Finally, assert that the output array is equal to the natural numbers + // after it has been sorted. + ASSERT_EQ(output_values.size(), kNumElements); + // All of the elements should be equal too. + for (int i = 0; i < output_values.size(); ++i) { + ASSERT_EQ(output_values[i], T(i)); + } +} + +// A thread that will invoke increment() and decrement() and equal number +// of times to atomic_value. The value after this is done should be equal to +// 0. +template <typename AtomicT> +class IncrementAndDecrementThread : public TestThread { + public: + typedef typename AtomicT::ValueType T; + IncrementAndDecrementThread(size_t half_number_of_operations, + AtomicT* atomic_value) + : atomic_value_(atomic_value) { + for (size_t i = 0; i < half_number_of_operations; ++i) { + operation_sequence_.push_back(true); + } + for (size_t i = 0; i < half_number_of_operations; ++i) { + operation_sequence_.push_back(false); + } + std::random_shuffle(operation_sequence_.begin(), operation_sequence_.end()); + } + + virtual void Run() { + for (size_t i = 0; i < operation_sequence_.size(); ++i) { + if (std::rand() % 3 == 0) { + // 1 in 3 chance of yielding. + // Attempt to cause more contention by giving other threads a chance + // to run. + SbThreadYield(); + } + T prev_value = 0; + if (operation_sequence_[i]) { + prev_value = atomic_value_->increment(); + } else { + prev_value = atomic_value_->decrement(); + } + } + } + + private: + // Used purely for true/false values. Note that we don't + // use std::vector<bool> because some platforms won't support + // swapping elements of std::vector<bool>, which is required for + // std::random_shuffle(). + std::vector<uint8_t> operation_sequence_; + AtomicT* const atomic_value_; +}; + +TYPED_TEST(AtomicIntegralTest, Test_IncrementAndDecrement_MultiThreaded) { + typedef TypeParam AtomicT; + typedef typename AtomicT::ValueType T; + + static const int kNumOperations = 10000; + + AtomicT atomic_value(T(0)); + std::vector<TestThread*> threads; + + for (int i = 0; i < NUM_THREADS; ++i) { + threads.push_back(new IncrementAndDecrementThread<AtomicT>(kNumOperations, + &atomic_value)); + } + + for (int i = 0; i < NUM_THREADS; ++i) { + threads[i]->Start(); + } + + for (int i = 0; i < NUM_THREADS; ++i) { + threads[i]->Join(); + } + // Cleanup threads. + for (int i = 0; i < NUM_THREADS; ++i) { + delete threads[i]; + } + threads.clear(); + + // After an equal number of decrements and increments, the final value should + // be 0. + ASSERT_EQ(0, atomic_value.load()); +} + +template <typename AtomicT> +class FetchAddSubThread : public TestThread { + public: + typedef typename AtomicT::ValueType T; + FetchAddSubThread(const int32_t start_value, + const int32_t end_value, + AtomicT* atomic_value) + : start_value_(start_value), + end_value_(end_value), + atomic_value_(atomic_value) {} + + virtual void Run() { + for (int32_t i = start_value_; i < end_value_; ++i) { + if (std::rand() % 3 == 0) { + // 1 in 3 chance of yielding. + // Attempt to cause more contention by giving other threads a chance + // to run.s + SbThreadYield(); + } + + if (std::rand() % 2 == 0) { + atomic_value_->fetch_add(i); + } else { + atomic_value_->fetch_sub(-i); + } + } + } + + private: + int32_t start_value_; + int32_t end_value_; + AtomicT* const atomic_value_; +}; + +TYPED_TEST(AtomicIntegralTest, Test_FetchAdd_MultiThreaded) { + typedef TypeParam AtomicT; + typedef typename AtomicT::ValueType T; + + static const int kNumOperations = 10000; + + AtomicT atomic_value(T(0)); + std::vector<TestThread*> threads; + + // First value is inclusive, second is exclusive. + threads.push_back( + new FetchAddSubThread<AtomicT>(-kNumOperations, 0, &atomic_value)); + + threads.push_back( + new FetchAddSubThread<AtomicT>(1, kNumOperations + 1, &atomic_value)); + + for (int i = 0; i < threads.size(); ++i) { + threads[i]->Start(); + } + + for (int i = 0; i < threads.size(); ++i) { + threads[i]->Join(); + } + // Cleanup threads. + for (int i = 0; i < threads.size(); ++i) { + delete threads[i]; + } + threads.clear(); + + // After an equal number of decrements and increments, the final value should + // be 0. + ASSERT_EQ(0, atomic_value.load()); +} + +} // namespace +} // namespace nplb +} // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_create_test.cc b/src/starboard/nplb/audio_sink_create_test.cc index 590ada8..7377caf 100644 --- a/src/starboard/nplb/audio_sink_create_test.cc +++ b/src/starboard/nplb/audio_sink_create_test.cc
@@ -57,8 +57,9 @@ TEST(SbAudioSinkCreateTest, SunnyDayAllCombinations) { std::vector<SbMediaAudioSampleType> sample_types; - if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) { - sample_types.push_back(kSbMediaAudioSampleTypeInt16); + if (SbAudioSinkIsAudioSampleTypeSupported( + kSbMediaAudioSampleTypeInt16Deprecated)) { + sample_types.push_back(kSbMediaAudioSampleTypeInt16Deprecated); } if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) { sample_types.push_back(kSbMediaAudioSampleTypeFloat32); @@ -142,13 +143,14 @@ TEST(SbAudioSinkCreateTest, RainyDayInvalidSampleType) { SbMediaAudioSampleType invalid_sample_type; - if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) { + if (SbAudioSinkIsAudioSampleTypeSupported( + kSbMediaAudioSampleTypeInt16Deprecated)) { if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) { return; } invalid_sample_type = kSbMediaAudioSampleTypeFloat32; } else { - invalid_sample_type = kSbMediaAudioSampleTypeInt16; + invalid_sample_type = kSbMediaAudioSampleTypeInt16Deprecated; } AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),
diff --git a/src/starboard/nplb/audio_sink_helpers.cc b/src/starboard/nplb/audio_sink_helpers.cc index 51aa218..ab8cb5b 100644 --- a/src/starboard/nplb/audio_sink_helpers.cc +++ b/src/starboard/nplb/audio_sink_helpers.cc
@@ -20,8 +20,9 @@ namespace { SbMediaAudioSampleType GetAnySupportedSampleType() { - if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) { - return kSbMediaAudioSampleTypeInt16; + if (SbAudioSinkIsAudioSampleTypeSupported( + kSbMediaAudioSampleTypeInt16Deprecated)) { + return kSbMediaAudioSampleTypeInt16Deprecated; } return kSbMediaAudioSampleTypeFloat32; }
diff --git a/src/starboard/nplb/audio_sink_helpers.h b/src/starboard/nplb/audio_sink_helpers.h index 068634f..1acaebe 100644 --- a/src/starboard/nplb/audio_sink_helpers.h +++ b/src/starboard/nplb/audio_sink_helpers.h
@@ -45,7 +45,7 @@ int channels() const { return channels_; } int bytes_per_frame() const { - return sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4; + return sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated ? 2 : 4; } static int frames_per_channel() { return kFramesPerChannel; } void** frame_buffers() {
diff --git a/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc b/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc index b1adfbf..3a86303 100644 --- a/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc +++ b/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc
@@ -19,8 +19,8 @@ namespace nplb { TEST(SbAudioSinkIsAudioSampleTypeSupportedTest, SunnyDay) { - bool int16_supported = - SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16); + bool int16_supported = SbAudioSinkIsAudioSampleTypeSupported( + kSbMediaAudioSampleTypeInt16Deprecated); bool float32_supported = SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32); // A platform must support at least one of the sample types.
diff --git a/src/starboard/nplb/blitter_pixel_tests/blitter_pixel_tests.gyp b/src/starboard/nplb/blitter_pixel_tests/blitter_pixel_tests.gyp index 168d98c..cb30701 100644 --- a/src/starboard/nplb/blitter_pixel_tests/blitter_pixel_tests.gyp +++ b/src/starboard/nplb/blitter_pixel_tests/blitter_pixel_tests.gyp
@@ -30,19 +30,19 @@ '<(DEPTH)/starboard/starboard.gyp:starboard', # libpng is needed for the Blitter API pixel tests. '<(DEPTH)/third_party/libpng/libpng.gyp:libpng', + 'nplb_blitter_pixel_tests_copy_test_data', ], - 'actions': [ - { - 'action_name': 'nplb_copy_blitter_pixel_test_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/starboard/nplb/blitter_pixel_tests/data', - ], - 'output_dir': 'starboard/nplb/blitter_pixel_tests', - }, - 'includes': ['../../build/copy_test_data.gypi'], - } - ], + }, + { + 'target_name': 'nplb_blitter_pixel_tests_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/starboard/nplb/blitter_pixel_tests/data', + ], + 'content_test_output_subdir': 'starboard/nplb/blitter_pixel_tests', + }, + 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], }, { 'target_name': 'nplb_blitter_pixel_tests_deploy',
diff --git a/src/starboard/nplb/blitter_pixel_tests/fixture.cc b/src/starboard/nplb/blitter_pixel_tests/fixture.cc index a149d13..751caee 100644 --- a/src/starboard/nplb/blitter_pixel_tests/fixture.cc +++ b/src/starboard/nplb/blitter_pixel_tests/fixture.cc
@@ -83,13 +83,13 @@ // The input directory in which all the expected results PNG test files can // be found. std::string GetTestInputDirectory() { - char test_output_path[kPathSize]; - EXPECT_TRUE(SbSystemGetPath(kSbSystemPathSourceDirectory, test_output_path, + char content_path[kPathSize]; + EXPECT_TRUE(SbSystemGetPath(kSbSystemPathContentDirectory, content_path, kPathSize)); std::string directory_path = - std::string(test_output_path) + SB_FILE_SEP_CHAR + "starboard" + - SB_FILE_SEP_CHAR + "nplb" + SB_FILE_SEP_CHAR + "blitter_pixel_tests" + - SB_FILE_SEP_CHAR + "data"; + std::string(content_path) + SB_FILE_SEP_CHAR + "test" + + SB_FILE_SEP_CHAR + "starboard" + SB_FILE_SEP_CHAR + "nplb" + + SB_FILE_SEP_CHAR + "blitter_pixel_tests" + SB_FILE_SEP_CHAR + "data"; SB_CHECK(SbDirectoryCanOpen(directory_path.c_str())); return directory_path;
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp index d48b409..80c0e30 100644 --- a/src/starboard/nplb/nplb.gyp +++ b/src/starboard/nplb/nplb.gyp
@@ -23,10 +23,9 @@ 'type': '<(gtest_target_type)', 'sources': [ '<(DEPTH)/starboard/common/test_main.cc', - '<(DEPTH)/starboard/testing/fake_graphics_context_provider.cc', - '<(DEPTH)/starboard/testing/fake_graphics_context_provider.h', 'accessibility_get_setting_test.cc', 'align_test.cc', + 'atomic_base_test.cc', 'atomic_test.cc', 'audio_sink_create_test.cc', 'audio_sink_destroy_test.cc', @@ -149,7 +148,6 @@ 'mutex_destroy_test.cc', 'once_test.cc', 'optional_test.cc', - 'player_create_with_url_test.cc', 'random_helpers.cc', 'rwlock_test.cc', 'semaphore_test.cc', @@ -244,6 +242,7 @@ 'thread_local_value_test.cc', 'thread_set_name_test.cc', 'thread_sleep_test.cc', + 'thread_test.cc', 'thread_yield_test.cc', 'time_get_monotonic_now_test.cc', 'time_get_now_test.cc', @@ -267,14 +266,17 @@ ], 'dependencies': [ '<@(cobalt_platform_dependencies)', + '<(DEPTH)/starboard/starboard.gyp:starboard', '<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gtest.gyp:gtest', - '<(DEPTH)/starboard/starboard.gyp:starboard', ], 'conditions': [ ['sb_media_platform=="starboard"', { 'sources': [ + '<(DEPTH)/starboard/testing/fake_graphics_context_provider.cc', + '<(DEPTH)/starboard/testing/fake_graphics_context_provider.h', 'player_create_test.cc', + 'player_create_with_url_test.cc', 'player_output_mode_supported_test.cc', ], }],
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc index 971950d..a32327c 100644 --- a/src/starboard/nplb/player_create_test.cc +++ b/src/starboard/nplb/player_create_test.cc
@@ -16,7 +16,6 @@ #include "starboard/decode_target.h" #include "starboard/player.h" #include "starboard/testing/fake_graphics_context_provider.h" -#include "starboard/window.h" #include "testing/gtest/include/gtest/gtest.h" namespace starboard { @@ -31,16 +30,6 @@ class SbPlayerTest : public ::testing::Test { protected: - void SetUp() { - SbWindowOptions window_options; - SbWindowSetDefaultOptions(&window_options); - - window_ = SbWindowCreate(&window_options); - EXPECT_TRUE(SbWindowIsValid(window_)); - } - void TearDown() { SbWindowDestroy(window_); } - - SbWindow window_; FakeGraphicsContextProvider fake_graphics_context_provider_; }; @@ -70,9 +59,13 @@ } SbPlayer player = SbPlayerCreate( - window_, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac, - SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, &audio_header, NULL, NULL, - NULL, NULL, output_mode, + fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264, + kSbMediaAudioCodecAac, SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, + &audio_header, NULL, NULL, NULL, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + NULL, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + NULL, output_mode, fake_graphics_context_provider_.decoder_target_provider()); EXPECT_TRUE(SbPlayerIsValid(player)); @@ -84,7 +77,7 @@ } } -#if SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION +#if SB_HAS(AUDIOLESS_VIDEO) TEST_F(SbPlayerTest, Audioless) { SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264; SbDrmSystem kDrmSystem = kSbDrmSystemInvalid; @@ -99,8 +92,12 @@ } SbPlayer player = SbPlayerCreate( - window_, kSbMediaVideoCodecH264, kSbMediaAudioCodecNone, - SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, NULL, NULL, NULL, NULL, + fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264, + kSbMediaAudioCodecNone, SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, + NULL, NULL, NULL, NULL, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + NULL, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) NULL, output_mode, fake_graphics_context_provider_.decoder_target_provider()); EXPECT_TRUE(SbPlayerIsValid(player)); @@ -112,7 +109,7 @@ SbPlayerDestroy(player); } } -#endif // SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION +#endif // SB_HAS(AUDIOLESS_VIDEO) #endif // SB_HAS(PLAYER_WITH_URL)
diff --git a/src/starboard/nplb/player_create_with_url_test.cc b/src/starboard/nplb/player_create_with_url_test.cc index 1fd48f5..18632ea 100644 --- a/src/starboard/nplb/player_create_with_url_test.cc +++ b/src/starboard/nplb/player_create_with_url_test.cc
@@ -23,6 +23,7 @@ namespace { #if SB_HAS(PLAYER_WITH_URL) + TEST(SbPlayerUrlTest, SunnyDay) { SbWindowOptions window_options; SbWindowSetDefaultOptions(&window_options); @@ -42,7 +43,7 @@ char url[] = "about:blank"; SB_DLOG(ERROR) << "Creating player"; SbPlayer player = SbPlayerCreateWithUrl(url, window, SB_PLAYER_NO_DURATION, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); EXPECT_TRUE(SbPlayerIsValid(player));
diff --git a/src/starboard/nplb/socket_resolve_test.cc b/src/starboard/nplb/socket_resolve_test.cc index 3c2b9bd..d77c775 100644 --- a/src/starboard/nplb/socket_resolve_test.cc +++ b/src/starboard/nplb/socket_resolve_test.cc
@@ -37,7 +37,7 @@ const void* kNull = NULL; // A random host name to use to test DNS resolution. -const char kTestHostName[] = "www.yahoo.com"; +const char kTestHostName[] = "www.example.com"; const char kLocalhost[] = "localhost"; #define LOG_ADDRESS(i) "In returned address #" << (i)
diff --git a/src/starboard/nplb/system_get_path_test.cc b/src/starboard/nplb/system_get_path_test.cc index 243a496..b205993 100644 --- a/src/starboard/nplb/system_get_path_test.cc +++ b/src/starboard/nplb/system_get_path_test.cc
@@ -14,10 +14,12 @@ #include <string.h> +#include "starboard/file.h" #include "starboard/memory.h" #include "starboard/nplb/file_helpers.h" #include "starboard/string.h" #include "starboard/system.h" +#include "starboard/time.h" #include "testing/gtest/include/gtest/gtest.h" namespace starboard { @@ -76,7 +78,6 @@ TEST(SbSystemGetPathTest, DoesNotBlowUpForDefinedIds) { BasicTest(kSbSystemPathDebugOutputDirectory, false, false, __LINE__); - BasicTest(kSbSystemPathSourceDirectory, false, false, __LINE__); BasicTest(kSbSystemPathTempDirectory, false, false, __LINE__); BasicTest(kSbSystemPathTestOutputDirectory, false, false, __LINE__); BasicTest(kSbSystemPathCacheDirectory, false, false, __LINE__); @@ -158,6 +159,26 @@ } } +TEST(SbSystemGetPath, ExecutableFileCreationTimeIsSound) { + // Verify that the creation time of the current executable file is not + // greater than the current time. + char path[kPathSize]; + SbMemorySet(path, 0xCD, kPathSize); + bool result = SbSystemGetPath(kSbSystemPathExecutableFile, path, kPathSize); + ASSERT_TRUE(result); + + EXPECT_NE('\xCD', path[0]); + int len = static_cast<int>(SbStringGetLength(path)); + EXPECT_GT(len, 0); + + SbFileInfo executable_file_info; + result = SbFileGetPathInfo(path, &executable_file_info); + ASSERT_TRUE(result); + + SbTime now = SbTimeGetNow(); + EXPECT_GT(now, executable_file_info.creation_time); +} + } // namespace } // namespace nplb } // namespace starboard
diff --git a/src/starboard/nplb/system_get_property_test.cc b/src/starboard/nplb/system_get_property_test.cc index d11c63b..e0fbf2e 100644 --- a/src/starboard/nplb/system_get_property_test.cc +++ b/src/starboard/nplb/system_get_property_test.cc
@@ -65,7 +65,6 @@ TEST(SbSystemGetPropertyTest, ReturnsRequired) { BasicTest(kSbSystemPropertyFriendlyName, true, true, __LINE__); BasicTest(kSbSystemPropertyPlatformName, true, true, __LINE__); - BasicTest(kSbSystemPropertyPlatformUuid, true, true, __LINE__); BasicTest(kSbSystemPropertyChipsetModelNumber, false, true, __LINE__); BasicTest(kSbSystemPropertyFirmwareVersion, false, true, __LINE__); @@ -121,7 +120,6 @@ kSbSystemPropertyModelYear, kSbSystemPropertyNetworkOperatorName, kSbSystemPropertyPlatformName, - kSbSystemPropertyPlatformUuid, }; for (SbSystemPropertyId val : enum_values) {
diff --git a/src/starboard/nplb/thread_test.cc b/src/starboard/nplb/thread_test.cc new file mode 100644 index 0000000..abcfbc2 --- /dev/null +++ b/src/starboard/nplb/thread_test.cc
@@ -0,0 +1,59 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <functional> + +#include "starboard/atomic.h" +#include "starboard/common/semaphore.h" +#include "starboard/common/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { +namespace { + +class TestRunThread : public Thread { + public: + TestRunThread() : Thread("TestThread"), finished_(false) {} + + void Run() override { + while (!WaitForJoin(kSbTimeMillisecond)) { + } + finished_.store(true); + } + atomic_bool finished_; +}; + +// Tests the expectation that a thread subclass will have the expected +// behavior of running the Run() function will execute only after +// Start(), and will exit on Join(). +TEST(Thread, TestRunThread) { + TestRunThread test_thread; + SbThreadSleep(100); + // Expect that test thread not in a run state when initialized. + EXPECT_FALSE(test_thread.finished_.load()); + EXPECT_FALSE(test_thread.join_called()); + + test_thread.Start(); + EXPECT_FALSE(test_thread.finished_.load()); + EXPECT_FALSE(test_thread.join_called()); + + test_thread.Join(); + EXPECT_TRUE(test_thread.finished_.load()); + EXPECT_TRUE(test_thread.join_called()); +} + +} // namespace. +} // namespace nplb. +} // namespace starboard.
diff --git a/src/starboard/player.h b/src/starboard/player.h index 6de176c..e0d2bba 100644 --- a/src/starboard/player.h +++ b/src/starboard/player.h
@@ -70,21 +70,26 @@ // The player has been destroyed, and will send no more callbacks. kSbPlayerStateDestroyed, - -#if SB_HAS(PLAYER_WITH_URL) - // The following error codes are used by the URL player to report detailed - // errors. They are not required in non-URL player mode. - kSbPlayerWithUrlStateNetworkError, - kSbPlayerWithUrlStateDecodeError, - kSbPlayerWithUrlStateSrcNotSupportedError, -#else // SB_HAS(PLAYER_WITH_URL) +#if !SB_HAS(PLAYER_ERROR_MESSAGE) // The player encountered an error. It expects an SbPlayerDestroy() call // to tear down the player. Calls to other functions may be ignored and // callbacks may not be triggered. kSbPlayerStateError, -#endif // SB_HAS(PLAYER_WITH_URL) +#endif // !SB_HAS(PLAYER_ERROR_MESSAGE) } SbPlayerState; +#if SB_HAS(PLAYER_ERROR_MESSAGE) +typedef enum SbPlayerError { + kSbPlayerErrorDecode, +#if SB_HAS(PLAYER_WITH_URL) + // The following error codes are used by the URL player to report detailed + // errors. They are not required in non-URL player mode. + kSbPlayerErrorNetwork, + kSbPlayerErrorSrcNotSupported, +#endif // SB_HAS(PLAYER_WITH_URL) +} SbPlayerError; +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + typedef enum SbPlayerOutputMode { // Requests for SbPlayer to produce an OpenGL texture that the client must // draw every frame with its graphics rendering. It may be that we get a @@ -195,6 +200,18 @@ SbPlayerState state, int ticket); +#if SB_HAS(PLAYER_ERROR_MESSAGE) +// Callback for player errors, that may set a |message|. +// |error|: indicates the error code. +// |message|: provides specific informative diagnostic message about the error +// condition encountered. It is ok for the message to be an empty +// string or NULL if no information is available. +typedef void (*SbPlayerErrorFunc)(SbPlayer player, + void* context, + SbPlayerError error, + const char* message); +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + // Callback to free the given sample buffer data. When more than one buffer // are sent in SbPlayerWriteSample(), the implementation only has to call this // callback with |sample_buffer| points to the the first buffer. @@ -259,6 +276,9 @@ SbPlayerStatusFunc player_status_func, SbPlayerEncryptedMediaInitDataEncounteredCB encrypted_media_init_data_encountered_cb, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + SbPlayerErrorFunc player_error_func, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) void* context); // Sets the DRM system of a running URL-based SbPlayer created with @@ -301,10 +321,10 @@ // |audio_codec|: The audio codec used for the player. The value should never // be |kSbMediaAudioCodecNone|. In addition, the caller must provide a // populated |audio_header| if the audio codec is |kSbMediaAudioCodecAac|. -#if SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION +#if SB_HAS(AUDIOLESS_VIDEO) // This can be set to |kSbMediaAudioCodecNone| to play a video without any // audio track. In such case |audio_header| must be NULL. -#endif // SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION +#endif // SB_HAS(AUDIOLESS_VIDEO) // // |duration_pts|: The expected media duration in 90KHz ticks (PTS). It may be // set to |SB_PLAYER_NO_DURATION| for live streams. @@ -322,9 +342,9 @@ // is no longer valid after this function returns. The implementation has to // make a copy of the content if it is needed after the function returns. #endif // SB_API_VERSION >= 6 -#if SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION +#if SB_HAS(AUDIOLESS_VIDEO) // When |audio_codec| is |kSbMediaAudioCodecNone|, this must be set to NULL. -#endif // SB_API_VERSION >= SB_AUDIOLESS_VIDEO_API_VERSION +#endif // SB_HAS(AUDIOLESS_VIDEO) // // |sample_deallocator_func|: If not |NULL|, the player calls this function // on an internal thread to free the sample buffers passed into @@ -340,6 +360,12 @@ // should be done on this thread. Rather, it should just signal the client // thread interacting with the decoder. // +#if SB_HAS(PLAYER_ERROR_MESSAGE) +// |player_error_func|: If not |NULL|, the player calls this function on an +// internal thread to provide an update on the error status. This callback is +// responsible for setting the media error message. +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) +// // |context|: This is passed to all callbacks and is generally used to point // at a class or struct that contains state associated with the player. // @@ -366,6 +392,9 @@ SbPlayerDeallocateSampleFunc sample_deallocate_func, SbPlayerDecoderStatusFunc decoder_status_func, SbPlayerStatusFunc player_status_func, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + SbPlayerErrorFunc player_error_func, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) void* context, SbPlayerOutputMode output_mode, SbDecodeTargetGraphicsContextProvider* context_provider);
diff --git a/src/starboard/raspi/shared/cobalt/configuration.gypi b/src/starboard/raspi/shared/cobalt/configuration.gypi index bb68af5..c8d8027 100644 --- a/src/starboard/raspi/shared/cobalt/configuration.gypi +++ b/src/starboard/raspi/shared/cobalt/configuration.gypi
@@ -18,6 +18,13 @@ 'variables': { 'in_app_dial%': 0, + # The maximum amount of memory that will be used to store media buffers when + # video resolution is no larger than 1080p. + 'cobalt_media_buffer_max_capacity_1080p': 36 * 1024 * 1024, + # The maximum amount of memory that will be used to store media buffers when + # video resolution is 4k. + 'cobalt_media_buffer_max_capacity_4k': 65 * 1024 * 1024, + # VideoCore's tiled renderer will do a render for every tile of a render # target even if only part of that target was rendered to. Since the # scratch surface cache is designed to choose large offscreen surfaces so
diff --git a/src/starboard/raspi/shared/dispmanx_util.h b/src/starboard/raspi/shared/dispmanx_util.h index ae78652..ab3eb03 100644 --- a/src/starboard/raspi/shared/dispmanx_util.h +++ b/src/starboard/raspi/shared/dispmanx_util.h
@@ -146,10 +146,10 @@ class DispmanxVideoFrame : public starboard::shared::starboard::player::filter::VideoFrame { public: - DispmanxVideoFrame(SbMediaTime pts, + DispmanxVideoFrame(SbTime time, DispmanxYUV420Resource* resource, std::function<void(DispmanxYUV420Resource*)> release_cb) - : VideoFrame(pts), resource_(resource), release_cb_(release_cb) { + : VideoFrame(time), resource_(resource), release_cb_(release_cb) { SB_DCHECK(resource_); SB_DCHECK(release_cb_); }
diff --git a/src/starboard/raspi/shared/gyp_configuration.gypi b/src/starboard/raspi/shared/gyp_configuration.gypi index 5f01bd1..1b16af3 100644 --- a/src/starboard/raspi/shared/gyp_configuration.gypi +++ b/src/starboard/raspi/shared/gyp_configuration.gypi
@@ -106,7 +106,6 @@ '-lasound', '-lavcodec', '-lavformat', - '-lavresample', '-lavutil', '-lEGL', '-lGLESv2',
diff --git a/src/starboard/raspi/shared/gyp_configuration.py b/src/starboard/raspi/shared/gyp_configuration.py index f6c72be..78ed966 100644 --- a/src/starboard/raspi/shared/gyp_configuration.py +++ b/src/starboard/raspi/shared/gyp_configuration.py
@@ -39,6 +39,13 @@ sys.exit(1) return raspi_home + def GetBuildFormat(self): + """Returns the desired build format.""" + # The comma means that ninja and qtcreator_ninja will be chained and use the + # same input information so that .gyp files will only have to be parsed + # once. + return 'ninja,qtcreator_ninja' + def GetVariables(self, configuration): raspi_home = self._GetRasPiHome() sysroot = os.path.realpath(os.path.join(raspi_home, 'sysroot')) @@ -74,6 +81,13 @@ """Gets the path to the launcher module for this platform.""" return os.path.dirname(__file__) + def GetGeneratorVariables(self, config_name): + del config_name + generator_variables = { + 'qtcreator_session_name_prefix': 'cobalt', + } + return generator_variables + def GetTestFilters(self): filters = super(RaspiPlatformConfig, self).GetTestFilters() filters.extend([
diff --git a/src/starboard/raspi/shared/open_max/video_decoder.cc b/src/starboard/raspi/shared/open_max/video_decoder.cc index 9ba3417..ba743fd 100644 --- a/src/starboard/raspi/shared/open_max/video_decoder.cc +++ b/src/starboard/raspi/shared/open_max/video_decoder.cc
@@ -167,8 +167,7 @@ while (offset < size) { int written = component.WriteData( current_buffer->data() + offset, size - offset, - OpenMaxComponent::kDataNonEOS, - current_buffer->pts() * kSbTimeSecond / kSbMediaTimeSecond); + OpenMaxComponent::kDataNonEOS, current_buffer->timestamp()); SB_DCHECK(written >= 0); offset += written; if (written == 0) { @@ -259,9 +258,8 @@ resource->WriteData(buffer->pBuffer); - SbMediaTime timestamp = ((buffer->nTimeStamp.nHighPart * 0x100000000ull) + - buffer->nTimeStamp.nLowPart) * - kSbMediaTimeSecond / kSbTimeSecond; + SbTime timestamp = ((buffer->nTimeStamp.nHighPart * 0x100000000ull) + + buffer->nTimeStamp.nLowPart); resource_pool_->AddRef(); frame = new DispmanxVideoFrame(
diff --git a/src/starboard/raspi/shared/player_components_impl.cc b/src/starboard/raspi/shared/player_components_impl.cc index faec026..60425a5 100644 --- a/src/starboard/raspi/shared/player_components_impl.cc +++ b/src/starboard/raspi/shared/player_components_impl.cc
@@ -44,7 +44,7 @@ SB_DCHECK(audio_decoder); SB_DCHECK(audio_renderer_sink); - scoped_ptr<AudioDecoderImpl> audio_decoder_impl(new AudioDecoderImpl( + scoped_ptr<AudioDecoderImpl> audio_decoder_impl(AudioDecoderImpl::Create( audio_parameters.audio_codec, audio_parameters.audio_header)); if (audio_decoder_impl->is_valid()) { audio_decoder->reset(audio_decoder_impl.release()); @@ -73,6 +73,15 @@ *video_renderer_sink = new VideoRendererSinkImpl( video_parameters.player, video_parameters.job_queue); } + + void GetAudioRendererParams(int* max_cached_frames, + int* max_frames_per_append) const override { + SB_DCHECK(max_cached_frames); + SB_DCHECK(max_frames_per_append); + + *max_cached_frames = 256 * 1024; + *max_frames_per_append = 16384; + } }; } // namespace
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi index e6b9a7c..74040e6 100644 --- a/src/starboard/raspi/shared/starboard_platform.gypi +++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -92,12 +92,6 @@ '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc', '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc', '<(DEPTH)/starboard/shared/dlmalloc/system_get_used_cpu_memory.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h', '<(DEPTH)/starboard/shared/gcc/atomic_gcc.h', '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc', '<(DEPTH)/starboard/shared/iso/character_is_digit.cc', @@ -295,6 +289,7 @@ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink.h', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h', + '<(DEPTH)/starboard/shared/starboard/player/filter/audio_resampler_impl.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.h', '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.cc', @@ -377,12 +372,10 @@ '<(DEPTH)/starboard/common/common.gyp:common', '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc', '<(DEPTH)/third_party/libevent/libevent.gyp:libevent', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg.gyp:ffmpeg_linked', 'starboard_base_symbolize', ], 'cflags': [ - # TODO: examine/upgrade code to see if these can be removed. - # Generated by shared/starboard/player/player_destroy.cc:29. - '-Wno-delete-non-virtual-dtor', # Generated by Audio Renderer and Audio Sink implementations. '-Wno-reorder', # Generated by code in the raspi/shared/open_max.
diff --git a/src/starboard/shared/alsa/alsa_audio_sink_type.cc b/src/starboard/shared/alsa/alsa_audio_sink_type.cc index cdda0f5..14c9385 100644 --- a/src/starboard/shared/alsa/alsa_audio_sink_type.cc +++ b/src/starboard/shared/alsa/alsa_audio_sink_type.cc
@@ -59,7 +59,7 @@ switch (sample_type) { case kSbMediaAudioSampleTypeFloat32: return sizeof(float); - case kSbMediaAudioSampleTypeInt16: + case kSbMediaAudioSampleTypeInt16Deprecated: return sizeof(int16_t); } SB_NOTREACHED();
diff --git a/src/starboard/shared/dlmalloc/page_internal.h b/src/starboard/shared/dlmalloc/page_internal.h index 6e1b93c..4073a2e 100644 --- a/src/starboard/shared/dlmalloc/page_internal.h +++ b/src/starboard/shared/dlmalloc/page_internal.h
@@ -120,7 +120,7 @@ // Unmap |size_bytes| of physical pages starting from |virtual_address|, // returning true on success. After this, [virtual_address, virtual_address + // size_bytes) will not be read/writable. SbUnmap() can unmap multiple -// contiguous regions that were mapped with seperate calls to +// contiguous regions that were mapped with separate calls to // SbPageMap(). E.g. if one call to SbPageMap(0x1000) returns (void*)0xA000 and // another call to SbPageMap(0x1000) returns (void*)0xB000, SbPageUnmap(0xA000, // 0x2000) should free both.
diff --git a/src/starboard/shared/ffmpeg/ffmpeg.gyp b/src/starboard/shared/ffmpeg/ffmpeg.gyp new file mode 100644 index 0000000..bb08955 --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg.gyp
@@ -0,0 +1,89 @@ +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +{ + 'target_defaults': { + 'defines': [ + 'STARBOARD_IMPLEMENTATION', + ], + }, + 'variables': { + 'ffmpeg_specialization_sources': [ + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h', + ], + 'ffmpeg_dispatch_sources': [ + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_dispatch.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_dispatch.h', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder_internal.cc', + ], + }, + 'targets': [ + { + 'target_name': 'libav.54.35.1', + 'type': '<(library)', + 'sources': [ '<@(ffmpeg_specialization_sources)' ], + 'dependencies': [ + '<(DEPTH)/third_party/ffmpeg_includes/ffmpeg_includes.gyp:libav.54.35.1', + ], + }, + { + 'target_name': 'libav.56.1.0', + 'type': '<(library)', + 'sources': [ '<@(ffmpeg_specialization_sources)' ], + 'dependencies': [ + '<(DEPTH)/third_party/ffmpeg_includes/ffmpeg_includes.gyp:libav.56.1.0', + ], + }, + { + 'target_name': 'ffmpeg.57.107.100', + 'type': '<(library)', + 'sources': [ '<@(ffmpeg_specialization_sources)' ], + 'dependencies': [ + '<(DEPTH)/third_party/ffmpeg_includes/ffmpeg_includes.gyp:ffmpeg.57.107.100', + ], + }, + { + 'target_name': 'ffmpeg_dynamic_load', + 'type': '<(library)', + 'sources': [ + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc', + '<@(ffmpeg_dispatch_sources)', + ], + 'dependencies': [ + 'ffmpeg.57.107.100', + 'libav.54.35.1', + 'libav.56.1.0', + ], + }, + { + 'target_name': 'ffmpeg_linked', + 'type': '<(library)', + 'sources': [ + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_linked_video_decoder_impl.cc', + '<@(ffmpeg_dispatch_sources)', + '<@(ffmpeg_specialization_sources)', + ], + }, + ], +}
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc deleted file mode 100644 index 3d94a3e..0000000 --- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc +++ /dev/null
@@ -1,282 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h" - -#include "starboard/audio_sink.h" -#include "starboard/log.h" -#include "starboard/memory.h" -#include "starboard/shared/starboard/media/media_util.h" - -namespace starboard { -namespace shared { -namespace ffmpeg { - -namespace { - -SbMediaAudioSampleType GetSupportedSampleType() { - if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) { - return kSbMediaAudioSampleTypeFloat32; - } - return kSbMediaAudioSampleTypeInt16; -} - -AVCodecID GetFfmpegCodecIdByMediaCodec(SbMediaAudioCodec audio_codec) { - switch (audio_codec) { - case kSbMediaAudioCodecAac: - return AV_CODEC_ID_AAC; - case kSbMediaAudioCodecOpus: - return AV_CODEC_ID_OPUS; - default: - return AV_CODEC_ID_NONE; - } -} - -} // namespace - -AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec, - const SbMediaAudioHeader& audio_header) - : audio_codec_(audio_codec), - codec_context_(NULL), - av_frame_(NULL), - stream_ended_(false), - audio_header_(audio_header) { - SB_DCHECK(GetFfmpegCodecIdByMediaCodec(audio_codec) != AV_CODEC_ID_NONE) - << "Unsupported audio codec " << audio_codec; - - InitializeCodec(); -} - -AudioDecoder::~AudioDecoder() { - TeardownCodec(); -} - -void AudioDecoder::Initialize(const OutputCB& output_cb, - const ErrorCB& error_cb) { - SB_DCHECK(BelongsToCurrentThread()); - SB_DCHECK(output_cb); - SB_DCHECK(!output_cb_); - SB_DCHECK(error_cb); - SB_DCHECK(!error_cb_); - - output_cb_ = output_cb; - error_cb_ = error_cb; -} - -void AudioDecoder::Decode(const scoped_refptr<InputBuffer>& input_buffer, - const ConsumedCB& consumed_cb) { - SB_DCHECK(BelongsToCurrentThread()); - SB_DCHECK(input_buffer); - SB_DCHECK(output_cb_); - SB_CHECK(codec_context_ != NULL); - - Schedule(consumed_cb); - - if (stream_ended_) { - SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called."; - return; - } - - AVPacket packet; - av_init_packet(&packet); - packet.data = const_cast<uint8_t*>(input_buffer->data()); - packet.size = input_buffer->size(); - -#if LIBAVUTIL_VERSION_MAJOR > 52 - av_frame_unref(av_frame_); -#else // LIBAVUTIL_VERSION_MAJOR > 52 - avcodec_get_frame_defaults(av_frame_); -#endif // LIBAVUTIL_VERSION_MAJOR > 52 - int frame_decoded = 0; - int result = - avcodec_decode_audio4(codec_context_, av_frame_, &frame_decoded, &packet); - if (result != input_buffer->size() || frame_decoded != 1) { - // TODO: Consider fill it with silence. - SB_DLOG(WARNING) << "avcodec_decode_audio4() failed with result: " << result - << " with input buffer size: " << input_buffer->size() - << " and frame decoded: " << frame_decoded; - error_cb_(); - return; - } - - int decoded_audio_size = av_samples_get_buffer_size( - NULL, codec_context_->channels, av_frame_->nb_samples, - codec_context_->sample_fmt, 1); - audio_header_.samples_per_second = codec_context_->sample_rate; - - if (decoded_audio_size > 0) { - scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio( - codec_context_->channels, GetSampleType(), GetStorageType(), - input_buffer->pts(), - codec_context_->channels * av_frame_->nb_samples * - starboard::media::GetBytesPerSample(GetSampleType())); - if (GetStorageType() == kSbMediaAudioFrameStorageTypeInterleaved) { - SbMemoryCopy(decoded_audio->buffer(), *av_frame_->extended_data, - decoded_audio->size()); - } else { - SB_DCHECK(GetStorageType() == kSbMediaAudioFrameStorageTypePlanar); - const int per_channel_size_in_bytes = - decoded_audio->size() / decoded_audio->channels(); - for (int i = 0; i < decoded_audio->channels(); ++i) { - SbMemoryCopy(decoded_audio->buffer() + per_channel_size_in_bytes * i, - av_frame_->extended_data[i], per_channel_size_in_bytes); - } - } - decoded_audios_.push(decoded_audio); - Schedule(output_cb_); - } else { - // TODO: Consider fill it with silence. - SB_LOG(ERROR) << "Decoded audio frame is empty."; - } -} - -void AudioDecoder::WriteEndOfStream() { - SB_DCHECK(BelongsToCurrentThread()); - SB_DCHECK(output_cb_); - - // AAC has no dependent frames so we needn't flush the decoder. Set the flag - // to ensure that Decode() is not called when the stream is ended. - stream_ended_ = true; - // Put EOS into the queue. - decoded_audios_.push(new DecodedAudio); - - Schedule(output_cb_); -} - -scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read() { - SB_DCHECK(BelongsToCurrentThread()); - SB_DCHECK(output_cb_); - SB_DCHECK(!decoded_audios_.empty()); - - scoped_refptr<DecodedAudio> result; - if (!decoded_audios_.empty()) { - result = decoded_audios_.front(); - decoded_audios_.pop(); - } - return result; -} - -void AudioDecoder::Reset() { - SB_DCHECK(BelongsToCurrentThread()); - - stream_ended_ = false; - while (!decoded_audios_.empty()) { - decoded_audios_.pop(); - } - - CancelPendingJobs(); -} - -SbMediaAudioSampleType AudioDecoder::GetSampleType() const { - SB_DCHECK(BelongsToCurrentThread()); - - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 || - codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) { - return kSbMediaAudioSampleTypeInt16; - } else if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT || - codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) { - return kSbMediaAudioSampleTypeFloat32; - } - - SB_NOTREACHED(); - - return kSbMediaAudioSampleTypeFloat32; -} - -SbMediaAudioFrameStorageType AudioDecoder::GetStorageType() const { - SB_DCHECK(BelongsToCurrentThread()); - - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 || - codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { - return kSbMediaAudioFrameStorageTypeInterleaved; - } - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P || - codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) { - return kSbMediaAudioFrameStorageTypePlanar; - } - - SB_NOTREACHED(); - return kSbMediaAudioFrameStorageTypeInterleaved; -} - -int AudioDecoder::GetSamplesPerSecond() const { - return audio_header_.samples_per_second; -} - -void AudioDecoder::InitializeCodec() { - InitializeFfmpeg(); - - codec_context_ = avcodec_alloc_context3(NULL); - - if (codec_context_ == NULL) { - SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; - return; - } - - codec_context_->codec_type = AVMEDIA_TYPE_AUDIO; - codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(audio_codec_); - // Request_sample_fmt is set by us, but sample_fmt is set by the decoder. - if (GetSupportedSampleType() == kSbMediaAudioSampleTypeInt16) { - codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16; - } else { - codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT; - } - - codec_context_->channels = audio_header_.number_of_channels; - codec_context_->sample_rate = audio_header_.samples_per_second; - - codec_context_->extradata = NULL; - codec_context_->extradata_size = 0; - - AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); - - if (codec == NULL) { - SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; - TeardownCodec(); - return; - } - - int rv = OpenCodec(codec_context_, codec); - if (rv < 0) { - SB_LOG(ERROR) << "Unable to open codec"; - TeardownCodec(); - return; - } - -#if LIBAVUTIL_VERSION_MAJOR > 52 - av_frame_ = av_frame_alloc(); -#else // LIBAVUTIL_VERSION_MAJOR > 52 - av_frame_ = avcodec_alloc_frame(); -#endif // LIBAVUTIL_VERSION_MAJOR > 52 - if (av_frame_ == NULL) { - SB_LOG(ERROR) << "Unable to allocate audio frame"; - TeardownCodec(); - } -} - -void AudioDecoder::TeardownCodec() { - if (codec_context_) { - CloseCodec(codec_context_); - av_free(codec_context_); - codec_context_ = NULL; - } - if (av_frame_) { - av_free(av_frame_); - av_frame_ = NULL; - } -} - -} // namespace ffmpeg -} // namespace shared -} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h index 45e466e..f6d715a 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h +++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -15,54 +15,21 @@ #ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_ #define STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_ -#include <queue> - -#include "starboard/common/ref_counted.h" #include "starboard/media.h" -#include "starboard/shared/ffmpeg/ffmpeg_common.h" #include "starboard/shared/internal_only.h" -#include "starboard/shared/starboard/player/decoded_audio_internal.h" #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h" -#include "starboard/shared/starboard/player/job_queue.h" namespace starboard { namespace shared { namespace ffmpeg { -class AudioDecoder : public starboard::player::filter::AudioDecoder, - private starboard::player::JobQueue::JobOwner { +class AudioDecoder : public starboard::player::filter::AudioDecoder { public: - AudioDecoder(SbMediaAudioCodec audio_codec, - const SbMediaAudioHeader& audio_header); - ~AudioDecoder() override; - - void Initialize(const OutputCB& output_cb, const ErrorCB& error_cb) override; - void Decode(const scoped_refptr<InputBuffer>& input_buffer, - const ConsumedCB& consumed_cb) override; - void WriteEndOfStream() override; - scoped_refptr<DecodedAudio> Read() override; - void Reset() override; - SbMediaAudioSampleType GetSampleType() const override; - SbMediaAudioFrameStorageType GetStorageType() const override; - int GetSamplesPerSecond() const override; - - bool is_valid() const { return codec_context_ != NULL; } - - private: - void InitializeCodec(); - void TeardownCodec(); - - static const int kMaxDecodedAudiosSize = 64; - - OutputCB output_cb_; - ErrorCB error_cb_; - SbMediaAudioCodec audio_codec_; - AVCodecContext* codec_context_; - AVFrame* av_frame_; - - bool stream_ended_; - std::queue<scoped_refptr<DecodedAudio> > decoded_audios_; - SbMediaAudioHeader audio_header_; + // Create an audio decoder for the currently loaded ffmpeg library. + static AudioDecoder* Create(SbMediaAudioCodec audio_codec, + const SbMediaAudioHeader& audio_header); + // Returns true if the audio decoder is initialized successfully. + virtual bool is_valid() const = 0; }; } // namespace ffmpeg
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc new file mode 100644 index 0000000..ec1f8f9 --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
@@ -0,0 +1,327 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains the explicit specialization of the AudioDecoderImpl class +// for the value 'FFMPEG'. + +#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h" + +#include "starboard/audio_sink.h" +#include "starboard/log.h" +#include "starboard/memory.h" +#include "starboard/shared/starboard/media/media_util.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { + +namespace { + +SbMediaAudioSampleType GetSupportedSampleType() { + if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) { + return kSbMediaAudioSampleTypeFloat32; + } + return kSbMediaAudioSampleTypeInt16Deprecated; +} + +AVCodecID GetFfmpegCodecIdByMediaCodec(SbMediaAudioCodec audio_codec) { + switch (audio_codec) { + case kSbMediaAudioCodecAac: + return AV_CODEC_ID_AAC; + case kSbMediaAudioCodecOpus: + return AV_CODEC_ID_OPUS; + default: + return AV_CODEC_ID_NONE; + } +} + +const bool g_registered = + FFMPEGDispatch::RegisterSpecialization(FFMPEG, + LIBAVCODEC_VERSION_MAJOR, + LIBAVFORMAT_VERSION_MAJOR, + LIBAVUTIL_VERSION_MAJOR); + +} // namespace + +AudioDecoderImpl<FFMPEG>::AudioDecoderImpl( + SbMediaAudioCodec audio_codec, + const SbMediaAudioHeader& audio_header) + : audio_codec_(audio_codec), + codec_context_(NULL), + av_frame_(NULL), + stream_ended_(false), + audio_header_(audio_header) { + SB_DCHECK(g_registered) << "Decoder Specialization registration failed."; + SB_DCHECK(GetFfmpegCodecIdByMediaCodec(audio_codec) != AV_CODEC_ID_NONE) + << "Unsupported audio codec " << audio_codec; + ffmpeg_ = FFMPEGDispatch::GetInstance(); + SB_DCHECK(ffmpeg_); + if ((ffmpeg_->specialization_version()) == FFMPEG) { + InitializeCodec(); + } +} + +AudioDecoderImpl<FFMPEG>::~AudioDecoderImpl() { + TeardownCodec(); +} + +// static +AudioDecoder* AudioDecoderImpl<FFMPEG>::Create( + SbMediaAudioCodec audio_codec, + const SbMediaAudioHeader& audio_header) { + return new AudioDecoderImpl<FFMPEG>(audio_codec, audio_header); +} + +void AudioDecoderImpl<FFMPEG>::Initialize(const OutputCB& output_cb, + const ErrorCB& error_cb) { + SB_DCHECK(BelongsToCurrentThread()); + SB_DCHECK(output_cb); + SB_DCHECK(!output_cb_); + SB_DCHECK(error_cb); + SB_DCHECK(!error_cb_); + + output_cb_ = output_cb; + error_cb_ = error_cb; +} + +void AudioDecoderImpl<FFMPEG>::Decode( + const scoped_refptr<InputBuffer>& input_buffer, + const ConsumedCB& consumed_cb) { + SB_DCHECK(BelongsToCurrentThread()); + SB_DCHECK(input_buffer); + SB_DCHECK(output_cb_); + SB_CHECK(codec_context_ != NULL); + + Schedule(consumed_cb); + + if (stream_ended_) { + SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called."; + return; + } + + AVPacket packet; + ffmpeg_->av_init_packet(&packet); + packet.data = const_cast<uint8_t*>(input_buffer->data()); + packet.size = input_buffer->size(); + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + ffmpeg_->av_frame_unref(av_frame_); +#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + ffmpeg_->avcodec_get_frame_defaults(av_frame_); +#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + int frame_decoded = 0; + int result = ffmpeg_->avcodec_decode_audio4(codec_context_, av_frame_, + &frame_decoded, &packet); + if (result != input_buffer->size()) { + // TODO: Consider fill it with silence. + SB_DLOG(WARNING) << "avcodec_decode_audio4() failed with result: " << result + << " with input buffer size: " << input_buffer->size() + << " and frame decoded: " << frame_decoded; + error_cb_(); + return; + } + + if (frame_decoded != 1) { + // TODO: Adjust timestamp accordingly when decoding result is shifted. + SB_DCHECK(frame_decoded == 0); + SB_DLOG(WARNING) << "avcodec_decode_audio4() returns with 0 frames decoded"; + return; + } + + int decoded_audio_size = ffmpeg_->av_samples_get_buffer_size( + NULL, codec_context_->channels, av_frame_->nb_samples, + codec_context_->sample_fmt, 1); + audio_header_.samples_per_second = codec_context_->sample_rate; + + if (decoded_audio_size > 0) { + scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio( + codec_context_->channels, GetSampleType(), GetStorageType(), + input_buffer->timestamp(), + codec_context_->channels * av_frame_->nb_samples * + starboard::media::GetBytesPerSample(GetSampleType())); + if (GetStorageType() == kSbMediaAudioFrameStorageTypeInterleaved) { + SbMemoryCopy(decoded_audio->buffer(), *av_frame_->extended_data, + decoded_audio->size()); + } else { + SB_DCHECK(GetStorageType() == kSbMediaAudioFrameStorageTypePlanar); + const int per_channel_size_in_bytes = + decoded_audio->size() / decoded_audio->channels(); + for (int i = 0; i < decoded_audio->channels(); ++i) { + SbMemoryCopy(decoded_audio->buffer() + per_channel_size_in_bytes * i, + av_frame_->extended_data[i], per_channel_size_in_bytes); + } + } + decoded_audios_.push(decoded_audio); + Schedule(output_cb_); + } else { + // TODO: Consider fill it with silence. + SB_LOG(ERROR) << "Decoded audio frame is empty."; + } +} + +void AudioDecoderImpl<FFMPEG>::WriteEndOfStream() { + SB_DCHECK(BelongsToCurrentThread()); + SB_DCHECK(output_cb_); + + // AAC has no dependent frames so we needn't flush the decoder. Set the flag + // to ensure that Decode() is not called when the stream is ended. + stream_ended_ = true; + // Put EOS into the queue. + decoded_audios_.push(new DecodedAudio); + + Schedule(output_cb_); +} + +scoped_refptr<AudioDecoderImpl<FFMPEG>::DecodedAudio> +AudioDecoderImpl<FFMPEG>::Read() { + SB_DCHECK(BelongsToCurrentThread()); + SB_DCHECK(output_cb_); + SB_DCHECK(!decoded_audios_.empty()); + + scoped_refptr<DecodedAudio> result; + if (!decoded_audios_.empty()) { + result = decoded_audios_.front(); + decoded_audios_.pop(); + } + return result; +} + +void AudioDecoderImpl<FFMPEG>::Reset() { + SB_DCHECK(BelongsToCurrentThread()); + + stream_ended_ = false; + while (!decoded_audios_.empty()) { + decoded_audios_.pop(); + } + + CancelPendingJobs(); +} + +bool AudioDecoderImpl<FFMPEG>::is_valid() const { + return (ffmpeg_ != NULL) && ffmpeg_->is_valid() && (codec_context_ != NULL); +} + +SbMediaAudioSampleType AudioDecoderImpl<FFMPEG>::GetSampleType() const { + SB_DCHECK(BelongsToCurrentThread()); + + if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 || + codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) { + return kSbMediaAudioSampleTypeInt16Deprecated; + } else if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT || + codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) { + return kSbMediaAudioSampleTypeFloat32; + } + + SB_NOTREACHED(); + + return kSbMediaAudioSampleTypeFloat32; +} + +SbMediaAudioFrameStorageType AudioDecoderImpl<FFMPEG>::GetStorageType() const { + SB_DCHECK(BelongsToCurrentThread()); + + if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16 || + codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { + return kSbMediaAudioFrameStorageTypeInterleaved; + } + if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P || + codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) { + return kSbMediaAudioFrameStorageTypePlanar; + } + + SB_NOTREACHED(); + return kSbMediaAudioFrameStorageTypeInterleaved; +} + +int AudioDecoderImpl<FFMPEG>::GetSamplesPerSecond() const { + return audio_header_.samples_per_second; +} + +void AudioDecoderImpl<FFMPEG>::InitializeCodec() { + codec_context_ = ffmpeg_->avcodec_alloc_context3(NULL); + + if (codec_context_ == NULL) { + SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; + return; + } + + codec_context_->codec_type = AVMEDIA_TYPE_AUDIO; + codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(audio_codec_); + // Request_sample_fmt is set by us, but sample_fmt is set by the decoder. + if (GetSupportedSampleType() == kSbMediaAudioSampleTypeInt16Deprecated) { + codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16; + } else { + codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT; + } + + codec_context_->channels = audio_header_.number_of_channels; + codec_context_->sample_rate = audio_header_.samples_per_second; + codec_context_->extradata = NULL; + codec_context_->extradata_size = 0; + + if (codec_context_->codec_id == AV_CODEC_ID_OPUS && + audio_header_.audio_specific_config_size > 0) { + // AV_INPUT_BUFFER_PADDING_SIZE is not defined in ancient avcodec.h. Use a + // large enough padding here explicitly. + const int kAvInputBufferPaddingSize = 256; + codec_context_->extradata_size = audio_header_.audio_specific_config_size; + codec_context_->extradata = static_cast<uint8_t*>(ffmpeg_->av_malloc( + codec_context_->extradata_size + kAvInputBufferPaddingSize)); + SB_DCHECK(codec_context_->extradata); + SbMemoryCopy(codec_context_->extradata, audio_header_.audio_specific_config, + codec_context_->extradata_size); + SbMemorySet(codec_context_->extradata + codec_context_->extradata_size, 0, + kAvInputBufferPaddingSize); + } + + AVCodec* codec = ffmpeg_->avcodec_find_decoder(codec_context_->codec_id); + + if (codec == NULL) { + SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; + TeardownCodec(); + return; + } + + int rv = ffmpeg_->OpenCodec(codec_context_, codec); + if (rv < 0) { + SB_LOG(ERROR) << "Unable to open codec"; + TeardownCodec(); + return; + } + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + av_frame_ = ffmpeg_->av_frame_alloc(); +#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + av_frame_ = ffmpeg_->avcodec_alloc_frame(); +#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + if (av_frame_ == NULL) { + SB_LOG(ERROR) << "Unable to allocate audio frame"; + TeardownCodec(); + } +} + +void AudioDecoderImpl<FFMPEG>::TeardownCodec() { + if (codec_context_) { + ffmpeg_->CloseCodec(codec_context_); + if (codec_context_->extradata_size) { + ffmpeg_->av_freep(&codec_context_->extradata); + } + ffmpeg_->av_freep(&codec_context_); + } + ffmpeg_->av_freep(&av_frame_); +} + +} // namespace ffmpeg +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h new file mode 100644 index 0000000..17ab6ae --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h
@@ -0,0 +1,94 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_IMPL_H_ +#define STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_IMPL_H_ + +#include <queue> + +#include "starboard/common/ref_counted.h" +#include "starboard/media.h" +#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h" +#include "starboard/shared/ffmpeg/ffmpeg_common.h" +#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h" +#include "starboard/shared/internal_only.h" +#include "starboard/shared/starboard/player/decoded_audio_internal.h" +#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h" +#include "starboard/shared/starboard/player/job_queue.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { + +// For each version V that is supported, there will be an implementation of an +// explicit specialization of the AudioDecoder class. +template <int V> +class AudioDecoderImpl : public AudioDecoder { + public: + static AudioDecoder* Create(SbMediaAudioCodec audio_codec, + const SbMediaAudioHeader& audio_header); +}; + +// Forward class declaration of the explicit specialization with value FFMPEG. +template <> +class AudioDecoderImpl<FFMPEG>; + +// Declare the explicit specialization of the class with value FFMPEG. +template <> +class AudioDecoderImpl<FFMPEG> : public AudioDecoder, + private starboard::player::JobQueue::JobOwner { + public: + AudioDecoderImpl(SbMediaAudioCodec audio_codec, + const SbMediaAudioHeader& audio_header); + ~AudioDecoderImpl() override; + + // From: AudioDecoder + static AudioDecoder* Create(SbMediaAudioCodec audio_codec, + const SbMediaAudioHeader& audio_header); + bool is_valid() const override; + + // From: starboard::player::filter::AudioDecoder + void Initialize(const OutputCB& output_cb, const ErrorCB& error_cb) override; + void Decode(const scoped_refptr<InputBuffer>& input_buffer, + const ConsumedCB& consumed_cb) override; + void WriteEndOfStream() override; + scoped_refptr<DecodedAudio> Read() override; + void Reset() override; + SbMediaAudioSampleType GetSampleType() const override; + SbMediaAudioFrameStorageType GetStorageType() const override; + int GetSamplesPerSecond() const override; + + private: + void InitializeCodec(); + void TeardownCodec(); + + static const int kMaxDecodedAudiosSize = 64; + + FFMPEGDispatch* ffmpeg_; + OutputCB output_cb_; + ErrorCB error_cb_; + SbMediaAudioCodec audio_codec_; + AVCodecContext* codec_context_; + AVFrame* av_frame_; + + bool stream_ended_; + std::queue<scoped_refptr<DecodedAudio> > decoded_audios_; + SbMediaAudioHeader audio_header_; +}; + +} // namespace ffmpeg +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_IMPL_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc deleted file mode 100644 index 3892b04..0000000 --- a/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.cc +++ /dev/null
@@ -1,226 +0,0 @@ -// Copyright 2017 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "starboard/shared/ffmpeg/ffmpeg_audio_resampler.h" - -#include "starboard/log.h" -#include "starboard/memory.h" -#include "starboard/shared/starboard/media/media_util.h" - -namespace starboard { -namespace shared { -namespace ffmpeg { - -namespace { - -const int kMaxChannels = 8; -const int kMaxCachedSamples = 65536; - -int GetChannelLayoutFromChannels(int channels) { - if (channels == 1) { - return AV_CH_LAYOUT_MONO; - } - if (channels == 2) { - return AV_CH_LAYOUT_STEREO; - } - if (channels == 6) { - return AV_CH_LAYOUT_5POINT1; - } - if (channels == 8) { - return AV_CH_LAYOUT_7POINT1; - } - SB_NOTREACHED() << "Unsupported channel count: " << channels; - return AV_CH_LAYOUT_STEREO; -} - -int GetSampleFormatBySampleTypeAndStorageType( - SbMediaAudioSampleType sample_type, - SbMediaAudioFrameStorageType storage_type) { - if (sample_type == kSbMediaAudioSampleTypeInt16 && - storage_type == kSbMediaAudioFrameStorageTypeInterleaved) { - return AV_SAMPLE_FMT_S16; - } - if (sample_type == kSbMediaAudioSampleTypeFloat32 && - storage_type == kSbMediaAudioFrameStorageTypeInterleaved) { - return AV_SAMPLE_FMT_FLT; - } - if (sample_type == kSbMediaAudioSampleTypeInt16 && - storage_type == kSbMediaAudioFrameStorageTypePlanar) { - return AV_SAMPLE_FMT_S16P; - } - if (sample_type == kSbMediaAudioSampleTypeFloat32 && - storage_type == kSbMediaAudioFrameStorageTypePlanar) { - return AV_SAMPLE_FMT_FLTP; - } - SB_NOTREACHED() << "Unsupported sample type (" << sample_type << ") and " - << " storage type (" << storage_type << ") combination"; - return AV_SAMPLE_FMT_FLT; -} - -} // namespace - -AudioResampler::AudioResampler( - SbMediaAudioSampleType source_sample_type, - SbMediaAudioFrameStorageType source_storage_type, - int source_sample_rate, - SbMediaAudioSampleType destination_sample_type, - SbMediaAudioFrameStorageType destination_storage_type, - int destination_sample_rate, - int channels) - : source_sample_type_(source_sample_type), - source_storage_type_(source_storage_type), - destination_sample_type_(destination_sample_type), - destination_storage_type_(destination_storage_type), - destination_sample_rate_(destination_sample_rate), - channels_(channels), - start_pts_(-1), - samples_returned_(0), - eos_reached_(false) { - SB_DCHECK(channels_ <= kMaxChannels); - - context_ = avresample_alloc_context(); - SB_DCHECK(context_ != NULL); - - av_opt_set_int(context_, "in_channel_layout", - GetChannelLayoutFromChannels(channels_), 0); - av_opt_set_int(context_, "out_channel_layout", - GetChannelLayoutFromChannels(channels_), 0); - av_opt_set_int(context_, "in_sample_rate", source_sample_rate, 0); - av_opt_set_int(context_, "out_sample_rate", destination_sample_rate, 0); - av_opt_set_int(context_, "in_sample_fmt", - GetSampleFormatBySampleTypeAndStorageType(source_sample_type, - source_storage_type), - 0); - av_opt_set_int(context_, "out_sample_fmt", - GetSampleFormatBySampleTypeAndStorageType( - destination_sample_type, destination_storage_type), - 0); - av_opt_set_int(context_, "internal_sample_fmt", AV_SAMPLE_FMT_S16P, 0); - - int result = avresample_open(context_); - SB_DCHECK(!result); -} - -AudioResampler::~AudioResampler() { - SB_DCHECK(thread_checker_.CalledOnValidThread()); - avresample_close(context_); - av_free(context_); -} - -scoped_refptr<AudioResampler::DecodedAudio> AudioResampler::Resample( - const scoped_refptr<DecodedAudio>& audio_data) { - SB_DCHECK(thread_checker_.CalledOnValidThread()); - SB_DCHECK(audio_data); - SB_DCHECK(audio_data->sample_type() == source_sample_type_); - SB_DCHECK(audio_data->storage_type() == source_storage_type_); - SB_DCHECK(audio_data->channels() == channels_); - SB_DCHECK(!eos_reached_); - - if (channels_ > kMaxChannels) { - return new DecodedAudio; - } - - if (eos_reached_) { - return new DecodedAudio; - } - - if (start_pts_ < 0) { - start_pts_ = audio_data->pts(); - } - - uint8_t* input_buffers[kMaxChannels] = { - const_cast<uint8_t*>(audio_data->buffer())}; - if (source_storage_type_ == kSbMediaAudioFrameStorageTypePlanar) { - for (int i = 1; i < channels_; ++i) { - input_buffers[i] = const_cast<uint8_t*>( - audio_data->buffer() + audio_data->size() / channels_ * i); - } - } - - int result = avresample_convert(context_, NULL, 0, 0, input_buffers, 0, - audio_data->frames()); - SB_DCHECK(result == 0); - - return RetrieveOutput(); -} - -scoped_refptr<AudioResampler::DecodedAudio> AudioResampler::WriteEndOfStream() { - SB_DCHECK(thread_checker_.CalledOnValidThread()); - SB_DCHECK(!eos_reached_); - - eos_reached_ = true; - int result = avresample_convert(context_, NULL, 0, 0, NULL, 0, 0); - SB_DCHECK(result == 0); - - return RetrieveOutput(); -} - -scoped_refptr<AudioResampler::DecodedAudio> AudioResampler::RetrieveOutput() { - SB_DCHECK(thread_checker_.CalledOnValidThread()); - - int frames_in_buffer = avresample_available(context_); - SbMediaTime pts = start_pts_ + samples_returned_ * kSbMediaTimeSecond / - destination_sample_rate_; - int bytes_per_sample = - starboard::media::GetBytesPerSample(destination_sample_type_); - scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio( - channels_, destination_sample_type_, destination_storage_type_, pts, - frames_in_buffer * bytes_per_sample * channels_); - samples_returned_ += frames_in_buffer; - - if (frames_in_buffer > 0) { - uint8_t* output_buffers[kMaxChannels] = { - const_cast<uint8_t*>(decoded_audio->buffer())}; - if (destination_storage_type_ == kSbMediaAudioFrameStorageTypePlanar) { - for (int i = 1; i < channels_; ++i) { - output_buffers[i] = const_cast<uint8_t*>( - decoded_audio->buffer() + decoded_audio->size() / channels_ * i); - } - } - - int frames_read = - avresample_read(context_, output_buffers, frames_in_buffer); - SB_DCHECK(frames_read == frames_in_buffer); - } - - return decoded_audio; -} - -} // namespace ffmpeg - -namespace starboard { -namespace player { -namespace filter { - -// static -scoped_ptr<AudioResampler> AudioResampler::Create( - SbMediaAudioSampleType source_sample_type, - SbMediaAudioFrameStorageType source_storage_type, - int source_sample_rate, - SbMediaAudioSampleType destination_sample_type, - SbMediaAudioFrameStorageType destination_storage_type, - int destination_sample_rate, - int channels) { - return scoped_ptr<AudioResampler>(new ffmpeg::AudioResampler( - source_sample_type, source_storage_type, source_sample_rate, - destination_sample_type, destination_storage_type, - destination_sample_rate, channels)); -} - -} // namespace filter -} // namespace player -} // namespace starboard - -} // namespace shared -} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h deleted file mode 100644 index dac58a4..0000000 --- a/src/starboard/shared/ffmpeg/ffmpeg_audio_resampler.h +++ /dev/null
@@ -1,67 +0,0 @@ -// Copyright 2017 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_RESAMPLER_H_ -#define STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_RESAMPLER_H_ - -#include <queue> - -#include "starboard/media.h" -#include "starboard/shared/ffmpeg/ffmpeg_common.h" -#include "starboard/shared/internal_only.h" -#include "starboard/shared/starboard/player/decoded_audio_internal.h" -#include "starboard/shared/starboard/player/filter/audio_resampler.h" -#include "starboard/shared/starboard/thread_checker.h" - -namespace starboard { -namespace shared { -namespace ffmpeg { - -class AudioResampler : public starboard::player::filter::AudioResampler { - public: - AudioResampler(SbMediaAudioSampleType source_sample_type, - SbMediaAudioFrameStorageType source_storage_type, - int source_sample_rate, - SbMediaAudioSampleType destination_sample_type, - SbMediaAudioFrameStorageType destination_storage_type, - int destination_sample_rate, - int channels); - ~AudioResampler() override; - - scoped_refptr<DecodedAudio> Resample( - const scoped_refptr<DecodedAudio>& audio_data) override; - scoped_refptr<DecodedAudio> WriteEndOfStream() override; - - private: - scoped_refptr<DecodedAudio> RetrieveOutput(); - - shared::starboard::ThreadChecker thread_checker_; - - SbMediaAudioSampleType source_sample_type_; - SbMediaAudioFrameStorageType source_storage_type_; - SbMediaAudioSampleType destination_sample_type_; - SbMediaAudioFrameStorageType destination_storage_type_; - int destination_sample_rate_; // Used for timestamp adjustment. - int channels_; - SbMediaTime start_pts_; - int samples_returned_; - bool eos_reached_; - AVAudioResampleContext* context_; -}; - -} // namespace ffmpeg -} // namespace shared -} // namespace starboard - -#endif // STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_RESAMPLER_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_common.cc b/src/starboard/shared/ffmpeg/ffmpeg_common.cc deleted file mode 100644 index b3fd3b6..0000000 --- a/src/starboard/shared/ffmpeg/ffmpeg_common.cc +++ /dev/null
@@ -1,52 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "starboard/shared/ffmpeg/ffmpeg_common.h" - -#include "starboard/log.h" -#include "starboard/mutex.h" -#include "starboard/once.h" - -namespace starboard { -namespace shared { -namespace ffmpeg { - -namespace { - -SbOnceControl ffmpeg_initialization_once = SB_ONCE_INITIALIZER; -SbMutex codec_mutex = SB_MUTEX_INITIALIZER; - -} // namespace - -void InitializeFfmpeg() { - bool initialized = SbOnce(&ffmpeg_initialization_once, av_register_all); - SB_DCHECK(initialized); -} - -int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec) { - SbMutexAcquire(&codec_mutex); - int result = avcodec_open2(codec_context, codec, NULL); - SbMutexRelease(&codec_mutex); - return result; -} - -void CloseCodec(AVCodecContext* codec_context) { - SbMutexAcquire(&codec_mutex); - avcodec_close(codec_context); - SbMutexRelease(&codec_mutex); -} - -} // namespace ffmpeg -} // namespace shared -} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_common.h b/src/starboard/shared/ffmpeg/ffmpeg_common.h index 9c63c1d..86f0169 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_common.h +++ b/src/starboard/shared/ffmpeg/ffmpeg_common.h
@@ -37,22 +37,23 @@ #define PIX_FMT_YUVJ420P AV_PIX_FMT_YUVJ420P #endif // LIBAVUTIL_VERSION_MAJOR > 52 -namespace starboard { -namespace shared { -namespace ffmpeg { +#if !defined(LIBAVCODEC_VERSION_MAJOR) +#error "LIBAVCODEC_VERSION_MAJOR not defined" +#endif // !defined(LIBAVCODEC_VERSION_MAJOR) -void InitializeFfmpeg(); +#if !defined(LIBAVCODEC_VERSION_MICRO) +#error "LIBAVCODEC_VERSION_MICRO not defined" +#endif // !defined(LIBAVCODEC_VERSION_MICRO) -// In Ffmpeg, the calls to avcodec_open2() and avcodec_close() are not -// synchronized internally so it is the responsibility of its user to ensure -// that these calls don't overlap. The following functions acquires a lock -// internally before calling avcodec_open2() and avcodec_close() to enforce -// this. -int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec); -void CloseCodec(AVCodecContext* codec_context); +#if LIBAVCODEC_VERSION_MICRO >= 100 +#define LIBAVCODEC_LIBRARY_IS_FFMPEG 1 +#else +#define LIBAVCODEC_LIBRARY_IS_FFMPEG 0 +#endif // LIBAVCODEC_VERSION_MICRO >= 100 -} // namespace ffmpeg -} // namespace shared -} // namespace starboard +// Use the major version number of libavcodec plus a single digit distinguishing +// between ffmpeg and libav as the template parameter for the +// explicit specialization of the audio and video decoder classes. +#define FFMPEG ((LIBAVCODEC_VERSION_MAJOR * 10) + LIBAVCODEC_LIBRARY_IS_FFMPEG) #endif // STARBOARD_SHARED_FFMPEG_FFMPEG_COMMON_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dispatch.cc b/src/starboard/shared/ffmpeg/ffmpeg_dispatch.cc new file mode 100644 index 0000000..384baf2 --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_dispatch.cc
@@ -0,0 +1,47 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements the FFMPEGDispatch interface with dynamic loading of +// the libraries. + +#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h" + +#include "starboard/mutex.h" +#include "starboard/once.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { + +namespace { +SbMutex g_codec_mutex = SB_MUTEX_INITIALIZER; +} // namespace + +int FFMPEGDispatch::OpenCodec(AVCodecContext* codec_context, + const AVCodec* codec) { + SbMutexAcquire(&g_codec_mutex); + int result = avcodec_open2(codec_context, codec, NULL); + SbMutexRelease(&g_codec_mutex); + return result; +} + +void FFMPEGDispatch::CloseCodec(AVCodecContext* codec_context) { + SbMutexAcquire(&g_codec_mutex); + avcodec_close(codec_context); + SbMutexRelease(&g_codec_mutex); +} + +} // namespace ffmpeg +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dispatch.h b/src/starboard/shared/ffmpeg/ffmpeg_dispatch.h new file mode 100644 index 0000000..4d1713f --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_dispatch.h
@@ -0,0 +1,114 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This class defines an interface for dispatching calls to the ffmpeg +// libraries. + +#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_DISPATCH_H_ +#define STARBOARD_SHARED_FFMPEG_FFMPEG_DISPATCH_H_ + +#include "starboard/mutex.h" +#include "starboard/types.h" + +struct AVCodec; +struct AVCodecContext; +struct AVDictionary; +struct AVFrame; +struct AVPacket; + +namespace starboard { +namespace shared { +namespace ffmpeg { + +class FFMPEGDispatch { + public: + FFMPEGDispatch(); + ~FFMPEGDispatch(); + + static FFMPEGDispatch* GetInstance(); + + // Register an ffmpeg decoder specialization for the given combination of + // major versions of the libavcodec, libavformat, and libavutil libraries. + // Returns true if the specialization did not exist yet, or if it matched + // an already existing registration. + static bool RegisterSpecialization(int specialization, + int avcodec, + int avformat, + int avutil); + + bool is_valid() const; + + unsigned (*avutil_version)(void); + void* (*av_malloc)(size_t size); + void (*av_freep)(void* ptr); + AVFrame* (*av_frame_alloc)(void); + void (*av_frame_unref)(AVFrame* frame); + int (*av_samples_get_buffer_size)(int* linesize, + int nb_channels, + int nb_samples, + int sample_fmt, + int align); + int (*av_opt_set_int)(void* obj, + const char* name, + int64_t val, + int search_flags); + int (*av_image_check_size)(unsigned int w, + unsigned int h, + int log_offset, + void* log_ctx); + void* (*av_buffer_create)(uint8_t* data, + int size, + void (*free)(void* opaque, uint8_t* data), + void* opaque, + int flags); + + unsigned (*avcodec_version)(void); + AVCodecContext* (*avcodec_alloc_context3)(const AVCodec* codec); + AVCodec* (*avcodec_find_decoder)(int id); + int (*avcodec_close)(AVCodecContext* avctx); + int (*avcodec_open2)(AVCodecContext* avctx, + const AVCodec* codec, + AVDictionary** options); + void (*av_init_packet)(AVPacket* pkt); + int (*avcodec_decode_audio4)(AVCodecContext* avctx, + AVFrame* frame, + int* got_frame_ptr, + const AVPacket* avpkt); + int (*avcodec_decode_video2)(AVCodecContext* avctx, + AVFrame* picture, + int* got_picture_ptr, + const AVPacket* avpkt); + void (*avcodec_flush_buffers)(AVCodecContext* avctx); + AVFrame* (*avcodec_alloc_frame)(void); + void (*avcodec_get_frame_defaults)(AVFrame* frame); + + unsigned (*avformat_version)(void); + void (*av_register_all)(void); + + int specialization_version() const; + + // In Ffmpeg, the calls to avcodec_open2() and avcodec_close() are not + // synchronized internally so it is the responsibility of its user to ensure + // that these calls don't overlap. The following functions acquires a lock + // internally before calling avcodec_open2() and avcodec_close() to enforce + // this. + int OpenCodec(AVCodecContext* codec_context, const AVCodec* codec); + void CloseCodec(AVCodecContext* codec_context); +}; + +} // namespace ffmpeg +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_FFMPEG_FFMPEG_DISPATCH_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc new file mode 100644 index 0000000..2a8dcaf --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc
@@ -0,0 +1,60 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains the creation of the specialized AudioDecoderImpl object +// corresponding to the version of the dynamically loaded ffmpeg library. + +#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h" + +#include "starboard/memory.h" +#include "starboard/player.h" +#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h" +#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { + +// static +AudioDecoder* AudioDecoder::Create(SbMediaAudioCodec audio_codec, + const SbMediaAudioHeader& audio_header) { + FFMPEGDispatch* ffmpeg = FFMPEGDispatch::GetInstance(); + if (!ffmpeg || !ffmpeg->is_valid()) { + SB_NOTREACHED(); + return NULL; + } + + AudioDecoder* audio_decoder = NULL; + switch (ffmpeg->specialization_version()) { + case 540: + audio_decoder = AudioDecoderImpl<540>::Create(audio_codec, audio_header); + break; + case 550: + case 560: + audio_decoder = AudioDecoderImpl<560>::Create(audio_codec, audio_header); + break; + case 571: + audio_decoder = AudioDecoderImpl<571>::Create(audio_codec, audio_header); + break; + default: + SB_NOTREACHED() << "Unsupported FFMPEG specialization " << std::hex + << ffmpeg->specialization_version(); + break; + } + return audio_decoder; +} + +} // namespace ffmpeg +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc new file mode 100644 index 0000000..8afa042 --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc
@@ -0,0 +1,279 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements the FFMPEGDispatch interface with dynamic loading of +// the libraries. + +#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h" + +#include <dlfcn.h> +#include <map> + +#include "starboard/common/scoped_ptr.h" +#include "starboard/log.h" +#include "starboard/once.h" +#include "starboard/shared/starboard/lazy_initialization_internal.h" +#include "starboard/string.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { + +namespace { + +const char kAVCodecLibraryName[] = "libavcodec.so"; +const char kAVFormatLibraryName[] = "libavformat.so"; +const char kAVUtilLibraryName[] = "libavutil.so"; + +SbOnceControl g_dynamic_load_once = SB_ONCE_INITIALIZER; + +struct LibraryMajorVersions { + int avcodec; + int avformat; + int avutil; + LibraryMajorVersions(int avcodec, int avformat, int avutil) + : avcodec(avcodec), avformat(avformat), avutil(avutil) {} +}; + +// Map containing the major version for all combinations of libraries in +// supported ffmpeg installations. The index is the specialization version +// matching the template parameter for the available specialization. +typedef std::map<int, LibraryMajorVersions> LibraryMajorVersionsMap; + +class FFMPEGDispatchImpl { + public: + FFMPEGDispatchImpl(); + ~FFMPEGDispatchImpl(); + + static FFMPEGDispatchImpl* GetInstance(); + + bool RegisterSpecialization(int specialization, + int avcodec, + int avformat, + int avutil); + + bool is_valid() const { return avcodec_ && avformat_ && avutil_; } + + FFMPEGDispatch* get_ffmpeg_dispatch(); + + private: + SbMutex mutex_; + FFMPEGDispatch* ffmpeg_; + // Load the ffmpeg shared libraries, return true if successful. + bool OpenLibraries(); + // Load the symbols from the shared libraries. + void LoadSymbols(); + + void* avcodec_; + void* avformat_; + void* avutil_; + LibraryMajorVersionsMap versions_; +}; + +FFMPEGDispatchImpl* g_ffmpeg_dispatch_impl; + +void construct_ffmpeg_dispatch_impl() { + g_ffmpeg_dispatch_impl = new FFMPEGDispatchImpl(); +} + +FFMPEGDispatchImpl::FFMPEGDispatchImpl() + : mutex_(SB_MUTEX_INITIALIZER), + ffmpeg_(NULL), + avcodec_(NULL), + avformat_(NULL), + avutil_(NULL) {} + +FFMPEGDispatchImpl::~FFMPEGDispatchImpl() { + delete ffmpeg_; + if (avformat_) { + dlclose(avformat_); + } + if (avcodec_) { + dlclose(avcodec_); + } + if (avutil_) { + dlclose(avutil_); + } +} + +// static +FFMPEGDispatchImpl* FFMPEGDispatchImpl::GetInstance() { + bool initialized = + SbOnce(&g_dynamic_load_once, construct_ffmpeg_dispatch_impl); + SB_DCHECK(initialized); + return g_ffmpeg_dispatch_impl; +} + +bool FFMPEGDispatchImpl::RegisterSpecialization(int specialization, + int avcodec, + int avformat, + int avutil) { + SbMutexAcquire(&mutex_); + auto result = versions_.insert(std::make_pair( + specialization, LibraryMajorVersions(avcodec, avformat, avutil))); + bool success = result.second; + if (!success) { + // Element was not inserted because an entry with the same key already + // exists. Registration is still successful if the parameters are the same. + const LibraryMajorVersions& existing_versions = result.first->second; + success = existing_versions.avcodec == avcodec && + existing_versions.avformat == avformat && + existing_versions.avutil == avutil; + } + SbMutexRelease(&mutex_); + return success; +} + +FFMPEGDispatch* FFMPEGDispatchImpl::get_ffmpeg_dispatch() { + SbMutexAcquire(&mutex_); + if (!ffmpeg_) { + ffmpeg_ = new FFMPEGDispatch(); + // Dynamically load the libraries and retrieve the function pointers. + if (OpenLibraries()) { + LoadSymbols(); + ffmpeg_->av_register_all(); + } + } + SbMutexRelease(&mutex_); + return ffmpeg_; +} + +const int kMaxVersionedLibraryNameLength = 32; + +std::string GetVersionedLibraryName(const char* name, const int version) { + char s[kMaxVersionedLibraryNameLength]; + SbStringFormatF(s, kMaxVersionedLibraryNameLength, "%s.%d", name, version); + return std::string(s); +} + +bool FFMPEGDispatchImpl::OpenLibraries() { + for (auto version_iterator = versions_.rbegin(); + version_iterator != versions_.rend(); ++version_iterator) { + LibraryMajorVersions& versions = version_iterator->second; + avutil_ = dlopen( + GetVersionedLibraryName(kAVUtilLibraryName, versions.avutil).c_str(), + RTLD_NOW | RTLD_GLOBAL); + if (!avutil_) { + SB_DLOG(WARNING) << "Unable to open shared library " + << kAVUtilLibraryName; + continue; + } + + avcodec_ = dlopen( + GetVersionedLibraryName(kAVCodecLibraryName, versions.avcodec).c_str(), + RTLD_NOW | RTLD_GLOBAL); + if (!avcodec_) { + SB_DLOG(WARNING) << "Unable to open shared library " + << kAVCodecLibraryName; + dlclose(avutil_); + avutil_ = NULL; + continue; + } + + avformat_ = + dlopen(GetVersionedLibraryName(kAVFormatLibraryName, versions.avformat) + .c_str(), + RTLD_NOW | RTLD_GLOBAL); + if (!avformat_) { + SB_DLOG(WARNING) << "Unable to open shared library " + << kAVFormatLibraryName; + dlclose(avcodec_); + avcodec_ = NULL; + dlclose(avutil_); + avutil_ = NULL; + continue; + } + SB_DCHECK(is_valid()); + break; + } + return is_valid(); +} + +void FFMPEGDispatchImpl::LoadSymbols() { + SB_DCHECK(is_valid()); +// Load the desired symbols from the shared libraries. Note: If a symbol is +// listed as a '.text' entry in the output of 'objdump -T' on the shared +// library file, then it is directly available from it. + +#define INITSYMBOL(library, symbol) \ + ffmpeg_->symbol = reinterpret_cast<decltype(FFMPEGDispatch::symbol)>( \ + dlsym(library, #symbol)); + + // Load symbols from the avutil shared library. + INITSYMBOL(avutil_, avutil_version); + SB_DCHECK(ffmpeg_->avutil_version); + INITSYMBOL(avutil_, av_malloc); + INITSYMBOL(avutil_, av_freep); + INITSYMBOL(avutil_, av_frame_alloc); + INITSYMBOL(avutil_, av_frame_unref); + INITSYMBOL(avutil_, av_samples_get_buffer_size); + INITSYMBOL(avutil_, av_opt_set_int); + INITSYMBOL(avutil_, av_image_check_size); + INITSYMBOL(avutil_, av_buffer_create); + + // Load symbols from the avcodec shared library. + INITSYMBOL(avcodec_, avcodec_version); + SB_DCHECK(ffmpeg_->avcodec_version); + INITSYMBOL(avcodec_, avcodec_alloc_context3); + INITSYMBOL(avcodec_, avcodec_find_decoder); + INITSYMBOL(avcodec_, avcodec_close); + INITSYMBOL(avcodec_, avcodec_open2); + INITSYMBOL(avcodec_, av_init_packet); + INITSYMBOL(avcodec_, avcodec_decode_audio4); + INITSYMBOL(avcodec_, avcodec_decode_video2); + INITSYMBOL(avcodec_, avcodec_flush_buffers); + INITSYMBOL(avcodec_, avcodec_alloc_frame); + INITSYMBOL(avcodec_, avcodec_get_frame_defaults); + + // Load symbols from the avformat shared library. + INITSYMBOL(avformat_, avformat_version); + SB_DCHECK(ffmpeg_->avformat_version); + INITSYMBOL(avformat_, av_register_all); + SB_DCHECK(ffmpeg_->av_register_all); + +#undef INITSYMBOL +} +} // namespace + +FFMPEGDispatch::FFMPEGDispatch() {} +FFMPEGDispatch::~FFMPEGDispatch() {} + +// static +FFMPEGDispatch* FFMPEGDispatch::GetInstance() { + return FFMPEGDispatchImpl::GetInstance()->get_ffmpeg_dispatch(); +} + +// static +bool FFMPEGDispatch::RegisterSpecialization(int specialization, + int avcodec, + int avformat, + int avutil) { + return FFMPEGDispatchImpl::GetInstance()->RegisterSpecialization( + specialization, avcodec, avformat, avutil); +} + +bool FFMPEGDispatch::is_valid() const { + return FFMPEGDispatchImpl::GetInstance()->is_valid(); +} + +int FFMPEGDispatch::specialization_version() const { + // Return the specialization version, which is the major version of the + // avcodec library plus a single digit indicating if it's ffmpeg or libav, + // derived from the libavcodec micro version number. + return (avcodec_version() >> 16) * 10 + ((avcodec_version() & 0xFF) >= 100); +} + +} // namespace ffmpeg +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc new file mode 100644 index 0000000..7825b72 --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_dynamic_load_video_decoder_impl.cc
@@ -0,0 +1,65 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains the creation of the specialized VideoDecoderImpl object +// corresponding to the version of the dynamically loaded ffmpeg library. + +#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h" + +#include "starboard/memory.h" +#include "starboard/player.h" +#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h" +#include "starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { + +// static +VideoDecoder* VideoDecoder::Create( + SbMediaVideoCodec video_codec, + SbPlayerOutputMode output_mode, + SbDecodeTargetGraphicsContextProvider* + decode_target_graphics_context_provider) { + FFMPEGDispatch* ffmpeg = FFMPEGDispatch::GetInstance(); + if (!ffmpeg || !ffmpeg->is_valid()) { + SB_NOTREACHED(); + return NULL; + } + + VideoDecoder* video_decoder = NULL; + switch (ffmpeg->specialization_version()) { + case 540: + video_decoder = VideoDecoderImpl<540>::Create( + video_codec, output_mode, decode_target_graphics_context_provider); + break; + case 550: + case 560: + video_decoder = VideoDecoderImpl<560>::Create( + video_codec, output_mode, decode_target_graphics_context_provider); + break; + case 571: + video_decoder = VideoDecoderImpl<571>::Create( + video_codec, output_mode, decode_target_graphics_context_provider); + break; + default: + SB_NOTREACHED() << "Unsupported FFMPEG version " << std::hex + << ffmpeg->avutil_version(); + break; + } + return video_decoder; +} +} // namespace ffmpeg +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc new file mode 100644 index 0000000..7c667b3 --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc
@@ -0,0 +1,42 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains the creation of the specialized AudioDecoderImpl object +// corresponding to the version of the linked ffmpeg library. + +#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h" + +#include "starboard/memory.h" +#include "starboard/player.h" +#include "starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h" +#include "starboard/shared/ffmpeg/ffmpeg_common.h" +#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { + +// static +AudioDecoder* AudioDecoder::Create(SbMediaAudioCodec audio_codec, + const SbMediaAudioHeader& audio_header) { + FFMPEGDispatch* ffmpeg = FFMPEGDispatch::GetInstance(); + SB_DCHECK(ffmpeg && ffmpeg->is_valid()); + SB_DCHECK(FFMPEG == ffmpeg->specialization_version()); + + return AudioDecoderImpl<FFMPEG>::Create(audio_codec, audio_header); +} + +} // namespace ffmpeg +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc new file mode 100644 index 0000000..181e224 --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc
@@ -0,0 +1,128 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements the FFMPEGDispatch interface for a library linked +// directly, or which the symbols are already available in the global namespace. + +#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h" + +#include <dlfcn.h> +#include <map> + +#include "starboard/common/scoped_ptr.h" +#include "starboard/log.h" +#include "starboard/once.h" +#include "starboard/shared/ffmpeg/ffmpeg_common.h" +#include "starboard/shared/starboard/lazy_initialization_internal.h" +#include "starboard/string.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { +namespace { + +FFMPEGDispatch* g_ffmpeg_dispatch_impl; +SbOnceControl g_construct_ffmpeg_dispatch_once = SB_ONCE_INITIALIZER; + +void construct_ffmpeg_dispatch() { + g_ffmpeg_dispatch_impl = new FFMPEGDispatch(); +} + +void LoadSymbols(FFMPEGDispatch* ffmpeg) { + SB_DCHECK(ffmpeg->is_valid()); +// Load the desired symbols from the shared libraries. Note: If a symbol is +// listed as a '.text' entry in the output of 'objdump -T' on the shared +// library file, then it is directly available from it. + +#define INITSYMBOL(symbol) \ + ffmpeg->symbol = reinterpret_cast<decltype(FFMPEGDispatch::symbol)>(symbol); + + // Load symbols from the avutil shared library. + INITSYMBOL(avutil_version); + SB_DCHECK(ffmpeg->avutil_version); + INITSYMBOL(av_malloc); + INITSYMBOL(av_freep); +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + INITSYMBOL(av_frame_alloc); + INITSYMBOL(av_frame_unref); +#endif + INITSYMBOL(av_samples_get_buffer_size); + INITSYMBOL(av_opt_set_int); + INITSYMBOL(av_image_check_size); + INITSYMBOL(av_buffer_create); + + // Load symbols from the avcodec shared library. + INITSYMBOL(avcodec_version); + SB_DCHECK(ffmpeg->avcodec_version); + INITSYMBOL(avcodec_alloc_context3); + INITSYMBOL(avcodec_find_decoder); + INITSYMBOL(avcodec_close); + INITSYMBOL(avcodec_open2); + INITSYMBOL(av_init_packet); + INITSYMBOL(avcodec_decode_audio4); + INITSYMBOL(avcodec_decode_video2); + INITSYMBOL(avcodec_flush_buffers); +#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 8, 0) + INITSYMBOL(avcodec_alloc_frame); + INITSYMBOL(avcodec_get_frame_defaults); +#endif + + // Load symbols from the avformat shared library. + INITSYMBOL(avformat_version); + SB_DCHECK(ffmpeg->avformat_version); + INITSYMBOL(av_register_all); + SB_DCHECK(ffmpeg->av_register_all); + +#undef INITSYMBOL +} +} // namespace + +FFMPEGDispatch::FFMPEGDispatch() { + LoadSymbols(this); + av_register_all(); +} + +FFMPEGDispatch::~FFMPEGDispatch() {} + +// static +FFMPEGDispatch* FFMPEGDispatch::GetInstance() { + bool initialized = + SbOnce(&g_construct_ffmpeg_dispatch_once, construct_ffmpeg_dispatch); + SB_DCHECK(initialized); + return g_ffmpeg_dispatch_impl; +} + +// static +bool FFMPEGDispatch::RegisterSpecialization(int specialization, + int avcodec, + int avformat, + int avutil) { + // There is always only a single version of the library linked to the + // application, so there is no need to explicitly track multiple versions. + return true; +} + +bool FFMPEGDispatch::is_valid() const { + // Loading of a linked library is always successful. + return true; +} + +int FFMPEGDispatch::specialization_version() const { + // There is only one version of ffmpeg linked to the binary. + return FFMPEG; +} + +} // namespace ffmpeg +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_linked_video_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_linked_video_decoder_impl.cc new file mode 100644 index 0000000..eb13a07 --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_linked_video_decoder_impl.cc
@@ -0,0 +1,45 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains the creation of the specialized VideoDecoderImpl object +// corresponding to the version of the linked ffmpeg library. + +#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h" + +#include "starboard/memory.h" +#include "starboard/player.h" +#include "starboard/shared/ffmpeg/ffmpeg_common.h" +#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h" +#include "starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { + +// static +VideoDecoder* VideoDecoder::Create( + SbMediaVideoCodec video_codec, + SbPlayerOutputMode output_mode, + SbDecodeTargetGraphicsContextProvider* + decode_target_graphics_context_provider) { + FFMPEGDispatch* ffmpeg = FFMPEGDispatch::GetInstance(); + SB_DCHECK(ffmpeg && ffmpeg->is_valid()); + SB_DCHECK(FFMPEG == ffmpeg->specialization_version()); + + return VideoDecoderImpl<FFMPEG>::Create( + video_codec, output_mode, decode_target_graphics_context_provider); +} +} // namespace ffmpeg +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc deleted file mode 100644 index 3def412..0000000 --- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc +++ /dev/null
@@ -1,473 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h" - -#include "starboard/linux/shared/decode_target_internal.h" -#include "starboard/memory.h" -#include "starboard/thread.h" - -namespace starboard { -namespace shared { -namespace ffmpeg { - -namespace { - -// FFmpeg requires its decoding buffers to align with platform alignment. It -// mentions inside -// http://ffmpeg.org/doxygen/trunk/structAVFrame.html#aa52bfc6605f6a3059a0c3226cc0f6567 -// that the alignment on most modern desktop systems are 16 or 32. -static const int kAlignment = 32; - -size_t AlignUp(size_t size, int alignment) { - SB_DCHECK((alignment & (alignment - 1)) == 0); - return (size + alignment - 1) & ~(alignment - 1); -} - -size_t GetYV12SizeInBytes(int32_t width, int32_t height) { - return width * height * 3 / 2; -} - -#if LIBAVUTIL_VERSION_MAJOR > 52 - -void ReleaseBuffer(void* opaque, uint8_t* data) { - SbMemoryDeallocate(data); -} - -int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame, int flags) { - if (codec_context->pix_fmt != PIX_FMT_YUV420P && - codec_context->pix_fmt != PIX_FMT_YUVJ420P) { - SB_DLOG(WARNING) << "Unsupported pix_fmt " << codec_context->pix_fmt; - return AVERROR(EINVAL); - } - - int ret = - av_image_check_size(codec_context->width, codec_context->height, 0, NULL); - if (ret < 0) { - return ret; - } - - // Align to kAlignment * 2 as we will divide y_stride by 2 for u and v planes - size_t y_stride = AlignUp(codec_context->width, kAlignment * 2); - size_t uv_stride = y_stride / 2; - size_t aligned_height = AlignUp(codec_context->height, kAlignment * 2); - - uint8_t* frame_buffer = reinterpret_cast<uint8_t*>(SbMemoryAllocateAligned( - kAlignment, GetYV12SizeInBytes(y_stride, aligned_height))); - - frame->data[0] = frame_buffer; - frame->linesize[0] = y_stride; - - frame->data[1] = frame_buffer + y_stride * aligned_height; - frame->linesize[1] = uv_stride; - - frame->data[2] = frame->data[1] + uv_stride * aligned_height / 2; - frame->linesize[2] = uv_stride; - - frame->opaque = frame; - frame->width = codec_context->width; - frame->height = codec_context->height; - frame->format = codec_context->pix_fmt; - - frame->reordered_opaque = codec_context->reordered_opaque; - - frame->buf[0] = av_buffer_create(frame_buffer, - GetYV12SizeInBytes(y_stride, aligned_height), - &ReleaseBuffer, frame->opaque, 0); - return 0; -} - -#else // LIBAVUTIL_VERSION_MAJOR > 52 - -int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame) { - if (codec_context->pix_fmt != PIX_FMT_YUV420P && - codec_context->pix_fmt != PIX_FMT_YUVJ420P) { - SB_DLOG(WARNING) << "Unsupported pix_fmt " << codec_context->pix_fmt; - return AVERROR(EINVAL); - } - - int ret = - av_image_check_size(codec_context->width, codec_context->height, 0, NULL); - if (ret < 0) { - return ret; - } - - // Align to kAlignment * 2 as we will divide y_stride by 2 for u and v planes - size_t y_stride = AlignUp(codec_context->width, kAlignment * 2); - size_t uv_stride = y_stride / 2; - size_t aligned_height = AlignUp(codec_context->height, kAlignment * 2); - uint8_t* frame_buffer = reinterpret_cast<uint8_t*>(SbMemoryAllocateAligned( - kAlignment, GetYV12SizeInBytes(y_stride, aligned_height))); - - // y plane - frame->base[0] = frame_buffer; - frame->data[0] = frame->base[0]; - frame->linesize[0] = y_stride; - // u plane - frame->base[1] = frame_buffer + y_stride * aligned_height; - frame->data[1] = frame->base[1]; - frame->linesize[1] = uv_stride; - // v plane - frame->base[2] = frame->base[1] + uv_stride * aligned_height / 2; - frame->data[2] = frame->base[2]; - frame->linesize[2] = uv_stride; - - frame->opaque = frame_buffer; - frame->type = FF_BUFFER_TYPE_USER; - frame->pkt_pts = - codec_context->pkt ? codec_context->pkt->pts : AV_NOPTS_VALUE; - frame->width = codec_context->width; - frame->height = codec_context->height; - frame->format = codec_context->pix_fmt; - - frame->reordered_opaque = codec_context->reordered_opaque; - - return 0; -} - -void ReleaseBuffer(AVCodecContext*, AVFrame* frame) { - SbMemoryDeallocate(frame->opaque); - frame->opaque = NULL; - - // The FFmpeg API expects us to zero the data pointers in this callback. - SbMemorySet(frame->data, 0, sizeof(frame->data)); -} - -#endif // LIBAVUTIL_VERSION_MAJOR > 52 -} // namespace - -VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec, - SbPlayerOutputMode output_mode, - SbDecodeTargetGraphicsContextProvider* - decode_target_graphics_context_provider) - : video_codec_(video_codec), - codec_context_(NULL), - av_frame_(NULL), - stream_ended_(false), - error_occured_(false), - decoder_thread_(kSbThreadInvalid), - output_mode_(output_mode), - decode_target_graphics_context_provider_( - decode_target_graphics_context_provider), - decode_target_(kSbDecodeTargetInvalid) { - InitializeCodec(); -} - -VideoDecoder::~VideoDecoder() { - Reset(); - TeardownCodec(); -} - -void VideoDecoder::Initialize(const DecoderStatusCB& decoder_status_cb, - const ErrorCB& error_cb) { - SB_DCHECK(decoder_status_cb); - SB_DCHECK(!decoder_status_cb_); - SB_DCHECK(error_cb); - SB_DCHECK(!error_cb_); - - decoder_status_cb_ = decoder_status_cb; - error_cb_ = error_cb; -} - -void VideoDecoder::WriteInputBuffer( - const scoped_refptr<InputBuffer>& input_buffer) { - SB_DCHECK(input_buffer); - SB_DCHECK(queue_.Poll().type == kInvalid); - SB_DCHECK(decoder_status_cb_); - - if (stream_ended_) { - SB_LOG(ERROR) << "WriteInputFrame() was called after WriteEndOfStream()."; - return; - } - - if (!SbThreadIsValid(decoder_thread_)) { - decoder_thread_ = - SbThreadCreate(0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true, - "ff_video_dec", &VideoDecoder::ThreadEntryPoint, this); - SB_DCHECK(SbThreadIsValid(decoder_thread_)); - } - - queue_.Put(Event(input_buffer)); -} - -void VideoDecoder::WriteEndOfStream() { - SB_DCHECK(decoder_status_cb_); - - // We have to flush the decoder to decode the rest frames and to ensure that - // Decode() is not called when the stream is ended. - stream_ended_ = true; - - if (!SbThreadIsValid(decoder_thread_)) { - // In case there is no WriteInputBuffer() call before WriteEndOfStream(), - // don't create the decoder thread and send the EOS frame directly. - decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame()); - return; - } - - queue_.Put(Event(kWriteEndOfStream)); -} - -void VideoDecoder::Reset() { - // Join the thread to ensure that all callbacks in process are finished. - if (SbThreadIsValid(decoder_thread_)) { - queue_.Put(Event(kReset)); - SbThreadJoin(decoder_thread_, NULL); - } - - if (codec_context_ != NULL) { - avcodec_flush_buffers(codec_context_); - } - - decoder_thread_ = kSbThreadInvalid; - stream_ended_ = false; - - if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) { - TeardownCodec(); - InitializeCodec(); - } -} - -// static -void* VideoDecoder::ThreadEntryPoint(void* context) { - SB_DCHECK(context); - VideoDecoder* decoder = reinterpret_cast<VideoDecoder*>(context); - decoder->DecoderThreadFunc(); - return NULL; -} - -void VideoDecoder::DecoderThreadFunc() { - for (;;) { - Event event = queue_.Get(); - if (event.type == kReset) { - return; - } - if (error_occured_) { - continue; - } - if (event.type == kWriteInputBuffer) { - // Send |input_buffer| to ffmpeg and try to decode one frame. - AVPacket packet; - av_init_packet(&packet); - packet.data = const_cast<uint8_t*>(event.input_buffer->data()); - packet.size = event.input_buffer->size(); - packet.pts = event.input_buffer->pts(); - codec_context_->reordered_opaque = packet.pts; - - DecodePacket(&packet); - decoder_status_cb_(kNeedMoreInput, NULL); - } else { - SB_DCHECK(event.type == kWriteEndOfStream); - // Stream has ended, try to decode any frames left in ffmpeg. - AVPacket packet; - do { - av_init_packet(&packet); - packet.data = NULL; - packet.size = 0; - packet.pts = 0; - } while (DecodePacket(&packet)); - - decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame()); - } - } -} - -bool VideoDecoder::DecodePacket(AVPacket* packet) { - SB_DCHECK(packet != NULL); - -#if LIBAVUTIL_VERSION_MAJOR > 52 - av_frame_unref(av_frame_); -#else // LIBAVUTIL_VERSION_MAJOR > 52 - avcodec_get_frame_defaults(av_frame_); -#endif // LIBAVUTIL_VERSION_MAJOR > 52 - int frame_decoded = 0; - int decode_result = - avcodec_decode_video2(codec_context_, av_frame_, &frame_decoded, packet); - if (decode_result < 0) { - SB_DLOG(ERROR) << "avcodec_decode_video2() failed with result " - << decode_result; - error_cb_(); - error_occured_ = true; - return false; - } - if (frame_decoded == 0) { - return false; - } - - if (av_frame_->opaque == NULL) { - SB_DLOG(ERROR) << "Video frame was produced yet has invalid frame data."; - error_cb_(); - error_occured_ = true; - return false; - } - - int pitch = AlignUp(av_frame_->width, kAlignment * 2); - - scoped_refptr<CpuVideoFrame> frame = CpuVideoFrame::CreateYV12Frame( - av_frame_->width, av_frame_->height, pitch, av_frame_->reordered_opaque, - av_frame_->data[0], av_frame_->data[1], av_frame_->data[2]); - - bool result = true; - if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) { - result = UpdateDecodeTarget(frame); - } - - decoder_status_cb_(kBufferFull, frame); - - return result; -} - -bool VideoDecoder::UpdateDecodeTarget( - const scoped_refptr<CpuVideoFrame>& frame) { - SbDecodeTarget decode_target = DecodeTargetCreate( - decode_target_graphics_context_provider_, frame, decode_target_); - - // Lock only after the post to the renderer thread, to prevent deadlock. - ScopedLock lock(decode_target_mutex_); - decode_target_ = decode_target; - - if (!SbDecodeTargetIsValid(decode_target)) { - SB_LOG(ERROR) << "Could not acquire a decode target from provider."; - return false; - } - - return true; -} - -void VideoDecoder::InitializeCodec() { - InitializeFfmpeg(); - - codec_context_ = avcodec_alloc_context3(NULL); - - if (codec_context_ == NULL) { - SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; - return; - } - - codec_context_->codec_type = AVMEDIA_TYPE_VIDEO; - codec_context_->codec_id = AV_CODEC_ID_H264; - codec_context_->profile = FF_PROFILE_UNKNOWN; - codec_context_->coded_width = 0; - codec_context_->coded_height = 0; - codec_context_->pix_fmt = PIX_FMT_NONE; - - codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; - codec_context_->thread_count = 2; - codec_context_->opaque = this; - codec_context_->flags |= CODEC_FLAG_EMU_EDGE; -#if LIBAVUTIL_VERSION_MAJOR > 52 - codec_context_->get_buffer2 = AllocateBuffer; -#else // LIBAVUTIL_VERSION_MAJOR > 52 - codec_context_->get_buffer = AllocateBuffer; - codec_context_->release_buffer = ReleaseBuffer; -#endif // LIBAVUTIL_VERSION_MAJOR > 52 - - codec_context_->extradata = NULL; - codec_context_->extradata_size = 0; - - AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); - - if (codec == NULL) { - SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; - TeardownCodec(); - return; - } - - int rv = OpenCodec(codec_context_, codec); - if (rv < 0) { - SB_LOG(ERROR) << "Unable to open codec"; - TeardownCodec(); - return; - } - -#if LIBAVUTIL_VERSION_MAJOR > 52 - av_frame_ = av_frame_alloc(); -#else // LIBAVUTIL_VERSION_MAJOR > 52 - av_frame_ = avcodec_alloc_frame(); -#endif // LIBAVUTIL_VERSION_MAJOR > 52 - if (av_frame_ == NULL) { - SB_LOG(ERROR) << "Unable to allocate audio frame"; - TeardownCodec(); - } -} - -void VideoDecoder::TeardownCodec() { - if (codec_context_) { - CloseCodec(codec_context_); - av_free(codec_context_); - codec_context_ = NULL; - } - if (av_frame_) { - av_free(av_frame_); - av_frame_ = NULL; - } - - if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) { - ScopedLock lock(decode_target_mutex_); - if (SbDecodeTargetIsValid(decode_target_)) { - DecodeTargetRelease(decode_target_graphics_context_provider_, - decode_target_); - decode_target_ = kSbDecodeTargetInvalid; - } - } -} - -// When in decode-to-texture mode, this returns the current decoded video frame. -SbDecodeTarget VideoDecoder::GetCurrentDecodeTarget() { - SB_DCHECK(output_mode_ == kSbPlayerOutputModeDecodeToTexture); - - // We must take a lock here since this function can be called from a - // separate thread. - ScopedLock lock(decode_target_mutex_); - if (SbDecodeTargetIsValid(decode_target_)) { - // Make a disposable copy, since the state is internally reused by this - // class (to avoid recreating GL objects). - return DecodeTargetCopy(decode_target_); - } else { - return kSbDecodeTargetInvalid; - } -} - -} // namespace ffmpeg - -namespace starboard { -namespace player { -namespace filter { - -// static -bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode, - SbMediaVideoCodec codec, - SbDrmSystem drm_system) { - SB_UNREFERENCED_PARAMETER(codec); - SB_UNREFERENCED_PARAMETER(drm_system); - -#if defined(SB_FORCE_DECODE_TO_TEXTURE_ONLY) - // Starboard lib targets may not draw directly to the window, so punch through - // video is not made available. - return output_mode == kSbPlayerOutputModeDecodeToTexture; -#endif // defined(SB_FORCE_DECODE_TO_TEXTURE_ONLY) - - if (output_mode == kSbPlayerOutputModePunchOut || - output_mode == kSbPlayerOutputModeDecodeToTexture) { - return true; - } - - return false; -} - -} // namespace filter -} // namespace player -} // namespace starboard - -} // namespace shared -} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h index 83a527b..64227c5 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h +++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
@@ -15,16 +15,10 @@ #ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_ #define STARBOARD_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_H_ -#include "starboard/common/ref_counted.h" -#include "starboard/log.h" #include "starboard/media.h" -#include "starboard/queue.h" -#include "starboard/shared/ffmpeg/ffmpeg_common.h" +#include "starboard/player.h" #include "starboard/shared/internal_only.h" -#include "starboard/shared/starboard/player/filter/cpu_video_frame.h" #include "starboard/shared/starboard/player/filter/video_decoder_internal.h" -#include "starboard/shared/starboard/player/input_buffer_internal.h" -#include "starboard/thread.h" namespace starboard { namespace shared { @@ -32,98 +26,14 @@ class VideoDecoder : public starboard::player::filter::VideoDecoder { public: - VideoDecoder(SbMediaVideoCodec video_codec, - SbPlayerOutputMode output_mode, - SbDecodeTargetGraphicsContextProvider* - decode_target_graphics_context_provider); - ~VideoDecoder() override; + // Create a video decoder for the currently loaded ffmpeg library. + static VideoDecoder* Create(SbMediaVideoCodec video_codec, + SbPlayerOutputMode output_mode, + SbDecodeTargetGraphicsContextProvider* + decode_target_graphics_context_provider); - void Initialize(const DecoderStatusCB& decoder_status_cb, - const ErrorCB& error_cb) override; - size_t GetPrerollFrameCount() const override { return 8; } - SbTime GetPrerollTimeout() const override { return kSbTimeMax; } - - void WriteInputBuffer(const scoped_refptr<InputBuffer>& input_buffer) - override; - void WriteEndOfStream() override; - void Reset() override; - - bool is_valid() const { return codec_context_ != NULL; } - - private: - typedef ::starboard::shared::starboard::player::filter::CpuVideoFrame - CpuVideoFrame; - - enum EventType { - kInvalid, - kReset, - kWriteInputBuffer, - kWriteEndOfStream, - }; - - struct Event { - EventType type; - // |input_buffer| is only used when |type| is kWriteInputBuffer. - scoped_refptr<InputBuffer> input_buffer; - - explicit Event(EventType type = kInvalid) : type(type) { - SB_DCHECK(type != kWriteInputBuffer); - } - - explicit Event(const scoped_refptr<InputBuffer>& input_buffer) - : type(kWriteInputBuffer), input_buffer(input_buffer) {} - }; - - static void* ThreadEntryPoint(void* context); - void DecoderThreadFunc(); - - bool DecodePacket(AVPacket* packet); - void InitializeCodec(); - void TeardownCodec(); - SbDecodeTarget GetCurrentDecodeTarget() override; - - bool UpdateDecodeTarget(const scoped_refptr<CpuVideoFrame>& frame); - - // |video_codec_| will be initialized inside ctor and won't be changed during - // the life time of this class. - const SbMediaVideoCodec video_codec_; - // The following callbacks will be initialized in Initialize() and won't be - // changed during the life time of this class. - DecoderStatusCB decoder_status_cb_; - ErrorCB error_cb_; - - Queue<Event> queue_; - - // The AV related classes will only be created and accessed on the decoder - // thread. - AVCodecContext* codec_context_; - AVFrame* av_frame_; - - bool stream_ended_; - bool error_occured_; - - // Working thread to avoid lengthy decoding work block the player thread. - SbThread decoder_thread_; - - // Decode-to-texture related state. - SbPlayerOutputMode output_mode_; - - SbDecodeTargetGraphicsContextProvider* - decode_target_graphics_context_provider_; - - // If decode-to-texture is enabled, then we store the decode target texture - // inside of this |decode_target_| member. - SbDecodeTarget decode_target_; - - // GetCurrentDecodeTarget() needs to be called from an arbitrary thread - // to obtain the current decode target (which ultimately ends up being a - // copy of |decode_target_|), we need to safe-guard access to |decode_target_| - // and we do so through this mutex. - Mutex decode_target_mutex_; - // Mutex frame_mutex_; - - // int frame_last_rendered_pts_; - // scoped_refptr<VideoFrame> frame_; + // Returns true if the video decoder is initialized successfully. + virtual bool is_valid() const = 0; }; } // namespace ffmpeg
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc new file mode 100644 index 0000000..233b93e --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.cc
@@ -0,0 +1,490 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains the explicit specialization of the VideoDecoderImpl class +// for the value 'FFMPEG'. + +#include "starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h" + +#include "starboard/linux/shared/decode_target_internal.h" +#include "starboard/memory.h" +#include "starboard/thread.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { + +namespace { + +// FFmpeg requires its decoding buffers to align with platform alignment. It +// mentions inside +// http://ffmpeg.org/doxygen/trunk/structAVFrame.html#aa52bfc6605f6a3059a0c3226cc0f6567 +// that the alignment on most modern desktop systems are 16 or 32. +static const int kAlignment = 32; + +size_t AlignUp(size_t size, int alignment) { + SB_DCHECK((alignment & (alignment - 1)) == 0); + return (size + alignment - 1) & ~(alignment - 1); +} + +size_t GetYV12SizeInBytes(int32_t width, int32_t height) { + return width * height * 3 / 2; +} + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + +void ReleaseBuffer(void* opaque, uint8_t* data) { + SbMemoryDeallocate(data); +} + +int AllocateBufferCallback(AVCodecContext* codec_context, + AVFrame* frame, + int flags) { + VideoDecoderImpl<FFMPEG>* video_decoder = + static_cast<VideoDecoderImpl<FFMPEG>*>(codec_context->opaque); + return video_decoder->AllocateBuffer(codec_context, frame, flags); +} + +#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + +int AllocateBufferCallback(AVCodecContext* codec_context, AVFrame* frame) { + VideoDecoderImpl<FFMPEG>* video_decoder = + static_cast<VideoDecoderImpl<FFMPEG>*>(codec_context->opaque); + return video_decoder->AllocateBuffer(codec_context, frame); +} + +void ReleaseBuffer(AVCodecContext*, AVFrame* frame) { + SbMemoryDeallocate(frame->opaque); + frame->opaque = NULL; + + // The FFmpeg API expects us to zero the data pointers in this callback. + SbMemorySet(frame->data, 0, sizeof(frame->data)); +} +#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + +const bool g_registered = + FFMPEGDispatch::RegisterSpecialization(FFMPEG, + LIBAVCODEC_VERSION_MAJOR, + LIBAVFORMAT_VERSION_MAJOR, + LIBAVUTIL_VERSION_MAJOR); + +} // namespace + +VideoDecoderImpl<FFMPEG>::VideoDecoderImpl( + SbMediaVideoCodec video_codec, + SbPlayerOutputMode output_mode, + SbDecodeTargetGraphicsContextProvider* + decode_target_graphics_context_provider) + : video_codec_(video_codec), + codec_context_(NULL), + av_frame_(NULL), + stream_ended_(false), + error_occured_(false), + decoder_thread_(kSbThreadInvalid), + output_mode_(output_mode), + decode_target_graphics_context_provider_( + decode_target_graphics_context_provider), + decode_target_(kSbDecodeTargetInvalid) { + SB_DCHECK(g_registered) << "Decoder Specialization registration failed."; + ffmpeg_ = FFMPEGDispatch::GetInstance(); + SB_DCHECK(ffmpeg_); + if ((ffmpeg_->specialization_version()) == FFMPEG) { + InitializeCodec(); + } +} + +VideoDecoderImpl<FFMPEG>::~VideoDecoderImpl() { + Reset(); + TeardownCodec(); +} + +// static +VideoDecoder* VideoDecoderImpl<FFMPEG>::Create( + SbMediaVideoCodec video_codec, + SbPlayerOutputMode output_mode, + SbDecodeTargetGraphicsContextProvider* + decode_target_graphics_context_provider) { + return new VideoDecoderImpl<FFMPEG>(video_codec, output_mode, + decode_target_graphics_context_provider); +} + +void VideoDecoderImpl<FFMPEG>::Initialize( + const DecoderStatusCB& decoder_status_cb, + const ErrorCB& error_cb) { + SB_DCHECK(decoder_status_cb); + SB_DCHECK(!decoder_status_cb_); + SB_DCHECK(error_cb); + SB_DCHECK(!error_cb_); + + decoder_status_cb_ = decoder_status_cb; + error_cb_ = error_cb; +} + +void VideoDecoderImpl<FFMPEG>::WriteInputBuffer( + const scoped_refptr<InputBuffer>& input_buffer) { + SB_DCHECK(input_buffer); + SB_DCHECK(queue_.Poll().type == kInvalid); + SB_DCHECK(decoder_status_cb_); + + if (stream_ended_) { + SB_LOG(ERROR) << "WriteInputFrame() was called after WriteEndOfStream()."; + return; + } + + if (!SbThreadIsValid(decoder_thread_)) { + decoder_thread_ = SbThreadCreate( + 0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true, "ff_video_dec", + &VideoDecoderImpl<FFMPEG>::ThreadEntryPoint, this); + SB_DCHECK(SbThreadIsValid(decoder_thread_)); + } + + queue_.Put(Event(input_buffer)); +} + +void VideoDecoderImpl<FFMPEG>::WriteEndOfStream() { + SB_DCHECK(decoder_status_cb_); + + // We have to flush the decoder to decode the rest frames and to ensure that + // Decode() is not called when the stream is ended. + stream_ended_ = true; + + if (!SbThreadIsValid(decoder_thread_)) { + // In case there is no WriteInputBuffer() call before WriteEndOfStream(), + // don't create the decoder thread and send the EOS frame directly. + decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame()); + return; + } + + queue_.Put(Event(kWriteEndOfStream)); +} + +void VideoDecoderImpl<FFMPEG>::Reset() { + // Join the thread to ensure that all callbacks in process are finished. + if (SbThreadIsValid(decoder_thread_)) { + queue_.Put(Event(kReset)); + SbThreadJoin(decoder_thread_, NULL); + } + + if (codec_context_ != NULL) { + ffmpeg_->avcodec_flush_buffers(codec_context_); + } + + decoder_thread_ = kSbThreadInvalid; + stream_ended_ = false; + + if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) { + TeardownCodec(); + InitializeCodec(); + } +} + +bool VideoDecoderImpl<FFMPEG>::is_valid() const { + return (ffmpeg_ != NULL) && ffmpeg_->is_valid() && (codec_context_ != NULL); +} + +// static +void* VideoDecoderImpl<FFMPEG>::ThreadEntryPoint(void* context) { + SB_DCHECK(context); + VideoDecoderImpl<FFMPEG>* decoder = + reinterpret_cast<VideoDecoderImpl<FFMPEG>*>(context); + decoder->DecoderThreadFunc(); + return NULL; +} + +void VideoDecoderImpl<FFMPEG>::DecoderThreadFunc() { + for (;;) { + Event event = queue_.Get(); + if (event.type == kReset) { + return; + } + if (error_occured_) { + continue; + } + if (event.type == kWriteInputBuffer) { + // Send |input_buffer| to ffmpeg and try to decode one frame. + AVPacket packet; + ffmpeg_->av_init_packet(&packet); + packet.data = const_cast<uint8_t*>(event.input_buffer->data()); + packet.size = event.input_buffer->size(); + packet.pts = event.input_buffer->timestamp(); + codec_context_->reordered_opaque = packet.pts; + + DecodePacket(&packet); + decoder_status_cb_(kNeedMoreInput, NULL); + } else { + SB_DCHECK(event.type == kWriteEndOfStream); + // Stream has ended, try to decode any frames left in ffmpeg. + AVPacket packet; + do { + ffmpeg_->av_init_packet(&packet); + packet.data = NULL; + packet.size = 0; + packet.pts = 0; + } while (DecodePacket(&packet)); + + decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame()); + } + } +} + +bool VideoDecoderImpl<FFMPEG>::DecodePacket(AVPacket* packet) { + SB_DCHECK(packet != NULL); + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + ffmpeg_->av_frame_unref(av_frame_); +#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + ffmpeg_->avcodec_get_frame_defaults(av_frame_); +#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + int frame_decoded = 0; + int decode_result = ffmpeg_->avcodec_decode_video2(codec_context_, av_frame_, + &frame_decoded, packet); + if (decode_result < 0) { + SB_DLOG(ERROR) << "avcodec_decode_video2() failed with result " + << decode_result; + error_cb_(); + error_occured_ = true; + return false; + } + if (frame_decoded == 0) { + return false; + } + + if (av_frame_->opaque == NULL) { + SB_DLOG(ERROR) << "Video frame was produced yet has invalid frame data."; + error_cb_(); + error_occured_ = true; + return false; + } + + int pitch = AlignUp(av_frame_->width, kAlignment * 2); + + scoped_refptr<CpuVideoFrame> frame = CpuVideoFrame::CreateYV12Frame( + av_frame_->width, av_frame_->height, pitch, av_frame_->reordered_opaque, + av_frame_->data[0], av_frame_->data[1], av_frame_->data[2]); + + bool result = true; + if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) { + result = UpdateDecodeTarget(frame); + } + + decoder_status_cb_(kBufferFull, frame); + + return result; +} + +bool VideoDecoderImpl<FFMPEG>::UpdateDecodeTarget( + const scoped_refptr<CpuVideoFrame>& frame) { + SbDecodeTarget decode_target = DecodeTargetCreate( + decode_target_graphics_context_provider_, frame, decode_target_); + + // Lock only after the post to the renderer thread, to prevent deadlock. + ScopedLock lock(decode_target_mutex_); + decode_target_ = decode_target; + + if (!SbDecodeTargetIsValid(decode_target)) { + SB_LOG(ERROR) << "Could not acquire a decode target from provider."; + return false; + } + + return true; +} + +void VideoDecoderImpl<FFMPEG>::InitializeCodec() { + codec_context_ = ffmpeg_->avcodec_alloc_context3(NULL); + + if (codec_context_ == NULL) { + SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; + return; + } + + codec_context_->codec_type = AVMEDIA_TYPE_VIDEO; + codec_context_->codec_id = AV_CODEC_ID_H264; + codec_context_->profile = FF_PROFILE_UNKNOWN; + codec_context_->coded_width = 0; + codec_context_->coded_height = 0; + codec_context_->pix_fmt = PIX_FMT_NONE; + + codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; + codec_context_->thread_count = 2; + codec_context_->opaque = this; + codec_context_->flags |= CODEC_FLAG_EMU_EDGE; +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + codec_context_->get_buffer2 = AllocateBufferCallback; +#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + codec_context_->get_buffer = AllocateBufferCallback; + codec_context_->release_buffer = ReleaseBuffer; +#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + + codec_context_->extradata = NULL; + codec_context_->extradata_size = 0; + + AVCodec* codec = ffmpeg_->avcodec_find_decoder(codec_context_->codec_id); + + if (codec == NULL) { + SB_LOG(ERROR) << "Unable to allocate ffmpeg codec context"; + TeardownCodec(); + return; + } + + int rv = ffmpeg_->OpenCodec(codec_context_, codec); + if (rv < 0) { + SB_LOG(ERROR) << "Unable to open codec"; + TeardownCodec(); + return; + } + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + av_frame_ = ffmpeg_->av_frame_alloc(); +#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + av_frame_ = ffmpeg_->avcodec_alloc_frame(); +#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + if (av_frame_ == NULL) { + SB_LOG(ERROR) << "Unable to allocate audio frame"; + TeardownCodec(); + } +} + +void VideoDecoderImpl<FFMPEG>::TeardownCodec() { + if (codec_context_) { + ffmpeg_->CloseCodec(codec_context_); + ffmpeg_->av_freep(&codec_context_); + } + ffmpeg_->av_freep(&av_frame_); + + if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) { + ScopedLock lock(decode_target_mutex_); + if (SbDecodeTargetIsValid(decode_target_)) { + DecodeTargetRelease(decode_target_graphics_context_provider_, + decode_target_); + decode_target_ = kSbDecodeTargetInvalid; + } + } +} + +// When in decode-to-texture mode, this returns the current decoded video frame. +SbDecodeTarget VideoDecoderImpl<FFMPEG>::GetCurrentDecodeTarget() { + SB_DCHECK(output_mode_ == kSbPlayerOutputModeDecodeToTexture); + + // We must take a lock here since this function can be called from a + // separate thread. + ScopedLock lock(decode_target_mutex_); + if (SbDecodeTargetIsValid(decode_target_)) { + // Make a disposable copy, since the state is internally reused by this + // class (to avoid recreating GL objects). + return DecodeTargetCopy(decode_target_); + } else { + return kSbDecodeTargetInvalid; + } +} + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + +int VideoDecoderImpl<FFMPEG>::AllocateBuffer(AVCodecContext* codec_context, + AVFrame* frame, + int flags) { + if (codec_context->pix_fmt != PIX_FMT_YUV420P && + codec_context->pix_fmt != PIX_FMT_YUVJ420P) { + SB_DLOG(WARNING) << "Unsupported pix_fmt " << codec_context->pix_fmt; + return AVERROR(EINVAL); + } + + int ret = ffmpeg_->av_image_check_size(codec_context->width, + codec_context->height, 0, NULL); + if (ret < 0) { + return ret; + } + + // Align to kAlignment * 2 as we will divide y_stride by 2 for u and v planes + size_t y_stride = AlignUp(codec_context->width, kAlignment * 2); + size_t uv_stride = y_stride / 2; + size_t aligned_height = AlignUp(codec_context->height, kAlignment * 2); + + uint8_t* frame_buffer = reinterpret_cast<uint8_t*>(SbMemoryAllocateAligned( + kAlignment, GetYV12SizeInBytes(y_stride, aligned_height))); + + frame->data[0] = frame_buffer; + frame->linesize[0] = y_stride; + + frame->data[1] = frame_buffer + y_stride * aligned_height; + frame->linesize[1] = uv_stride; + + frame->data[2] = frame->data[1] + uv_stride * aligned_height / 2; + frame->linesize[2] = uv_stride; + + frame->opaque = frame; + frame->width = codec_context->width; + frame->height = codec_context->height; + frame->format = codec_context->pix_fmt; + + frame->reordered_opaque = codec_context->reordered_opaque; + + frame->buf[0] = static_cast<AVBufferRef*>(ffmpeg_->av_buffer_create( + frame_buffer, GetYV12SizeInBytes(y_stride, aligned_height), + &ReleaseBuffer, frame->opaque, 0)); + return 0; +} + +#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + +int VideoDecoderImpl<FFMPEG>::AllocateBuffer(AVCodecContext* codec_context, + AVFrame* frame) { + if (codec_context->pix_fmt != PIX_FMT_YUV420P && + codec_context->pix_fmt != PIX_FMT_YUVJ420P) { + SB_DLOG(WARNING) << "Unsupported pix_fmt " << codec_context->pix_fmt; + return AVERROR(EINVAL); + } + + int ret = ffmpeg_->av_image_check_size(codec_context->width, + codec_context->height, 0, NULL); + if (ret < 0) { + return ret; + } + + // Align to kAlignment * 2 as we will divide y_stride by 2 for u and v planes + size_t y_stride = AlignUp(codec_context->width, kAlignment * 2); + size_t uv_stride = y_stride / 2; + size_t aligned_height = AlignUp(codec_context->height, kAlignment * 2); + uint8_t* frame_buffer = reinterpret_cast<uint8_t*>(SbMemoryAllocateAligned( + kAlignment, GetYV12SizeInBytes(y_stride, aligned_height))); + + // y plane + frame->base[0] = frame_buffer; + frame->data[0] = frame->base[0]; + frame->linesize[0] = y_stride; + // u plane + frame->base[1] = frame_buffer + y_stride * aligned_height; + frame->data[1] = frame->base[1]; + frame->linesize[1] = uv_stride; + // v plane + frame->base[2] = frame->base[1] + uv_stride * aligned_height / 2; + frame->data[2] = frame->base[2]; + frame->linesize[2] = uv_stride; + + frame->opaque = frame_buffer; + frame->type = FF_BUFFER_TYPE_USER; + frame->pkt_pts = + codec_context->pkt ? codec_context->pkt->pts : AV_NOPTS_VALUE; + frame->width = codec_context->width; + frame->height = codec_context->height; + frame->format = codec_context->pix_fmt; + + frame->reordered_opaque = codec_context->reordered_opaque; + + return 0; +} +#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + +} // namespace ffmpeg +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h new file mode 100644 index 0000000..86f2cd6 --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_impl.h
@@ -0,0 +1,166 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_IMPL_H_ +#define STARBOARD_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_IMPL_H_ + +#include "starboard/common/ref_counted.h" +#include "starboard/log.h" +#include "starboard/media.h" +#include "starboard/queue.h" +#include "starboard/shared/ffmpeg/ffmpeg_common.h" +#include "starboard/shared/ffmpeg/ffmpeg_dispatch.h" +#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h" +#include "starboard/shared/internal_only.h" +#include "starboard/shared/starboard/player/filter/cpu_video_frame.h" +#include "starboard/shared/starboard/player/filter/video_decoder_internal.h" +#include "starboard/shared/starboard/player/input_buffer_internal.h" +#include "starboard/thread.h" + +namespace starboard { +namespace shared { +namespace ffmpeg { + +// For each version V that is supported, there will be an explicit +// specialization of the VideoDecoder class. +template <int V> +class VideoDecoderImpl : public VideoDecoder { + public: + static VideoDecoder* Create(SbMediaVideoCodec video_codec, + SbPlayerOutputMode output_mode, + SbDecodeTargetGraphicsContextProvider* + decode_target_graphics_context_provider); +}; + +// Forward class declaration of the explicit specialization with value FFMPEG. +template <> +class VideoDecoderImpl<FFMPEG>; + +// Declare the explicit specialization of the class with value FFMPEG. +template <> +class VideoDecoderImpl<FFMPEG> : public VideoDecoder { + public: + VideoDecoderImpl(SbMediaVideoCodec video_codec, + SbPlayerOutputMode output_mode, + SbDecodeTargetGraphicsContextProvider* + decode_target_graphics_context_provider); + ~VideoDecoderImpl() override; + + // From: VideoDecoder + static VideoDecoder* Create(SbMediaVideoCodec video_codec, + SbPlayerOutputMode output_mode, + SbDecodeTargetGraphicsContextProvider* + decode_target_graphics_context_provider); + bool is_valid() const override; + + // From: starboard::player::filter::VideoDecoder + void Initialize(const DecoderStatusCB& decoder_status_cb, + const ErrorCB& error_cb) override; + size_t GetPrerollFrameCount() const override { return 8; } + SbTime GetPrerollTimeout() const override { return kSbTimeMax; } + + void WriteInputBuffer( + const scoped_refptr<InputBuffer>& input_buffer) override; + void WriteEndOfStream() override; + void Reset() override; + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame, int flags); +#else // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + int AllocateBuffer(AVCodecContext* codec_context, AVFrame* frame); +#endif // LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 8, 0) + + private: + typedef ::starboard::shared::starboard::player::filter::CpuVideoFrame + CpuVideoFrame; + + enum EventType { + kInvalid, + kReset, + kWriteInputBuffer, + kWriteEndOfStream, + }; + + struct Event { + EventType type; + // |input_buffer| is only used when |type| is kWriteInputBuffer. + scoped_refptr<InputBuffer> input_buffer; + + explicit Event(EventType type = kInvalid) : type(type) { + SB_DCHECK(type != kWriteInputBuffer); + } + + explicit Event(const scoped_refptr<InputBuffer>& input_buffer) + : type(kWriteInputBuffer), input_buffer(input_buffer) {} + }; + + static void* ThreadEntryPoint(void* context); + void DecoderThreadFunc(); + + bool DecodePacket(AVPacket* packet); + void InitializeCodec(); + void TeardownCodec(); + SbDecodeTarget GetCurrentDecodeTarget() override; + + bool UpdateDecodeTarget(const scoped_refptr<CpuVideoFrame>& frame); + + FFMPEGDispatch* ffmpeg_; + + // |video_codec_| will be initialized inside ctor and won't be changed during + // the life time of this class. + const SbMediaVideoCodec video_codec_; + // The following callbacks will be initialized in Initialize() and won't be + // changed during the life time of this class. + DecoderStatusCB decoder_status_cb_; + ErrorCB error_cb_; + + Queue<Event> queue_; + + // The AV related classes will only be created and accessed on the decoder + // thread. + AVCodecContext* codec_context_; + AVFrame* av_frame_; + + bool stream_ended_; + bool error_occured_; + + // Working thread to avoid lengthy decoding work block the player thread. + SbThread decoder_thread_; + + // Decode-to-texture related state. + SbPlayerOutputMode output_mode_; + + SbDecodeTargetGraphicsContextProvider* + decode_target_graphics_context_provider_; + + // If decode-to-texture is enabled, then we store the decode target texture + // inside of this |decode_target_| member. + SbDecodeTarget decode_target_; + + // GetCurrentDecodeTarget() needs to be called from an arbitrary thread + // to obtain the current decode target (which ultimately ends up being a + // copy of |decode_target_|), we need to safe-guard access to |decode_target_| + // and we do so through this mutex. + Mutex decode_target_mutex_; + // Mutex frame_mutex_; + + // int frame_last_rendered_pts_; + // scoped_refptr<VideoFrame> frame_; +}; + +} // namespace ffmpeg +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_FFMPEG_FFMPEG_VIDEO_DECODER_IMPL_H_
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_internal.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_internal.cc new file mode 100644 index 0000000..7ad130e --- /dev/null +++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder_internal.cc
@@ -0,0 +1,53 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains the implementation of methods from +// starboard::player::filter::VideoDecoder for the VideoDecoder class that are +// the same for dynamically loaded and linked implementation for the ffmpeg +// library. + +#include "starboard/shared/ffmpeg/ffmpeg_video_decoder.h" + +namespace starboard { +namespace shared { +namespace starboard { +namespace player { +namespace filter { + +// static +bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode, + SbMediaVideoCodec codec, + SbDrmSystem drm_system) { + SB_UNREFERENCED_PARAMETER(codec); + SB_UNREFERENCED_PARAMETER(drm_system); + +#if defined(SB_FORCE_DECODE_TO_TEXTURE_ONLY) + // Starboard lib targets may not draw directly to the window, so punch through + // video is not made available. + return output_mode == kSbPlayerOutputModeDecodeToTexture; +#endif // defined(SB_FORCE_DECODE_TO_TEXTURE_ONLY) + + if (output_mode == kSbPlayerOutputModePunchOut || + output_mode == kSbPlayerOutputModeDecodeToTexture) { + return true; + } + + return false; +} + +} // namespace filter +} // namespace player +} // namespace starboard +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/libvpx/vpx_video_decoder.cc b/src/starboard/shared/libvpx/vpx_video_decoder.cc index ee10f8d..bb3c462 100644 --- a/src/starboard/shared/libvpx/vpx_video_decoder.cc +++ b/src/starboard/shared/libvpx/vpx_video_decoder.cc
@@ -206,9 +206,10 @@ SB_DCHECK(context_); - SbMediaTime pts = input_buffer->pts(); - vpx_codec_err_t status = vpx_codec_decode( - context_.get(), input_buffer->data(), input_buffer->size(), &pts, 0); + SbTime timestamp = input_buffer->timestamp(); + vpx_codec_err_t status = + vpx_codec_decode(context_.get(), input_buffer->data(), + input_buffer->size(), ×tamp, 0); if (status != VPX_CODEC_OK) { SB_DLOG(ERROR) << "vpx_codec_decode() failed, status=" << status; ReportError(); @@ -222,7 +223,7 @@ return; } - if (vpx_image->user_priv != &pts) { + if (vpx_image->user_priv != ×tamp) { SB_DLOG(ERROR) << "Invalid output timestamp."; ReportError(); return; @@ -256,7 +257,7 @@ // UV planes have half resolution both vertically and horizontally. scoped_refptr<CpuVideoFrame> frame = CpuVideoFrame::CreateYV12Frame( current_frame_width_, current_frame_height_, - vpx_image->stride[VPX_PLANE_Y], pts, vpx_image->planes[VPX_PLANE_Y], + vpx_image->stride[VPX_PLANE_Y], timestamp, vpx_image->planes[VPX_PLANE_Y], vpx_image->planes[VPX_PLANE_U], vpx_image->planes[VPX_PLANE_V]); if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) { UpdateDecodeTarget(frame);
diff --git a/src/starboard/shared/posix/handle_eintr.h b/src/starboard/shared/posix/handle_eintr.h index 9b568de..c675f3f 100644 --- a/src/starboard/shared/posix/handle_eintr.h +++ b/src/starboard/shared/posix/handle_eintr.h
@@ -21,7 +21,7 @@ #define HANDLE_EINTR(x) \ ({ \ - typeof(x) __eintr_result__; \ + decltype(x) __eintr_result__; \ do { \ __eintr_result__ = (x); \ } while (__eintr_result__ == -1 && errno == EINTR); \
diff --git a/src/starboard/shared/speechd/speechd_internal.cc b/src/starboard/shared/speechd/speechd_internal.cc index 86ca3e0..062ec8d 100644 --- a/src/starboard/shared/speechd/speechd_internal.cc +++ b/src/starboard/shared/speechd/speechd_internal.cc
@@ -14,6 +14,19 @@ #include "starboard/shared/speechd/speechd_internal.h" +#if defined(ADDRESS_SANITIZER) +// By default, Leak Sanitizer and Address Sanitizer is expected to exist +// together. However, this is not true for all platforms. +// HAS_LEAK_SANTIZIER=0 explicitly removes the Leak Sanitizer from code. +#ifndef HAS_LEAK_SANITIZER +#define HAS_LEAK_SANITIZER 1 +#endif // HAS_LEAK_SANITIZER +#endif // defined(ADDRESS_SANITIZER) + +#if HAS_LEAK_SANITIZER +#include <sanitizer/lsan_interface.h> +#endif // HAS_LEAK_SANITIZER + #include "starboard/once.h" #include "starboard/shared/starboard/application.h" @@ -36,7 +49,18 @@ SpeechDispatcher::SpeechDispatcher() { const char* client_name = "starboard_application"; + +#if HAS_LEAK_SANITIZER + // spd_open leaks memory even though spd_close is eventually called. + // Chromium's text-to-speech system for linux ran into the same issue: + // http://crbug.com/317360 + __lsan_disable(); +#endif // HAS_LEAK_SANITIZER connection_ = spd_open(client_name, NULL, NULL, SPD_MODE_THREADED); +#if HAS_LEAK_SANITIZER + __lsan_enable(); +#endif // HAS_LEAK_SANITIZER + if (!connection_) { SB_DLOG(ERROR) << "Failed to initialize SpeechDispatcher."; }
diff --git a/src/starboard/shared/starboard/media/media_util.cc b/src/starboard/shared/starboard/media/media_util.cc index 550dd52..b35455a 100644 --- a/src/starboard/shared/starboard/media/media_util.cc +++ b/src/starboard/shared/starboard/media/media_util.cc
@@ -52,7 +52,7 @@ int GetBytesPerSample(SbMediaAudioSampleType sample_type) { switch (sample_type) { - case kSbMediaAudioSampleTypeInt16: + case kSbMediaAudioSampleTypeInt16Deprecated: return 2; case kSbMediaAudioSampleTypeFloat32: return 4;
diff --git a/src/starboard/shared/starboard/net_log.cc b/src/starboard/shared/starboard/net_log.cc new file mode 100644 index 0000000..dd74ceb --- /dev/null +++ b/src/starboard/shared/starboard/net_log.cc
@@ -0,0 +1,443 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/shared/starboard/net_log.h" + +#include <windows.h> + +#include <algorithm> +#include <deque> +#include <functional> +#include <map> +#include <string> + +#include "starboard/atomic.h" +#include "starboard/common/semaphore.h" +#include "starboard/common/thread.h" +#include "starboard/log.h" +#include "starboard/mutex.h" +#include "starboard/once.h" +#include "starboard/socket.h" +#include "starboard/socket_waiter.h" +#include "starboard/string.h" + +#ifndef NET_LOG_PORT +// Default port was generated from a random number generator. +#define NET_LOG_PORT 49353 +#endif + +// Controls whether using IPv4 or IPv6. +#ifndef NET_LOG_IP_VERSION +#define NET_LOG_IP_VERSION kSbSocketAddressTypeIpv4 +#endif + +// Default Socket write buffer is 100k. +#ifndef NET_LOG_SOCKET_BUFFER_SIZE +#define NET_LOG_SOCKET_BUFFER_SIZE (1024 * 100) +#endif + +// Default in memory write buffer is 1mb. +#ifndef NET_LOG_MAX_IN_MEMORY_BUFFER +#define NET_LOG_MAX_IN_MEMORY_BUFFER (1024 * 1024) +#endif + +// Default block to send to the socket is 1k. +#ifndef NET_LOG_SOCKET_SEND_SIZE +#define NET_LOG_SOCKET_SEND_SIZE 1024 +#endif + +namespace starboard { +namespace shared { +namespace starboard { +namespace { + +using JoinFunction = std::function<void()>; +using RunFunction = std::function<void(Semaphore*, atomic_bool*)>; +using std::placeholders::_1; +using std::placeholders::_2; + +class FunctionThread : public Thread { + public: + static scoped_ptr<Thread> Create( + const std::string& thread_name, + RunFunction run_function, + JoinFunction on_join_called = JoinFunction()) { + scoped_ptr<Thread> out( + new FunctionThread(thread_name, run_function, on_join_called)); + return out.Pass(); + } + + FunctionThread(const std::string& name, + RunFunction run_function, + JoinFunction join_function) + : Thread(name), + run_function_(run_function), + join_function_(join_function) { + } + + void Run() override { + run_function_(join_sema(), joined_bool()); + } + + void Join() override { + if (join_function_) { + join_function_(); + } + Thread::Join(); + } + + private: + RunFunction run_function_; + JoinFunction join_function_; +}; + +scoped_ptr<Socket> CreateListenSocket() { + scoped_ptr<Socket> socket( + new Socket(NET_LOG_IP_VERSION, kSbSocketProtocolTcp)); + socket->SetReuseAddress(true); + SbSocketAddress sock_addr; + // Ip address will be set to 0.0.0.0 so that it will bind to all sockets. + SbMemorySet(&sock_addr, 0, sizeof(SbSocketAddress)); + sock_addr.type = NET_LOG_IP_VERSION; + sock_addr.port = NET_LOG_PORT; + SbSocketError sock_err = socket->Bind(&sock_addr); + + const char kErrFmt[] = "Socket error while attempting to bind, error = %d\n"; + // Can't use SB_LOG_IF() because SB_LOG_IF() might have triggered this call, + // and therefore would triggered reentrant behavior and then deadlock. + // SbLogRawFormat() is assumed to be safe to call. Note that NetLogWrite() + // ignores reentrant calls. + if (sock_err != kSbSocketOk) { + SbLogRawFormatF(kErrFmt, sock_err); + } + sock_err = socket->Listen(); + if (sock_err != kSbSocketOk) { + SbLogRawFormatF(kErrFmt, sock_err); + } + return socket.Pass(); +} + +// This class will listen to the provided socket for a client +// connection. When a client connection is established, a +// callback will be invoked. +class SocketListener { + public: + typedef std::function<void(scoped_ptr<Socket>)> Callback; + + SocketListener(Socket* listen_socket, Callback cb) + : listen_socket_(listen_socket), + callback_(cb) { + RunFunction run_cb = std::bind(&SocketListener::Run, this, _1, _2); + thread_ = FunctionThread::Create("NetLogSocketListener", + run_cb); + thread_->Start(); + } + + ~SocketListener() { + thread_->Join(); + } + + private: + void Run(Semaphore* joined_sema, atomic_bool*) { + while (!joined_sema->TakeWait(100 * kSbTimeMillisecond)) { + scoped_ptr<Socket> client_connection(listen_socket_->Accept()); + + if (client_connection) { + callback_(client_connection.Pass()); + break; + } + } + } + + Socket* listen_socket_; + Callback callback_; + scoped_ptr<Thread> thread_; +}; + +class NetLogServer { + public: + static NetLogServer* Instance(); + NetLogServer() { + ScopedLock lock(socket_mutex_); + listen_socket_ = CreateListenSocket(); + ListenForClient(); + + writer_thread_ = FunctionThread::Create( + "NetLogSocketWriter", + std::bind(&NetLogServer::WriterTask, this, _1, _2), + std::bind(&NetLogServer::OnWriterTaskJoined, this)); + writer_thread_->Start(); + } + + ~NetLogServer() { + SB_NOTREACHED(); // Should never reach here because of singleton use. + Close(); + } + + void ListenForClient() { + SocketListener::Callback cb = std::bind(&NetLogServer::OnClientConnect, + this, + std::placeholders::_1); + socket_listener_.reset(); + socket_listener_.reset(new SocketListener(listen_socket_.get(), cb)); + } + + void OnClientConnect(scoped_ptr<Socket> client_socket) { + ScopedLock lock(socket_mutex_); + client_socket_ = client_socket.Pass(); + client_socket_->SetSendBufferSize(NET_LOG_SOCKET_BUFFER_SIZE); + } + + // Has a client ever connected? + bool HasClientConnected() { + ScopedLock lock(socket_mutex_); + return client_socket_; + } + + void OnLog(const char* msg) { + AppendToLog(msg); + writer_thread_sema_.Put(); + } + + void Close() { + writer_thread_->Join(); + writer_thread_.reset(nullptr); + socket_listener_.reset(); + + InternalFlush(); // One last flush to the socket. + ScopedLock lock(socket_mutex_); + client_socket_.reset(); + listen_socket_.reset(); + } + + bool Flush() { return InternalFlush(); } + + private: + void OnWriterTaskJoined() { + writer_thread_sema_.Put(); + } + + void WriterTask(Semaphore* /*joined_sema*/, atomic_bool* is_joined) { + std::string curr_log_data; + while (true) { + writer_thread_sema_.Take(); + + if (HasClientConnected()) { + if (!Flush()) { + break; // Connection dropped. + } + } + if (is_joined->load()) { + break; + } + } + } + + std::string ToString(SbSocketError error) { + switch (error) { + case kSbSocketOk: { return "kSbSocketOk"; } + case kSbSocketPending: { return "kSbSocketErrorConnectionReset"; } + case kSbSocketErrorFailed: { return "kSbSocketErrorFailed"; } + +#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || \ + SB_API_VERSION >= 9 + case kSbSocketErrorConnectionReset: { + return "kSbSocketErrorConnectionReset"; + } +#endif // SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || + // SB_API_VERSION >= 9 + } + SB_NOTREACHED() << "Unexpected case " << error; + std::stringstream ss; + ss << "Unknown-" << error; + return ss.str(); + } + + // Return false if socket disconnected. + bool InternalFlush() { + ScopedLock lock(socket_mutex_); + if (!client_socket_) { + return false; + } + // Dummy read from client socket. This is required by the socket api. + char buff[2048]; + client_socket_->ReceiveFrom(buff, sizeof(buff), NULL); + std::string curr_write_block; + while (TransferLog(NET_LOG_SOCKET_SEND_SIZE, &curr_write_block)) { + while (!curr_write_block.empty()) { + int bytes_to_write = static_cast<int>(curr_write_block.size()); + int result = client_socket_->SendTo(curr_write_block.c_str(), + bytes_to_write, + NULL); + if (result < 0) { + SbSocketError err = client_socket_->GetLastError(); + client_socket_->ClearLastError(); + if (err == kSbSocketPending) { + // Buffer was full. + SbThreadSleep(kSbTimeMillisecond); + continue; + } else { + // Unexpected error. Clear the current write block to prevent + // endless loop. + SB_LOG(ERROR) << "An error happened while writing to socket: " + << ToString(err); + curr_write_block.clear(); + } + break; + } else if (result == 0) { + // Socket has closed. + return false; + } else { + // Expected condition. Partial or full write was successful. + size_t bytes_written = static_cast<size_t>(result); + SB_DCHECK(bytes_written <= bytes_to_write); + curr_write_block.erase(0, bytes_written); + } + } + } + return true; + } + + bool TransferLog(size_t max_size, std::string* destination) { + ScopedLock lock_log(log_mutex_); + size_t log_size = log_.size(); + if (log_size == 0) { + return false; + } + + size_t size = std::min<size_t>(max_size, log_size); + std::deque<char>::iterator begin_it = log_.begin(); + std::deque<char>::iterator end_it = begin_it; + std::advance(end_it, size); + + destination->assign(begin_it, end_it); + log_.erase(begin_it, end_it); + return true; + } + + void AppendToLog(const char* msg) { + size_t str_len = SbStringGetLength(msg); + bool overflow = false; + log_mutex_.Acquire(); + for (const char* curr = msg; curr != msg + str_len; ++curr) { + log_.push_back(*curr); + if (log_.size() > NET_LOG_MAX_IN_MEMORY_BUFFER) { + overflow = true; + log_.pop_front(); + } + } + log_mutex_.Release(); + + SB_LOG_IF(ERROR, overflow) << "Net log dropped buffer data."; + } + + scoped_ptr<Socket> listen_socket_; + scoped_ptr<Socket> client_socket_; + Mutex socket_mutex_; + std::deque<char> log_; + Mutex log_mutex_; + + scoped_ptr<SocketListener> socket_listener_; + scoped_ptr<Thread> writer_thread_; + Semaphore writer_thread_sema_; +}; + +class ThreadLocalBoolean { + public: + ThreadLocalBoolean() : slot_(SbThreadCreateLocalKey(NULL)) {} + ~ThreadLocalBoolean() { SbThreadDestroyLocalKey(slot_); } + + void Set(bool val) { + void* ptr = val ? reinterpret_cast<void*>(0x1) : nullptr; + SbThreadSetLocalValue(slot_, ptr); + } + + bool Get() const { + void* ptr = SbThreadGetLocalValue(slot_); + return ptr != nullptr; + } + private: + SbThreadLocalKey slot_; + SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean); +}; + +SB_ONCE_INITIALIZE_FUNCTION(NetLogServer, NetLogServer::Instance); +SB_ONCE_INITIALIZE_FUNCTION(ThreadLocalBoolean, ScopeGuardTLB); + +// Prevents re-entrant behavior for sending logs. This is useful if/when +// sub-routines will invoke logging on an error condition. +class ScopeGuard { + public: + ScopeGuard() : disabled_(false), tlb_(ScopeGuardTLB()) { + disabled_ = tlb_->Get(); + tlb_->Set(true); + } + + ~ScopeGuard() { + tlb_->Set(disabled_); + } + + bool IsEnabled() { + return !disabled_; + } + private: + bool disabled_; + ThreadLocalBoolean* tlb_; +}; + +} // namespace. + +const char kNetLogCommandSwitchWait[] = "net_log_wait_for_connection"; + +void NetLogWaitForClientConnected() { +#if !SB_LOGGING_IS_OFFICIAL_BUILD + ScopeGuard guard; + if (guard.IsEnabled()) { + while (!NetLogServer::Instance()->HasClientConnected()) { + SbThreadSleep(kSbTimeMillisecond); + } + } +#endif +} + +void NetLogWrite(const char* data) { +#if !SB_LOGGING_IS_OFFICIAL_BUILD + ScopeGuard guard; + if (guard.IsEnabled()) { + NetLogServer::Instance()->OnLog(data); + } +#endif +} + +void NetLogFlush() { +#if !SB_LOGGING_IS_OFFICIAL_BUILD + ScopeGuard guard; + if (guard.IsEnabled()) { + NetLogServer::Instance()->Flush(); + } +#endif +} + +void NetLogFlushThenClose() { +#if !SB_LOGGING_IS_OFFICIAL_BUILD + ScopeGuard guard; + if (guard.IsEnabled()) { + NetLogServer::Instance()->Close(); + } +#endif +} + +} // namespace starboard +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/starboard/net_log.h b/src/starboard/shared/starboard/net_log.h new file mode 100644 index 0000000..cdf0683 --- /dev/null +++ b/src/starboard/shared/starboard/net_log.h
@@ -0,0 +1,73 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// NetLog is an optional component that allows socket access to starboard +// logs. Installing this component requires that the platform starboard +// implementation call NetLogWrite(...) from the LogRaw(...) and +// LogRawFormat(...) implementations. +// +// It's recommended to call NetLogWaitForClientConnected() to ensure that the +// NetLog is running a predictable time from launch. It's also STRONGLY +// recommended that NetLogFlushThenClose() is called during shutdown to +// guarantee that any pending data is flushed out through the network layer. +// Failure to do this will often result in the netlog being truncated as the +// process shuts down. +// +// The netlog create a server that will bind to ip 0.0.0.0 (and similar in +// ipv6) and will listen for client connections. See net_log.cc for which +// port net_log will listen to. +// +// See also net_log.py for an example script that can connect to a running +// netlog instance. + +#ifndef STARBOARD_SHARED_STARBOARD_NET_LOG_H_ +#define STARBOARD_SHARED_STARBOARD_NET_LOG_H_ + +#include "starboard/common/scoped_ptr.h" +#include "starboard/socket.h" + +namespace starboard { +namespace shared { +namespace starboard { + +// Command line switch useful for determining if NetLogWaitForClientConnected() +// should be called. +extern const char kNetLogCommandSwitchWait[]; + +// Optional - Pauses execution of the current thread until +// a client has connected. +void NetLogWaitForClientConnected(); + +// Writes to the netlog buffer socket stream. +// Note that the NetLog is completely disabled for official +// builds, and the resources will be lazily created on the first +// call. +void NetLogWrite(const char* data); + +// If no client is connected, then this will be a no-op. +// Otherwise blocks until all the pending data is written to the currently +// connected client. +void NetLogFlush(); + +// Optional and may be called during shutdown, this will cause all pending +// data in the Netlog to be flushed before returning. After this call +// the NetLog is effectively disabled and cannot be re-enabled for the life +// of the process. Calling multiple times is safe. +void NetLogFlushThenClose(); + +} // namespace starboard +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_STARBOARD_NET_LOG_H_
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.cc b/src/starboard/shared/starboard/player/decoded_audio_internal.cc index 1c357c3..533d643 100644 --- a/src/starboard/shared/starboard/player/decoded_audio_internal.cc +++ b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
@@ -17,34 +17,50 @@ #include <algorithm> #include "starboard/log.h" +#include "starboard/memory.h" +#include "starboard/shared/starboard/media/media_util.h" namespace starboard { namespace shared { namespace starboard { namespace player { +namespace { + +void ConvertSample(const int16_t* source, float* destination) { + *destination = static_cast<float>(*source) / 32768.f; +} + +void ConvertSample(const float* source, int16_t* destination) { + float sample = std::max(*source, -1.f); + sample = std::min(sample, 1.f); + *destination = static_cast<int16_t>(sample * 32767.f); +} + +} // namespace + DecodedAudio::DecodedAudio() : channels_(0), - sample_type_(kSbMediaAudioSampleTypeInt16), + sample_type_(kSbMediaAudioSampleTypeInt16Deprecated), storage_type_(kSbMediaAudioFrameStorageTypeInterleaved), - pts_(0), + timestamp_(0), size_(0) {} DecodedAudio::DecodedAudio(int channels, SbMediaAudioSampleType sample_type, SbMediaAudioFrameStorageType storage_type, - SbMediaTime pts, + SbTime timestamp, size_t size) : channels_(channels), sample_type_(sample_type), storage_type_(storage_type), - pts_(pts), + timestamp_(timestamp), buffer_(new uint8_t[size]), size_(size) {} int DecodedAudio::frames() const { int bytes_per_sample; - if (sample_type_ == kSbMediaAudioSampleTypeInt16) { + if (sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated) { bytes_per_sample = 2; } else { SB_DCHECK(sample_type_ == kSbMediaAudioSampleTypeFloat32); @@ -66,57 +82,136 @@ return; } - if (storage_type_ != kSbMediaAudioFrameStorageTypeInterleaved || - new_storage_type != kSbMediaAudioFrameStorageTypeInterleaved) { - SB_NOTREACHED(); - // TODO: Implement switching between other storage type pairs. + if (new_storage_type == storage_type_) { + SwitchSampleTypeTo(new_sample_type); return; } - if (sample_type_ == kSbMediaAudioSampleTypeInt16 && + if (new_sample_type == sample_type_) { + SwitchStorageTypeTo(new_storage_type); + return; + } + + // Both sample types and storage types are different, use the slowest way. + size_t new_size = + media::GetBytesPerSample(new_sample_type) * frames() * channels(); + scoped_array<uint8_t> new_buffer(new uint8_t[new_size]); + +#define InterleavedSampleAddr(start_addr, channel, frame) \ + (start_addr + (frame * channels() + channel)) +#define PlanarSampleAddr(start_addr, channel, frame) \ + (start_addr + (channel * frames() + frame)) +#define GetSampleAddr(StorageType, start_addr, channel, frame) \ + (StorageType##SampleAddr(start_addr, channel, frame)) +#define SwitchTo(OldSampleType, OldStorageType, NewSampleType, NewStorageType) \ + do { \ + const OldSampleType* old_samples = \ + reinterpret_cast<OldSampleType*>(buffer_.get()); \ + NewSampleType* new_samples = \ + reinterpret_cast<NewSampleType*>(new_buffer.get()); \ + \ + for (int channel = 0; channel < channels(); ++channel) { \ + for (int frame = 0; frame < frames(); ++frame) { \ + const OldSampleType* old_sample = \ + GetSampleAddr(OldStorageType, old_samples, channel, frame); \ + NewSampleType* new_sample = \ + GetSampleAddr(NewStorageType, new_samples, channel, frame); \ + ConvertSample(old_sample, new_sample); \ + } \ + } \ + } while (false) + + if (sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated && + storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved && new_sample_type == kSbMediaAudioSampleTypeFloat32 && - storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved && - new_storage_type == kSbMediaAudioFrameStorageTypeInterleaved) { - size_t new_size = sizeof(float) * frames() * channels(); - scoped_array<uint8_t> new_buffer(new uint8_t[new_size]); + new_storage_type == kSbMediaAudioFrameStorageTypePlanar) { + SwitchTo(int16_t, Interleaved, float, Planar); + } else if (sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated && + storage_type_ == kSbMediaAudioFrameStorageTypePlanar && + new_sample_type == kSbMediaAudioSampleTypeFloat32 && + new_storage_type == kSbMediaAudioFrameStorageTypeInterleaved) { + SwitchTo(int16_t, Planar, float, Interleaved); + } else if (sample_type_ == kSbMediaAudioSampleTypeFloat32 && + storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved && + new_sample_type == kSbMediaAudioSampleTypeInt16Deprecated && + new_storage_type == kSbMediaAudioFrameStorageTypePlanar) { + SwitchTo(float, Interleaved, int16_t, Planar); + } else if (sample_type_ == kSbMediaAudioSampleTypeFloat32 && + storage_type_ == kSbMediaAudioFrameStorageTypePlanar && + new_sample_type == kSbMediaAudioSampleTypeInt16Deprecated && + new_storage_type == kSbMediaAudioFrameStorageTypeInterleaved) { + SwitchTo(float, Planar, int16_t, Interleaved); + } else { + SB_NOTREACHED(); + } + + buffer_.swap(new_buffer); + sample_type_ = new_sample_type; + storage_type_ = new_storage_type; + size_ = new_size; +} + +void DecodedAudio::SwitchSampleTypeTo(SbMediaAudioSampleType new_sample_type) { + size_t new_size = + media::GetBytesPerSample(new_sample_type) * frames() * channels(); + scoped_array<uint8_t> new_buffer(new uint8_t[new_size]); + + if (sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated && + new_sample_type == kSbMediaAudioSampleTypeFloat32) { + const int16_t* old_samples = reinterpret_cast<int16_t*>(buffer_.get()); float* new_samples = reinterpret_cast<float*>(new_buffer.get()); - int16_t* old_samples = reinterpret_cast<int16_t*>(buffer_.get()); for (int i = 0; i < frames() * channels(); ++i) { - new_samples[i] = static_cast<float>(old_samples[i]) / 32768.f; + ConvertSample(old_samples + i, new_samples + i); } - - buffer_.swap(new_buffer); - sample_type_ = new_sample_type; - size_ = new_size; - - return; - } - - if (sample_type_ == kSbMediaAudioSampleTypeFloat32 && - new_sample_type == kSbMediaAudioSampleTypeInt16 && - storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved && - new_storage_type == kSbMediaAudioFrameStorageTypeInterleaved) { - size_t new_size = sizeof(int16_t) * frames() * channels(); - scoped_array<uint8_t> new_buffer(new uint8_t[new_size]); + } else if (sample_type_ == kSbMediaAudioSampleTypeFloat32 && + new_sample_type == kSbMediaAudioSampleTypeInt16Deprecated) { + const float* old_samples = reinterpret_cast<float*>(buffer_.get()); int16_t* new_samples = reinterpret_cast<int16_t*>(new_buffer.get()); - float* old_samples = reinterpret_cast<float*>(buffer_.get()); for (int i = 0; i < frames() * channels(); ++i) { - float sample = std::max(old_samples[i], -1.f); - sample = std::min(sample, 1.f); - new_samples[i] = static_cast<int16_t>(sample * 32767.f); + ConvertSample(old_samples + i, new_samples + i); } - - buffer_.swap(new_buffer); - sample_type_ = new_sample_type; - size_ = new_size; - - return; } - // TODO: Implement switching between other sample and storage types. - SB_NOTREACHED(); + buffer_.swap(new_buffer); + sample_type_ = new_sample_type; + size_ = new_size; +} + +void DecodedAudio::SwitchStorageTypeTo( + SbMediaAudioFrameStorageType new_storage_type) { + scoped_array<uint8_t> new_buffer(new uint8_t[size_]); + int bytes_per_sample = media::GetBytesPerSample(sample_type_); + uint8_t* old_samples = buffer_.get(); + uint8_t* new_samples = new_buffer.get(); + + if (storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved && + new_storage_type == kSbMediaAudioFrameStorageTypePlanar) { + for (int channel = 0; channel < channels(); ++channel) { + for (int frame = 0; frame < frames(); ++frame) { + uint8_t* old_sample = + old_samples + (frame * channels() + channel) * bytes_per_sample; + uint8_t* new_sample = + new_samples + (channel * frames() + frame) * bytes_per_sample; + SbMemoryCopy(new_sample, old_sample, bytes_per_sample); + } + } + } else if (storage_type_ == kSbMediaAudioFrameStorageTypePlanar && + new_storage_type == kSbMediaAudioFrameStorageTypeInterleaved) { + for (int channel = 0; channel < channels(); ++channel) { + for (int frame = 0; frame < frames(); ++frame) { + uint8_t* old_sample = + old_samples + (channel * frames() + frame) * bytes_per_sample; + uint8_t* new_sample = + new_samples + (frame * channels() + channel) * bytes_per_sample; + SbMemoryCopy(new_sample, old_sample, bytes_per_sample); + } + } + } + + buffer_.swap(new_buffer); + storage_type_ = new_storage_type; } } // namespace player
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.h b/src/starboard/shared/starboard/player/decoded_audio_internal.h index 366f412..88dc78f 100644 --- a/src/starboard/shared/starboard/player/decoded_audio_internal.h +++ b/src/starboard/shared/starboard/player/decoded_audio_internal.h
@@ -36,7 +36,7 @@ DecodedAudio(int channels, SbMediaAudioSampleType sample_type, SbMediaAudioFrameStorageType storage_type, - SbMediaTime pts, + SbTime timestamp, size_t size); int channels() const { return channels_; } @@ -44,7 +44,7 @@ SbMediaAudioFrameStorageType storage_type() const { return storage_type_; } bool is_end_of_stream() const { return buffer_ == NULL; } - SbMediaTime pts() const { return pts_; } + SbTime timestamp() const { return timestamp_; } const uint8_t* buffer() const { return buffer_.get(); } size_t size() const { return size_; } @@ -56,11 +56,14 @@ void ShrinkTo(size_t new_size); private: + void SwitchSampleTypeTo(SbMediaAudioSampleType new_sample_type); + void SwitchStorageTypeTo(SbMediaAudioFrameStorageType new_storage_type); + int channels_; SbMediaAudioSampleType sample_type_; SbMediaAudioFrameStorageType storage_type_; // The timestamp of the first audio frame. - SbMediaTime pts_; + SbTime timestamp_; // Use scoped_array<uint8_t> instead of std::vector<uint8_t> to avoid wasting // time on setting content to 0. scoped_array<uint8_t> buffer_;
diff --git a/src/starboard/shared/starboard/player/filter/audio_frame_tracker.cc b/src/starboard/shared/starboard/player/filter/audio_frame_tracker.cc index fa37439..c6c4ebc 100644 --- a/src/starboard/shared/starboard/player/filter/audio_frame_tracker.cc +++ b/src/starboard/shared/starboard/player/filter/audio_frame_tracker.cc
@@ -29,29 +29,28 @@ namespace player { namespace filter { -AudioFrameTracker::Impl::Impl() : frames_played_adjusted_to_playback_rate_(0) {} - -void AudioFrameTracker::Impl::Reset() { - while (!frame_records_.empty()) { - frame_records_.pop(); - } +void AudioFrameTracker::Reset() { + frame_records_.clear(); frames_played_adjusted_to_playback_rate_ = 0; } -void AudioFrameTracker::Impl::AddFrames(int number_of_frames, - double playback_rate) { +void AudioFrameTracker::AddFrames(int number_of_frames, double playback_rate) { SB_DCHECK(playback_rate > 0); + if (number_of_frames == 0) { + return; + } + if (frame_records_.empty() || frame_records_.back().playback_rate != playback_rate) { FrameRecord record = {number_of_frames, playback_rate}; - frame_records_.push(record); + frame_records_.push_back(record); } else { frame_records_.back().number_of_frames += number_of_frames; } } -void AudioFrameTracker::Impl::RecordPlayedFrames(int number_of_frames) { +void AudioFrameTracker::RecordPlayedFrames(int number_of_frames) { while (number_of_frames > 0 && !frame_records_.empty()) { FrameRecord& record = frame_records_.front(); if (record.number_of_frames > number_of_frames) { @@ -63,13 +62,33 @@ number_of_frames -= record.number_of_frames; frames_played_adjusted_to_playback_rate_ += static_cast<int>(record.number_of_frames * record.playback_rate); - frame_records_.pop(); + frame_records_.erase(frame_records_.begin()); } } + SB_LOG_IF(ERROR, number_of_frames != 0) + << "played frames overflow " << number_of_frames; } -int64_t AudioFrameTracker::Impl::GetFramesPlayedAdjustedToPlaybackRate() const { - return frames_played_adjusted_to_playback_rate_; +int64_t AudioFrameTracker::GetFutureFramesPlayedAdjustedToPlaybackRate( + int number_of_frames) const { + auto frames_played = frames_played_adjusted_to_playback_rate_; + for (auto& record : frame_records_) { + if (number_of_frames == 0) { + break; + } + + if (record.number_of_frames > number_of_frames) { + frames_played += + static_cast<int>(number_of_frames * record.playback_rate); + number_of_frames = 0; + } else { + number_of_frames -= record.number_of_frames; + frames_played += + static_cast<int>(record.number_of_frames * record.playback_rate); + } + } + + return frames_played; } } // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/audio_frame_tracker.h b/src/starboard/shared/starboard/player/filter/audio_frame_tracker.h index 5bb82da..4ccc09c 100644 --- a/src/starboard/shared/starboard/player/filter/audio_frame_tracker.h +++ b/src/starboard/shared/starboard/player/filter/audio_frame_tracker.h
@@ -15,12 +15,11 @@ #ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_FRAME_TRACKER_H_ #define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_FRAME_TRACKER_H_ -#include <queue> +#include <vector> #include "starboard/common/scoped_ptr.h" #include "starboard/log.h" #include "starboard/media.h" -#include "starboard/mutex.h" #include "starboard/shared/internal_only.h" #include "starboard/shared/starboard/thread_checker.h" @@ -39,64 +38,21 @@ public: // Reset the class to its initial state. In this state there is no frames // tracked and the playback frames is 0. - void Reset() { - ScopedLock lock(mutex_); - impl_.Reset(); - } - - void AddFrames(int number_of_frames, double playback_rate) { - if (number_of_frames == 0) { - return; - } - - ScopedLock lock(mutex_); - impl_.AddFrames(number_of_frames, playback_rate); - } - - void RecordPlayedFrames(int number_of_frames) { - if (number_of_frames == 0) { - return; - } - ScopedLock lock(mutex_); - impl_.RecordPlayedFrames(number_of_frames); - } - + void Reset(); + void AddFrames(int number_of_frames, double playback_rate); + void RecordPlayedFrames(int number_of_frames); int64_t GetFutureFramesPlayedAdjustedToPlaybackRate( - int number_of_frames) const { - Impl impl_copy; - { - ScopedLock lock(mutex_); - impl_copy = impl_; - } - - impl_copy.RecordPlayedFrames(number_of_frames); - - return impl_copy.GetFramesPlayedAdjustedToPlaybackRate(); - } + int number_of_frames) const; private: - // Impl carries the core functionalities of AudioFrameTracker. It doesn't - // have any synchronization. - class Impl { - public: - Impl(); - void Reset(); - void AddFrames(int number_of_frames, double playback_rate); - void RecordPlayedFrames(int number_of_frames); - int64_t GetFramesPlayedAdjustedToPlaybackRate() const; - - private: - struct FrameRecord { - int number_of_frames; - double playback_rate; - }; - - std::queue<FrameRecord> frame_records_; - int64_t frames_played_adjusted_to_playback_rate_; + struct FrameRecord { + int number_of_frames; + double playback_rate; }; - Mutex mutex_; - Impl impl_; + // Usually there are very few elements, so std::vector<> is efficient enough. + std::vector<FrameRecord> frame_records_; + int64_t frames_played_adjusted_to_playback_rate_ = 0; }; } // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc index b961c03..b2855ad 100644 --- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc +++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
@@ -51,24 +51,28 @@ // AudioRenderer uses AudioTimeStretcher internally to adjust to playback rate // and AudioTimeStretcher can only process float32 samples. So we try to use -// kSbMediaAudioSampleTypeFloat32 and only use kSbMediaAudioSampleTypeInt16 when -// float32 is not supported. To use kSbMediaAudioSampleTypeFloat32 will cause -// an extra conversion from float32 to int16 before the samples are sent to the -// audio sink. +// kSbMediaAudioSampleTypeFloat32 and only use +// kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported. To use +// kSbMediaAudioSampleTypeFloat32 will cause an extra conversion from float32 to +// int16 before the samples are sent to the audio sink. SbMediaAudioSampleType GetSinkAudioSampleType( AudioRendererSink* audio_renderer_sink) { return audio_renderer_sink->IsAudioSampleTypeSupported( kSbMediaAudioSampleTypeFloat32) ? kSbMediaAudioSampleTypeFloat32 - : kSbMediaAudioSampleTypeInt16; + : kSbMediaAudioSampleTypeInt16Deprecated; } } // namespace AudioRenderer::AudioRenderer(scoped_ptr<AudioDecoder> decoder, scoped_ptr<AudioRendererSink> audio_renderer_sink, - const SbMediaAudioHeader& audio_header) - : eos_state_(kEOSNotReceived), + const SbMediaAudioHeader& audio_header, + int max_cached_frames, + int max_frames_per_append) + : max_cached_frames_(max_cached_frames), + max_frames_per_append_(max_frames_per_append), + eos_state_(kEOSNotReceived), channels_(audio_header.number_of_channels), sink_sample_type_(GetSinkAudioSampleType(audio_renderer_sink.get())), bytes_per_frame_(media::GetBytesPerSample(sink_sample_type_) * channels_), @@ -76,8 +80,9 @@ paused_(true), consume_frames_called_(false), seeking_(false), - seeking_to_pts_(0), - frame_buffer_(kMaxCachedFrames * bytes_per_frame_), + seeking_to_time_(0), + last_time_(0), + frame_buffer_(max_cached_frames_ * bytes_per_frame_), frames_sent_to_sink_(0), pending_decoder_outputs_(0), frames_consumed_by_sink_(0), @@ -89,6 +94,8 @@ first_input_written_(false), audio_renderer_sink_(audio_renderer_sink.Pass()) { SB_DCHECK(decoder_ != NULL); + SB_DCHECK(max_frames_per_append_ > 0); + SB_DCHECK(max_cached_frames_ >= max_frames_per_append_ * 2); frame_buffers_[0] = &frame_buffer_[0]; @@ -119,8 +126,8 @@ SB_DCHECK(input_buffer); SB_DCHECK(can_accept_more_data_); - if (eos_state_.load() >= kEOSWrittenToDecoder) { - SB_LOG(ERROR) << "Appending audio sample at " << input_buffer->pts() + if (eos_state_ >= kEOSWrittenToDecoder) { + SB_LOG(ERROR) << "Appending audio sample at " << input_buffer->timestamp() << " after EOS reached."; return; } @@ -139,14 +146,15 @@ // SB_DCHECK(can_accept_more_data_); // can_accept_more_data_ = false; - if (eos_state_.load() >= kEOSWrittenToDecoder) { + if (eos_state_ >= kEOSWrittenToDecoder) { SB_LOG(ERROR) << "Try to write EOS after EOS is reached"; return; } decoder_->WriteEndOfStream(); - eos_state_.store(kEOSWrittenToDecoder); + ScopedLock lock(mutex_); + eos_state_ = kEOSWrittenToDecoder; first_input_written_ = true; } @@ -155,65 +163,83 @@ audio_renderer_sink_->SetVolume(volume); } +bool AudioRenderer::IsEndOfStreamWritten() const { + SB_DCHECK(BelongsToCurrentThread()); + return eos_state_ >= kEOSWrittenToDecoder; +} + bool AudioRenderer::IsEndOfStreamPlayed() const { - return eos_state_.load() >= kEOSSentToSink && - frames_sent_to_sink_.load() == frames_consumed_by_sink_.load(); + ScopedLock lock(mutex_); + return IsEndOfStreamPlayed_Locked(); } bool AudioRenderer::CanAcceptMoreData() const { SB_DCHECK(BelongsToCurrentThread()); - return eos_state_.load() == kEOSNotReceived && can_accept_more_data_ && + return eos_state_ == kEOSNotReceived && can_accept_more_data_ && (!decoder_sample_rate_ || !time_stretcher_.IsQueueFull()); } bool AudioRenderer::IsSeekingInProgress() const { SB_DCHECK(BelongsToCurrentThread()); - return seeking_.load(); + return seeking_; } void AudioRenderer::Play() { SB_DCHECK(BelongsToCurrentThread()); - paused_.store(false); - consume_frames_called_.store(false); + ScopedLock lock(mutex_); + paused_ = false; + consume_frames_called_ = false; } void AudioRenderer::Pause() { SB_DCHECK(BelongsToCurrentThread()); - paused_.store(true); + ScopedLock lock(mutex_); + paused_ = true; } void AudioRenderer::SetPlaybackRate(double playback_rate) { SB_DCHECK(BelongsToCurrentThread()); - if (playback_rate_.load() == 0.f && playback_rate > 0.f) { - consume_frames_called_.store(false); + ScopedLock lock(mutex_); + + if (playback_rate_ == 0.f && playback_rate > 0.f) { + consume_frames_called_ = false; } - playback_rate_.store(playback_rate); + playback_rate_ = playback_rate; - audio_renderer_sink_->SetPlaybackRate(playback_rate_.load() > 0.0 ? 1.0 - : 0.0); + audio_renderer_sink_->SetPlaybackRate(playback_rate_ > 0.0 ? 1.0 : 0.0); if (audio_renderer_sink_->HasStarted()) { // TODO: Remove SetPlaybackRate() support from audio sink as it only need to // support play/pause. - if (playback_rate_.load() > 0.0) { + if (playback_rate_ > 0.0) { if (process_audio_data_job_token_.is_valid()) { RemoveJobByToken(process_audio_data_job_token_); process_audio_data_job_token_.ResetToInvalid(); } - ProcessAudioData(); + process_audio_data_job_token_ = Schedule(process_audio_data_job_); } } } -void AudioRenderer::Seek(SbMediaTime seek_to_pts) { +void AudioRenderer::Seek(SbTime seek_to_time) { SB_DCHECK(BelongsToCurrentThread()); - SB_DCHECK(seek_to_pts >= 0); + SB_DCHECK(seek_to_time >= 0); audio_renderer_sink_->Stop(); + { + // Set the following states under a lock first to ensure that from now on + // GetCurrentMediaTime() returns |seeking_to_time_|. + ScopedLock scoped_lock(mutex_); + eos_state_ = kEOSNotReceived; + seeking_to_time_ = std::max<SbTime>(seek_to_time, 0); + last_time_ = seek_to_time; + seeking_ = true; + } + // Now the sink is stopped and the callbacks will no longer be called, so the // following modifications are safe without lock. if (resampler_) { @@ -221,18 +247,21 @@ time_stretcher_.FlushBuffers(); } - eos_state_.store(kEOSNotReceived); - seeking_to_pts_ = std::max<SbMediaTime>(seek_to_pts, 0); - seeking_.store(true); - frames_sent_to_sink_.store(0); - frames_consumed_by_sink_.store(0); - frames_consumed_by_sink_since_last_get_current_time_.store(0); + frames_sent_to_sink_ = 0; + frames_consumed_by_sink_ = 0; + frames_consumed_by_sink_since_last_get_current_time_ = 0; pending_decoder_outputs_ = 0; audio_frame_tracker_.Reset(); - frames_consumed_set_at_.store(SbTimeGetMonotonicNow()); + frames_consumed_set_at_ = SbTimeGetMonotonicNow(); can_accept_more_data_ = true; process_audio_data_job_token_.ResetToInvalid(); + is_eos_reached_on_sink_thread_ = false; + is_playing_on_sink_thread_ = false; + frames_in_buffer_on_sink_thread_ = 0; + offset_in_frames_on_sink_thread_ = 0; + frames_consumed_on_sink_thread_ = 0; + if (first_input_written_) { decoder_->Reset(); decoder_sample_rate_ = nullopt; @@ -246,68 +275,148 @@ } } -SbMediaTime AudioRenderer::GetCurrentMediaTime(bool* is_playing, - bool* is_eos_played) { +SbTime AudioRenderer::GetCurrentMediaTime(bool* is_playing, + bool* is_eos_played) { SB_DCHECK(is_playing); SB_DCHECK(is_eos_played); - *is_playing = !paused_.load() && !seeking_.load(); - *is_eos_played = IsEndOfStreamPlayed(); - - if (seeking_.load() || !decoder_sample_rate_) { - return seeking_to_pts_; - } - - audio_frame_tracker_.RecordPlayedFrames( - frames_consumed_by_sink_since_last_get_current_time_.exchange(0)); - + SbTime media_sb_time = 0; + SbTimeMonotonic now = -1; SbTimeMonotonic elasped_since_last_set = 0; - // When the audio sink is transitioning from pause to play, it may come with a - // long delay. So ensure that ConsumeFrames() is called after Play() before - // taking elapsed time into account. - if (!paused_.load() && playback_rate_.load() > 0.f && - consume_frames_called_.load()) { - elasped_since_last_set = - SbTimeGetMonotonicNow() - frames_consumed_set_at_.load(); - } - int samples_per_second = *decoder_sample_rate_; - int64_t elapsed_frames = - elasped_since_last_set * samples_per_second / kSbTimeSecond; - int64_t frames_played = - audio_frame_tracker_.GetFutureFramesPlayedAdjustedToPlaybackRate( - elapsed_frames); + int64_t frames_played = 0; + int samples_per_second = 1; - return seeking_to_pts_ + - frames_played * kSbMediaTimeSecond / samples_per_second; + { + ScopedLock scoped_lock(mutex_); + + *is_playing = !paused_ && !seeking_; + *is_eos_played = IsEndOfStreamPlayed_Locked(); + + if (seeking_ || !decoder_sample_rate_) { + return seeking_to_time_; + } + + if (frames_consumed_by_sink_since_last_get_current_time_ > 0) { + audio_frame_tracker_.RecordPlayedFrames( + frames_consumed_by_sink_since_last_get_current_time_); + frames_consumed_by_sink_since_last_get_current_time_ = 0; + } + + // When the audio sink is transitioning from pause to play, it may come with + // a long delay. So ensure that ConsumeFrames() is called after Play() + // before taking elapsed time into account. + if (!paused_ && playback_rate_ > 0.f && consume_frames_called_) { + now = SbTimeGetMonotonicNow(); + elasped_since_last_set = now - frames_consumed_set_at_; + } + samples_per_second = *decoder_sample_rate_; + int64_t elapsed_frames = + elasped_since_last_set * samples_per_second / kSbTimeSecond; + frames_played = + audio_frame_tracker_.GetFutureFramesPlayedAdjustedToPlaybackRate( + elapsed_frames); + media_sb_time = + seeking_to_time_ + frames_played * kSbTimeSecond / samples_per_second; + if (media_sb_time < last_time_) { + SB_DLOG(WARNING) << "Audio time runs backwards from " << last_time_ + << " to " << media_sb_time; + media_sb_time = last_time_; + } + last_time_ = media_sb_time; + } + +#if SB_LOG_MEDIA_TIME_STATS + if (system_and_media_time_offset_ < 0 && frames_played > 0) { + system_and_media_time_offset_ = now - media_sb_time; + } + if (system_and_media_time_offset_ > 0) { + SbTime offset = now - media_sb_time; + SbTime diff = std::abs(offset - system_and_media_time_offset_); + max_offset_difference_ = std::max(diff, max_offset_difference_); + SB_LOG(ERROR) << "Media time stats: (" << now << "-" + << frames_consumed_set_at_ << "=" << elasped_since_last_set + << ") => " << frames_played << " => " << media_sb_time + << " diff: " << diff << "/" << max_offset_difference_; + } +#endif // SB_LOG_MEDIA_TIME_STATS + + return media_sb_time; } void AudioRenderer::GetSourceStatus(int* frames_in_buffer, int* offset_in_frames, bool* is_playing, bool* is_eos_reached) { - *is_eos_reached = eos_state_.load() >= kEOSSentToSink; + { + ScopedTryLock lock(mutex_); + if (lock.is_locked()) { + UpdateVariablesOnSinkThread_Locked( + frames_consumed_set_at_on_sink_thread_); + } + } - *is_playing = !paused_.load() && !seeking_.load(); + *is_eos_reached = is_eos_reached_on_sink_thread_; + *is_playing = is_playing_on_sink_thread_; if (*is_playing) { - *frames_in_buffer = static_cast<int>(frames_sent_to_sink_.load() - - frames_consumed_by_sink_.load()); - *offset_in_frames = frames_consumed_by_sink_.load() % kMaxCachedFrames; + *frames_in_buffer = + frames_in_buffer_on_sink_thread_ - frames_consumed_on_sink_thread_; + *offset_in_frames = + (offset_in_frames_on_sink_thread_ + frames_consumed_on_sink_thread_) % + max_cached_frames_; } else { *frames_in_buffer = *offset_in_frames = 0; } } void AudioRenderer::ConsumeFrames(int frames_consumed) { - frames_consumed_by_sink_.fetch_add(frames_consumed); - SB_DCHECK(frames_consumed_by_sink_.load() <= frames_sent_to_sink_.load()); - frames_consumed_by_sink_since_last_get_current_time_.fetch_add( - frames_consumed); - frames_consumed_set_at_.store(SbTimeGetMonotonicNow()); - consume_frames_called_.store(true); + // Note that occasionally thread context switch may cause that the time + // recorded here is several milliseconds later than the time |frames_consumed| + // is recorded. This causes the audio time to drift as much as the difference + // between the two times. + // This is usually not a huge issue as: + // 1. It happens rarely. + // 2. It doesn't accumulate. + // 3. It doesn't affect frame presenting even with a 60fps video. + // However, if this ever becomes a problem, we can smooth it out over multiple + // ConsumeFrames() calls. + auto system_time = SbTimeGetMonotonicNow(); + + ScopedTryLock lock(mutex_); + if (lock.is_locked()) { + frames_consumed_on_sink_thread_ += frames_consumed; + + UpdateVariablesOnSinkThread_Locked(system_time); + } else { + frames_consumed_on_sink_thread_ += frames_consumed; + frames_consumed_set_at_on_sink_thread_ = system_time; + } +} + +void AudioRenderer::UpdateVariablesOnSinkThread_Locked( + SbTime system_time_on_consume_frames) { + mutex_.DCheckAcquired(); + + if (frames_consumed_on_sink_thread_ > 0) { + frames_consumed_by_sink_ += frames_consumed_on_sink_thread_; + SB_DCHECK(frames_consumed_by_sink_ <= frames_sent_to_sink_); + frames_consumed_by_sink_since_last_get_current_time_ += + frames_consumed_on_sink_thread_; + frames_consumed_set_at_ = system_time_on_consume_frames; + consume_frames_called_ = true; + frames_consumed_on_sink_thread_ = 0; + } + + is_eos_reached_on_sink_thread_ = eos_state_ >= kEOSSentToSink; + is_playing_on_sink_thread_ = !paused_ && !seeking_; + frames_in_buffer_on_sink_thread_ = + static_cast<int>(frames_sent_to_sink_ - frames_consumed_by_sink_); + offset_in_frames_on_sink_thread_ = + frames_consumed_by_sink_ % max_cached_frames_; } void AudioRenderer::OnFirstOutput() { + SB_DCHECK(BelongsToCurrentThread()); SB_DCHECK(!decoder_sample_rate_); decoder_sample_rate_ = decoder_->GetSamplesPerSecond(); int destination_sample_rate = @@ -337,13 +446,14 @@ channels_, destination_sample_rate, sink_sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_), - kMaxCachedFrames, this); + max_cached_frames_, this); SB_DCHECK(audio_renderer_sink_->HasStarted()); } void AudioRenderer::LogFramesConsumed() { + SB_DCHECK(BelongsToCurrentThread()); SbTimeMonotonic time_since = - SbTimeGetMonotonicNow() - frames_consumed_set_at_.load(); + SbTimeGetMonotonicNow() - frames_consumed_set_at_; if (time_since > kSbTimeSecond) { SB_DLOG(WARNING) << "|frames_consumed_| has not been updated for " << (time_since / kSbTimeSecond) << "." @@ -353,12 +463,18 @@ Schedule(log_frames_consumed_closure_, kSbTimeSecond); } +bool AudioRenderer::IsEndOfStreamPlayed_Locked() const { + mutex_.DCheckAcquired(); + return eos_state_ >= kEOSSentToSink && + frames_sent_to_sink_ == frames_consumed_by_sink_; +} + void AudioRenderer::OnDecoderConsumed() { SB_DCHECK(BelongsToCurrentThread()); // TODO: Unify EOS and non EOS request once WriteEndOfStream() depends on // CanAcceptMoreData(). - if (eos_state_.load() == kEOSNotReceived) { + if (eos_state_ == kEOSNotReceived) { SB_DCHECK(!can_accept_more_data_); can_accept_more_data_ = true; @@ -391,7 +507,8 @@ // Loop until no audio is appended, i.e. AppendAudioToFrameBuffer() returns // false. - while (AppendAudioToFrameBuffer()) { + bool is_frame_buffer_full = false; + while (AppendAudioToFrameBuffer(&is_frame_buffer_full)) { } while (pending_decoder_outputs_ > 0) { @@ -399,7 +516,7 @@ // There is no room to do any further processing, schedule the function // again for a later time. The delay time is 1/4 of the buffer size. const SbTimeMonotonic delay = - kMaxCachedFrames * kSbTimeSecond / *decoder_sample_rate_ / 4; + max_cached_frames_ * kSbTimeSecond / *decoder_sample_rate_ / 4; process_audio_data_job_token_ = Schedule(process_audio_data_job_, delay); return; } @@ -414,14 +531,17 @@ } if (decoded_audio->is_end_of_stream()) { - SB_DCHECK(eos_state_.load() == kEOSWrittenToDecoder) << eos_state_.load(); - eos_state_.store(kEOSDecoded); - seeking_.store(false); + SB_DCHECK(eos_state_ == kEOSWrittenToDecoder) << eos_state_; + { + ScopedLock lock(mutex_); + eos_state_ = kEOSDecoded; + seeking_ = false; + } resampled_audio = resampler_->WriteEndOfStream(); } else { // Discard any audio data before the seeking target. - if (seeking_.load() && decoded_audio->pts() < seeking_to_pts_) { + if (seeking_ && decoded_audio->timestamp() < seeking_to_time_) { continue; } @@ -434,59 +554,66 @@ // Loop until no audio is appended, i.e. AppendAudioToFrameBuffer() returns // false. - while (AppendAudioToFrameBuffer()) { + while (AppendAudioToFrameBuffer(&is_frame_buffer_full)) { } } - if (seeking_.load() || playback_rate_.load() == 0.0) { + if (seeking_ || playback_rate_ == 0.0) { process_audio_data_job_token_ = Schedule(process_audio_data_job_, 5 * kSbTimeMillisecond); return; } - int64_t frames_in_buffer = - frames_sent_to_sink_.load() - frames_consumed_by_sink_.load(); - if (kMaxCachedFrames - frames_in_buffer < kFrameAppendUnit && - eos_state_.load() < kEOSSentToSink) { + if (is_frame_buffer_full) { // There are still audio data not appended so schedule a callback later. SbTimeMonotonic delay = 0; - if (kMaxCachedFrames - frames_in_buffer < kMaxCachedFrames / 4) { + int64_t frames_in_buffer = frames_sent_to_sink_ - frames_consumed_by_sink_; + if (max_cached_frames_ - frames_in_buffer < max_cached_frames_ / 4) { int frames_to_delay = static_cast<int>( - kMaxCachedFrames / 4 - (kMaxCachedFrames - frames_in_buffer)); + max_cached_frames_ / 4 - (max_cached_frames_ - frames_in_buffer)); delay = frames_to_delay * kSbTimeSecond / *decoder_sample_rate_; } process_audio_data_job_token_ = Schedule(process_audio_data_job_, delay); } } -bool AudioRenderer::AppendAudioToFrameBuffer() { +bool AudioRenderer::AppendAudioToFrameBuffer(bool* is_frame_buffer_full) { SB_DCHECK(BelongsToCurrentThread()); + SB_DCHECK(is_frame_buffer_full); - if (seeking_.load() && time_stretcher_.IsQueueFull()) { - seeking_.store(false); + *is_frame_buffer_full = false; + + if (seeking_ && time_stretcher_.IsQueueFull()) { + ScopedLock lock(mutex_); + seeking_ = false; } - if (seeking_.load() || playback_rate_.load() == 0.0) { + if (seeking_ || playback_rate_ == 0.0) { return false; } - int frames_in_buffer = static_cast<int>(frames_sent_to_sink_.load() - - frames_consumed_by_sink_.load()); + int frames_in_buffer = + static_cast<int>(frames_sent_to_sink_ - frames_consumed_by_sink_); - if (kMaxCachedFrames - frames_in_buffer < kFrameAppendUnit) { + if (max_cached_frames_ - frames_in_buffer < max_frames_per_append_) { + *is_frame_buffer_full = true; return false; } - int offset_to_append = frames_sent_to_sink_.load() % kMaxCachedFrames; + int offset_to_append = frames_sent_to_sink_ % max_cached_frames_; scoped_refptr<DecodedAudio> decoded_audio = - time_stretcher_.Read(kFrameAppendUnit, playback_rate_.load()); + time_stretcher_.Read(max_frames_per_append_, playback_rate_); SB_DCHECK(decoded_audio); - if (decoded_audio->frames() == 0 && eos_state_.load() == kEOSDecoded) { - eos_state_.store(kEOSSentToSink); + + { + ScopedLock lock(mutex_); + if (decoded_audio->frames() == 0 && eos_state_ == kEOSDecoded) { + eos_state_ = kEOSSentToSink; + } + audio_frame_tracker_.AddFrames(decoded_audio->frames(), playback_rate_); } - audio_frame_tracker_.AddFrames(decoded_audio->frames(), - playback_rate_.load()); + // TODO: Support kSbMediaAudioFrameStorageTypePlanar. decoded_audio->SwitchFormatTo(sink_sample_type_, kSbMediaAudioFrameStorageTypeInterleaved); @@ -494,13 +621,13 @@ int frames_to_append = decoded_audio->frames(); int frames_appended = 0; - if (frames_to_append > kMaxCachedFrames - offset_to_append) { + if (frames_to_append > max_cached_frames_ - offset_to_append) { SbMemoryCopy(&frame_buffer_[offset_to_append * bytes_per_frame_], source_buffer, - (kMaxCachedFrames - offset_to_append) * bytes_per_frame_); - source_buffer += (kMaxCachedFrames - offset_to_append) * bytes_per_frame_; - frames_to_append -= kMaxCachedFrames - offset_to_append; - frames_appended += kMaxCachedFrames - offset_to_append; + (max_cached_frames_ - offset_to_append) * bytes_per_frame_); + source_buffer += (max_cached_frames_ - offset_to_append) * bytes_per_frame_; + frames_to_append -= max_cached_frames_ - offset_to_append; + frames_appended += max_cached_frames_ - offset_to_append; offset_to_append = 0; } @@ -508,7 +635,7 @@ source_buffer, frames_to_append * bytes_per_frame_); frames_appended += frames_to_append; - frames_sent_to_sink_.fetch_add(frames_appended); + frames_sent_to_sink_ += frames_appended; return frames_appended > 0; }
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h index 2ea8fc4..7149d98 100644 --- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h +++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -18,11 +18,11 @@ #include <functional> #include <vector> -#include "starboard/atomic.h" #include "starboard/common/optional.h" #include "starboard/common/scoped_ptr.h" #include "starboard/log.h" #include "starboard/media.h" +#include "starboard/mutex.h" #include "starboard/shared/internal_only.h" #include "starboard/shared/starboard/player/decoded_audio_internal.h" #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h" @@ -35,6 +35,10 @@ #include "starboard/shared/starboard/player/job_queue.h" #include "starboard/types.h" +// Uncomment the following statement to log the media time stats with deviation +// when GetCurrentMediaTime() is called. +// #define SB_LOG_MEDIA_TIME_STATS 1 + namespace starboard { namespace shared { namespace starboard { @@ -48,9 +52,18 @@ private AudioRendererSink::RenderCallback, private JobQueue::JobOwner { public: + // |max_cached_frames| is a soft limit for the max audio frames this class can + // cache so it can: + // 1. Avoid using too much memory. + // 2. Have the audio cache full to simulate the state that the renderer can no + // longer accept more data. + // |max_frames_per_append| is the max number of frames that the audio renderer + // tries to append to the sink buffer at once. AudioRenderer(scoped_ptr<AudioDecoder> decoder, scoped_ptr<AudioRendererSink> audio_renderer_sink, - const SbMediaAudioHeader& audio_header); + const SbMediaAudioHeader& audio_header, + int max_cached_frames, + int max_frames_per_append); ~AudioRenderer(); void Initialize(const AudioDecoder::ErrorCB& error_cb); @@ -59,9 +72,7 @@ void SetVolume(double volume); - bool IsEndOfStreamWritten() const { - return eos_state_.load() >= kEOSWrittenToDecoder; - } + bool IsEndOfStreamWritten() const; bool IsEndOfStreamPlayed() const; bool CanAcceptMoreData() const; bool IsSeekingInProgress() const; @@ -70,25 +81,8 @@ void Play() override; void Pause() override; void SetPlaybackRate(double playback_rate) override; - void Seek(SbMediaTime seek_to_pts) override; - SbMediaTime GetCurrentMediaTime(bool* is_playing, - bool* is_eos_played) override; - - protected: - atomic_bool paused_; - atomic_bool consume_frames_called_; - atomic_bool seeking_; - SbMediaTime seeking_to_pts_; - AudioFrameTracker audio_frame_tracker_; - - atomic_int64_t frames_sent_to_sink_; - atomic_int64_t frames_consumed_by_sink_; - atomic_int32_t frames_consumed_by_sink_since_last_get_current_time_; - - scoped_ptr<AudioDecoder> decoder_; - - atomic_int64_t frames_consumed_set_at_; - atomic_double playback_rate_; + void Seek(SbTime seek_to_time) override; + SbTime GetCurrentMediaTime(bool* is_playing, bool* is_eos_played) override; private: enum EOSState { @@ -98,14 +92,26 @@ kEOSSentToSink }; - // Set a soft limit for the max audio frames we can cache so we can: - // 1. Avoid using too much memory. - // 2. Have the audio cache full to simulate the state that the renderer can no - // longer accept more data. - static const size_t kMaxCachedFrames = 256 * 1024; - // The audio renderer tries to append |kAppendFrameUnit| frames every time to - // the sink buffer. - static const size_t kFrameAppendUnit = 16384; + const int max_cached_frames_; + const int max_frames_per_append_; + + Mutex mutex_; + + bool paused_; + bool consume_frames_called_; + bool seeking_; + SbTime seeking_to_time_; + SbTime last_time_; + AudioFrameTracker audio_frame_tracker_; + + int64_t frames_sent_to_sink_; + int64_t frames_consumed_by_sink_; + int32_t frames_consumed_by_sink_since_last_get_current_time_; + + scoped_ptr<AudioDecoder> decoder_; + + int64_t frames_consumed_set_at_; + double playback_rate_; // AudioRendererSink methods void GetSourceStatus(int* frames_in_buffer, @@ -114,16 +120,19 @@ bool* is_eos_reached) override; void ConsumeFrames(int frames_consumed) override; + void UpdateVariablesOnSinkThread_Locked(SbTime system_time_on_consume_frames); + void OnFirstOutput(); void LogFramesConsumed(); + bool IsEndOfStreamPlayed_Locked() const; void OnDecoderConsumed(); void OnDecoderOutput(); void ProcessAudioData(); void FillResamplerAndTimeStretcher(); - bool AppendAudioToFrameBuffer(); + bool AppendAudioToFrameBuffer(bool* is_frame_buffer_full); - atomic_int32_t eos_state_; + EOSState eos_state_; const int channels_; const SbMediaAudioSampleType sink_sample_type_; const int bytes_per_frame_; @@ -142,7 +151,7 @@ JobQueue::JobToken process_audio_data_job_token_; std::function<void()> process_audio_data_job_; - // Our owner will attempt to seek to pts 0 when playback begins. In + // Our owner will attempt to seek to time 0 when playback begins. In // general, seeking could require a full reset of the underlying decoder on // some platforms, so we make an effort to improve playback startup // performance by keeping track of whether we already have a fresh decoder, @@ -150,6 +159,17 @@ bool first_input_written_; scoped_ptr<AudioRendererSink> audio_renderer_sink_; + bool is_eos_reached_on_sink_thread_ = false; + bool is_playing_on_sink_thread_ = false; + int64_t frames_in_buffer_on_sink_thread_ = 0; + int64_t offset_in_frames_on_sink_thread_ = 0; + int64_t frames_consumed_on_sink_thread_ = 0; + SbTime frames_consumed_set_at_on_sink_thread_ = 0; + +#if SB_LOG_MEDIA_TIME_STATS + SbTime system_and_media_time_offset_ = -1; + SbTime max_offset_difference_ = 0; +#endif // SB_LOG_MEDIA_TIME_STATS }; } // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/cpu_video_frame.cc b/src/starboard/shared/starboard/player/filter/cpu_video_frame.cc index f2e0f4d..eb0a5ec 100644 --- a/src/starboard/shared/starboard/player/filter/cpu_video_frame.cc +++ b/src/starboard/shared/starboard/player/filter/cpu_video_frame.cc
@@ -101,7 +101,7 @@ EnsureYUVToRGBLookupTableInitialized(); - scoped_refptr<CpuVideoFrame> target_frame(new CpuVideoFrame(pts())); + scoped_refptr<CpuVideoFrame> target_frame(new CpuVideoFrame(timestamp())); target_frame->format_ = target_format; target_frame->width_ = width(); @@ -158,11 +158,11 @@ scoped_refptr<CpuVideoFrame> CpuVideoFrame::CreateYV12Frame(int width, int height, int pitch_in_bytes, - SbMediaTime pts, + SbTime timestamp, const uint8_t* y, const uint8_t* u, const uint8_t* v) { - scoped_refptr<CpuVideoFrame> frame(new CpuVideoFrame(pts)); + scoped_refptr<CpuVideoFrame> frame(new CpuVideoFrame(timestamp)); frame->format_ = kYV12; frame->width_ = width; frame->height_ = height;
diff --git a/src/starboard/shared/starboard/player/filter/cpu_video_frame.h b/src/starboard/shared/starboard/player/filter/cpu_video_frame.h index e188ab9..f6522da 100644 --- a/src/starboard/shared/starboard/player/filter/cpu_video_frame.h +++ b/src/starboard/shared/starboard/player/filter/cpu_video_frame.h
@@ -55,7 +55,7 @@ const uint8_t* data; }; - explicit CpuVideoFrame(SbMediaTime pts) : VideoFrame(pts) {} + explicit CpuVideoFrame(SbTime timestamp) : VideoFrame(timestamp) {} Format format() const { return format_; } int width() const { return width_; } @@ -69,7 +69,7 @@ static scoped_refptr<CpuVideoFrame> CreateYV12Frame(int width, int height, int pitch_in_bytes, - SbMediaTime pts, + SbTime timestamp, const uint8_t* y, const uint8_t* u, const uint8_t* v);
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc index b2a5220..36719a9 100644 --- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc +++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -62,14 +62,14 @@ audio_header_ = *audio_header; #if SB_API_VERSION >= 6 - if (audio_header_.audio_specific_config_size > 0) { - audio_specific_config_.reset( - new int8_t[audio_header_.audio_specific_config_size]); - audio_header_.audio_specific_config = audio_specific_config_.get(); - SbMemoryCopy(audio_specific_config_.get(), - audio_header_.audio_specific_config, - audio_header_.audio_specific_config_size); - } + if (audio_header_.audio_specific_config_size > 0) { + audio_specific_config_.reset( + new int8_t[audio_header_.audio_specific_config_size]); + SbMemoryCopy(audio_specific_config_.get(), + audio_header->audio_specific_config, + audio_header->audio_specific_config_size); + audio_header_.audio_specific_config = audio_specific_config_.get(); + } #endif // SB_API_VERSION >= 6 } @@ -87,7 +87,12 @@ SbPlayer player, UpdateMediaTimeCB update_media_time_cb, GetPlayerStateCB get_player_state_cb, - UpdatePlayerStateCB update_player_state_cb) { + UpdatePlayerStateCB update_player_state_cb +#if SB_HAS(PLAYER_ERROR_MESSAGE) + , + UpdatePlayerErrorCB update_player_error_cb +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + ) { // This function should only be called once. SB_DCHECK(player_worker_ == NULL); @@ -106,6 +111,9 @@ update_media_time_cb_ = update_media_time_cb; get_player_state_cb_ = get_player_state_cb; update_player_state_cb_ = update_player_state_cb; +#if SB_HAS(PLAYER_ERROR_MESSAGE) + update_player_error_cb_ = update_player_error_cb; +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) scoped_ptr<PlayerComponents> player_components = PlayerComponents::Create(); SB_DCHECK(player_components); @@ -123,10 +131,13 @@ audio_codec_, audio_header_, drm_system_, job_queue_}; audio_renderer_ = player_components->CreateAudioRenderer(audio_parameters); - audio_renderer_->SetPlaybackRate(playback_rate_); - audio_renderer_->SetVolume(volume_); - audio_renderer_->Initialize( - std::bind(&FilterBasedPlayerWorkerHandler::OnError, this)); + SB_DCHECK(audio_renderer_); + if (audio_renderer_) { + audio_renderer_->SetPlaybackRate(playback_rate_); + audio_renderer_->SetVolume(volume_); + audio_renderer_->Initialize( + std::bind(&FilterBasedPlayerWorkerHandler::OnError, this)); + } } else { media_time_provider_impl_.reset( new MediaTimeProviderImpl(scoped_ptr<MonotonicSystemTimeProvider>( @@ -145,15 +156,18 @@ video_renderer_ = player_components->CreateVideoRenderer(video_parameters, media_time_provider); - video_renderer_->Initialize( - std::bind(&FilterBasedPlayerWorkerHandler::OnError, this)); + SB_DCHECK(video_renderer_); + if (video_renderer_) { + video_renderer_->Initialize( + std::bind(&FilterBasedPlayerWorkerHandler::OnError, this)); + } update_job_token_ = job_queue_->Schedule(update_job_, kUpdateInterval); return true; } -bool FilterBasedPlayerWorkerHandler::Seek(SbMediaTime seek_to_pts, int ticket) { +bool FilterBasedPlayerWorkerHandler::Seek(SbTime seek_to_time, int ticket) { SB_UNREFERENCED_PARAMETER(ticket); SB_DCHECK(job_queue_->BelongsToCurrentThread()); @@ -161,14 +175,14 @@ return false; } - if (seek_to_pts < 0) { - SB_DLOG(ERROR) << "Try to seek to negative timestamp " << seek_to_pts; - seek_to_pts = 0; + if (seek_to_time < 0) { + SB_DLOG(ERROR) << "Try to seek to negative timestamp " << seek_to_time; + seek_to_time = 0; } GetMediaTimeProvider()->Pause(); - video_renderer_->Seek(seek_to_pts); - GetMediaTimeProvider()->Seek(seek_to_pts); + video_renderer_->Seek(seek_to_time); + GetMediaTimeProvider()->Seek(seek_to_time); return true; } @@ -199,7 +213,7 @@ return false; } SbDrmSystemPrivate::DecryptStatus decrypt_status = - drm_system_->Decrypt(input_buffer); + drm_system_->Decrypt(input_buffer); if (decrypt_status == SbDrmSystemPrivate::kRetry) { *written = false; return true; @@ -232,7 +246,7 @@ return false; } SbDrmSystemPrivate::DecryptStatus decrypt_status = - drm_system_->Decrypt(input_buffer); + drm_system_->Decrypt(input_buffer); if (decrypt_status == SbDrmSystemPrivate::kRetry) { *written = false; return true; @@ -243,7 +257,8 @@ } } if (media_time_provider_impl_) { - media_time_provider_impl_->UpdateVideoDuration(input_buffer->pts()); + media_time_provider_impl_->UpdateVideoDuration( + input_buffer->timestamp()); } video_renderer_->WriteSample(input_buffer); } @@ -348,7 +363,14 @@ return; } +#if SB_HAS(PLAYER_ERROR_MESSAGE) + if (update_player_error_cb_) { + (*player_worker_.* + update_player_error_cb_)("FilterBasedPlayerWorkerHandler error."); + } +#else // SB_HAS(PLAYER_ERROR_MESSAGE) (*player_worker_.*update_player_state_cb_)(kSbPlayerStateError); +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) } // TODO: This should be driven by callbacks instead polling.
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h index cc5820a..a04000c 100644 --- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h +++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -54,8 +54,13 @@ SbPlayer player, UpdateMediaTimeCB update_media_time_cb, GetPlayerStateCB get_player_state_cb, - UpdatePlayerStateCB update_player_state_cb) override; - bool Seek(SbMediaTime seek_to_pts, int ticket) override; + UpdatePlayerStateCB update_player_state_cb +#if SB_HAS(PLAYER_ERROR_MESSAGE) + , + UpdatePlayerErrorCB update_player_error_cb +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + ) override; + bool Seek(SbTime seek_to_time, int ticket) override; bool WriteSample(const scoped_refptr<InputBuffer>& input_buffer, bool* written) override; bool WriteEndOfStream(SbMediaType sample_type) override; @@ -77,6 +82,9 @@ UpdateMediaTimeCB update_media_time_cb_ = NULL; GetPlayerStateCB get_player_state_cb_ = NULL; UpdatePlayerStateCB update_player_state_cb_ = NULL; +#if SB_HAS(PLAYER_ERROR_MESSAGE) + UpdatePlayerErrorCB update_player_error_cb_ = NULL; +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) SbMediaVideoCodec video_codec_; SbMediaAudioCodec audio_codec_;
diff --git a/src/starboard/shared/starboard/player/filter/media_time_provider.h b/src/starboard/shared/starboard/player/filter/media_time_provider.h index 50c7880..211495e 100644 --- a/src/starboard/shared/starboard/player/filter/media_time_provider.h +++ b/src/starboard/shared/starboard/player/filter/media_time_provider.h
@@ -28,13 +28,12 @@ virtual void Play() = 0; virtual void Pause() = 0; virtual void SetPlaybackRate(double playback_rate) = 0; - virtual void Seek(SbMediaTime seek_to_pts) = 0; + virtual void Seek(SbTime seek_to_pts) = 0; // This function can be called from *any* thread. - virtual SbMediaTime GetCurrentMediaTime(bool* is_playing, - bool* is_eos_played) = 0; + virtual SbTime GetCurrentMediaTime(bool* is_playing, bool* is_eos_played) = 0; protected: - ~MediaTimeProvider() {} + virtual ~MediaTimeProvider() {} }; } // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/media_time_provider_impl.cc b/src/starboard/shared/starboard/player/filter/media_time_provider_impl.cc index bf66b99..e525073 100644 --- a/src/starboard/shared/starboard/player/filter/media_time_provider_impl.cc +++ b/src/starboard/shared/starboard/player/filter/media_time_provider_impl.cc
@@ -36,7 +36,7 @@ } ScopedLock scoped_lock(mutex_); - seek_to_pts_ = GetCurrentMediaTime_Locked(&seek_to_pts_set_at_); + seek_to_time_ = GetCurrentMediaTime_Locked(&seek_to_time_set_at_); playback_rate_ = playback_rate; } @@ -48,7 +48,7 @@ } ScopedLock scoped_lock(mutex_); - seek_to_pts_ = GetCurrentMediaTime_Locked(&seek_to_pts_set_at_); + seek_to_time_ = GetCurrentMediaTime_Locked(&seek_to_time_set_at_); is_playing_ = true; } @@ -60,35 +60,37 @@ } ScopedLock scoped_lock(mutex_); - seek_to_pts_ = GetCurrentMediaTime_Locked(&seek_to_pts_set_at_); + seek_to_time_ = GetCurrentMediaTime_Locked(&seek_to_time_set_at_); is_playing_ = false; } -void MediaTimeProviderImpl::Seek(SbMediaTime seek_to_pts) { +void MediaTimeProviderImpl::Seek(SbTime seek_to_time) { SB_DCHECK(thread_checker_.CalledOnValidThread()); ScopedLock scoped_lock(mutex_); - seek_to_pts_ = seek_to_pts; - seek_to_pts_set_at_ = system_time_provider_->GetMonotonicNow(); - video_duration_ = kSbMediaTimeInvalid; + seek_to_time_ = seek_to_time; + seek_to_time_set_at_ = system_time_provider_->GetMonotonicNow(); + video_duration_ = nullopt; is_video_end_of_stream_reached_ = false; } -SbMediaTime MediaTimeProviderImpl::GetCurrentMediaTime(bool* is_playing, - bool* is_eos_played) { +SbTime MediaTimeProviderImpl::GetCurrentMediaTime(bool* is_playing, + bool* is_eos_played) { ScopedLock scoped_lock(mutex_); - SbMediaTime current = GetCurrentMediaTime_Locked(); + SbTime current = GetCurrentMediaTime_Locked(); *is_playing = is_playing_; *is_eos_played = - is_video_end_of_stream_reached_ && current >= video_duration_; + is_video_end_of_stream_reached_ && + (!video_duration_.has_engaged() || current >= video_duration_.value()); return current; } -void MediaTimeProviderImpl::UpdateVideoDuration(SbMediaTime video_duration) { +void MediaTimeProviderImpl::UpdateVideoDuration( + optional<SbTime> video_duration) { ScopedLock scoped_lock(mutex_); video_duration_ = video_duration; } @@ -96,12 +98,12 @@ void MediaTimeProviderImpl::VideoEndOfStreamReached() { ScopedLock scoped_lock(mutex_); is_video_end_of_stream_reached_ = true; - if (video_duration_ == kSbMediaTimeInvalid) { - video_duration_ = seek_to_pts_; + if (!video_duration_.has_engaged()) { + video_duration_ = seek_to_time_; } } -SbMediaTime MediaTimeProviderImpl::GetCurrentMediaTime_Locked( +SbTime MediaTimeProviderImpl::GetCurrentMediaTime_Locked( SbTimeMonotonic* current_time /*= NULL*/) { mutex_.DCheckAcquired(); @@ -111,16 +113,14 @@ if (current_time) { *current_time = now; } - return seek_to_pts_; + return seek_to_time_; } - SbTimeMonotonic elapsed = (now - seek_to_pts_set_at_) * playback_rate_; - // Convert |elapsed| in microseconds to SbMediaTime in 90hz. - SbMediaTime elapsed_in_media_time = elapsed * 9 / 100; + SbTimeMonotonic elapsed = (now - seek_to_time_set_at_) * playback_rate_; if (current_time) { *current_time = now; } - return seek_to_pts_ + elapsed_in_media_time; + return seek_to_time_ + elapsed; } } // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/media_time_provider_impl.h b/src/starboard/shared/starboard/player/filter/media_time_provider_impl.h index 1e8c391..7ba310c 100644 --- a/src/starboard/shared/starboard/player/filter/media_time_provider_impl.h +++ b/src/starboard/shared/starboard/player/filter/media_time_provider_impl.h
@@ -15,6 +15,7 @@ #ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_MEDIA_TIME_PROVIDER_IMPL_H_ #define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_MEDIA_TIME_PROVIDER_IMPL_H_ +#include "starboard/common/optional.h" #include "starboard/common/scoped_ptr.h" #include "starboard/media.h" #include "starboard/mutex.h" @@ -43,23 +44,22 @@ void SetPlaybackRate(double playback_rate) override; void Play() override; void Pause() override; - void Seek(SbMediaTime seek_to_pts) override; - SbMediaTime GetCurrentMediaTime(bool* is_playing, - bool* is_eos_played) override; + void Seek(SbTime seek_to_time) override; + SbTime GetCurrentMediaTime(bool* is_playing, bool* is_eos_played) override; // When video end of stream is reached and the current media time passes the // video duration, |is_eos_played| of GetCurrentMediaTime() will return true. // When VideoEndOfStreamReached() is called without any prior calls to - // UpdateVideoDuration(), the |seek_to_pts_| will be used as video duration. - void UpdateVideoDuration(SbMediaTime video_duration); + // UpdateVideoDuration(), the |seek_to_time_| will be used as video duration. + void UpdateVideoDuration(optional<SbTime> video_duration); void VideoEndOfStreamReached(); private: // When not NULL, |current_time| will be set to the current monotonic time // used to calculate the returned media time. Note that it is possible that - // |current_time| points to |seek_to_pts_set_at_| and the implementation + // |current_time| points to |seek_to_time_set_at_| and the implementation // should handle this properly. - SbMediaTime GetCurrentMediaTime_Locked(SbTimeMonotonic* current_time = NULL); + SbTime GetCurrentMediaTime_Locked(SbTimeMonotonic* current_time = NULL); ThreadChecker thread_checker_; @@ -69,10 +69,10 @@ double playback_rate_ = 1.0; bool is_playing_ = false; - SbMediaTime seek_to_pts_ = 0; - SbTimeMonotonic seek_to_pts_set_at_ = SbTimeGetMonotonicNow(); + SbTime seek_to_time_ = 0; + SbTimeMonotonic seek_to_time_set_at_ = SbTimeGetMonotonicNow(); - SbMediaTime video_duration_ = kSbMediaTimeInvalid; + optional<SbTime> video_duration_; bool is_video_end_of_stream_reached_ = false; };
diff --git a/src/starboard/shared/starboard/player/filter/player_components.h b/src/starboard/shared/starboard/player/filter/player_components.h index 2fe7343..ef8dec3 100644 --- a/src/starboard/shared/starboard/player/filter/player_components.h +++ b/src/starboard/shared/starboard/player/filter/player_components.h
@@ -75,9 +75,12 @@ if (!audio_decoder || !audio_renderer_sink) { return scoped_ptr<AudioRenderer>(); } - return make_scoped_ptr(new AudioRenderer(audio_decoder.Pass(), - audio_renderer_sink.Pass(), - audio_parameters.audio_header)); + int max_cached_frames, max_frames_per_append; + GetAudioRendererParams(&max_cached_frames, &max_frames_per_append); + return make_scoped_ptr( + new AudioRenderer(audio_decoder.Pass(), audio_renderer_sink.Pass(), + audio_parameters.audio_header, max_cached_frames, + max_frames_per_append)); } scoped_ptr<VideoRenderer> CreateVideoRenderer( const VideoParameters& video_parameters, @@ -100,6 +103,7 @@ #endif // COBALT_BUILD_TYPE_GOLD // Note that the following functions are exposed in non-Gold build to allow // unit tests to run. + virtual void CreateAudioComponents( const AudioParameters& audio_parameters, scoped_ptr<AudioDecoder>* audio_decoder, @@ -111,6 +115,10 @@ scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm, scoped_refptr<VideoRendererSink>* video_renderer_sink) = 0; + // Check AudioRenderer ctor for more details on the parameters. + virtual void GetAudioRendererParams(int* max_cached_frames, + int* max_frames_per_append) const = 0; + protected: PlayerComponents() {}
diff --git a/src/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.cc b/src/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.cc index 3f18110..91b2957 100644 --- a/src/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.cc +++ b/src/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.cc
@@ -54,7 +54,7 @@ render_cb_ = render_cb; thread_ = SbThreadCreate(0, kSbThreadNoPriority, kSbThreadNoAffinity, true, - "punchout_video_sink", + "punchoutvidsink", &PunchoutVideoRendererSink::ThreadEntryPoint, this); }
diff --git a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc index 8bf7b89..723af10 100644 --- a/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc +++ b/src/starboard/shared/starboard/player/filter/stub_player_components_impl.cc
@@ -37,9 +37,9 @@ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) { return kSbMediaAudioSampleTypeFloat32; } - SB_DCHECK( - SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)); - return kSbMediaAudioSampleTypeInt16; + SB_DCHECK(SbAudioSinkIsAudioSampleTypeSupported( + kSbMediaAudioSampleTypeInt16Deprecated)); + return kSbMediaAudioSampleTypeInt16Deprecated; } } // namespace @@ -70,17 +70,17 @@ const FillType fill_type = kSilence; if (last_input_buffer_) { - SbMediaTime diff = input_buffer->pts() - last_input_buffer_->pts(); + SbTime diff = input_buffer->timestamp() - last_input_buffer_->timestamp(); SB_DCHECK(diff >= 0); size_t sample_size = - GetSampleType() == kSbMediaAudioSampleTypeInt16 ? 2 : 4; + GetSampleType() == kSbMediaAudioSampleTypeInt16Deprecated ? 2 : 4; size_t size = diff * GetSamplesPerSecond() * sample_size * - audio_header_.number_of_channels / kSbMediaTimeSecond; + audio_header_.number_of_channels / kSbTimeSecond; size += size % (sample_size * audio_header_.number_of_channels); decoded_audios_.push(new DecodedAudio(audio_header_.number_of_channels, GetSampleType(), GetStorageType(), - input_buffer->pts(), size)); + input_buffer->timestamp(), size)); if (fill_type == kSilence) { SbMemorySet(decoded_audios_.back()->buffer(), 0, size); @@ -109,12 +109,12 @@ // 4 times the encoded size. size_t fake_size = 4 * last_input_buffer_->size(); size_t sample_size = - GetSampleType() == kSbMediaAudioSampleTypeInt16 ? 2 : 4; + GetSampleType() == kSbMediaAudioSampleTypeInt16Deprecated ? 2 : 4; fake_size += fake_size % (sample_size * audio_header_.number_of_channels); decoded_audios_.push(new DecodedAudio( audio_header_.number_of_channels, GetSampleType(), GetStorageType(), - last_input_buffer_->pts(), fake_size)); + last_input_buffer_->timestamp(), fake_size)); Schedule(output_cb_); } decoded_audios_.push(new DecodedAudio()); @@ -173,7 +173,8 @@ void WriteInputBuffer(const scoped_refptr<InputBuffer>& input_buffer) override { SB_DCHECK(input_buffer); - decoder_status_cb_(kNeedMoreInput, new VideoFrame(input_buffer->pts())); + decoder_status_cb_(kNeedMoreInput, + new VideoFrame(input_buffer->timestamp())); } void WriteEndOfStream() override { decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame()); @@ -222,6 +223,15 @@ *video_renderer_sink = new PunchoutVideoRendererSink( video_parameters.player, kVideoSinkRenderInterval); } + + void GetAudioRendererParams(int* max_cached_frames, + int* max_frames_per_append) const override { + SB_DCHECK(max_cached_frames); + SB_DCHECK(max_frames_per_append); + + *max_cached_frames = 256 * 1024; + *max_frames_per_append = 16384; + } }; // static
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 6bf38cf..f0030a1 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
@@ -47,13 +47,14 @@ std::string GetTestInputDirectory() { const size_t kPathSize = SB_FILE_MAX_PATH + 1; - char test_output_path[kPathSize]; - SB_CHECK(SbSystemGetPath(kSbSystemPathSourceDirectory, test_output_path, + char content_path[kPathSize]; + SB_CHECK(SbSystemGetPath(kSbSystemPathContentDirectory, content_path, kPathSize)); std::string directory_path = - std::string(test_output_path) + SB_FILE_SEP_CHAR + "starboard" + - SB_FILE_SEP_CHAR + "shared" + SB_FILE_SEP_CHAR + "starboard" + - SB_FILE_SEP_CHAR + "player" + SB_FILE_SEP_CHAR + "testdata"; + std::string(content_path) + SB_FILE_SEP_CHAR + "test" + + SB_FILE_SEP_CHAR + "starboard" + SB_FILE_SEP_CHAR + "shared" + + SB_FILE_SEP_CHAR + "starboard" + SB_FILE_SEP_CHAR + "player" + + SB_FILE_SEP_CHAR + "testdata"; SB_CHECK(SbDirectoryCanOpen(directory_path.c_str())) << "Cannot open directory " << directory_path; @@ -84,13 +85,6 @@ std::bind(&AudioDecoderTest::OnError, this)); } - protected: - enum Event { kConsumed, kOutput, kError }; - - void OnConsumed() { - ScopedLock scoped_lock(event_queue_mutex_); - event_queue_.push_back(kConsumed); - } void OnOutput() { ScopedLock scoped_lock(event_queue_mutex_); event_queue_.push_back(kOutput); @@ -100,6 +94,14 @@ event_queue_.push_back(kError); } + protected: + enum Event { kConsumed, kOutput, kError }; + + void OnConsumed() { + ScopedLock scoped_lock(event_queue_mutex_); + event_queue_.push_back(kConsumed); + } + void WaitForNextEvent(Event* event) { SbTimeMonotonic start = SbTimeGetMonotonicNow(); while (SbTimeGetMonotonicNow() - start < kWaitForNextEventTimeOut) { @@ -151,7 +153,8 @@ return; } if (last_decoded_audio_) { - ASSERT_LT(last_decoded_audio_->pts(), local_decoded_audio->pts()); + ASSERT_LT(last_decoded_audio_->timestamp(), + local_decoded_audio->timestamp()); } last_decoded_audio_ = local_decoded_audio; *decoded_audio = local_decoded_audio; @@ -232,6 +235,27 @@ scoped_refptr<DecodedAudio> last_decoded_audio_; }; +TEST_P(AudioDecoderTest, ThreeMoreDecoders) { + const int kDecodersToCreate = 3; + + PlayerComponents::AudioParameters audio_parameters = { + dmp_reader_.audio_codec(), dmp_reader_.audio_header(), + kSbDrmSystemInvalid, &job_queue_}; + + scoped_ptr<PlayerComponents> components = PlayerComponents::Create(); + scoped_ptr<AudioDecoder> audio_decoders[kDecodersToCreate]; + scoped_ptr<AudioRendererSink> audio_renderer_sinks[kDecodersToCreate]; + + for (int i = 0; i < kDecodersToCreate; ++i) { + components->CreateAudioComponents(audio_parameters, &audio_decoders[i], + &audio_renderer_sinks[i]); + ASSERT_TRUE(audio_decoders[i]); + + audio_decoders[i]->Initialize(std::bind(&AudioDecoderTest::OnOutput, this), + std::bind(&AudioDecoderTest::OnError, this)); + } +} + TEST_P(AudioDecoderTest, SingleInput) { ASSERT_NO_FATAL_FAILURE(WriteSingleInput(0)); audio_decoder_->WriteEndOfStream(); @@ -293,8 +317,8 @@ } std::vector<const char*> GetSupportedTests() { - const char* kFilenames[] = {"google_glass_h264_aac.dmp", - "google_glass_vp9_opus.dmp"}; + const char* kFilenames[] = {"beneath_the_canopy_avc_aac.dmp", + "beneath_the_canopy_vp9_opus.dmp"}; static std::vector<const char*> test_params;
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc index babf1f1..b7067ed 100644 --- a/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc +++ b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
@@ -103,10 +103,12 @@ EXPECT_CALL(*audio_decoder_, Initialize(_, _)) .WillOnce(SaveArg<0>(&output_cb_)); + const int kMaxCachedFrames = 256 * 1024; + const int kMaxFramesPerAppend = 16384; audio_renderer_.reset(new AudioRenderer( make_scoped_ptr<AudioDecoder>(audio_decoder_), make_scoped_ptr<AudioRendererSink>(audio_renderer_sink_), - GetDefaultAudioHeader())); + GetDefaultAudioHeader(), kMaxCachedFrames, kMaxFramesPerAppend)); audio_renderer_->Initialize(std::bind(&AudioRendererTest::OnError, this)); } @@ -120,13 +122,13 @@ int frames_written = 0; while (audio_renderer_->IsSeekingInProgress()) { - SbMediaTime pts = - frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond; - scoped_refptr<InputBuffer> input_buffer = CreateInputBuffer(pts); + SbTime timestamp = + frames_written * kSbTimeSecond / kDefaultSamplesPerSecond; + scoped_refptr<InputBuffer> input_buffer = CreateInputBuffer(timestamp); WriteSample(input_buffer); CallConsumedCB(); scoped_refptr<DecodedAudio> decoded_audio = - CreateDecodedAudio(pts, kFramesPerBuffer); + CreateDecodedAudio(timestamp, kFramesPerBuffer); SendDecoderOutput(decoded_audio); frames_written += kFramesPerBuffer; } @@ -154,9 +156,8 @@ audio_renderer_->WriteEndOfStream(); job_queue_.RunUntilIdle(); } - - void Seek(SbMediaTime seek_to_pts) { - audio_renderer_->Seek(seek_to_pts); + void Seek(SbTime seek_to_time) { + audio_renderer_->Seek(seek_to_time); job_queue_.RunUntilIdle(); EXPECT_TRUE(audio_renderer_->IsSeekingInProgress()); } @@ -176,16 +177,16 @@ job_queue_.RunUntilIdle(); } - scoped_refptr<InputBuffer> CreateInputBuffer(SbMediaTime pts) { + scoped_refptr<InputBuffer> CreateInputBuffer(SbTime timestamp) { const int kInputBufferSize = 4; return new InputBuffer(kSbMediaTypeAudio, DeallocateSampleCB, NULL, this, SbMemoryAllocate(kInputBufferSize), kInputBufferSize, - pts, NULL, NULL); + timestamp, NULL, NULL); } - scoped_refptr<DecodedAudio> CreateDecodedAudio(SbMediaTime pts, int frames) { + scoped_refptr<DecodedAudio> CreateDecodedAudio(SbTime timestamp, int frames) { scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio( - kDefaultNumberOfChannels, sample_type_, storage_type_, pts, + kDefaultNumberOfChannels, sample_type_, storage_type_, timestamp, frames * kDefaultNumberOfChannels * media::GetBytesPerSample(sample_type_)); SbMemorySet(decoded_audio->buffer(), 0, decoded_audio->size()); @@ -279,7 +280,7 @@ SendDecoderOutput(new DecodedAudio); - SbMediaTime media_time = + SbTime media_time = audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played); EXPECT_TRUE(is_playing); EXPECT_FALSE(is_eos_played); @@ -297,7 +298,7 @@ // Consume frames in two batches, so we can test if |GetCurrentMediaTime()| // is incrementing in an expected manner. const int frames_to_consume = frames_in_buffer / 4; - SbMediaTime new_media_time; + SbTime new_media_time; EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed()); @@ -357,7 +358,7 @@ SendDecoderOutput(new DecodedAudio); - SbMediaTime media_time = + SbTime media_time = audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played); int frames_in_buffer; @@ -374,7 +375,7 @@ // Consume frames in two batches, so we can test if |GetCurrentMediaTime()| // is incrementing in an expected manner. const int frames_to_consume = frames_in_buffer / 4; - SbMediaTime new_media_time; + SbTime new_media_time; EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed()); @@ -413,7 +414,7 @@ bool is_playing = false; bool is_eos_played = true; - SbMediaTime media_time = + SbTime media_time = audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played); int frames_in_buffer; @@ -429,7 +430,7 @@ // Consume frames in two batches, so we can test if |GetCurrentMediaTime()| // is incrementing in an expected manner. const int frames_to_consume = frames_in_buffer / 4; - SbMediaTime new_media_time; + SbTime new_media_time; EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed()); @@ -545,14 +546,14 @@ int frames_written = 0; while (audio_renderer_->IsSeekingInProgress()) { - SbMediaTime pts = - frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond; - WriteSample(CreateInputBuffer(pts)); + SbTime timestamp = + frames_written * kSbTimeSecond / kDefaultSamplesPerSecond; + WriteSample(CreateInputBuffer(timestamp)); CallConsumedCB(); - SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer / 2)); + SendDecoderOutput(CreateDecodedAudio(timestamp, kFramesPerBuffer / 2)); frames_written += kFramesPerBuffer / 2; - pts = frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond; - SendDecoderOutput(CreateDecodedAudio(pts, kFramesPerBuffer / 2)); + timestamp = frames_written * kSbTimeSecond / kDefaultSamplesPerSecond; + SendDecoderOutput(CreateDecodedAudio(timestamp, kFramesPerBuffer / 2)); frames_written += kFramesPerBuffer / 2; } @@ -570,7 +571,7 @@ SendDecoderOutput(new DecodedAudio); - SbMediaTime media_time = + SbTime media_time = audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played); int frames_in_buffer; @@ -587,7 +588,7 @@ // Consume frames in two batches, so we can test if |GetCurrentMediaTime()| // is incrementing in an expected manner. const int frames_to_consume = frames_in_buffer / 4; - SbMediaTime new_media_time; + SbTime new_media_time; EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed()); @@ -632,17 +633,17 @@ int frames_written = 0; while (audio_renderer_->IsSeekingInProgress()) { - SbMediaTime pts = - frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond; - SbMediaTime output_pts = pts; - WriteSample(CreateInputBuffer(pts)); + SbTime timestamp = + frames_written * kSbTimeSecond / kDefaultSamplesPerSecond; + SbTime output_time = timestamp; + WriteSample(CreateInputBuffer(timestamp)); CallConsumedCB(); frames_written += kFramesPerBuffer / 2; - pts = frames_written * kSbMediaTimeSecond / kDefaultSamplesPerSecond; - WriteSample(CreateInputBuffer(pts)); + timestamp = frames_written * kSbTimeSecond / kDefaultSamplesPerSecond; + WriteSample(CreateInputBuffer(timestamp)); CallConsumedCB(); frames_written += kFramesPerBuffer / 2; - SendDecoderOutput(CreateDecodedAudio(output_pts, kFramesPerBuffer)); + SendDecoderOutput(CreateDecodedAudio(output_time, kFramesPerBuffer)); } WriteEndOfStream(); @@ -657,7 +658,7 @@ SendDecoderOutput(new DecodedAudio); - SbMediaTime media_time = + SbTime media_time = audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played); int frames_in_buffer; @@ -673,7 +674,7 @@ // Consume frames in two batches, so we can test if |GetCurrentMediaTime()| // is incrementing in an expected manner. const int frames_to_consume = frames_in_buffer / 4; - SbMediaTime new_media_time; + SbTime new_media_time; EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed()); @@ -722,7 +723,7 @@ SendDecoderOutput(new DecodedAudio); - SbMediaTime media_time = + SbTime media_time = audio_renderer_->GetCurrentMediaTime(&is_playing, &is_eos_played); int frames_in_buffer; @@ -737,9 +738,9 @@ // Consume frames in multiple batches, so we can test if // |GetCurrentMediaTime()| is incrementing in an expected manner. - const double seek_time = 0.5 * kSbMediaTimeSecond; + const double seek_time = 0.5 * kSbTimeSecond; const int frames_to_consume = frames_in_buffer / 10; - SbMediaTime new_media_time; + SbTime new_media_time; EXPECT_FALSE(audio_renderer_->IsEndOfStreamPlayed());
diff --git a/src/starboard/shared/starboard/player/filter/testing/filter_tests.gypi b/src/starboard/shared/starboard/player/filter/testing/filter_tests.gypi index c635d56..e87511b 100644 --- a/src/starboard/shared/starboard/player/filter/testing/filter_tests.gypi +++ b/src/starboard/shared/starboard/player/filter/testing/filter_tests.gypi
@@ -35,23 +35,23 @@ 'STARBOARD_IMPLEMENTATION', ], 'dependencies': [ - '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles', + '<@(cobalt_platform_dependencies)', '<(DEPTH)/starboard/starboard.gyp:starboard', '<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gtest.gyp:gtest', + 'filter_tests_copy_test_data', ], - 'actions': [ - { - 'action_name': 'copy_filter_tests_data', - 'variables': { - 'input_files': [ - '<(DEPTH)/starboard/shared/starboard/player/testdata', - ], - 'output_dir': '/starboard/shared/starboard/player', - }, - 'includes': ['../../../../../build/copy_test_data.gypi'], - } - ], + }, + { + 'target_name': 'filter_tests_copy_test_data', + 'type': 'none', + 'variables': { + 'content_test_input_files': [ + '<(DEPTH)/starboard/shared/starboard/player/testdata', + ], + 'content_test_output_subdir': 'starboard/shared/starboard/player', + }, + 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'], }, { 'target_name': 'filter_tests_deploy',
diff --git a/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc b/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc index 7dfcdfc..0156a3e 100644 --- a/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc +++ b/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc
@@ -14,8 +14,6 @@ #include "starboard/shared/starboard/player/filter/media_time_provider_impl.h" -#include <cinttypes> - #include "starboard/thread.h" #include "starboard/time.h" #include "testing/gmock/include/gmock/gmock.h" @@ -36,16 +34,17 @@ using ::testing::Return; using ::testing::StrictMock; -::testing::AssertionResult AlmostEqual(SbMediaTime left, SbMediaTime right) { +::testing::AssertionResult AlmostEqual(SbTime left, SbTime right) { // Use 1 millisecond as epsilon. - const SbMediaTime kEpsilon = kSbMediaTimeSecond / 1000; + const SbTime kEpsilon = kSbTimeSecond / 1000; + SbTime diff = left > right ? left - right : right - left; - if (std::llabs(left - right) <= kEpsilon) + if (diff <= kEpsilon) return ::testing::AssertionSuccess(); else return ::testing::AssertionFailure() << left << " is not almost equal to " << right - << " with a difference of " << std::llabs(left - right); + << " with a difference of " << diff; } class MockMonotonicSystemTimeProvider : public MonotonicSystemTimeProvider { @@ -123,7 +122,7 @@ EXPECT_TRUE(AlmostEqual(media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played), - kSbMediaTimeSecond)); + kSbTimeSecond)); EXPECT_TRUE(is_playing); EXPECT_FALSE(is_eos_played); } @@ -135,30 +134,30 @@ bool is_playing = true, is_eos_played = true; EXPECT_TRUE(AlmostEqual(media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played), - kSbMediaTimeSecond)); + kSbTimeSecond)); media_time_provider_impl_.SetPlaybackRate(2.0); system_time_provider_->AdvanceTime(kSbTimeSecond); EXPECT_TRUE(AlmostEqual(media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played), - kSbMediaTimeSecond * 3)); + kSbTimeSecond * 3)); media_time_provider_impl_.SetPlaybackRate(0.0); system_time_provider_->AdvanceTime(kSbTimeSecond); EXPECT_TRUE(AlmostEqual(media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played), - kSbMediaTimeSecond * 3)); + kSbTimeSecond * 3)); } TEST_F(MediaTimeProviderImplTest, SeekWhileNotPlaying) { - const SbMediaTime kSeekToPts = 100 * kSbMediaTimeSecond; + const SbTime kSeekToTime = 100 * kSbTimeSecond; - media_time_provider_impl_.Seek(kSeekToPts); + media_time_provider_impl_.Seek(kSeekToTime); bool is_playing = true, is_eos_played = true; EXPECT_TRUE(AlmostEqual(media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played), - kSeekToPts)); + kSeekToTime)); EXPECT_FALSE(is_playing); EXPECT_FALSE(is_eos_played); @@ -166,19 +165,19 @@ EXPECT_TRUE(AlmostEqual(media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played), - kSeekToPts)); + kSeekToTime)); } TEST_F(MediaTimeProviderImplTest, SeekForwardWhilePlaying) { - const SbMediaTime kSeekToPts = 100 * kSbMediaTimeSecond; + const SbTime kSeekToTime = 100 * kSbTimeSecond; media_time_provider_impl_.Play(); - media_time_provider_impl_.Seek(kSeekToPts); + media_time_provider_impl_.Seek(kSeekToTime); bool is_playing = false, is_eos_played = true; EXPECT_TRUE(AlmostEqual(media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played), - kSeekToPts)); + kSeekToTime)); EXPECT_TRUE(is_playing); EXPECT_FALSE(is_eos_played); @@ -186,7 +185,7 @@ EXPECT_TRUE(AlmostEqual(media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played), - kSeekToPts + kSbMediaTimeSecond)); + kSeekToTime + kSbTimeSecond)); EXPECT_TRUE(is_playing); EXPECT_FALSE(is_eos_played); } @@ -200,11 +199,11 @@ // Query for media time and ignore the result. media_time_provider_impl_.GetCurrentMediaTime(&is_playing, &is_eos_played); - const SbMediaTime kSeekToPts = 0; - media_time_provider_impl_.Seek(kSeekToPts); + const SbTime kSeekToTime = 0; + media_time_provider_impl_.Seek(kSeekToTime); EXPECT_TRUE(AlmostEqual(media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played), - kSeekToPts)); + kSeekToTime)); } TEST_F(MediaTimeProviderImplTest, Pause) { @@ -220,7 +219,7 @@ system_time_provider_->AdvanceTime(kSbTimeSecond); EXPECT_TRUE(AlmostEqual(media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played), - kSbMediaTimeSecond)); + kSbTimeSecond)); media_time_provider_impl_.Seek(0); system_time_provider_->AdvanceTime(kSbTimeSecond); @@ -230,7 +229,7 @@ } TEST_F(MediaTimeProviderImplTest, EndOfStream) { - const SbMediaTime kVideoDuration = kSbMediaTimeSecond; + const SbTime kVideoDuration = kSbTimeSecond; system_time_provider_->AdvanceTime(kSbTimeSecond); media_time_provider_impl_.UpdateVideoDuration(kVideoDuration); @@ -256,7 +255,7 @@ media_time_provider_impl_.Pause(); media_time_provider_impl_.SetPlaybackRate(0); - SbMediaTime current_time = media_time_provider_impl_.GetCurrentMediaTime( + SbTime current_time = media_time_provider_impl_.GetCurrentMediaTime( &is_playing, &is_eos_played); EXPECT_FALSE(is_playing); EXPECT_TRUE(is_eos_played);
diff --git a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc index e7e452f..e434a4b 100644 --- a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc +++ b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
@@ -51,6 +51,9 @@ using ::starboard::testing::FakeGraphicsContextProvider; using ::std::placeholders::_1; using ::std::placeholders::_2; +using ::testing::AssertionFailure; +using ::testing::AssertionResult; +using ::testing::AssertionSuccess; using ::testing::ValuesIn; using video_dmp::VideoDmpReader; @@ -64,13 +67,13 @@ std::string GetTestInputDirectory() { const size_t kPathSize = SB_FILE_MAX_PATH + 1; - char test_output_path[kPathSize]; - EXPECT_TRUE(SbSystemGetPath(kSbSystemPathSourceDirectory, test_output_path, - kPathSize)); + char content_path[kPathSize]; + EXPECT_TRUE( + SbSystemGetPath(kSbSystemPathContentDirectory, content_path, kPathSize)); std::string directory_path = - std::string(test_output_path) + SB_FILE_SEP_CHAR + "starboard" + - SB_FILE_SEP_CHAR + "shared" + SB_FILE_SEP_CHAR + "starboard" + - SB_FILE_SEP_CHAR + "player" + SB_FILE_SEP_CHAR + "testdata"; + std::string(content_path) + SB_FILE_SEP_CHAR + "test" + SB_FILE_SEP_CHAR + + "starboard" + SB_FILE_SEP_CHAR + "shared" + SB_FILE_SEP_CHAR + + "starboard" + SB_FILE_SEP_CHAR + "player" + SB_FILE_SEP_CHAR + "testdata"; SB_CHECK(SbDirectoryCanOpen(directory_path.c_str())) << directory_path; return directory_path; @@ -80,10 +83,21 @@ return GetTestInputDirectory() + SB_FILE_SEP_CHAR + filename; } +AssertionResult AlmostEqualTime(SbTime time1, SbTime time2) { + const SbTime kEpsilon = kSbTimeSecond / 1000; + SbTime diff = time1 - time2; + if (-kEpsilon <= diff && diff <= kEpsilon) { + return AssertionSuccess(); + } + return AssertionFailure() + << "time " << time1 << " doesn't match with time " << time2; +} + class VideoDecoderTest : public ::testing::TestWithParam<TestParam> { public: VideoDecoderTest() : dmp_reader_(ResolveTestFileName(GetParam().filename).c_str()) {} + void SetUp() override { ASSERT_NE(dmp_reader_.video_codec(), kSbMediaVideoCodecNone); ASSERT_GT(dmp_reader_.number_of_video_buffers(), 0); @@ -108,11 +122,41 @@ &video_renderer_sink_); ASSERT_TRUE(video_decoder_); + video_renderer_sink_->SetRenderCB( + std::bind(&VideoDecoderTest::Render, this, _1)); + video_decoder_->Initialize( std::bind(&VideoDecoderTest::OnDecoderStatusUpdate, this, _1, _2), std::bind(&VideoDecoderTest::OnError, this)); } + void Render(VideoRendererSink::DrawFrameCB draw_frame_cb) { + SB_UNREFERENCED_PARAMETER(draw_frame_cb); + } + + void OnDecoderStatusUpdate(VideoDecoder::Status status, + const scoped_refptr<VideoFrame>& frame) { + ScopedLock scoped_lock(mutex_); + // TODO: Ensure that this is only called during dtor or Reset(). + if (status == VideoDecoder::kReleaseAllFrames) { + SB_DCHECK(!frame); + event_queue_.clear(); + decoded_frames_.clear(); + return; + } else if (status == VideoDecoder::kNeedMoreInput) { + event_queue_.push_back(Event(kNeedMoreInput, frame)); + } else if (status == VideoDecoder::kBufferFull) { + event_queue_.push_back(Event(kBufferFull, frame)); + } else { + event_queue_.push_back(Event(kError, frame)); + } + } + + void OnError() { + ScopedLock scoped_lock(mutex_); + event_queue_.push_back(Event(kError, NULL)); + } + protected: enum Status { kNeedMoreInput = VideoDecoder::kNeedMoreInput, @@ -190,7 +234,7 @@ { ScopedLock scoped_lock(mutex_); need_more_input_ = false; - outstanding_inputs_.insert(input_buffer->pts()); + outstanding_inputs_.insert(input_buffer->timestamp()); } video_decoder_->WriteInputBuffer(input_buffer); @@ -227,12 +271,13 @@ if (event.frame) { ASSERT_FALSE(event.frame->is_end_of_stream()); if (!decoded_frames_.empty()) { - ASSERT_LT(decoded_frames_.back()->pts(), event.frame->pts()); + ASSERT_LT(decoded_frames_.back()->timestamp(), + event.frame->timestamp()); } decoded_frames_.push_back(event.frame); - ASSERT_TRUE(outstanding_inputs_.find(event.frame->pts()) != - outstanding_inputs_.end()); - outstanding_inputs_.erase(outstanding_inputs_.find(event.frame->pts())); + ASSERT_TRUE(AlmostEqualTime(*outstanding_inputs_.begin(), + event.frame->timestamp())); + outstanding_inputs_.erase(outstanding_inputs_.begin()); } if (event_cb) { bool continue_process = true; @@ -268,13 +313,13 @@ ASSERT_TRUE(outstanding_inputs_.empty()); } else { if (!decoded_frames_.empty()) { - ASSERT_LT(decoded_frames_.back()->pts(), event.frame->pts()); + ASSERT_LT(decoded_frames_.back()->timestamp(), + event.frame->timestamp()); } decoded_frames_.push_back(event.frame); - ASSERT_TRUE(outstanding_inputs_.find(event.frame->pts()) != - outstanding_inputs_.end()); - outstanding_inputs_.erase( - outstanding_inputs_.find(event.frame->pts())); + ASSERT_TRUE(AlmostEqualTime(*outstanding_inputs_.begin(), + event.frame->timestamp())); + outstanding_inputs_.erase(outstanding_inputs_.begin()); } } if (event_cb) { @@ -296,6 +341,8 @@ outstanding_inputs_.clear(); } + JobQueue job_queue_; + Mutex mutex_; std::deque<Event> event_queue_; @@ -304,35 +351,11 @@ scoped_ptr<VideoDecoder> video_decoder_; bool need_more_input_ = true; - std::set<SbMediaTime> outstanding_inputs_; + std::set<SbTime> outstanding_inputs_; std::deque<scoped_refptr<VideoFrame>> decoded_frames_; private: - void OnDecoderStatusUpdate(VideoDecoder::Status status, - const scoped_refptr<VideoFrame>& frame) { - ScopedLock scoped_lock(mutex_); - // TODO: Ensure that this is only called during dtor or Reset(). - if (status == VideoDecoder::kReleaseAllFrames) { - SB_DCHECK(!frame); - event_queue_.clear(); - decoded_frames_.clear(); - return; - } else if (status == VideoDecoder::kNeedMoreInput) { - event_queue_.push_back(Event(kNeedMoreInput, frame)); - } else if (status == VideoDecoder::kBufferFull) { - event_queue_.push_back(Event(kBufferFull, frame)); - } else { - event_queue_.push_back(Event(kError, frame)); - } - } - - void OnError() { - ScopedLock scoped_lock(mutex_); - event_queue_.push_back(Event(kError, NULL)); - } - SbPlayerPrivate player_; - JobQueue job_queue_; scoped_ptr<VideoRenderAlgorithm> video_render_algorithm_; scoped_refptr<VideoRendererSink> video_renderer_sink_; @@ -371,6 +394,63 @@ } } +TEST_P(VideoDecoderTest, ThreeMoreDecoders) { + // Create three more decoders for each supported combinations. + const int kDecodersToCreate = 3; + + scoped_ptr<PlayerComponents> components = PlayerComponents::Create(); + + SbPlayerOutputMode kOutputModes[] = {kSbPlayerOutputModeDecodeToTexture, + kSbPlayerOutputModePunchOut}; + SbMediaVideoCodec kVideoCodecs[] = { + kSbMediaVideoCodecNone, kSbMediaVideoCodecH264, kSbMediaVideoCodecH265, + kSbMediaVideoCodecMpeg2, kSbMediaVideoCodecTheora, kSbMediaVideoCodecVc1, + kSbMediaVideoCodecVp10, kSbMediaVideoCodecVp8, kSbMediaVideoCodecVp9}; + + for (auto output_mode : kOutputModes) { + for (auto video_codec : kVideoCodecs) { + if (VideoDecoder::OutputModeSupported(output_mode, video_codec, + kSbDrmSystemInvalid)) { + SbPlayerPrivate players[kDecodersToCreate]; + scoped_ptr<VideoDecoder> video_decoders[kDecodersToCreate]; + scoped_ptr<VideoRenderAlgorithm> + video_render_algorithms[kDecodersToCreate]; + scoped_refptr<VideoRendererSink> + video_renderer_sinks[kDecodersToCreate]; + + for (int i = 0; i < kDecodersToCreate; ++i) { + PlayerComponents::VideoParameters video_parameters = { + &players[i], + dmp_reader_.video_codec(), + kSbDrmSystemInvalid, + &job_queue_, + output_mode, + fake_graphics_context_provider_.decoder_target_provider()}; + + components->CreateVideoComponents( + video_parameters, &video_decoders[i], &video_render_algorithms[i], + &video_renderer_sinks[i]); + ASSERT_TRUE(video_decoders[i]); + + video_renderer_sinks[i]->SetRenderCB( + std::bind(&VideoDecoderTest::Render, this, _1)); + + video_decoders[i]->Initialize( + std::bind(&VideoDecoderTest::OnDecoderStatusUpdate, this, _1, _2), + std::bind(&VideoDecoderTest::OnError, this)); + + if (output_mode == kSbPlayerOutputModeDecodeToTexture) { + SbDecodeTarget decode_target = + video_decoders[i]->GetCurrentDecodeTarget(); + EXPECT_FALSE(SbDecodeTargetIsValid(decode_target)); + fake_graphics_context_provider_.ReleaseDecodeTarget(decode_target); + } + } + } + } + } +} + TEST_P(VideoDecoderTest, SingleInput) { WriteSingleInput(0); WriteEndOfStream(); @@ -389,7 +469,7 @@ TEST_P(VideoDecoderTest, SingleInvalidInput) { need_more_input_ = false; auto input_buffer = dmp_reader_.GetVideoInputBuffer(0); - outstanding_inputs_.insert(input_buffer->pts()); + outstanding_inputs_.insert(input_buffer->timestamp()); std::vector<uint8_t> content(input_buffer->size(), 0xab); // Replace the content with invalid data. input_buffer->SetDecryptedContent(content.data(), @@ -476,20 +556,20 @@ } TEST_P(VideoDecoderTest, HoldFramesUntilFull) { - // TODO: Move the following to VideoDecoder::GetMaxNumberOfCachedFrames(). - const size_t kMaxCachedFrames = 12; ASSERT_NO_FATAL_FAILURE(WriteMultipleInputs( 0, dmp_reader_.number_of_video_buffers(), [=](const Event& event, bool* continue_process) { SB_UNREFERENCED_PARAMETER(event); - *continue_process = decoded_frames_.size() < kMaxCachedFrames; + *continue_process = decoded_frames_.size() < + video_decoder_->GetMaxNumberOfCachedFrames(); })); WriteEndOfStream(); bool error_occurred = false; ASSERT_NO_FATAL_FAILURE(DrainOutputs( &error_occurred, [=](const Event& event, bool* continue_process) { SB_UNREFERENCED_PARAMETER(event); - *continue_process = decoded_frames_.size() < kMaxCachedFrames; + *continue_process = decoded_frames_.size() < + video_decoder_->GetMaxNumberOfCachedFrames(); })); ASSERT_FALSE(error_occurred); } @@ -508,22 +588,33 @@ ASSERT_NO_FATAL_FAILURE(WriteMultipleInputs( 0, gop_size, [=](const Event& event, bool* continue_process) { SB_UNREFERENCED_PARAMETER(event); - while (decoded_frames_.size() > + while (decoded_frames_.size() >= video_decoder_->GetPrerollFrameCount()) { decoded_frames_.pop_front(); } *continue_process = true; })); WriteEndOfStream(); - ASSERT_NO_FATAL_FAILURE(DrainOutputs()); + + bool error_occurred = true; + ASSERT_NO_FATAL_FAILURE(DrainOutputs( + &error_occurred, [=](const Event& event, bool* continue_process) { + SB_UNREFERENCED_PARAMETER(event); + while (decoded_frames_.size() >= + video_decoder_->GetMaxNumberOfCachedFrames()) { + decoded_frames_.pop_front(); + } + *continue_process = true; + })); + ASSERT_FALSE(error_occurred); } std::vector<TestParam> GetSupportedTests() { SbPlayerOutputMode kOutputModes[] = {kSbPlayerOutputModeDecodeToTexture, kSbPlayerOutputModePunchOut}; - const char* kFilenames[] = {"google_glass_h264_aac.dmp", - "google_glass_vp9_opus.dmp"}; + const char* kFilenames[] = {"beneath_the_canopy_avc_aac.dmp", + "beneath_the_canopy_vp9_opus.dmp"}; static std::vector<TestParam> test_params;
diff --git a/src/starboard/shared/starboard/player/filter/video_decoder_internal.h b/src/starboard/shared/starboard/player/filter/video_decoder_internal.h index de8bbb1..5749658 100644 --- a/src/starboard/shared/starboard/player/filter/video_decoder_internal.h +++ b/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
@@ -74,6 +74,15 @@ // On most platforms this can be simply set to |kSbTimeMax|. virtual SbTime GetPrerollTimeout() const = 0; + // Returns a soft limit of the maximum number of frames the user of this class + // (usually the VideoRenderer) should cache, i.e. the user won't write more + // inputs once the frames it holds on exceed this limit. Note that other part + // of the video pipeline like the VideoRendererSink may also cache frames. It + // is the responsibility of the decoder to ensure that this wouldn't result in + // anything catastrophic. + // TODO: Turn this into pure virtual. + virtual size_t GetMaxNumberOfCachedFrames() const { return 12; } + // Send encoded video frame stored in |input_buffer| to decode. virtual void WriteInputBuffer( const scoped_refptr<InputBuffer>& input_buffer) = 0;
diff --git a/src/starboard/shared/starboard/player/filter/video_frame_internal.cc b/src/starboard/shared/starboard/player/filter/video_frame_internal.cc deleted file mode 100644 index bb715ba..0000000 --- a/src/starboard/shared/starboard/player/filter/video_frame_internal.cc +++ /dev/null
@@ -1,261 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "starboard/shared/starboard/player/filter/video_frame_internal.h" - -#include "starboard/log.h" -#include "starboard/memory.h" - -namespace starboard { -namespace shared { -namespace starboard { -namespace player { - -namespace { - -bool s_yuv_to_rgb_lookup_table_initialized = false; -int s_y_to_rgb[256]; -int s_v_to_r[256]; -int s_u_to_g[256]; -int s_v_to_g[256]; -int s_u_to_b[256]; -uint8_t s_clamp_table[256 * 5]; - -void EnsureYUVToRGBLookupTableInitialized() { - if (s_yuv_to_rgb_lookup_table_initialized) { - return; - } - - // The YUV to RGBA conversion is based on - // http://www.equasys.de/colorconversion.html. - // The formula is: - // r = 1.164f * y + 1.793f * (v - 128); - // g = 1.164f * y - 0.213f * (u - 128) - 0.533f * (v - 128); - // b = 1.164f * y + 2.112f * (u - 128); - // And r/g/b has to be clamped to [0, 255]. - // - // We optimize the conversion algorithm by creating two kinds of lookup - // tables. The color component table contains pre-calculated color component - // values. The clamp table contains a map between |v| + 512 to the clamped - // |v| to avoid conditional operation. - // The minimum value of |v| can be 2.112f * (-128) = -271, the maximum value - // of |v| can be 1.164f * 255 + 2.112f * 127 = 565. So we need 512 bytes at - // each side of the clamp buffer. - SbMemorySet(s_clamp_table, 0, 512); - SbMemorySet(s_clamp_table + 768, 0xff, 512); - - uint8_t i = 0; - while (true) { - s_y_to_rgb[i] = static_cast<int>((i - 16) * 1.164f); - s_v_to_r[i] = static_cast<int>((i - 128) * 1.793f); - s_u_to_g[i] = static_cast<int>((i - 128) * -0.213f); - s_v_to_g[i] = static_cast<int>((i - 128) * -0.533f); - s_u_to_b[i] = static_cast<int>((i - 128) * 2.112f); - s_clamp_table[512 + static_cast<std::size_t>(i)] = i; - if (i == 255) { - break; - } - ++i; - } - - s_yuv_to_rgb_lookup_table_initialized = true; -} - -uint8_t ClampColorComponent(int component) { - return s_clamp_table[component + 512]; -} - -} // namespace - -VideoFrame::VideoFrame() { - InitializeToInvalidFrame(); -} - -VideoFrame::VideoFrame(int width, - int height, - SbMediaTime pts, - void* native_texture, - void* native_texture_context, - FreeNativeTextureFunc free_native_texture_func) { - SB_DCHECK(native_texture != NULL); - SB_DCHECK(free_native_texture_func != NULL); - - InitializeToInvalidFrame(); - - format_ = kNativeTexture; - width_ = width; - height_ = height; - pts_ = pts; - native_texture_ = native_texture; - native_texture_context_ = native_texture_context; - free_native_texture_func_ = free_native_texture_func; -} - -VideoFrame::~VideoFrame() { - if (format_ == kNativeTexture) { - free_native_texture_func_(native_texture_context_, native_texture_); - } -} - -int VideoFrame::GetPlaneCount() const { - SB_DCHECK(format_ != kInvalid); - SB_DCHECK(format_ != kNativeTexture); - - return static_cast<int>(planes_.size()); -} - -const VideoFrame::Plane& VideoFrame::GetPlane(int index) const { - SB_DCHECK(format_ != kInvalid); - SB_DCHECK(format_ != kNativeTexture); - SB_DCHECK(index >= 0 && index < GetPlaneCount()) << "Invalid index: " - << index; - return planes_[index]; -} - -void* VideoFrame::native_texture() const { - SB_DCHECK(format_ == kNativeTexture); - return native_texture_; -} - -scoped_refptr<VideoFrame> VideoFrame::ConvertTo(Format target_format) const { - SB_DCHECK(format_ == kYV12); - SB_DCHECK(target_format == kBGRA32); - - EnsureYUVToRGBLookupTableInitialized(); - - scoped_refptr<VideoFrame> target_frame(new VideoFrame); - - target_frame->format_ = target_format; - target_frame->width_ = width(); - target_frame->height_ = height(); - target_frame->pts_ = pts_; - target_frame->pixel_buffer_.reset(new uint8_t[width() * height() * 4]); - target_frame->planes_.push_back( - Plane(width(), height(), width() * 4, target_frame->pixel_buffer_.get())); - - const uint8_t* y_data = GetPlane(0).data; - const uint8_t* u_data = GetPlane(1).data; - const uint8_t* v_data = GetPlane(2).data; - uint8_t* bgra_data = target_frame->pixel_buffer_.get(); - - int height = this->height(); - int width = this->width(); - - for (int row = 0; row < height; ++row) { - const uint8_t* y = &y_data[row * GetPlane(0).pitch_in_bytes]; - const uint8_t* u = &u_data[row / 2 * GetPlane(1).pitch_in_bytes]; - const uint8_t* v = &v_data[row / 2 * GetPlane(2).pitch_in_bytes]; - int v_to_r = 0; - int u_to_g = 0; - int v_to_g = 0; - int u_to_b = 0; - - for (int column = 0; column < width; ++column) { - if (column % 2 == 0) { - v_to_r = s_v_to_r[*v]; - u_to_g = s_u_to_g[*u]; - v_to_g = s_v_to_g[*v]; - u_to_b = s_u_to_b[*u]; - } else { - ++u, ++v; - } - - int y_to_rgb = s_y_to_rgb[*y]; - int r = y_to_rgb + v_to_r; - int g = y_to_rgb + u_to_g + v_to_g; - int b = y_to_rgb + u_to_b; - - *bgra_data++ = ClampColorComponent(b); - *bgra_data++ = ClampColorComponent(g); - *bgra_data++ = ClampColorComponent(r); - *bgra_data++ = 0xff; - - ++y; - } - } - - return target_frame; -} - -// static -scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() { - return new VideoFrame; -} - -// static -scoped_refptr<VideoFrame> VideoFrame::CreateYV12Frame(int width, - int height, - int pitch_in_bytes, - SbMediaTime pts, - const uint8_t* y, - const uint8_t* u, - const uint8_t* v) { - scoped_refptr<VideoFrame> frame(new VideoFrame); - frame->format_ = kYV12; - frame->width_ = width; - frame->height_ = height; - frame->pts_ = pts; - - // U/V planes generally have half resolution of the Y plane. However, in the - // extreme case that any dimension of Y plane is odd, we want to have an - // extra pixel on U/V planes. - int uv_height = height / 2 + height % 2; - int uv_width = width / 2 + width % 2; - int uv_pitch_in_bytes = pitch_in_bytes / 2 + pitch_in_bytes % 2; - - int y_plane_size_in_bytes = height * pitch_in_bytes; - int uv_plane_size_in_bytes = uv_height * uv_pitch_in_bytes; - frame->pixel_buffer_.reset( - new uint8_t[y_plane_size_in_bytes + uv_plane_size_in_bytes * 2]); - SbMemoryCopy(frame->pixel_buffer_.get(), y, y_plane_size_in_bytes); - SbMemoryCopy(frame->pixel_buffer_.get() + y_plane_size_in_bytes, u, - uv_plane_size_in_bytes); - SbMemoryCopy(frame->pixel_buffer_.get() + y_plane_size_in_bytes + - uv_plane_size_in_bytes, - v, uv_plane_size_in_bytes); - - frame->planes_.push_back( - Plane(width, height, pitch_in_bytes, frame->pixel_buffer_.get())); - frame->planes_.push_back( - Plane(uv_width, uv_height, uv_pitch_in_bytes, - frame->pixel_buffer_.get() + y_plane_size_in_bytes)); - frame->planes_.push_back(Plane(uv_width, uv_height, uv_pitch_in_bytes, - frame->pixel_buffer_.get() + - y_plane_size_in_bytes + - uv_plane_size_in_bytes)); - return frame; -} - -// static -scoped_refptr<VideoFrame> VideoFrame::CreateEmptyFrame(SbMediaTime pts) { - VideoFrame* frame = new VideoFrame(); - frame->pts_ = pts; - return frame; -} - -void VideoFrame::InitializeToInvalidFrame() { - format_ = kInvalid; - width_ = 0; - height_ = 0; - - pts_ = 0; - native_texture_ = NULL; - native_texture_context_ = NULL; - free_native_texture_func_ = NULL; -} - -} // namespace player -} // namespace starboard -} // namespace shared -} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/video_frame_internal.h b/src/starboard/shared/starboard/player/filter/video_frame_internal.h index 6a2162e..9063d87 100644 --- a/src/starboard/shared/starboard/player/filter/video_frame_internal.h +++ b/src/starboard/shared/starboard/player/filter/video_frame_internal.h
@@ -30,16 +30,16 @@ // A video frame produced by a video decoder. class VideoFrame : public RefCountedThreadSafe<VideoFrame> { public: - // An invalid media time to indicate end of stream. - static const SbMediaTime kMediaTimeEndOfStream = -1; + // An invalid media timestamp to indicate end of stream. + static const SbTime kMediaTimeEndOfStream = -1; - explicit VideoFrame(SbMediaTime pts) : pts_(pts) {} + explicit VideoFrame(SbTime timestamp) : timestamp_(timestamp) {} virtual ~VideoFrame() {} - bool is_end_of_stream() const { return pts_ == kMediaTimeEndOfStream; } - SbMediaTime pts() const { + bool is_end_of_stream() const { return timestamp_ == kMediaTimeEndOfStream; } + SbTime timestamp() const { SB_DCHECK(!is_end_of_stream()); - return pts_; + return timestamp_; } static scoped_refptr<VideoFrame> CreateEOSFrame() { @@ -47,7 +47,7 @@ } private: - SbMediaTime pts_; + SbTime timestamp_; SB_DISALLOW_COPY_AND_ASSIGN(VideoFrame); };
diff --git a/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc b/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc index 9aa65e1..b0282e9 100644 --- a/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc +++ b/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc
@@ -26,7 +26,7 @@ namespace filter { VideoRenderAlgorithmImpl::VideoRenderAlgorithmImpl() - : last_frame_pts_(-1), dropped_frames_(0) {} + : last_frame_timestamp_(-1), dropped_frames_(0) {} void VideoRenderAlgorithmImpl::Render( MediaTimeProvider* media_time_provider, @@ -36,12 +36,13 @@ SB_DCHECK(frames); SB_DCHECK(draw_frame_cb); - if (frames->empty()) { + if (frames->empty() || frames->front()->is_end_of_stream()) { return; } + bool is_audio_playing; bool is_audio_eos_played; - SbMediaTime media_time = media_time_provider->GetCurrentMediaTime( + SbTime media_time = media_time_provider->GetCurrentMediaTime( &is_audio_playing, &is_audio_eos_played); // Video frames are synced to the audio timestamp. However, the audio @@ -61,7 +62,7 @@ // * Then the frame with timestamp 40 is displayed twice (for sample // timestamps 31 and 40). // * Then the frame with timestamp 50 is dropped. - const SbMediaTime kMediaTimeThreshold = kSbMediaTimeSecond / 250; + const SbTime kMediaTimeThreshold = kSbTimeSecond / 250; // Favor advancing the frame sooner. This addresses the situation where the // audio timestamp query interval is a little shorter than a frame. This @@ -70,8 +71,8 @@ // In the above example, this ensures advancement from frame timestamp 20 // to frame timestamp 30 when the sample time is 19. if (is_audio_playing && frames->size() > 1 && - frames->front()->pts() == last_frame_pts_ && - last_frame_pts_ - kMediaTimeThreshold < media_time) { + frames->front()->timestamp() == last_frame_timestamp_ && + last_frame_timestamp_ - kMediaTimeThreshold < media_time) { frames->pop_front(); } @@ -85,8 +86,8 @@ // however, the "early advance" logic from above would force frame 30 to // move onto frame 40 on sample timestamp 31. while (frames->size() > 1 && - frames->front()->pts() + kMediaTimeThreshold < media_time) { - if (frames->front()->pts() != last_frame_pts_) { + frames->front()->timestamp() + kMediaTimeThreshold < media_time) { + if (frames->front()->timestamp() != last_frame_timestamp_) { ++dropped_frames_; } frames->pop_front(); @@ -99,7 +100,7 @@ } if (!frames->front()->is_end_of_stream()) { - last_frame_pts_ = frames->front()->pts(); + last_frame_timestamp_ = frames->front()->timestamp(); auto status = draw_frame_cb(frames->front(), 0); if (status == VideoRendererSink::kReleased) { frames->pop_front();
diff --git a/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.h b/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.h index bb17e16..edfd1ee 100644 --- a/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.h +++ b/src/starboard/shared/starboard/player/filter/video_render_algorithm_impl.h
@@ -41,7 +41,7 @@ int GetDroppedFrames() override { return dropped_frames_; } private: - SbMediaTime last_frame_pts_; + SbTime last_frame_timestamp_; int dropped_frames_; };
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc index 8d4fd20..4aae342 100644 --- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc +++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
@@ -34,7 +34,7 @@ algorithm_(algorithm.Pass()), sink_(sink), seeking_(false), - seeking_to_pts_(0), + seeking_to_time_(0), end_of_stream_written_(false), need_more_input_(true), decoder_(decoder.Pass()), @@ -78,7 +78,7 @@ SB_DCHECK(input_buffer); if (end_of_stream_written_) { - SB_LOG(ERROR) << "Appending video sample at " << input_buffer->pts() + SB_LOG(ERROR) << "Appending video sample at " << input_buffer->timestamp() << " after EOS reached."; return; } @@ -109,9 +109,9 @@ decoder_->WriteEndOfStream(); } -void VideoRenderer::Seek(SbMediaTime seek_to_pts) { +void VideoRenderer::Seek(SbTime seek_to_time) { SB_DCHECK(thread_checker_.CalledOnValidThread()); - SB_DCHECK(seek_to_pts >= 0); + SB_DCHECK(seek_to_time >= 0); if (first_input_written_) { decoder_->Reset(); @@ -120,7 +120,7 @@ ScopedLock lock(mutex_); - seeking_to_pts_ = std::max<SbMediaTime>(seek_to_pts, 0); + seeking_to_time_ = std::max<SbTime>(seek_to_time, 0); seeking_ = true; end_of_stream_written_ = false; need_more_input_ = true; @@ -137,8 +137,8 @@ bool VideoRenderer::CanAcceptMoreData() const { SB_DCHECK(thread_checker_.CalledOnValidThread()); ScopedLock lock(mutex_); - return frames_.size() < kMaxCachedFrames && !end_of_stream_written_ && - need_more_input_; + return frames_.size() < decoder_->GetMaxNumberOfCachedFrames() && + !end_of_stream_written_ && need_more_input_; } bool VideoRenderer::IsSeekingInProgress() const { @@ -162,7 +162,7 @@ if (seeking_) { if (frame->is_end_of_stream()) { seeking_ = false; - } else if (frame->pts() < seeking_to_pts_) { + } else if (frame->timestamp() < seeking_to_time_) { frame_too_early = true; } }
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h index 609c075..61c55a7 100644 --- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h +++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
@@ -58,7 +58,7 @@ void WriteSample(const scoped_refptr<InputBuffer>& input_buffer); void WriteEndOfStream(); - void Seek(SbMediaTime seek_to_pts); + void Seek(SbTime seek_to_time); bool IsEndOfStreamWritten() const { return end_of_stream_written_; } bool IsEndOfStreamPlayed() const; @@ -70,12 +70,6 @@ private: typedef std::list<scoped_refptr<VideoFrame>> Frames; - // Set a soft limit for the max video frames we can cache so we can: - // 1. Avoid using too much memory. - // 2. Have the frame cache full to simulate the state that the renderer can - // no longer accept more data. - static const size_t kMaxCachedFrames = 12; - void OnDecoderStatus(VideoDecoder::Status status, const scoped_refptr<VideoFrame>& frame); void Render(VideoRendererSink::DrawFrameCB draw_frame_cb); @@ -92,13 +86,13 @@ Frames frames_; - SbMediaTime seeking_to_pts_; + SbTime seeking_to_time_; bool end_of_stream_written_; bool need_more_input_; scoped_ptr<VideoDecoder> decoder_; - // Our owner will attempt to seek to pts 0 when playback begins. In + // Our owner will attempt to seek to time 0 when playback begins. In // general, seeking could require a full reset of the underlying decoder on // some platforms, so we make an effort to improve playback startup // performance by keeping track of whether we already have a fresh decoder,
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.cc b/src/starboard/shared/starboard/player/input_buffer_internal.cc index 3e2b30f..cb38d04 100644 --- a/src/starboard/shared/starboard/player/input_buffer_internal.cc +++ b/src/starboard/shared/starboard/player/input_buffer_internal.cc
@@ -93,7 +93,7 @@ void* context, const void* sample_buffer, int sample_buffer_size, - SbMediaTime sample_pts, + SbTime sample_timestamp, const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* sample_drm_info) : sample_type_(sample_type), @@ -102,7 +102,7 @@ context_(context), data_(static_cast<const uint8_t*>(sample_buffer)), size_(sample_buffer_size), - pts_(sample_pts) { + timestamp_(sample_timestamp) { SB_DCHECK(deallocate_sample_func); TryToAssignVideoSampleInfo(video_sample_info); TryToAssignDrmSampleInfo(sample_drm_info); @@ -115,14 +115,14 @@ const void* const* sample_buffers, const int* sample_buffer_sizes, int number_of_sample_buffers, - SbMediaTime sample_pts, + SbTime sample_timestamp, const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* sample_drm_info) : sample_type_(sample_type), deallocate_sample_func_(deallocate_sample_func), player_(player), context_(context), - pts_(sample_pts) { + timestamp_(sample_timestamp) { SB_DCHECK(deallocate_sample_func); SB_DCHECK(number_of_sample_buffers > 0); @@ -175,7 +175,8 @@ std::stringstream ss; ss << "========== " << (has_drm_info_ ? "encrypted " : "clear ") << (sample_type_ == kSbMediaTypeAudio ? "audio" : "video") - << " sample @ pts: " << pts_ << " in " << size_ << " bytes ==========\n"; + << " sample @ timestamp: " << timestamp_ << " in " << size_ + << " bytes ==========\n"; if (has_video_sample_info_) { ss << video_sample_info_.frame_width << " x " << video_sample_info_.frame_height << '\n';
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.h b/src/starboard/shared/starboard/player/input_buffer_internal.h index b0aacd6..d680ef3 100644 --- a/src/starboard/shared/starboard/player/input_buffer_internal.h +++ b/src/starboard/shared/starboard/player/input_buffer_internal.h
@@ -38,7 +38,7 @@ void* context, const void* sample_buffer, int sample_buffer_size, - SbMediaTime sample_pts, + SbTime sample_timestamp, const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* sample_drm_info); InputBuffer(SbMediaType sample_type, @@ -48,7 +48,7 @@ const void* const* sample_buffers, const int* sample_buffer_sizes, int number_of_sample_buffers, - SbMediaTime sample_pts, + SbTime sample_timestamp, const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* sample_drm_info); ~InputBuffer(); @@ -56,7 +56,7 @@ SbMediaType sample_type() const { return sample_type_; } const uint8_t* data() const { return data_; } int size() const { return size_; } - SbMediaTime pts() const { return pts_; } + SbTime timestamp() const { return timestamp_; } const SbMediaVideoSampleInfo* video_sample_info() const { return has_video_sample_info_ ? &video_sample_info_ : NULL; } @@ -79,7 +79,7 @@ void* context_; const uint8_t* data_; int size_; - SbMediaTime pts_; + SbTime timestamp_; bool has_video_sample_info_; SbMediaColorMetadata color_metadata_; SbMediaVideoSampleInfo video_sample_info_;
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc index 3b07144..1d75d2d 100644 --- a/src/starboard/shared/starboard/player/player_create.cc +++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -46,6 +46,9 @@ SbPlayerDeallocateSampleFunc sample_deallocate_func, SbPlayerDecoderStatusFunc decoder_status_func, SbPlayerStatusFunc player_status_func, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + SbPlayerErrorFunc player_error_func, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) void* context, SbPlayerOutputMode output_mode, SbDecodeTargetGraphicsContextProvider* provider) { @@ -86,13 +89,17 @@ audio_header, output_mode, provider)); SbPlayer player = new SbPlayerPrivate( - audio_codec, duration_pts, sample_deallocate_func, decoder_status_func, - player_status_func, context, handler.Pass()); + audio_codec, SB_MEDIA_TIME_TO_SB_TIME(duration_pts), + sample_deallocate_func, decoder_status_func, player_status_func, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + player_error_func, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + context, handler.Pass()); #if SB_PLAYER_ENABLE_VIDEO_DUMPER using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter; - VideoDmpWriter::OnPlayerCreate(player, video_codec, audio_codec, duration_pts, - drm_system, audio_header); + VideoDmpWriter::OnPlayerCreate(player, video_codec, audio_codec, drm_system, + audio_header); #endif // SB_PLAYER_ENABLE_VIDEO_DUMPER return player;
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc index 29c52e7..ec5246e 100644 --- a/src/starboard/shared/starboard/player/player_internal.cc +++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -20,28 +20,30 @@ namespace { -SbMediaTime GetMediaTime(SbMediaTime media_pts, - SbTimeMonotonic media_pts_update_time) { - SbTimeMonotonic elapsed = SbTimeGetMonotonicNow() - media_pts_update_time; - return media_pts + elapsed * kSbMediaTimeSecond / kSbTimeSecond; +SbTime GetMediaTime(SbTime media_time, SbTimeMonotonic media_time_update_time) { + SbTimeMonotonic elapsed = SbTimeGetMonotonicNow() - media_time_update_time; + return media_time + elapsed; } } // namespace SbPlayerPrivate::SbPlayerPrivate( SbMediaAudioCodec audio_codec, - SbMediaTime duration_pts, + SbTime duration, SbPlayerDeallocateSampleFunc sample_deallocate_func, SbPlayerDecoderStatusFunc decoder_status_func, SbPlayerStatusFunc player_status_func, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + SbPlayerErrorFunc player_error_func, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) void* context, starboard::scoped_ptr<PlayerWorker::Handler> player_worker_handler) : sample_deallocate_func_(sample_deallocate_func), context_(context), ticket_(SB_PLAYER_INITIAL_TICKET), - duration_pts_(duration_pts), - media_pts_(0), - media_pts_update_time_(SbTimeGetMonotonicNow()), + duration_(duration), + media_time_(0), + media_time_update_time_(SbTimeGetMonotonicNow()), frame_width_(0), frame_height_(0), is_paused_(true), @@ -54,19 +56,23 @@ player_worker_handler.Pass(), decoder_status_func, player_status_func, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + player_error_func, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) this, - context)) {} + context)) { +} -void SbPlayerPrivate::Seek(SbMediaTime seek_to_pts, int ticket) { +void SbPlayerPrivate::Seek(SbTime seek_to_time, int ticket) { { starboard::ScopedLock lock(mutex_); SB_DCHECK(ticket_ != ticket); - media_pts_ = seek_to_pts; - media_pts_update_time_ = SbTimeGetMonotonicNow(); + media_time_ = seek_to_time; + media_time_update_time_ = SbTimeGetMonotonicNow(); ticket_ = ticket; } - worker_->Seek(seek_to_pts, ticket); + worker_->Seek(seek_to_time, ticket); } void SbPlayerPrivate::WriteSample( @@ -74,7 +80,7 @@ const void* const* sample_buffers, const int* sample_buffer_sizes, int number_of_sample_buffers, - SbMediaTime sample_pts, + SbTime sample_timestamp, const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* sample_drm_info) { if (sample_type == kSbMediaTypeVideo) { @@ -82,7 +88,7 @@ } starboard::scoped_refptr<InputBuffer> input_buffer = new InputBuffer( sample_type, sample_deallocate_func_, this, context_, sample_buffers, - sample_buffer_sizes, number_of_sample_buffers, sample_pts, + sample_buffer_sizes, number_of_sample_buffers, sample_timestamp, video_sample_info, sample_drm_info); worker_->WriteSample(input_buffer); } @@ -105,12 +111,13 @@ SB_DCHECK(out_player_info != NULL); starboard::ScopedLock lock(mutex_); - out_player_info->duration_pts = duration_pts_; + // TODO: change out_player_info to have duration (in SbTime). + out_player_info->duration_pts = SB_TIME_TO_SB_MEDIA_TIME(duration_); if (is_paused_) { - out_player_info->current_media_pts = media_pts_; + out_player_info->current_media_pts = SB_TIME_TO_SB_MEDIA_TIME(media_time_); } else { - out_player_info->current_media_pts = - GetMediaTime(media_pts_, media_pts_update_time_); + out_player_info->current_media_pts = SB_TIME_TO_SB_MEDIA_TIME( + GetMediaTime(media_time_, media_time_update_time_)); } out_player_info->frame_width = frame_width_; out_player_info->frame_height = frame_height_; @@ -136,13 +143,13 @@ worker_->SetVolume(volume_); } -void SbPlayerPrivate::UpdateMediaTime(SbMediaTime media_time, int ticket) { +void SbPlayerPrivate::UpdateMediaTime(SbTime media_time, int ticket) { starboard::ScopedLock lock(mutex_); if (ticket_ != ticket) { return; } - media_pts_ = media_time; - media_pts_update_time_ = SbTimeGetMonotonicNow(); + media_time_ = media_time; + media_time_update_time_ = SbTimeGetMonotonicNow(); } void SbPlayerPrivate::UpdateDroppedVideoFrames(int dropped_video_frames) {
diff --git a/src/starboard/shared/starboard/player/player_internal.h b/src/starboard/shared/starboard/player/player_internal.h index 5301b7e..9a85dd4 100644 --- a/src/starboard/shared/starboard/player/player_internal.h +++ b/src/starboard/shared/starboard/player/player_internal.h
@@ -31,19 +31,22 @@ SbPlayerPrivate( SbMediaAudioCodec audio_codec, - SbMediaTime duration_pts, + SbTime duration, SbPlayerDeallocateSampleFunc sample_deallocate_func, SbPlayerDecoderStatusFunc decoder_status_func, SbPlayerStatusFunc player_status_func, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + SbPlayerErrorFunc player_error_func, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) void* context, starboard::scoped_ptr<PlayerWorker::Handler> player_worker_handler); - void Seek(SbMediaTime seek_to_pts, int ticket); + void Seek(SbTime seek_to_time, int ticket); void WriteSample(SbMediaType sample_type, const void* const* sample_buffers, const int* sample_buffer_sizes, int number_of_sample_buffers, - SbMediaTime sample_pts, + SbTime sample_time, const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* sample_drm_info); void WriteEndOfStream(SbMediaType stream_type); @@ -58,7 +61,7 @@ private: // PlayerWorker::Host methods. - void UpdateMediaTime(SbMediaTime media_time, int ticket) override; + void UpdateMediaTime(SbTime media_time, int ticket) override; void UpdateDroppedVideoFrames(int dropped_video_frames) override; SbPlayerDeallocateSampleFunc sample_deallocate_func_; @@ -66,9 +69,9 @@ starboard::Mutex mutex_; int ticket_; - SbMediaTime duration_pts_; - SbMediaTime media_pts_; - SbTimeMonotonic media_pts_update_time_; + SbTime duration_; + SbTime media_time_; + SbTimeMonotonic media_time_update_time_; int frame_width_; int frame_height_; bool is_paused_;
diff --git a/src/starboard/shared/starboard/player/player_seek.cc b/src/starboard/shared/starboard/player/player_seek.cc index 757a1f9..868a845 100644 --- a/src/starboard/shared/starboard/player/player_seek.cc +++ b/src/starboard/shared/starboard/player/player_seek.cc
@@ -23,5 +23,5 @@ return; } - player->Seek(seek_to_pts, ticket); + player->Seek(SB_MEDIA_TIME_TO_SB_TIME(seek_to_pts), ticket); }
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc index 3089a1e..7c050f2 100644 --- a/src/starboard/shared/starboard/player/player_worker.cc +++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -14,6 +14,8 @@ #include "starboard/shared/starboard/player/player_worker.h" +#include <string> + #include "starboard/common/reset_and_return.h" #include "starboard/condition_variable.h" #include "starboard/memory.h" @@ -48,6 +50,9 @@ scoped_ptr<Handler> handler, SbPlayerDecoderStatusFunc decoder_status_func, SbPlayerStatusFunc player_status_func, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + SbPlayerErrorFunc player_error_func, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) SbPlayer player, void* context) : thread_(kSbThreadInvalid), @@ -56,6 +61,9 @@ handler_(handler.Pass()), decoder_status_func_(decoder_status_func), player_status_func_(player_status_func), +#if SB_HAS(PLAYER_ERROR_MESSAGE) + player_error_func_(player_error_func), +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) player_(player), context_(context), ticket_(SB_PLAYER_INITIAL_TICKET), @@ -85,13 +93,22 @@ // effects are gone. } -void PlayerWorker::UpdateMediaTime(SbMediaTime time) { +void PlayerWorker::UpdateMediaTime(SbTime time) { host_->UpdateMediaTime(time, ticket_); } - void PlayerWorker::UpdatePlayerState(SbPlayerState player_state) { - SB_DLOG_IF(WARNING, player_state == kSbPlayerStateError) - << "encountered kSbPlayerStateError"; +#if SB_HAS(PLAYER_ERROR_MESSAGE) + SB_DCHECK(!error_occurred_) << "Player state should not update after error."; + if (error_occurred_) { + return; + } +#else // SB_HAS(PLAYER_ERROR_MESSAGE) + SB_DCHECK(error_occurred_ == (player_state == kSbPlayerStateError)) + << "Player state error if and only if error occurred."; + if (error_occurred_ && (player_state != kSbPlayerStateError)) { + return; + } +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) player_state_ = player_state; if (!player_status_func_) { @@ -101,6 +118,20 @@ player_status_func_(player_, context_, player_state_, ticket_); } +void PlayerWorker::UpdatePlayerError(const std::string& message) { + SB_DLOG(WARNING) << "encountered player error: " << message; + error_occurred_ = true; +#if SB_HAS(PLAYER_ERROR_MESSAGE) + if (!player_error_func_) { + return; + } + + player_error_func_(player_, context_, kSbPlayerErrorDecode, message.c_str()); +#else // SB_HAS(PLAYER_ERROR_MESSAGE) + UpdatePlayerState(kSbPlayerStateError); +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) +} + // static void* PlayerWorker::ThreadEntryPoint(void* context) { ThreadParam* param = static_cast<ThreadParam*>(context); @@ -128,22 +159,26 @@ if (handler_->Init( this, job_queue_.get(), player_, &PlayerWorker::UpdateMediaTime, - &PlayerWorker::player_state, &PlayerWorker::UpdatePlayerState)) { + &PlayerWorker::player_state, &PlayerWorker::UpdatePlayerState +#if SB_HAS(PLAYER_ERROR_MESSAGE) + , + &PlayerWorker::UpdatePlayerError +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + )) { UpdatePlayerState(kSbPlayerStateInitialized); } else { - UpdatePlayerState(kSbPlayerStateError); + UpdatePlayerError("Failed to initialize PlayerWorker."); } } -void PlayerWorker::DoSeek(SbMediaTime seek_to_pts, int ticket) { +void PlayerWorker::DoSeek(SbTime seek_to_time, int ticket) { SB_DCHECK(job_queue_->BelongsToCurrentThread()); SB_DCHECK(player_state_ != kSbPlayerStateDestroyed); - SB_DCHECK(player_state_ != kSbPlayerStateError); + SB_DCHECK(!error_occurred_); SB_DCHECK(ticket_ != ticket); - SB_DLOG(INFO) << "Try to seek to timestamp " - << seek_to_pts / kSbMediaTimeSecond; + SB_DLOG(INFO) << "Try to seek to timestamp " << seek_to_time / kSbTimeSecond; if (write_pending_sample_job_token_.is_valid()) { job_queue_->RemoveJobByToken(write_pending_sample_job_token_); @@ -152,8 +187,8 @@ pending_audio_buffer_ = NULL; pending_video_buffer_ = NULL; - if (!handler_->Seek(seek_to_pts, ticket)) { - UpdatePlayerState(kSbPlayerStateError); + if (!handler_->Seek(seek_to_time, ticket)) { + UpdatePlayerError("Failed seek."); return; } @@ -173,12 +208,15 @@ if (player_state_ == kSbPlayerStateInitialized || player_state_ == kSbPlayerStateEndOfStream || - player_state_ == kSbPlayerStateDestroyed || - player_state_ == kSbPlayerStateError) { - SB_LOG(ERROR) << "Try to write sample when |player_state_| is " + player_state_ == kSbPlayerStateDestroyed) { + SB_LOG(ERROR) << "Tried to write sample when |player_state_| is " << player_state_; return; } + if (error_occurred_) { + SB_LOG(ERROR) << "Tried to write sample after error occurred."; + return; + } if (input_buffer->sample_type() == kSbMediaTypeAudio) { SB_DCHECK(audio_codec_ != kSbMediaAudioCodecNone); @@ -189,7 +227,7 @@ bool written; bool result = handler_->WriteSample(input_buffer, &written); if (!result) { - UpdatePlayerState(kSbPlayerStateError); + UpdatePlayerError("Failed to write sample."); return; } if (written) { @@ -228,12 +266,14 @@ SB_DCHECK(player_state_ != kSbPlayerStateDestroyed); if (player_state_ == kSbPlayerStateInitialized || - player_state_ == kSbPlayerStateEndOfStream || - player_state_ == kSbPlayerStateError) { - SB_LOG(ERROR) << "Try to write EOS when |player_state_| is " + player_state_ == kSbPlayerStateEndOfStream) { + SB_LOG(ERROR) << "Tried to write EOS when |player_state_| is " << player_state_; - // Return true so the pipeline will continue running with the particular - // call ignored. + return; + } + + if (error_occurred_) { + SB_LOG(ERROR) << "Tried to write EOS after error occurred."; return; } @@ -245,14 +285,14 @@ } if (!handler_->WriteEndOfStream(sample_type)) { - UpdatePlayerState(kSbPlayerStateError); + UpdatePlayerError("Failed to write end of stream."); } } void PlayerWorker::DoSetBounds(Bounds bounds) { SB_DCHECK(job_queue_->BelongsToCurrentThread()); if (!handler_->SetBounds(bounds)) { - UpdatePlayerState(kSbPlayerStateError); + UpdatePlayerError("Failed to set bounds"); } } @@ -260,7 +300,7 @@ SB_DCHECK(job_queue_->BelongsToCurrentThread()); if (!handler_->SetPause(pause)) { - UpdatePlayerState(kSbPlayerStateError); + UpdatePlayerError("Failed to set pause."); } } @@ -268,7 +308,7 @@ SB_DCHECK(job_queue_->BelongsToCurrentThread()); if (!handler_->SetPlaybackRate(playback_rate)) { - UpdatePlayerState(kSbPlayerStateError); + UpdatePlayerError("Failed to set playback rate."); } }
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h index 488d94e..14f1936 100644 --- a/src/starboard/shared/starboard/player/player_worker.h +++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -16,6 +16,7 @@ #define STARBOARD_SHARED_STARBOARD_PLAYER_PLAYER_WORKER_H_ #include <functional> +#include <string> #include "starboard/common/ref_counted.h" #include "starboard/common/scoped_ptr.h" @@ -44,11 +45,11 @@ public: class Host { public: - virtual void UpdateMediaTime(SbMediaTime media_time, int ticket) = 0; + virtual void UpdateMediaTime(SbTime media_time, int ticket) = 0; virtual void UpdateDroppedVideoFrames(int dropped_video_frames) = 0; protected: - ~Host() {} + virtual ~Host() {} }; struct Bounds { @@ -62,11 +63,12 @@ // All functions of this class will be called from the JobQueue thread. class Handler { public: - typedef void (PlayerWorker::*UpdateMediaTimeCB)(SbMediaTime media_time); + typedef void (PlayerWorker::*UpdateMediaTimeCB)(SbTime media_time); typedef SbPlayerState (PlayerWorker::*GetPlayerStateCB)() const; typedef void (PlayerWorker::*UpdatePlayerStateCB)( SbPlayerState player_state); - + typedef void (PlayerWorker::*UpdatePlayerErrorCB)( + const std::string& message); virtual ~Handler() {} // All the following functions return false to signal a fatal error. The @@ -76,8 +78,13 @@ SbPlayer player, UpdateMediaTimeCB update_media_time_cb, GetPlayerStateCB get_player_state_cb, - UpdatePlayerStateCB update_player_state_cb) = 0; - virtual bool Seek(SbMediaTime seek_to_pts, int ticket) = 0; + UpdatePlayerStateCB update_player_state_cb +#if SB_HAS(PLAYER_ERROR_MESSAGE) + , + UpdatePlayerErrorCB update_player_error_cb +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + ) = 0; + virtual bool Seek(SbTime seek_to_time, int ticket) = 0; virtual bool WriteSample(const scoped_refptr<InputBuffer>& input_buffer, bool* written) = 0; virtual bool WriteEndOfStream(SbMediaType sample_type) = 0; @@ -100,13 +107,16 @@ scoped_ptr<Handler> handler, SbPlayerDecoderStatusFunc decoder_status_func, SbPlayerStatusFunc player_status_func, +#if SB_HAS(PLAYER_ERROR_MESSAGE) + SbPlayerErrorFunc player_error_func, +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) SbPlayer player, void* context); ~PlayerWorker(); - void Seek(SbMediaTime seek_to_pts, int ticket) { + void Seek(SbTime seek_to_time, int ticket) { job_queue_->Schedule( - std::bind(&PlayerWorker::DoSeek, this, seek_to_pts, ticket)); + std::bind(&PlayerWorker::DoSeek, this, seek_to_time, ticket)); } void WriteSample(const scoped_refptr<InputBuffer>& input_buffer) { @@ -157,15 +167,16 @@ } private: - void UpdateMediaTime(SbMediaTime time); + void UpdateMediaTime(SbTime time); SbPlayerState player_state() const { return player_state_; } void UpdatePlayerState(SbPlayerState player_state); + void UpdatePlayerError(const std::string& message); static void* ThreadEntryPoint(void* context); void RunLoop(); void DoInit(); - void DoSeek(SbMediaTime seek_to_pts, int ticket); + void DoSeek(SbTime seek_to_time, int ticket); void DoWriteSample(const scoped_refptr<InputBuffer>& input_buffer); void DoWritePendingSamples(); void DoWriteEndOfStream(SbMediaType sample_type); @@ -186,6 +197,10 @@ SbPlayerDecoderStatusFunc decoder_status_func_; SbPlayerStatusFunc player_status_func_; +#if SB_HAS(PLAYER_ERROR_MESSAGE) + SbPlayerErrorFunc player_error_func_; +#endif // SB_HAS(PLAYER_ERROR_MESSAGE) + bool error_occurred_ = false; SbPlayer player_; void* context_; int ticket_;
diff --git a/src/starboard/shared/starboard/player/player_write_sample.cc b/src/starboard/shared/starboard/player/player_write_sample.cc index 7b1f8c6..4cfa242 100644 --- a/src/starboard/shared/starboard/player/player_write_sample.cc +++ b/src/starboard/shared/starboard/player/player_write_sample.cc
@@ -54,14 +54,17 @@ return; } + SbTime sample_timestamp = SB_MEDIA_TIME_TO_SB_TIME(sample_pts); + #if SB_PLAYER_ENABLE_VIDEO_DUMPER using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter; VideoDmpWriter::OnPlayerWriteSample( player, sample_type, sample_buffers, sample_buffer_sizes, - number_of_sample_buffers, sample_pts, video_sample_info, sample_drm_info); + number_of_sample_buffers, sample_timestamp, video_sample_info, + sample_drm_info); #endif // SB_PLAYER_ENABLE_VIDEO_DUMPER player->WriteSample(sample_type, sample_buffers, sample_buffer_sizes, - number_of_sample_buffers, sample_pts, video_sample_info, - sample_drm_info); + number_of_sample_buffers, sample_timestamp, + video_sample_info, sample_drm_info); }
diff --git a/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_avc_aac.dmp b/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_avc_aac.dmp new file mode 100644 index 0000000..bec6752 --- /dev/null +++ b/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_avc_aac.dmp Binary files differ
diff --git a/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_avc_aac.dmp.sha1 b/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_avc_aac.dmp.sha1 new file mode 100644 index 0000000..644c757 --- /dev/null +++ b/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_avc_aac.dmp.sha1
@@ -0,0 +1 @@ +139c88771089aaa3bc70a8653fb1cfab1d2b4c2d
diff --git a/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_vp9_opus.dmp b/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_vp9_opus.dmp new file mode 100644 index 0000000..4d663f9 --- /dev/null +++ b/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_vp9_opus.dmp Binary files differ
diff --git a/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_vp9_opus.dmp.sha1 b/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_vp9_opus.dmp.sha1 new file mode 100644 index 0000000..3e2c14a --- /dev/null +++ b/src/starboard/shared/starboard/player/testdata/beneath_the_canopy_vp9_opus.dmp.sha1
@@ -0,0 +1 @@ +f3a43ba6aecfce70950e590f6095cf1edc735158
diff --git a/src/starboard/shared/starboard/player/testdata/google_glass_h264_aac.dmp.sha1 b/src/starboard/shared/starboard/player/testdata/google_glass_h264_aac.dmp.sha1 deleted file mode 100644 index 4af127b..0000000 --- a/src/starboard/shared/starboard/player/testdata/google_glass_h264_aac.dmp.sha1 +++ /dev/null
@@ -1,2 +0,0 @@ -d6fda9bbaabcc1297c521f416e53a4db9c91cb76 -
diff --git a/src/starboard/shared/starboard/player/testdata/google_glass_vp9_opus.dmp.sha1 b/src/starboard/shared/starboard/player/testdata/google_glass_vp9_opus.dmp.sha1 deleted file mode 100644 index d6d0c51..0000000 --- a/src/starboard/shared/starboard/player/testdata/google_glass_vp9_opus.dmp.sha1 +++ /dev/null
@@ -1,2 +0,0 @@ -41e5b16a5e32fec8f0b02f08054a4d1b44deaa51 -
diff --git a/src/starboard/shared/starboard/player/video_dmp_common.h b/src/starboard/shared/starboard/player/video_dmp_common.h index c6f6e44..20a10a3 100644 --- a/src/starboard/shared/starboard/player/video_dmp_common.h +++ b/src/starboard/shared/starboard/player/video_dmp_common.h
@@ -44,7 +44,7 @@ // // audio/video access unit; // fourcc type: 'adat'/'vdat' -// <8 bytes time stamp in SbMediaTime> +// <8 bytes time stamp in SbTime> // <4 bytes size of key_id> + |size| bytes of key id // <4 bytes size of iv> + |size| bytes of iv // <4 bytes count> (0 for non-encrypted AU/frame)
diff --git a/src/starboard/shared/starboard/player/video_dmp_reader.cc b/src/starboard/shared/starboard/player/video_dmp_reader.cc index 882c6e1..a73425c 100644 --- a/src/starboard/shared/starboard/player/video_dmp_reader.cc +++ b/src/starboard/shared/starboard/player/video_dmp_reader.cc
@@ -35,7 +35,7 @@ return 1024; } - SbMediaTime duration = + SbTime duration = access_units.back().timestamp() - access_units.front().timestamp(); SB_DCHECK(duration > 0); @@ -45,7 +45,7 @@ total_bitrate += au.data().size(); } - return total_bitrate * 8 * kSbMediaTimeSecond / duration; + return total_bitrate * 8 * kSbTimeSecond / duration; } static void DeallocateSampleFunc(SbPlayer player, @@ -64,7 +64,8 @@ VideoDmpReader::VideoDmpReader(const char* filename) : reverse_byte_order_(false), read_cb_(std::bind(&VideoDmpReader::ReadFromFile, this, _1, _2)) { - SB_CHECK(SbFileCanOpen(filename, kSbFileOpenOnly | kSbFileRead)); + SB_CHECK(SbFileCanOpen(filename, kSbFileOpenOnly | kSbFileRead)) + << "Can't open " << filename; file_ = SbFileOpen(filename, kSbFileOpenOnly | kSbFileRead, NULL, NULL); SB_DCHECK(SbFileIsValid(file_)); @@ -143,20 +144,21 @@ // Guestimate the video fps. if (video_access_units_.size() > 1) { - SbMediaTime first_pts = video_access_units_.front().timestamp(); - SbMediaTime second_pts = video_access_units_.back().timestamp(); + SbTime first_timestamp = video_access_units_.front().timestamp(); + SbTime second_timestamp = video_access_units_.back().timestamp(); for (const auto& au : video_access_units_) { - if (au.timestamp() != first_pts && au.timestamp() < second_pts) { - second_pts = au.timestamp(); + if (au.timestamp() != first_timestamp && + au.timestamp() < second_timestamp) { + second_timestamp = au.timestamp(); } } - SB_DCHECK(first_pts < second_pts); - video_fps_ = kSbMediaTimeSecond / (second_pts - first_pts); + SB_DCHECK(first_timestamp < second_timestamp); + video_fps_ = kSbTimeSecond / (second_timestamp - first_timestamp); } } VideoDmpReader::AudioAccessUnit VideoDmpReader::ReadAudioAccessUnit() { - SbMediaTime timestamp; + SbTime timestamp; Read(read_cb_, reverse_byte_order_, ×tamp); bool drm_sample_info_present; @@ -178,7 +180,7 @@ } VideoDmpReader::VideoAccessUnit VideoDmpReader::ReadVideoAccessUnit() { - SbMediaTime timestamp; + SbTime timestamp; Read(read_cb_, reverse_byte_order_, ×tamp); bool drm_sample_info_present;
diff --git a/src/starboard/shared/starboard/player/video_dmp_reader.h b/src/starboard/shared/starboard/player/video_dmp_reader.h index 1518c85..e3674aa 100644 --- a/src/starboard/shared/starboard/player/video_dmp_reader.h +++ b/src/starboard/shared/starboard/player/video_dmp_reader.h
@@ -35,7 +35,7 @@ public: class AccessUnit { public: - AccessUnit(SbMediaTime timestamp, + AccessUnit(SbTime timestamp, const SbDrmSampleInfoWithSubSampleMapping* drm_sample_info, std::vector<uint8_t> data) : timestamp_(timestamp), @@ -44,14 +44,14 @@ ? SbDrmSampleInfoWithSubSampleMapping() : *drm_sample_info), data_(std::move(data)) {} - SbMediaTime timestamp() const { return timestamp_; } + SbTime timestamp() const { return timestamp_; } const SbDrmSampleInfo* drm_sample_info() const { return drm_sample_info_present_ ? &drm_sample_info_ : NULL; } const std::vector<uint8_t>& data() const { return data_; } private: - SbMediaTime timestamp_; + SbTime timestamp_; bool drm_sample_info_present_; SbDrmSampleInfoWithSubSampleMapping drm_sample_info_; std::vector<uint8_t> data_; @@ -61,7 +61,7 @@ class VideoAccessUnit : public AccessUnit { public: - VideoAccessUnit(SbMediaTime timestamp, + VideoAccessUnit(SbTime timestamp, const SbDrmSampleInfoWithSubSampleMapping* drm_sample_info, std::vector<uint8_t> data, const SbMediaVideoSampleInfoWithOptionalColorMetadata&
diff --git a/src/starboard/shared/starboard/player/video_dmp_writer.cc b/src/starboard/shared/starboard/player/video_dmp_writer.cc index bf9ae07..66338b4 100644 --- a/src/starboard/shared/starboard/player/video_dmp_writer.cc +++ b/src/starboard/shared/starboard/player/video_dmp_writer.cc
@@ -98,7 +98,6 @@ void VideoDmpWriter::OnPlayerCreate(SbPlayer player, SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, - SbMediaTime duration_pts, SbDrmSystem drm_system, const SbMediaAudioHeader* audio_header) { // TODO: Allow dump of drm initialization data @@ -109,8 +108,7 @@ return; } map->Register(player); - map->Get(player)->DumpConfigs(video_codec, audio_codec, duration_pts, - audio_header); + map->Get(player)->DumpConfigs(video_codec, audio_codec, audio_header); } // static @@ -120,16 +118,17 @@ const void* const* sample_buffers, const int* sample_buffer_sizes, int number_of_sample_buffers, - SbMediaTime sample_pts, + SbTime sample_timestamp, const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* drm_sample_info) { PlayerToWriterMap* map = GetOrCreatePlayerToWriterMap(); if (!map->dump_video_data()) { return; } - map->Get(player)->DumpAccessUnit( - sample_type, sample_buffers, sample_buffer_sizes, - number_of_sample_buffers, sample_pts, video_sample_info, drm_sample_info); + map->Get(player)->DumpAccessUnit(sample_type, sample_buffers, + sample_buffer_sizes, + number_of_sample_buffers, sample_timestamp, + video_sample_info, drm_sample_info); } // static @@ -143,7 +142,6 @@ void VideoDmpWriter::DumpConfigs(SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, - SbMediaTime duration_pts, const SbMediaAudioHeader* audio_header) { Write(write_cb_, kRecordTypeAudioConfig); Write(write_cb_, audio_codec); @@ -161,7 +159,7 @@ const void* const* sample_buffers, const int* sample_buffer_sizes, int number_of_sample_buffers, - SbMediaTime sample_pts, + SbTime sample_timestamp, const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* drm_sample_info) { SB_DCHECK(number_of_sample_buffers == 1); @@ -174,7 +172,7 @@ SB_NOTREACHED() << sample_type; } - Write(write_cb_, sample_pts); + Write(write_cb_, sample_timestamp); if (drm_sample_info && drm_sample_info->identifier_size == 16 && (drm_sample_info->initialization_vector_size == 8 ||
diff --git a/src/starboard/shared/starboard/player/video_dmp_writer.h b/src/starboard/shared/starboard/player/video_dmp_writer.h index 1a6d32d..23d5628 100644 --- a/src/starboard/shared/starboard/player/video_dmp_writer.h +++ b/src/starboard/shared/starboard/player/video_dmp_writer.h
@@ -36,7 +36,6 @@ static void OnPlayerCreate(SbPlayer player, SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, - SbMediaTime duration_pts, SbDrmSystem drm_system, const SbMediaAudioHeader* audio_header); static void OnPlayerWriteSample( @@ -45,7 +44,7 @@ const void* const* sample_buffers, const int* sample_buffer_sizes, int number_of_sample_buffers, - SbMediaTime sample_pts, + SbTime sample_timestamp, const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* drm_sample_info); static void OnPlayerDestroy(SbPlayer player); @@ -53,13 +52,12 @@ private: void DumpConfigs(SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, - SbMediaTime duration_pts, const SbMediaAudioHeader* audio_header); void DumpAccessUnit(SbMediaType sample_type, const void* const* sample_buffers, const int* sample_buffer_sizes, int number_of_sample_buffers, - SbMediaTime sample_pts, + SbTime sample_timestamp, const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* drm_sample_info); int WriteToFile(const void* buffer, int size);
diff --git a/src/starboard/shared/stub/drm_create_system.cc b/src/starboard/shared/stub/drm_create_system.cc index 5b43825..1bfb425 100644 --- a/src/starboard/shared/stub/drm_create_system.cc +++ b/src/starboard/shared/stub/drm_create_system.cc
@@ -14,7 +14,25 @@ #include "starboard/drm.h" -#if SB_API_VERSION >= 6 +#if SB_HAS(DRM_SESSION_CLOSED) + +SbDrmSystem SbDrmCreateSystem( + const char* key_system, + void* context, + SbDrmSessionUpdateRequestFunc update_request_callback, + SbDrmSessionUpdatedFunc session_updated_callback, + SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback, + SbDrmSessionClosedFunc session_closed_callback) { + SB_UNREFERENCED_PARAMETER(context); + SB_UNREFERENCED_PARAMETER(key_system); + SB_UNREFERENCED_PARAMETER(update_request_callback); + SB_UNREFERENCED_PARAMETER(session_updated_callback); + SB_UNREFERENCED_PARAMETER(key_statuses_changed_callback); + SB_UNREFERENCED_PARAMETER(session_closed_callback); + return kSbDrmSystemInvalid; +} + +#elif SB_HAS(DRM_KEY_STATUSES) SbDrmSystem SbDrmCreateSystem( const char* key_system,
diff --git a/src/starboard/shared/stub/player_create.cc b/src/starboard/shared/stub/player_create.cc index aa2b775..e61f14e 100644 --- a/src/starboard/shared/stub/player_create.cc +++ b/src/starboard/shared/stub/player_create.cc
@@ -23,6 +23,7 @@ SbPlayerDeallocateSampleFunc /*sample_deallocate_func*/, SbPlayerDecoderStatusFunc /*decoder_status_func*/, SbPlayerStatusFunc /*player_status_func*/, + SbPlayerErrorFunc /*player_error_func*/, void* /*context*/, SbPlayerOutputMode /*output_mode*/, SbDecodeTargetGraphicsContextProvider* /*provider*/) {
diff --git a/src/starboard/shared/uwp/analog_thumbstick_input_thread.cc b/src/starboard/shared/uwp/analog_thumbstick_input_thread.cc index 6ce605a..6405e6a 100644 --- a/src/starboard/shared/uwp/analog_thumbstick_input_thread.cc +++ b/src/starboard/shared/uwp/analog_thumbstick_input_thread.cc
@@ -20,28 +20,26 @@ #include <map> #include <vector> +#include "starboard/common/thread.h" #include "starboard/double.h" #include "starboard/shared/uwp/analog_thumbstick_input.h" -#include "starboard/shared/win32/simple_thread.h" #include "starboard/thread.h" namespace starboard { namespace shared { namespace uwp { -using starboard::shared::win32::SimpleThread; - -class AnalogThumbstickThread::Impl : public SimpleThread { +class AnalogThumbstickThread::Impl : public Thread { public: - explicit Impl(Callback* cb) : SimpleThread("AnalogGamepad"), callback_(cb) { + explicit Impl(Callback* cb) : Thread("AnalogGamepad"), callback_(cb) { stick_is_centered_[kSbKeyGamepadLeftStickLeft] = true; stick_is_centered_[kSbKeyGamepadRightStickLeft] = true; stick_is_centered_[kSbKeyGamepadLeftStickUp] = true; stick_is_centered_[kSbKeyGamepadRightStickUp] = true; - SimpleThread::Start(); + Thread::Start(); } - ~Impl() { SimpleThread::Join(); } + ~Impl() { Thread::Join(); } void Run() override { while (!join_called()) {
diff --git a/src/starboard/shared/uwp/app_accessors.h b/src/starboard/shared/uwp/app_accessors.h index 8d43775..8ba623a 100644 --- a/src/starboard/shared/uwp/app_accessors.h +++ b/src/starboard/shared/uwp/app_accessors.h
@@ -23,6 +23,8 @@ #include <string> +#include "starboard/key.h" + namespace starboard { namespace shared { namespace uwp { @@ -55,6 +57,8 @@ Windows::Security::Authentication::Web::Core::WebTokenRequestResult^> TryToFetchSsoToken(const std::string& url); +void InjectKeypress(SbKey key); + } // namespace uwp } // namespace shared } // namespace starboard
diff --git a/src/starboard/shared/uwp/application_uwp.cc b/src/starboard/shared/uwp/application_uwp.cc index a81e98f..a488e6a 100644 --- a/src/starboard/shared/uwp/application_uwp.cc +++ b/src/starboard/shared/uwp/application_uwp.cc
@@ -32,6 +32,7 @@ #include "starboard/mutex.h" #include "starboard/shared/starboard/application.h" #include "starboard/shared/starboard/audio_sink/audio_sink_internal.h" +#include "starboard/shared/starboard/net_log.h" #include "starboard/shared/uwp/analog_thumbstick_input_thread.h" #include "starboard/shared/uwp/app_accessors.h" #include "starboard/shared/uwp/async_utils.h" @@ -49,9 +50,13 @@ using Microsoft::WRL::ComPtr; using starboard::shared::starboard::Application; using starboard::shared::starboard::CommandLine; +using starboard::shared::starboard::kNetLogCommandSwitchWait; +using starboard::shared::starboard::NetLogFlushThenClose; +using starboard::shared::starboard::NetLogWaitForClientConnected; using starboard::shared::uwp::ApplicationUwp; using starboard::shared::uwp::RunInMainThreadAsync; using starboard::shared::uwp::WaitForResult; +using starboard::shared::win32::platformStringToString; using starboard::shared::win32::stringToPlatformString; using starboard::shared::win32::wchar_tToUTF8; using Windows::ApplicationModel::Activation::ActivationKind; @@ -79,8 +84,10 @@ using Windows::Security::Authentication::Web::Core::WebTokenRequestResult; using Windows::Security::Authentication::Web::Core::WebTokenRequestStatus; using Windows::Security::Credentials::WebAccountProvider; +using Windows::Storage::FileAttributes; using Windows::Storage::KnownFolders; using Windows::Storage::StorageFolder; +using Windows::Storage::StorageFile; using Windows::System::Threading::ThreadPoolTimer; using Windows::System::Threading::TimerElapsedHandler; using Windows::System::UserAuthenticationStatus; @@ -107,15 +114,6 @@ const char kStarboardArgumentsPath[] = "arguments\\starboard_arguments.txt"; const int64_t kMaxArgumentFileSizeBytes = 4 * 1024 * 1024; -// Filename, placed in the cache dir, whose presence indicates -// that the first run has happened. -const char kFirstRunFilename[] = "first_run"; -// The entry point used by the main app. -const char kMainAppDefaultEntryPoint[] = "https://www.youtube.com/tv"; -// The URL to be used for the main app on the first run. -const char kMainAppFirstRunUrl[] - = "https://www.youtube.com/api/xbox/cobalt_bootstrap"; - int main_return_value = 0; // IDisplayRequest is both "non-agile" and apparently @@ -250,34 +248,20 @@ return full_binary_path.substr(index + 1); } - -// Returns true of this is the app's first run on a given device. -bool IsFirstRun() { - char path[SB_FILE_MAX_PATH]; - bool success = SbSystemGetPath(kSbSystemPathCacheDirectory, - path, SB_FILE_MAX_PATH); - SB_DCHECK(success); - std::string file_path(path); - file_path.append(1, '/'); - file_path.append(kFirstRunFilename); - - if (SbFileExists(file_path.c_str())) { - return false; - } - - bool created; - SbFile file = SbFileOpen(file_path.c_str(), - kSbFileRead | kSbFileOpenAlways, &created, nullptr); - SbFileClose(file); - - return true; -} } // namespace +namespace starboard { +namespace shared { +namespace win32 { +// Called into drm_system_playready.cc +extern void DrmSystemOnUwpResume(); +} // namespace win32 +} // namespace shared +} // namespace starboard + ref class App sealed : public IFrameworkView { public: - App() : previously_activated_(false), - first_run_(IsFirstRun()) {} + App() : previously_activated_(false) {} // IFrameworkView methods. virtual void Initialize(CoreApplicationView^ application_view) { @@ -339,6 +323,7 @@ new ApplicationUwp::Event(kSbEventTypeResume, NULL, NULL)); ApplicationUwp::Get()->DispatchAndDelete( new ApplicationUwp::Event(kSbEventTypeUnpause, NULL, NULL)); + sbwin32::DrmSystemOnUwpResume(); } void OnKeyUp(CoreWindow^ sender, KeyEventArgs^ args) { @@ -349,47 +334,10 @@ ApplicationUwp::Get()->OnKeyEvent(sender, args, false); } -#pragma warning(push) -#pragma warning(disable: 4451) // False-positive Platform::Agile warning void OnActivated( CoreApplicationView^ application_view, IActivatedEventArgs^ args) { - SB_LOG(INFO) << "OnActivated first run:" << first_run_; - if (!first_run_) { - FinishActivated(application_view, args, entry_point_); - } else { - sbuwp::TryToFetchSsoToken(kMainAppFirstRunUrl).then( - [this, application_view, args]( - concurrency::task<WebTokenRequestResult^> previous_task) { - bool success = false; - try { - WebTokenRequestResult^ result = previous_task.get(); - success = result && - (result->ResponseStatus == WebTokenRequestStatus::Success); - } catch(Platform::Exception^) { - SB_LOG(INFO) << "Exception during RequestTokenAsync"; - } - // It seems like it should be able to specify a task_contination_context - // such that we don't need to additionally do RunInMainThreadAsync here, - // however obvious solutions seem to cause this to run in background - // threads. - SB_LOG(INFO) << "TryToFetchSsoToken success:" << success; - RunInMainThreadAsync([this, application_view, args, success]() { - std::string start_url = entry_point_; - if (success && start_url == kMainAppDefaultEntryPoint) { - SB_LOG(INFO) << "Using special main app first-run entry point."; - start_url = kMainAppFirstRunUrl; - } - FinishActivated(application_view, args, start_url); - }); - }); - } - } -#pragma warning(pop) - - private: - void FinishActivated( - CoreApplicationView^ application_view, IActivatedEventArgs^ args, - const std::string& start_url) { + SB_LOG(INFO) << "OnActivated"; + std::string start_url = entry_point_; bool command_line_set = false; // Please see application lifecyle description: @@ -470,11 +418,17 @@ if (!previously_activated_) { if (!command_line_set) { args_.push_back(GetArgvZero()); + std::string start_url_arg = "--url="; + start_url_arg.append(start_url); + args_.push_back(start_url_arg); + std::string partition_arg = "--local_storage_partition_url="; + partition_arg.append(entry_point_); + args_.push_back(partition_arg); + #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) char content_directory[SB_FILE_MAX_NAME]; content_directory[0] = '\0'; - if (SbSystemGetPath(kSbSystemPathContentDirectory, - content_directory, + if (SbSystemGetPath(kSbSystemPathContentDirectory, content_directory, SB_ARRAY_SIZE_INT(content_directory))) { std::string arguments_file_path = content_directory; arguments_file_path += SB_FILE_SEP_STRING; @@ -483,12 +437,6 @@ } #endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES) - std::string start_url_arg = "--url="; - start_url_arg.append(start_url); - args_.push_back(start_url_arg); - std::string partition_arg = "--local_storage_partition_url="; - partition_arg.append(entry_point_); - args_.push_back(partition_arg); for (auto& arg : args_) { argv_.push_back(arg.c_str()); } @@ -514,10 +462,12 @@ } if (command_line->HasSwitch(kLogPathSwitch)) { - std::string switch_val = command_line->GetSwitchValue(kLogPathSwitch); - sbuwp::OpenLogFile( - Windows::Storage::ApplicationData::Current->LocalCacheFolder, - switch_val.c_str()); + std::stringstream ss; + ss << sbwin32::platformStringToString( + Windows::Storage::ApplicationData::Current->LocalCacheFolder->Path); + ss << "\\" << "" << command_line->GetSwitchValue(kLogPathSwitch); + std::string full_path_log_file = ss.str(); + sbuwp::OpenLogFileWin32(full_path_log_file.c_str()); } else { #if !defined(COBALT_BUILD_TYPE_GOLD) // Log to a file on the last removable device available (probably the @@ -547,7 +497,7 @@ now->Year, now->Month, now->Day, now->Hour + now->FirstHourInThisPeriod, now->Minute, now->Second); - sbuwp::OpenLogFile(folder, filename); + sbuwp::OpenLogFileUWP(folder, filename); }); } #endif // !defined(COBALT_BUILD_TYPE_GOLD) @@ -569,6 +519,7 @@ previously_activated_ = true; } + private: void ProcessDeepLinkUri(std::string *uri_string) { SB_DCHECK(uri_string); if (previously_activated_) { @@ -583,7 +534,6 @@ std::string entry_point_; bool previously_activated_; - bool first_run_; // Only valid if previously_activated_ is true ActivationKind previous_activation_kind_; std::vector<std::string> args_; @@ -708,11 +658,32 @@ RunInMainThreadAsync([this, event]() { bool result = DispatchAndDelete(event); if (!result) { + NetLogFlushThenClose(); CoreApplication::Exit(); } }); } +void ApplicationUwp::InjectKeypress(SbKey key) { + std::unique_ptr<SbInputData> press_data(new SbInputData()); + std::unique_ptr<SbInputData> unpress_data(new SbInputData()); + + SbMemorySet(press_data.get(), 0, sizeof(*press_data)); + press_data->window = window_; + press_data->device_type = kSbInputDeviceTypeKeyboard; + press_data->device_id = device_id(); + press_data->key = key; + press_data->type = kSbInputEventTypePress; + + *unpress_data = *press_data; + unpress_data->type = kSbInputEventTypeUnpress; + + Inject(new Event(kSbEventTypeInput, press_data.release(), + &Application::DeleteDestructor<SbInputData>)); + Inject(new Event(kSbEventTypeInput, unpress_data.release(), + &Application::DeleteDestructor<SbInputData>)); +} + void ApplicationUwp::InjectTimedEvent(Application::TimedEvent* timed_event) { SbTimeMonotonic delay_usec = timed_event->target_time - SbTimeGetMonotonicNow(); @@ -879,6 +850,19 @@ }); } +void InjectKeypress(SbKey key) { + ApplicationUwp::Get()->InjectKeypress(key); +} + +bool HasNetLogSwitch(Platform::Array<Platform::String^>^ args) { + CommandLine::StringVector arg_v; + for (Platform::String^ arg : args) { + arg_v.push_back(platformStringToString(arg)); + } + CommandLine cmd_line(arg_v); + return cmd_line.HasSwitch(kNetLogCommandSwitchWait); +} + } // namespace uwp } // namespace shared } // namespace starboard @@ -910,8 +894,12 @@ starboard::shared::win32::RegisterMainThread(); + if (starboard::shared::uwp::HasNetLogSwitch(args)) { + NetLogWaitForClientConnected(); + } auto direct3DApplicationSource = ref new Direct3DApplicationSource(); CoreApplication::Run(direct3DApplicationSource); + NetLogFlushThenClose(); MFShutdown(); WSACleanup();
diff --git a/src/starboard/shared/uwp/application_uwp.h b/src/starboard/shared/uwp/application_uwp.h index 8d1c4a3..8727d5c 100644 --- a/src/starboard/shared/uwp/application_uwp.h +++ b/src/starboard/shared/uwp/application_uwp.h
@@ -98,6 +98,8 @@ void Inject(Event* event) override; + void InjectKeypress(SbKey key); + void SetStartLink(const char* link) { shared::starboard::Application::SetStartLink(link); }
diff --git a/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp b/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp index 9765ab9..4e56f13 100644 --- a/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp +++ b/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp
@@ -24,8 +24,6 @@ 'xb1_media_session_client.cc', 'xb1_media_session_client.h', 'xb1_media_session_updates.cc', - 'xb1_media_session_button_press.cc', - 'xb1_media_session_button_press.h', 'xhr_modify_headers.cc', ], 'dependencies': [
diff --git a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc index f3ce042..75bb089 100644 --- a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc +++ b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
@@ -34,8 +34,6 @@ namespace { -const char kCobaltBootstrapUrl[] = - "https://www.youtube.com/api/xbox/cobalt_bootstrap"; const char kXboxLiveAccountProviderId[] = "https://xsts.auth.xboxlive.com"; // The name of the header to send the STS token out on. @@ -136,29 +134,6 @@ } // namespace namespace cobalt { -namespace loader { - -std::string CobaltFetchMaybeAddHeader(const GURL& url) { - std::string out_string; - if (!StringStartsWith(url.spec(), kCobaltBootstrapUrl)) { - return out_string; - } - - if (!PopulateToken(url.spec(), &out_string)) { - return out_string; - } - std::string result; - result.append(kXauthHeaderName); - result.append(": "); - result.append(out_string); - - return result; -} - -} // namespace loader -} // namespace cobalt - -namespace cobalt { namespace xhr { void CobaltXhrModifyHeader(const GURL& request_url,
diff --git a/src/starboard/shared/uwp/log_file_impl.cc b/src/starboard/shared/uwp/log_file_impl.cc index fbcebc9..bc1f6e0 100644 --- a/src/starboard/shared/uwp/log_file_impl.cc +++ b/src/starboard/shared/uwp/log_file_impl.cc
@@ -14,107 +14,79 @@ #include "starboard/shared/uwp/log_file_impl.h" +#include <ppltasks.h> #include <string> -#include "starboard/log.h" +#include "starboard/common/scoped_ptr.h" #include "starboard/mutex.h" #include "starboard/once.h" -#include "starboard/shared/win32/wchar_utils.h" +#include "starboard/shared/uwp/log_writer_uwp.h" +#include "starboard/shared/uwp/log_writer_win32.h" #include "starboard/string.h" -using Windows::Foundation::AsyncOperationCompletedHandler; -using Windows::Foundation::AsyncStatus; -using Windows::Foundation::IAsyncOperation; -using Windows::Storage::FileAccessMode; -using Windows::Storage::StorageFile; using Windows::Storage::StorageFolder; -using Windows::Storage::Streams::DataWriter; -using Windows::Storage::Streams::IRandomAccessStream; -using Windows::Storage::Streams::IOutputStream; - -namespace { - -SbMutex log_mutex_ = SB_MUTEX_INITIALIZER; - -// The Windows Storage API must be used in order to access files in priviledged -// areas (e.g. KnownFolders::RemovableDevices). The win32 file API used by -// SbFile returns access denied errors in these situations. -DataWriter^ log_writer_ = nullptr; - -// SbMutex is not reentrant, so factor out close log file functionality for use -// by other functions. -void CloseLogFileWithoutLock() { - log_writer_ = nullptr; -} - -} // namespace namespace starboard { namespace shared { namespace uwp { +namespace { + +class LogFileImpl { + public: + static LogFileImpl* GetInstance(); + + void OpenUWP(StorageFolder^ folder, const char* filename) { + ScopedLock lock(mutex_); + impl_.reset(); + impl_ = CreateLogWriterUWP(folder, filename); + } + + void OpenWin32(const char* path) { + ScopedLock lock(mutex_); + impl_.reset(); + impl_ = CreateLogWriterWin32(path); + } + + void Close() { + ScopedLock lock(mutex_); + impl_.reset(); + } + + void Write(const char* text, int text_length) { + ScopedLock lock(mutex_); + if (impl_) { + impl_->Write(text, text_length); + } + } + + private: + LogFileImpl() {} + starboard::Mutex mutex_; + starboard::scoped_ptr<ILogWriter> impl_; +}; + +SB_ONCE_INITIALIZE_FUNCTION(LogFileImpl, LogFileImpl::GetInstance); + +} // namespace + void CloseLogFile() { - SbMutexAcquire(&log_mutex_); - CloseLogFileWithoutLock(); - SbMutexRelease(&log_mutex_); + LogFileImpl::GetInstance()->Close(); } -void OpenLogFile(StorageFolder^ folder, const char* filename) { - std::wstring wfilename = win32::CStringToWString(filename); +void OpenLogFileUWP(StorageFolder^ folder, const char* filename) { + LogFileImpl::GetInstance()->OpenUWP(folder, filename); +} - SbMutexAcquire(&log_mutex_); - CloseLogFileWithoutLock(); - - // Manually set the completion callback function instead of using - // concurrency::create_task() since those tasks may not execute before the - // UI thread wants the log_mutex_ to output another log. - auto task = folder->CreateFileAsync( - ref new Platform::String(wfilename.c_str()), - Windows::Storage::CreationCollisionOption::ReplaceExisting); - task->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>( - [folder](IAsyncOperation<StorageFile^>^ op, AsyncStatus) { - try { - auto task = op->GetResults()->OpenAsync(FileAccessMode::ReadWrite); - task->Completed = ref new - AsyncOperationCompletedHandler<IRandomAccessStream^>( - [](IAsyncOperation<IRandomAccessStream^>^ op, AsyncStatus) { - log_writer_ = ref new DataWriter( - op->GetResults()->GetOutputStreamAt(0)); - SbMutexRelease(&log_mutex_); - }); - } catch(Platform::Exception^) { - SbMutexRelease(&log_mutex_); - SB_LOG(ERROR) << "Unable to open log file in folder " - << win32::platformStringToString(folder->Name); - } - }); +void OpenLogFileWin32(const char* path) { + LogFileImpl::GetInstance()->OpenWin32(path); } void WriteToLogFile(const char* text, int text_length) { if (text_length <= 0) { return; } - - SbMutexAcquire(&log_mutex_); - if (log_writer_) { - log_writer_->WriteBytes(ref new Platform::Array<unsigned char>( - (unsigned char*) text, text_length)); - - // Manually set the completion callback function instead of using - // concurrency::create_task() since those tasks may not execute before the - // UI thread wants the log_mutex_ to output another log. - auto task = log_writer_->StoreAsync(); - task->Completed = ref new AsyncOperationCompletedHandler<unsigned int>( - [](IAsyncOperation<unsigned int>^, AsyncStatus) { - auto task = log_writer_->FlushAsync(); - task->Completed = ref new AsyncOperationCompletedHandler<bool>( - [](IAsyncOperation<bool>^, AsyncStatus) { - SbMutexRelease(&log_mutex_); - }); - }); - } else { - SbMutexRelease(&log_mutex_); - } + LogFileImpl::GetInstance()->Write(text, text_length); } } // namespace uwp
diff --git a/src/starboard/shared/uwp/log_file_impl.h b/src/starboard/shared/uwp/log_file_impl.h index 6f24f53..cfd1c17 100644 --- a/src/starboard/shared/uwp/log_file_impl.h +++ b/src/starboard/shared/uwp/log_file_impl.h
@@ -22,11 +22,12 @@ namespace shared { namespace uwp { +void OpenLogFileUWP(Windows::Storage::StorageFolder^ folder, + const char* filename); + +void OpenLogFileWin32(const char* path); + void CloseLogFile(); - -void OpenLogFile(Windows::Storage::StorageFolder^ folder, - const char* filename); - void WriteToLogFile(const char* text, int text_length); } // namespace uwp
diff --git a/src/starboard/shared/uwp/log_raw.cc b/src/starboard/shared/uwp/log_raw.cc index 892e649..e7a73c7 100644 --- a/src/starboard/shared/uwp/log_raw.cc +++ b/src/starboard/shared/uwp/log_raw.cc
@@ -17,16 +17,15 @@ #include <stdio.h> #include <windows.h> +#include "starboard/shared/starboard/net_log.h" #include "starboard/shared/uwp/log_file_impl.h" #include "starboard/string.h" namespace sbuwp = starboard::shared::uwp; -void SbLogRaw(const char* message) { - fprintf(stderr, "%s", message); - sbuwp::WriteToLogFile( - message, static_cast<int>(SbStringGetLength(message))); +namespace { +void OutputToDebugConsole(const char* message) { // OutputDebugStringA may stall for multiple seconds if the output string is // too long. Split |message| into shorter strings for output. char buffer[512]; @@ -43,3 +42,14 @@ } } } + +} // namespace. + +void SbLogRaw(const char* message) { + fprintf(stderr, "%s", message); + sbuwp::WriteToLogFile( + message, static_cast<int>(SbStringGetLength(message))); + + OutputToDebugConsole(message); + starboard::shared::starboard::NetLogWrite(message); +}
diff --git a/src/starboard/shared/uwp/log_writer_interface.h b/src/starboard/shared/uwp/log_writer_interface.h new file mode 100644 index 0000000..865583b --- /dev/null +++ b/src/starboard/shared/uwp/log_writer_interface.h
@@ -0,0 +1,33 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_SHARED_UWP_LOG_WRITER_INTERFACE_H_ +#define STARBOARD_SHARED_UWP_LOG_WRITER_INTERFACE_H_ + +namespace starboard { +namespace shared { +namespace uwp { + +class ILogWriter { + public: + ILogWriter() {} + virtual ~ILogWriter() {} + virtual void Write(const char* content, int size) = 0; +}; + +} // namespace uwp +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_UWP_LOG_WRITER_INTERFACE_H_
diff --git a/src/starboard/shared/uwp/log_writer_uwp.cc b/src/starboard/shared/uwp/log_writer_uwp.cc new file mode 100644 index 0000000..dc4959c --- /dev/null +++ b/src/starboard/shared/uwp/log_writer_uwp.cc
@@ -0,0 +1,157 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/shared/uwp/log_writer_uwp.h" + +#include <string> + +#include "starboard/common/semaphore.h" +#include "starboard/log.h" +#include "starboard/mutex.h" +#include "starboard/once.h" +#include "starboard/shared/win32/wchar_utils.h" +#include "starboard/string.h" + +using Windows::Foundation::AsyncOperationCompletedHandler; +using Windows::Foundation::AsyncStatus; +using Windows::Foundation::IAsyncOperation; +using Windows::Storage::FileAccessMode; +using Windows::Storage::StorageFile; +using Windows::Storage::StorageFolder; +using Windows::Storage::Streams::DataWriter; +using Windows::Storage::Streams::IRandomAccessStream; +using Windows::Storage::Streams::IOutputStream; + +namespace starboard { +namespace shared { +namespace uwp { +namespace { + +class SharedMutex { + public: + SharedMutex() : sema_(1) {} + void Acquire() { sema_.Take(); } + void Release() { sema_.Put(); } + bool AcquireTry() { return sema_.TakeTry(); } + private: + Semaphore sema_; +}; + +class LogWriterUWP : public ILogWriter { + public: + LogWriterUWP(StorageFolder^ folder, const char* filename) { + OpenLogFile(folder, filename); + } + + ~LogWriterUWP() { + CloseLogFile(); + } + + void Write(const char* content, int size) override { + WriteToLogFile(content, size); + } + + private: + // SbMutex is not reentrant, so factor out close log file functionality for + // use by other functions. + void CloseLogFile_Locked() { + log_writer_ = nullptr; + } + + void CloseLogFile() { + log_mutex_.Acquire(); + CloseLogFile_Locked(); + log_mutex_.Release(); + } + + void OpenLogFile(StorageFolder^ folder, const char* filename) { + std::wstring wfilename = win32::CStringToWString(filename); + + log_mutex_.Acquire(); + CloseLogFile_Locked(); + + // Manually set the completion callback function instead of using + // concurrency::create_task() since those tasks may not execute before the + // UI thread wants the log_mutex_ to output another log. + auto task = folder->CreateFileAsync( + ref new Platform::String(wfilename.c_str()), + Windows::Storage::CreationCollisionOption::ReplaceExisting); + task->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>( + [folder, this](IAsyncOperation<StorageFile^>^ op, AsyncStatus) { + if (op->Status != AsyncStatus::Completed) { + this->log_mutex_.Release(); + SB_LOG(ERROR) << "Unable to open log file in folder " + << win32::platformStringToString(folder->Name); + return; + } + + try { + auto task = op->GetResults()->OpenAsync(FileAccessMode::ReadWrite); + task->Completed = ref new + AsyncOperationCompletedHandler<IRandomAccessStream^>( + [this](IAsyncOperation<IRandomAccessStream^>^ op, AsyncStatus) { + this->log_writer_ = ref new DataWriter( + op->GetResults()->GetOutputStreamAt(0)); + this->log_mutex_.Release(); + }); + } catch(Platform::Exception^) { + this->log_mutex_.Release(); + SB_LOG(ERROR) << "Unable to open log file in folder " + << win32::platformStringToString(folder->Name); + } + }); + } + + void WriteToLogFile(const char* text, int text_length) { + if (text_length <= 0) { + return; + } + log_mutex_.Acquire(); + if (log_writer_) { + log_writer_->WriteBytes(ref new Platform::Array<unsigned char>( + (unsigned char*) text, text_length)); + + // Manually set the completion callback function instead of using + // concurrency::create_task() since those tasks may not execute before the + // UI thread wants the log_mutex_ to output another log. + auto task = log_writer_->StoreAsync(); + task->Completed = ref new AsyncOperationCompletedHandler<unsigned int>( + [this](IAsyncOperation<unsigned int>^, AsyncStatus) { + auto task = this->log_writer_->FlushAsync(); + task->Completed = ref new AsyncOperationCompletedHandler<bool>( + [this](IAsyncOperation<bool>^, AsyncStatus) { + this->log_mutex_.Release(); + }); + }); + } else { + log_mutex_.Release(); + } + } + SharedMutex log_mutex_; + // The Windows Storage API must be used in order to access files in + // privileged areas (e.g. KnownFolders::RemovableDevices). The win32 + // file API used by SbFile returns access denied errors in these situations. + DataWriter^ log_writer_ = nullptr; +}; +} // namespace. + +scoped_ptr<ILogWriter> CreateLogWriterUWP( + Windows::Storage::StorageFolder^ folder, const char* filename) { + scoped_ptr<ILogWriter> output(new LogWriterUWP(folder, filename)); + return output.Pass(); +} + +} // namespace uwp +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/uwp/log_writer_uwp.h b/src/starboard/shared/uwp/log_writer_uwp.h new file mode 100644 index 0000000..be0978b --- /dev/null +++ b/src/starboard/shared/uwp/log_writer_uwp.h
@@ -0,0 +1,34 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_SHARED_UWP_LOG_WRITER_UWP_H_ +#define STARBOARD_SHARED_UWP_LOG_WRITER_UWP_H_ + +#include <ppltasks.h> + +#include "starboard/common/scoped_ptr.h" +#include "starboard/shared/uwp/log_writer_interface.h" + +namespace starboard { +namespace shared { +namespace uwp { + +starboard::scoped_ptr<ILogWriter> CreateLogWriterUWP( + Windows::Storage::StorageFolder^ folder, const char* filename); + +} // namespace uwp +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_UWP_LOG_WRITER_UWP_H_
diff --git a/src/starboard/shared/uwp/log_writer_win32.cc b/src/starboard/shared/uwp/log_writer_win32.cc new file mode 100644 index 0000000..990e11a --- /dev/null +++ b/src/starboard/shared/uwp/log_writer_win32.cc
@@ -0,0 +1,86 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/shared/uwp/log_writer_win32.h" + +#include <string> + +#include "starboard/common/scoped_ptr.h" +#include "starboard/common/semaphore.h" +#include "starboard/file.h" +#include "starboard/log.h" +#include "starboard/string.h" + +using starboard::scoped_ptr; +using starboard::ScopedFile; + +namespace starboard { +namespace shared { +namespace uwp { +namespace { + +class LogWriterWin32 : public ILogWriter { + public: + explicit LogWriterWin32(const std::string& file_path) { + SbFileError out_error = kSbFileOk; + bool created_ok = false; + file_.reset( + new ScopedFile(file_path.c_str(), + kSbFileCreateAlways | kSbFileWrite, + &created_ok, + &out_error)); + if (!created_ok || out_error != kSbFileOk) { + SB_LOG(ERROR) << "Could not create watchdog file " << file_path; + file_.reset(); + } + } + + ~LogWriterWin32() { + FlushToDisk(); + } + + void Write(const char* content, int size) override { + starboard::ScopedLock lock(mutex_); + if (IsValid_Locked()) { + file_->Write(content, size); + } + return; + } + + private: + bool IsValid_Locked() const { + return file_ && file_->IsValid(); + } + + void FlushToDisk() { + starboard::ScopedLock lock(mutex_); + if (IsValid_Locked()) { + file_->Flush(); + } + } + std::string file_path_; + starboard::Mutex mutex_; + scoped_ptr<ScopedFile> file_; +}; + +} // namespace. + +scoped_ptr<ILogWriter> CreateLogWriterWin32(const char* path) { + scoped_ptr<ILogWriter> output(new LogWriterWin32(path)); + return output.Pass(); +} + +} // namespace uwp +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/uwp/log_writer_win32.h b/src/starboard/shared/uwp/log_writer_win32.h new file mode 100644 index 0000000..6cd233e --- /dev/null +++ b/src/starboard/shared/uwp/log_writer_win32.h
@@ -0,0 +1,31 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_SHARED_UWP_LOG_WRITER_WIN32_H_ +#define STARBOARD_SHARED_UWP_LOG_WRITER_WIN32_H_ + +#include "starboard/common/scoped_ptr.h" +#include "starboard/shared/uwp/log_writer_interface.h" + +namespace starboard { +namespace shared { +namespace uwp { + +starboard::scoped_ptr<ILogWriter> CreateLogWriterWin32(const char* path); + +} // namespace uwp +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_UWP_LOG_WRITER_WIN32_H_
diff --git a/src/starboard/shared/uwp/media_get_audio_configuration.cc b/src/starboard/shared/uwp/media_get_audio_configuration.cc new file mode 100644 index 0000000..ee7c923 --- /dev/null +++ b/src/starboard/shared/uwp/media_get_audio_configuration.cc
@@ -0,0 +1,44 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/media.h" +#include "starboard/shared/uwp/wasapi_audio.h" + +using starboard::shared::uwp::WASAPIAudioDevice; + +SB_EXPORT bool SbMediaGetAudioConfiguration( + int output_index, + SbMediaAudioConfiguration* out_configuration) { + if (output_index != 0 || out_configuration == NULL) { + return false; + } + + out_configuration->index = 0; + out_configuration->connector = kSbMediaAudioConnectorNone; + out_configuration->latency = 0; + out_configuration->coding_type = kSbMediaAudioCodingTypePcm; + + Microsoft::WRL::ComPtr<WASAPIAudioDevice> audioDevice = + Microsoft::WRL::Make<WASAPIAudioDevice>(); + + int channels = audioDevice->GetNumChannels(); + + if (channels < 2) { + out_configuration->number_of_channels = 2; + } else { + out_configuration->number_of_channels = channels; + } + + return true; +}
diff --git a/src/starboard/shared/uwp/media_is_audio_supported.cc b/src/starboard/shared/uwp/media_is_audio_supported.cc new file mode 100644 index 0000000..ea5e26f --- /dev/null +++ b/src/starboard/shared/uwp/media_is_audio_supported.cc
@@ -0,0 +1,42 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/shared/starboard/media/media_support_internal.h" + +#include "starboard/configuration.h" +#include "starboard/media.h" +#include "starboard/shared/uwp/wasapi_audio.h" + +using starboard::shared::uwp::WASAPIAudioDevice; + +SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec, + int64_t bitrate) { + // TODO: Add Opus. + if (audio_codec != kSbMediaAudioCodecAac) { + return false; + } + + Microsoft::WRL::ComPtr<WASAPIAudioDevice> audioDevice = + Microsoft::WRL::Make<WASAPIAudioDevice>(); + + int64_t local_bitrate = audioDevice->GetBitrate(); + + if (local_bitrate <= 0) { + local_bitrate = SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND; + } + + // Here we say : "The app cannot play any encoded audio whose encoded bitrate + // exceeds the output bitrate" + return bitrate <= local_bitrate; +}
diff --git a/src/starboard/shared/uwp/microphone_impl.cc b/src/starboard/shared/uwp/microphone_impl.cc index 71df3d2..dc1baeb 100644 --- a/src/starboard/shared/uwp/microphone_impl.cc +++ b/src/starboard/shared/uwp/microphone_impl.cc
@@ -29,13 +29,13 @@ #include "starboard/atomic.h" #include "starboard/common/semaphore.h" +#include "starboard/common/thread.h" #include "starboard/log.h" #include "starboard/mutex.h" #include "starboard/shared/uwp/app_accessors.h" #include "starboard/shared/uwp/application_uwp.h" #include "starboard/shared/uwp/async_utils.h" #include "starboard/shared/win32/error_utils.h" -#include "starboard/shared/win32/simple_thread.h" #include "starboard/shared/win32/wchar_utils.h" #include "starboard/string.h" #include "starboard/time.h" @@ -54,13 +54,13 @@ using starboard::shared::uwp::ApplicationUwp; using starboard::shared::win32::CheckResult; using starboard::shared::win32::platformStringToString; -using starboard::shared::win32::SimpleThread; using Windows::Devices::Enumeration::DeviceInformation; using Windows::Devices::Enumeration::DeviceInformationCollection; using Windows::Foundation::EventRegistrationToken; using Windows::Foundation::IMemoryBufferByteAccess; using Windows::Foundation::IMemoryBufferReference; using Windows::Foundation::TypedEventHandler; +using Windows::Foundation::Uri; using Windows::Media::Audio::AudioDeviceInputNode; using Windows::Media::Audio::AudioDeviceNodeCreationStatus; using Windows::Media::Audio::AudioFrameOutputNode; @@ -77,6 +77,7 @@ using Windows::Media::Devices::MediaDevice; using Windows::Media::MediaProperties::AudioEncodingProperties; using Windows::Media::Render::AudioRenderCategory; +using Windows::System::Launcher; namespace { @@ -136,6 +137,29 @@ return dispatcher->HasThreadAccess; } +void LaunchMicrophonePermissionsAppAsync() { + // Schedule a task to run on the main thread which will launch a URI to + // request microphone permissions. + auto main_thread_task = []() { + auto uri = ref new Uri("ms-settings:privacy-microphone"); + + concurrency::create_task(Launcher::LaunchUriAsync(uri)) + .then([](concurrency::task<bool> previous_task){ + try { + bool launched_ok = !!previous_task.get(); + SB_LOG_IF(ERROR, !launched_ok); + } catch (Platform::Exception^ e) { + HRESULT hr = e->HResult; + std::string msg = platformStringToString(e->Message); + SB_LOG(ERROR) + << "Exception while launching permissions app, HRESULT: " << hr + << ", msg: " << msg; + } + }); + }; + starboard::shared::uwp::RunInMainThreadAsync(main_thread_task); +} + std::vector<DeviceInformation^> GetAllMicrophoneDevices() { std::vector<DeviceInformation^> output; Platform::String^ audio_str = MediaDevice::GetAudioCaptureSelector(); @@ -159,12 +183,15 @@ AudioGraph^ graph = result->Graph; return graph; } - std::vector<AudioDeviceInputNode^> GenerateAudioInputNodes( const std::vector<DeviceInformation^>& microphone_devices, AudioEncodingProperties^ encoding_properties, AudioGraph^ graph) { std::vector<AudioDeviceInputNode^> output; + + SbTime start_time = SbTimeGetMonotonicNow(); + + bool had_permissions_error = false; for (DeviceInformation^ mic : microphone_devices) { auto create_microphone_input_task = graph->CreateDeviceInputNodeAsync( MediaCategory::Speech, encoding_properties, mic); @@ -178,16 +205,38 @@ SB_LOG(INFO) << "Failed to create microphone with device name \"" << platformStringToString(mic->Name) << "\" because " << ToString(status); + if (status == AudioDeviceNodeCreationStatus::AccessDenied) { + // The user hasn't given cobalt access to the microphone because they + // declined access to the microphone now or previously. + had_permissions_error = true; + } continue; } - SB_LOG(INFO) << "Created a microphone with device \"" << platformStringToString(mic->Name) << "\""; - input_node->ConsumeInput = true; input_node->OutgoingGain = kMicGain; output.push_back(input_node); } + + SbTime delta_time = SbTimeGetMonotonicNow() - start_time; + const bool had_ui_interaction = delta_time > (kSbTimeMillisecond*250); + + // We only care to retry permissions if there were + // 1. No microphones that could be opened. + // 2. There are 1 or more microphones that had errors. + // 3. There was no UI interaction, which is detected if the audio + // node creation completed really quickly. A quick action suggests + // that there was no user interaction and therefore we are in a + // permissions "cooldown" period. These typically last for 30 minutes + // and the work around requires an explicit permissions request. + const bool do_launch_microphone_permissions_app = + output.empty() && had_permissions_error && + !had_ui_interaction; + + if (do_launch_microphone_permissions_app) { + LaunchMicrophonePermissionsAppAsync(); + } return output; } @@ -263,7 +312,7 @@ // immediately start recording. A callback will be created which will process // audio data when new samples are available. The Microphone will stop // recording when Close() is called. -class MicrophoneProcessor : public SimpleThread { +class MicrophoneProcessor : public starboard::Thread { public: // This will try and create a microphone. This will fail (return null) if // there are not available microphones. @@ -288,7 +337,7 @@ } virtual ~MicrophoneProcessor() { - SimpleThread::Join(); + Thread::Join(); audio_graph_->Stop(); } @@ -315,7 +364,7 @@ size_t max_num_samples, int sample_rate, const std::vector<DeviceInformation^>& microphone_devices) - : SimpleThread("MicrophoneProcessor"), + : Thread("MicrophoneProcessor"), max_num_samples_(max_num_samples) { audio_graph_ = CreateAudioGraph(AudioRenderCategory::Speech, QuantumSizeSelectionMode::SystemDefault); @@ -335,7 +384,7 @@ } // Update the audio data whenever a new audio sample has been finished. audio_graph_->Start(); - SimpleThread::Start(); + Thread::Start(); } void Run() override {
diff --git a/src/starboard/shared/uwp/starboard_platform.gypi b/src/starboard/shared/uwp/starboard_platform.gypi index 4e7b967..8f369cd 100644 --- a/src/starboard/shared/uwp/starboard_platform.gypi +++ b/src/starboard/shared/uwp/starboard_platform.gypi
@@ -28,6 +28,13 @@ 'log_file_impl.h', 'log_raw.cc', 'log_raw_format.cc', + 'log_writer_interface.h', + 'log_writer_uwp.cc', + 'log_writer_uwp.h', + 'log_writer_win32.cc', + 'log_writer_win32.h', + 'media_get_audio_configuration.cc', + 'media_is_audio_supported.cc', 'media_is_output_protected.cc', 'media_set_output_protection.cc', 'sso.cc', @@ -39,6 +46,8 @@ 'system_platform_error_internal.cc', 'system_platform_error_internal.h', 'system_raise_platform_error.cc', + 'wasapi_audio.cc', + 'wasapi_audio.h', 'watchdog_log.cc', 'watchdog_log.h', 'window_create.cc',
diff --git a/src/starboard/shared/uwp/wasapi_audio.cc b/src/starboard/shared/uwp/wasapi_audio.cc new file mode 100644 index 0000000..ddc7fd1 --- /dev/null +++ b/src/starboard/shared/uwp/wasapi_audio.cc
@@ -0,0 +1,92 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/shared/uwp/wasapi_audio.h" + +#include "starboard/log.h" + +namespace starboard { +namespace shared { +namespace uwp { + +using Microsoft::WRL::ComPtr; +using Windows::Media::Devices::MediaDevice; + +namespace { +const int kWaitForActivateTimeout = 500; // 0.5 sec +} // namespace + +WASAPIAudioDevice::WASAPIAudioDevice() { + InitializeAudioDevice(); + CloseHandle(activate_completed_); +} + +void WASAPIAudioDevice::InitializeAudioDevice() { + ComPtr<IActivateAudioInterfaceAsyncOperation> async_operation; + + // Default Audio Device Renderer + Platform::String ^ deviceIdString = MediaDevice::GetDefaultAudioRenderId( + Windows::Media::Devices::AudioDeviceRole::Default); + + if (FAILED(ActivateAudioInterfaceAsync(deviceIdString->Data(), + __uuidof(IAudioClient3), nullptr, this, + &async_operation))) { + return; + } + WaitForSingleObject(activate_completed_, kWaitForActivateTimeout); +} + +HRESULT WASAPIAudioDevice::ActivateCompleted( + IActivateAudioInterfaceAsyncOperation* operation) { + HRESULT hr = S_OK; + HRESULT hr_activate = S_OK; + ComPtr<IUnknown> audio_interface; + + // Check for a successful activation result + hr = operation->GetActivateResult(&hr_activate, &audio_interface); + + if (SUCCEEDED(hr) && SUCCEEDED(hr_activate)) { + // Get the pointer for the Audio Client. + hr = audio_interface->QueryInterface(IID_PPV_ARGS(&audio_client_)); + SB_DCHECK(audio_client_); + if (SUCCEEDED(hr)) { + AudioClientProperties audio_props = {0}; + audio_props.cbSize = sizeof(AudioClientProperties); + audio_props.bIsOffload = false; + audio_props.eCategory = AudioCategory_Media; + + hr = audio_client_->SetClientProperties(&audio_props); + if (SUCCEEDED(hr)) { + WAVEFORMATEX* format; + hr = audio_client_->GetMixFormat(&format); + SB_DCHECK(format); + if (SUCCEEDED(hr)) { + bitrate_ = format->nSamplesPerSec * format->wBitsPerSample; + channels_ = format->nChannels; + CoTaskMemFree(format); + } // audio_client_->GetMixFormat + } // audio_client_->SetClientProperties + } // audio_interface->QueryInterface + } + + audio_client_ = nullptr; + SetEvent(activate_completed_); + + // Need to return S_OK + return S_OK; +} + +} // namespace uwp +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/uwp/wasapi_audio.h b/src/starboard/shared/uwp/wasapi_audio.h new file mode 100644 index 0000000..af25d66 --- /dev/null +++ b/src/starboard/shared/uwp/wasapi_audio.h
@@ -0,0 +1,51 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef STARBOARD_SHARED_UWP_WASAPI_AUDIO_H_ +#define STARBOARD_SHARED_UWP_WASAPI_AUDIO_H_ + +#include <Audioclient.h> +#include <mmdeviceapi.h> +#include <wrl\implements.h> + +namespace starboard { +namespace shared { +namespace uwp { + +class WASAPIAudioDevice + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + Microsoft::WRL::FtmBase, + IActivateAudioInterfaceCompletionHandler> { + public: + WASAPIAudioDevice::WASAPIAudioDevice(); + + int GetNumChannels() const { return channels_; } + int GetBitrate() const { return bitrate_; } + + private: + STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation*); + void InitializeAudioDevice(); + + HANDLE activate_completed_ = CreateEvent(nullptr, TRUE, FALSE, nullptr); + Microsoft::WRL::ComPtr<IAudioClient3> audio_client_ = nullptr; + int bitrate_ = -1; + int channels_ = -1; +}; + +} // namespace uwp +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_UWP_WASAPI_AUDIO_H_
diff --git a/src/starboard/shared/uwp/watchdog_log.cc b/src/starboard/shared/uwp/watchdog_log.cc index 4363a93..6fcd19b 100644 --- a/src/starboard/shared/uwp/watchdog_log.cc +++ b/src/starboard/shared/uwp/watchdog_log.cc
@@ -18,13 +18,11 @@ #include "starboard/common/scoped_ptr.h" #include "starboard/common/semaphore.h" +#include "starboard/common/thread.h" #include "starboard/file.h" #include "starboard/log.h" -#include "starboard/shared/win32/simple_thread.h" #include "starboard/string.h" -using starboard::shared::win32::SimpleThread; - namespace starboard { namespace shared { namespace uwp { @@ -34,11 +32,11 @@ // file. On destruction WatchDogThread will write out "done\n". // This allows a test runner to accurately determine whether this process // is still alive, finished, or crashed. -class WatchDogThread : public SimpleThread { +class WatchDogThread : public Thread { public: explicit WatchDogThread(const std::string& file_path) - : SimpleThread("WatchDogLog"), file_path_(file_path) { - SimpleThread::Start(); + : Thread("WatchDogLog"), file_path_(file_path) { + Start(); } ~WatchDogThread() { @@ -58,7 +56,7 @@ SB_LOG(ERROR) << "Could not create watchdog file " << file_path_; return; } - while (!exiting_sema_.TakeWait(kSleepTime)) { + while (!WaitForJoin(kSleepTime)) { std::stringstream ss; ss << "alive: " << counter++ << "\n"; std::string str = ss.str(); @@ -73,14 +71,8 @@ SB_LOG_IF(ERROR, closed) << "Could not close file " << file_path_; } - void Join() override { - exiting_sema_.Put(); - SimpleThread::Join(); - } - private: std::string file_path_; - starboard::Semaphore exiting_sema_; }; starboard::scoped_ptr<WatchDogThread> s_watchdog_singleton_; } // namespace.
diff --git a/src/starboard/shared/win32/application_win32.cc b/src/starboard/shared/win32/application_win32.cc index 39a0e2c..79cdbc6 100644 --- a/src/starboard/shared/win32/application_win32.cc +++ b/src/starboard/shared/win32/application_win32.cc
@@ -247,6 +247,11 @@ return 0; } +int ApplicationWin32::Run(int argc, char** argv) { + int return_val = Application::Run(argc, argv); + return return_val; +} + void ApplicationWin32::ProcessNextSystemMessage() { MSG msg; BOOL peek_message_return = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
diff --git a/src/starboard/shared/win32/application_win32.h b/src/starboard/shared/win32/application_win32.h index 5ecd7fa..d243a95 100644 --- a/src/starboard/shared/win32/application_win32.h +++ b/src/starboard/shared/win32/application_win32.h
@@ -83,6 +83,9 @@ LRESULT WindowProcess(HWND hWnd, UINT msg, WPARAM w_param, LPARAM l_param); VOID TimedEventCallback(PVOID lp, BOOLEAN timer_or_wait_fired); + // Non-virtual override. Calls into super class Run(). + int Run(int argc, char** argv); + private: // --- Application overrides --- bool IsStartImmediate() override { return true; }
diff --git a/src/starboard/shared/win32/audio_decoder_thread.cc b/src/starboard/shared/win32/audio_decoder_thread.cc index ce9bf33..7d53b63 100644 --- a/src/starboard/shared/win32/audio_decoder_thread.cc +++ b/src/starboard/shared/win32/audio_decoder_thread.cc
@@ -58,7 +58,7 @@ AudioDecoderThread::AudioDecoderThread(AbstractWin32AudioDecoder* decoder_impl, AudioDecodedCallback* callback) - : SimpleThread("AudioDecoderThread"), + : Thread("AudioDecoderThread"), win32_audio_decoder_(decoder_impl), callback_(callback) { Start();
diff --git a/src/starboard/shared/win32/audio_decoder_thread.h b/src/starboard/shared/win32/audio_decoder_thread.h index 8f4d72f..b8710fd 100644 --- a/src/starboard/shared/win32/audio_decoder_thread.h +++ b/src/starboard/shared/win32/audio_decoder_thread.h
@@ -21,11 +21,11 @@ #include "starboard/common/ref_counted.h" #include "starboard/common/scoped_ptr.h" #include "starboard/common/semaphore.h" +#include "starboard/common/thread.h" #include "starboard/media.h" #include "starboard/shared/starboard/player/decoded_audio_internal.h" #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h" #include "starboard/shared/win32/media_common.h" -#include "starboard/shared/win32/simple_thread.h" #include "starboard/shared/win32/win32_audio_decoder.h" namespace starboard { @@ -41,7 +41,7 @@ // This decoder thread simplifies decoding media. Data is pushed in via // QueueInput() and QueueEndOfStream() and output data is pushed via // the AudioDecodedCallback. -class AudioDecoderThread : private SimpleThread { +class AudioDecoderThread : private Thread { public: AudioDecoderThread(AbstractWin32AudioDecoder* decoder_impl, AudioDecodedCallback* callback);
diff --git a/src/starboard/shared/win32/audio_sink.cc b/src/starboard/shared/win32/audio_sink.cc index 1a81052..5a8affd 100644 --- a/src/starboard/shared/win32/audio_sink.cc +++ b/src/starboard/shared/win32/audio_sink.cc
@@ -20,6 +20,7 @@ #include <limits> #include <vector> +#include "starboard/atomic.h" #include "starboard/condition_variable.h" #include "starboard/configuration.h" #include "starboard/log.h" @@ -78,7 +79,7 @@ SbAudioSinkUpdateSourceStatusFunc update_source_status_func, SbAudioSinkConsumeFramesFunc consume_frame_func, void* context); - ~XAudioAudioSink() override; + ~XAudioAudioSink() override {} bool IsType(Type* type) override; void SetPlaybackRate(double playback_rate) override { @@ -97,9 +98,27 @@ } void Process(); + void StopCallbacks() { + SbAtomicBarrier_Increment(&stop_callbacks_, 1); + // Make sure that any call to Process() returns so we know that + // no future callbacks will be invoked. + process_mutex_.Acquire(); + process_mutex_.Release(); + + // This must happen on a non-XAudio callback thread. + source_voice_->DestroyVoice(); + } + private: + bool AreCallbacksStopped() const { + return SbAtomicAcquire_Load(&stop_callbacks_) != 0; + } void SubmitSourceBuffer(int offset_in_frames, int count_frames); + // If true, this instance's source_voice_ has been destroyed and + // future Process() calls should return immediately. + SbAtomic32 stop_callbacks_; + XAudioAudioSinkType* const type_; const SbAudioSinkUpdateSourceStatusFunc update_source_status_func_; const SbAudioSinkConsumeFramesFunc consume_frame_func_; @@ -113,7 +132,10 @@ // that IXAudio2SourceVoice cannot be a ComPtr. IXAudio2SourceVoice* source_voice_; - // |mutex_| protects only |destroying_| and |playback_rate_|. + // |process_mutex_| is held during Process. Others may rapidly + // acquire/release to ensure they wait until the current Process() ends. + Mutex process_mutex_; + // |mutex_| protects |playback_rate_| and |volume_|. Mutex mutex_; double playback_rate_; double volume_; @@ -158,10 +180,12 @@ ComPtr<IXAudio2> x_audio2_; IXAudio2MasteringVoice* mastering_voice_; + // This mutex protects |audio_sinks_to_add_| and |audio_sinks_to_delete_|. Mutex mutex_; std::vector<XAudioAudioSink*> audio_sinks_to_add_; std::vector<SbAudioSink> audio_sinks_to_delete_; - ConditionVariable sink_removed_signal_; + + // This must only be accessed from the OnProcessingPassStart callback std::vector<XAudioAudioSink*> audio_sinks_on_xaudio_callbacks_; }; @@ -174,7 +198,8 @@ SbAudioSinkUpdateSourceStatusFunc update_source_status_func, SbAudioSinkConsumeFramesFunc consume_frame_func, void* context) - : type_(type), + : stop_callbacks_(0), + type_(type), source_voice_(source_voice), update_source_status_func_(update_source_status_func), consume_frame_func_(consume_frame_func), @@ -192,15 +217,17 @@ CHECK_HRESULT_OK(source_voice_->Stop(0)); } -XAudioAudioSink::~XAudioAudioSink() { - source_voice_->DestroyVoice(); -} - bool XAudioAudioSink::IsType(Type* type) { return type_ == type; } void XAudioAudioSink::Process() { + ScopedLock process_lock(process_mutex_); + if (AreCallbacksStopped()) { + // We must not continue in this case, since |source_voice_| has been + // destroyed. + return; + } int frames_in_buffer, offset_in_frames; bool is_playing, is_eos_reached; bool is_playback_rate_zero = false; @@ -288,7 +315,7 @@ CHECK_HRESULT_OK(source_voice_->SubmitSourceBuffer(&audio_buffer_info)); } -XAudioAudioSinkType::XAudioAudioSinkType() : sink_removed_signal_(mutex_) { +XAudioAudioSinkType::XAudioAudioSinkType() { CHECK_HRESULT_OK(XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR)); #if !defined(COBALT_BUILD_TYPE_GOLD) @@ -353,12 +380,15 @@ SB_LOG(WARNING) << "audio_sink is invalid."; return; } + // Previous versions of this code waited for the next OnProcessingPassStart() + // call to occur before returning. However, various circumstances could + // cause that never to happen, especially during UWP suspend. + // Instead, we return immediately, ensuring no SbAudioSink callbacks occur + // and postpone the delete itself until the next OnProcessingPassStart() + static_cast<XAudioAudioSink*>(audio_sink)->StopCallbacks(); + ScopedLock lock(mutex_); audio_sinks_to_delete_.push_back(audio_sink); - while (!audio_sinks_to_delete_.empty()) { - sink_removed_signal_.Wait(); - } - delete audio_sink; } void XAudioAudioSinkType::OnProcessingPassStart() { @@ -374,14 +404,14 @@ audio_sinks_on_xaudio_callbacks_.erase( std::find(audio_sinks_on_xaudio_callbacks_.begin(), audio_sinks_on_xaudio_callbacks_.end(), sink)); + delete sink; } audio_sinks_to_delete_.clear(); - sink_removed_signal_.Broadcast(); } mutex_.Release(); } - for (auto sink : audio_sinks_on_xaudio_callbacks_) { + for (XAudioAudioSink* sink : audio_sinks_on_xaudio_callbacks_) { sink->Process(); } }
diff --git a/src/starboard/shared/win32/decrypting_decoder.cc b/src/starboard/shared/win32/decrypting_decoder.cc index c02670e..725cb6c 100644 --- a/src/starboard/shared/win32/decrypting_decoder.cc +++ b/src/starboard/shared/win32/decrypting_decoder.cc
@@ -146,7 +146,8 @@ const void* data = input_buffer->data() + bytes_to_skip_in_sample; int size = input_buffer->size() - bytes_to_skip_in_sample; - std::int64_t win32_timestamp = ConvertToWin32Time(input_buffer->pts()); + std::int64_t win32_timestamp = + ConvertToWin32Time(input_buffer->timestamp()); const uint8_t* iv = NULL; int iv_size = 0; const SbDrmSubSampleMapping* subsample_mapping = NULL;
diff --git a/src/starboard/shared/win32/drm_create_system.cc b/src/starboard/shared/win32/drm_create_system.cc index 332a7e2..e5b8a55 100644 --- a/src/starboard/shared/win32/drm_create_system.cc +++ b/src/starboard/shared/win32/drm_create_system.cc
@@ -23,7 +23,8 @@ void* context, SbDrmSessionUpdateRequestFunc update_request_callback, SbDrmSessionUpdatedFunc session_updated_callback, - SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback) { + SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback, + SbDrmSessionClosedFunc session_closed_callback) { using ::starboard::shared::win32::SbDrmSystemPlayready; if (SbStringCompareAll(key_system, "com.youtube.playready") != 0) { @@ -33,5 +34,6 @@ return new SbDrmSystemPlayready(context, update_request_callback, session_updated_callback, - key_statuses_changed_callback); + key_statuses_changed_callback, + session_closed_callback); }
diff --git a/src/starboard/shared/win32/drm_system_playready.cc b/src/starboard/shared/win32/drm_system_playready.cc index 08dd289..39bb1a4 100644 --- a/src/starboard/shared/win32/drm_system_playready.cc +++ b/src/starboard/shared/win32/drm_system_playready.cc
@@ -14,6 +14,7 @@ #include "starboard/shared/win32/drm_system_playready.h" +#include <algorithm> #include <cctype> #include <sstream> #include <vector> @@ -21,12 +22,10 @@ #include "starboard/configuration.h" #include "starboard/log.h" #include "starboard/memory.h" +#include "starboard/mutex.h" +#include "starboard/once.h" #include "starboard/string.h" -namespace starboard { -namespace shared { -namespace win32 { - namespace { const bool kLogPlayreadyChallengeResponse = false; @@ -66,25 +65,54 @@ return GetHexRepresentation(&value, sizeof(T)); } +class ActiveDrmSystems { + public: + ::starboard::Mutex mutex_; + std::vector<starboard::shared::win32::SbDrmSystemPlayready*> active_systems_; +}; + +SB_ONCE_INITIALIZE_FUNCTION(ActiveDrmSystems, GetActiveDrmSystems); + } // namespace +namespace starboard { +namespace shared { +namespace win32 { + +void DrmSystemOnUwpResume() { + ::starboard::ScopedLock lock(GetActiveDrmSystems()->mutex_); + for (SbDrmSystemPlayready* item : GetActiveDrmSystems()->active_systems_) { + item->OnUwpResume(); + } +} + SbDrmSystemPlayready::SbDrmSystemPlayready( void* context, SbDrmSessionUpdateRequestFunc session_update_request_callback, SbDrmSessionUpdatedFunc session_updated_callback, - SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback) + SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback, + SbDrmSessionClosedFunc session_closed_callback) : context_(context), session_update_request_callback_(session_update_request_callback), session_updated_callback_(session_updated_callback), key_statuses_changed_callback_(key_statuses_changed_callback), + session_closed_callback_(session_closed_callback), current_session_id_(1) { SB_DCHECK(session_update_request_callback); SB_DCHECK(session_updated_callback); SB_DCHECK(key_statuses_changed_callback); + SB_DCHECK(session_closed_callback); + + ScopedLock lock(GetActiveDrmSystems()->mutex_); + GetActiveDrmSystems()->active_systems_.push_back(this); } SbDrmSystemPlayready::~SbDrmSystemPlayready() { SB_DCHECK(thread_checker_.CalledOnValidThread()); + ScopedLock lock(GetActiveDrmSystems()->mutex_); + auto& active_systems = GetActiveDrmSystems()->active_systems_; + active_systems.erase(std::remove( + active_systems.begin(), active_systems.end(), this)); } void SbDrmSystemPlayready::GenerateSessionUpdateRequest( @@ -285,6 +313,13 @@ return NULL; } +void SbDrmSystemPlayready::OnUwpResume() { + for (auto& item : successful_requests_) { + session_closed_callback_(this, context_, + item.first.data(), static_cast<int>(item.first.size())); + } +} + std::string SbDrmSystemPlayready::GenerateAndAdvanceSessionId() { SB_DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/src/starboard/shared/win32/drm_system_playready.h b/src/starboard/shared/win32/drm_system_playready.h index 27d9b72..37444b3 100644 --- a/src/starboard/shared/win32/drm_system_playready.h +++ b/src/starboard/shared/win32/drm_system_playready.h
@@ -54,8 +54,8 @@ void* context, SbDrmSessionUpdateRequestFunc session_update_request_callback, SbDrmSessionUpdatedFunc session_updated_callback, - SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback); - + SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback, + SbDrmSessionClosedFunc session_closed_callback); ~SbDrmSystemPlayready() override; // From |SbDrmSystemPrivate|. @@ -77,6 +77,8 @@ // Used by audio and video decoders to retrieve the decryptors. scoped_refptr<License> GetLicense(const uint8_t* key_id, int key_id_size); + void OnUwpResume(); + private: std::string GenerateAndAdvanceSessionId(); // Note: requires mutex_ to be held @@ -88,6 +90,7 @@ SbDrmSessionUpdateRequestFunc session_update_request_callback_; SbDrmSessionUpdatedFunc session_updated_callback_; SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback_; + SbDrmSessionClosedFunc session_closed_callback_; int current_session_id_; std::map<std::string, scoped_refptr<License> > pending_requests_;
diff --git a/src/starboard/shared/win32/file_close.cc b/src/starboard/shared/win32/file_close.cc index ce61c54..5241359 100644 --- a/src/starboard/shared/win32/file_close.cc +++ b/src/starboard/shared/win32/file_close.cc
@@ -16,8 +16,11 @@ #include <windows.h> +#include "starboard/shared/win32/error_utils.h" #include "starboard/shared/win32/file_internal.h" +using starboard::shared::win32::DebugLogWinError; + bool SbFileClose(SbFile file) { if (!SbFileIsValid(file)) { return false; @@ -25,6 +28,10 @@ bool success = CloseHandle(file->file_handle); + if (!success) { + DebugLogWinError(); + } + delete file; return success;
diff --git a/src/starboard/shared/win32/file_internal.cc b/src/starboard/shared/win32/file_internal.cc index a2fa41e..602641b 100644 --- a/src/starboard/shared/win32/file_internal.cc +++ b/src/starboard/shared/win32/file_internal.cc
@@ -41,7 +41,7 @@ return string.find(kUnixSepW) == std::wstring::npos; } -std::string NormalizePathSeperator(std::string str) { +std::string NormalizePathSeparator(std::string str) { size_t start_pos = 0; while ((start_pos = str.find(kUnixSep, start_pos)) != std::string::npos) { str.replace(start_pos, sizeof(kUnixSep) - 1, kWin32Sep); @@ -50,7 +50,7 @@ return str; } -std::wstring NormalizePathSeperator(std::wstring str) { +std::wstring NormalizePathSeparator(std::wstring str) { size_t start_pos = 0; while ((start_pos = str.find(kUnixSepW, start_pos)) != std::wstring::npos) { str.replace(start_pos, sizeof(kUnixSepW) / 2 - 1, kWin32SepW); @@ -76,7 +76,7 @@ } std::wstring NormalizeWin32Path(std::wstring str) { - return NormalizePathSeperator(str); + return NormalizePathSeparator(str); } HANDLE OpenFileOrDirectory(const char* path,
diff --git a/src/starboard/shared/win32/get_home_directory.cc b/src/starboard/shared/win32/get_home_directory.cc index 7dc5b34..f4e11e5 100644 --- a/src/starboard/shared/win32/get_home_directory.cc +++ b/src/starboard/shared/win32/get_home_directory.cc
@@ -57,13 +57,15 @@ CoTaskMemFree(local_app_data_path); // Instead of using the raw local AppData directory, we create a program // app directory if it doesn't exist already. - const CommandLine* command_line = - sbwin32::ApplicationWin32::Get()->GetCommandLine(); - SB_DCHECK(command_line); - const std::string program_name = command_line->argv()[0]; - PathAppend( - wide_path, - sbwin32::CStringToWString(program_name.c_str()).c_str()); + TCHAR program_name[MAX_PATH]; + DWORD program_name_length = GetModuleFileName(NULL, program_name, MAX_PATH); + if (program_name_length == 0) { + SB_LOG(ERROR) << "GetModuleFileName failed"; + sbwin32::DebugLogWinError(); + return false; + } + PathStripPath(program_name); + PathAppend(wide_path, program_name); if (!PathFileExists(wide_path)) { SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
diff --git a/src/starboard/shared/win32/gyp_configuration.py b/src/starboard/shared/win32/gyp_configuration.py index 07f9739..43253e1 100644 --- a/src/starboard/shared/win32/gyp_configuration.py +++ b/src/starboard/shared/win32/gyp_configuration.py
@@ -13,20 +13,30 @@ # limitations under the License. """Starboard win32 shared platform configuration for gyp_cobalt.""" -import os -import sys - import config.base +import logging +import os +import re +import subprocess +import sys import starboard.shared.win32.sdk_configuration as sdk_configuration from starboard.tools.paths import STARBOARD_ROOT from starboard.tools.testing import test_filter +def GetWindowsVersion(): + out = subprocess.check_output('ver', universal_newlines = True, shell=True) + lines = [l for l in out.split('\n') if len(l) > 0] + for l in lines: + m = re.search(r"Version\s([0-9\.]+)", out) + if m and m.group(1): + major, minor, build = m.group(1).split('.') + return (int(major), int(minor), int(build)) + raise IOError("Could not retrieve windows version") def _QuotePath(path): return '"' + path + '"' - class PlatformConfig(config.base.PlatformConfigBase): """Starboard Microsoft Windows platform configuration.""" @@ -91,13 +101,28 @@ from msvc_toolchain import MSVCUWPToolchain # pylint: disable=g-import-not-at-top,g-bad-import-order return MSVCUWPToolchain() + def IsWin10orHigher(self): + try: + # Both Win10 and Win2016-Server will return 10.0+ + major, minor, build = GetWindowsVersion() + return major >= 10 + except Exception as e: + print("Error while getting version for windows: " + str(e)) + return False + + def GetTestFilters(self): """Gets all tests to be excluded from a unit test run. Returns: A list of initialized TestFilter objects. """ - return [ + + if not self.IsWin10orHigher(): + logging.error("Tests can only be executed on Win10 and higher.") + return [ test_filter.DISABLE_TESTING ] + else: + return [ # Fails on JSC. test_filter.TestFilter( 'bindings_test', ('EvaluateScriptTest.ThreeArguments')), @@ -124,4 +149,4 @@ test_filter.TestFilter('webdriver_test', test_filter.FILTER_ALL) - ] + ]
diff --git a/src/starboard/shared/win32/log_raw.cc b/src/starboard/shared/win32/log_raw.cc index 2929156..4aeed07 100644 --- a/src/starboard/shared/win32/log_raw.cc +++ b/src/starboard/shared/win32/log_raw.cc
@@ -17,6 +17,7 @@ #include <stdio.h> #include <windows.h> +#include "starboard/shared/starboard/net_log.h" #include "starboard/shared/win32/log_file_impl.h" #include "starboard/string.h" @@ -27,4 +28,6 @@ OutputDebugStringA(message); sbwin32::WriteToLogFile( message, static_cast<int>(SbStringGetLength(message))); + + starboard::shared::starboard::NetLogWrite(message); }
diff --git a/src/starboard/shared/win32/log_raw_format.cc b/src/starboard/shared/win32/log_raw_format.cc index 39b5400..3eb4207 100644 --- a/src/starboard/shared/win32/log_raw_format.cc +++ b/src/starboard/shared/win32/log_raw_format.cc
@@ -24,13 +24,11 @@ namespace sbwin32 = starboard::shared::win32; void SbLogRawFormat(const char* format, va_list arguments) { - vfprintf(stderr, format, arguments); - char log_buffer[kMaxLogLineChars]; - int result = vsprintf_s(log_buffer, kMaxLogLineChars, format, arguments); + char log_buffer[kMaxLogLineChars] = {0}; + int result = vsprintf_s(log_buffer, kMaxLogLineChars-1, format, arguments); if (result > 0) { - OutputDebugStringA(log_buffer); - sbwin32::WriteToLogFile(log_buffer, result); + SbLogRaw(log_buffer); } else { - OutputDebugStringA("[log line too long]"); + SbLogRaw("[log line too long]"); } }
diff --git a/src/starboard/shared/win32/media_common.cc b/src/starboard/shared/win32/media_common.cc index 057bd81..a0e6938 100644 --- a/src/starboard/shared/win32/media_common.cc +++ b/src/starboard/shared/win32/media_common.cc
@@ -35,19 +35,17 @@ namespace shared { namespace win32 { -// Converts 90khz to 10Mhz (100ns time). -int64_t ConvertToWin32Time(SbMediaTime input) { +// Converts microseconds to 10Mhz (100ns time). +int64_t ConvertToWin32Time(SbTime input) { int64_t out = input; - out *= 1000; - out /= 9; + out *= 10; return out; } // Convert the other way around. -SbMediaTime ConvertToMediaTime(int64_t input) { - SbMediaTime out = input; - out *= 9; - out /= 1000; +SbTime ConvertToSbTime(int64_t input) { + SbTime out = input; + out /= 10; return out; }
diff --git a/src/starboard/shared/win32/media_common.h b/src/starboard/shared/win32/media_common.h index 5431ad7..fb3f677 100644 --- a/src/starboard/shared/win32/media_common.h +++ b/src/starboard/shared/win32/media_common.h
@@ -50,11 +50,11 @@ using VideoFramePtr = ::starboard::scoped_refptr<VideoFrame>; using Microsoft::WRL::ComPtr; -// Converts 90khz to 10Mhz (100ns time). -int64_t ConvertToWin32Time(SbMediaTime input); +// Converts microseconds to 10Mhz (100ns time). +int64_t ConvertToWin32Time(SbTime input); // Convert the other way around. -SbMediaTime ConvertToMediaTime(int64_t input); +SbTime ConvertToSbTime(int64_t input); std::vector<ComPtr<IMFMediaType>> GetAllOutputMediaTypes(int stream_id, IMFTransform* decoder);
diff --git a/src/starboard/shared/win32/media_is_video_supported.cc b/src/starboard/shared/win32/media_is_video_supported.cc index 3003d5d..7e1ca35 100644 --- a/src/starboard/shared/win32/media_is_video_supported.cc +++ b/src/starboard/shared/win32/media_is_video_supported.cc
@@ -57,6 +57,7 @@ hr = CoCreateInstance(CLSID_MSVPxDecoder, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(transform.GetAddressOf())); } + if (SUCCEEDED(hr)) { hr = transform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, ULONG_PTR(device_manager.Get())); @@ -79,16 +80,28 @@ int frame_height, int64_t bitrate, int fps) { - // Only certain codecs support 4K resolution. - bool supports_4k = video_codec == kSbMediaVideoCodecVp9; - // Not all devices can support 4k H264; some (e.g. xb1) may crash in - // the decoder if provided too high of a resolution. Therefore - // platforms must explicitly opt-in to support 4k H264. + int max_width = 1920; + int max_height = 1080; + + if (video_codec == kSbMediaVideoCodecVp9) { + // Vp9 supports 8k only in whitelisted platforms, up to 4k in the others. +#ifdef ENABLE_VP9_8K_SUPPORT + max_width = 7680; + max_height = 4320; +#else // ENABLE_VP9_8K_SUPPORT + max_width = 3840; + max_height = 2160; +#endif // ENABLE_VP9_8K_SUPPORT + } else if (video_codec == kSbMediaVideoCodecH264) { + // Not all devices can support 4k H264; some (e.g. xb1) may crash in + // the decoder if provided too high of a resolution. Therefore + // platforms must explicitly opt-in to support 4k H264. #ifdef ENABLE_H264_4K_SUPPORT - supports_4k = supports_4k || video_codec == kSbMediaVideoCodecH264; -#endif - const int max_width = supports_4k ? 3840 : 1920; - const int max_height = supports_4k ? 2160 : 1080; + max_width = 3840; + max_height = 2160; +#endif // ENABLE_H264_4K_SUPPORT + } + if (frame_width > max_width || frame_height > max_height) { return false; }
diff --git a/src/starboard/shared/win32/player_components_impl.cc b/src/starboard/shared/win32/player_components_impl.cc index a211137..e4a70b2 100644 --- a/src/starboard/shared/win32/player_components_impl.cc +++ b/src/starboard/shared/win32/player_components_impl.cc
@@ -68,6 +68,15 @@ video_decoder->reset(video_decoder_impl.release()); video_render_algorithm->reset(new VideoRenderAlgorithmImpl); } + + void GetAudioRendererParams(int* max_cached_frames, + int* max_frames_per_append) const override { + SB_DCHECK(max_cached_frames); + SB_DCHECK(max_frames_per_append); + + *max_cached_frames = 256 * 1024; + *max_frames_per_append = 16384; + } }; } // namespace
diff --git a/src/starboard/shared/win32/simple_thread.cc b/src/starboard/shared/win32/simple_thread.cc deleted file mode 100644 index 3d75149..0000000 --- a/src/starboard/shared/win32/simple_thread.cc +++ /dev/null
@@ -1,74 +0,0 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "starboard/shared/win32/simple_thread.h" - -#include "starboard/log.h" -#include "starboard/mutex.h" -#include "starboard/thread.h" -#include "starboard/time.h" -#include "starboard/types.h" - -namespace starboard { -namespace shared { -namespace win32 { - -SimpleThread::SimpleThread(const std::string& name) - : thread_(kSbThreadInvalid), name_(name), join_called_(false) {} - -SimpleThread::~SimpleThread() { - SB_DCHECK(join_called_.load()) << "Join not called on thread."; -} - -void SimpleThread::Start() { - SbThreadEntryPoint entry_point = ThreadEntryPoint; - - thread_ = SbThreadCreate(0, // default stack_size. - kSbThreadNoPriority, // default priority. - kSbThreadNoAffinity, // default affinity. - true, // joinable. - name_.c_str(), entry_point, this); - - // SbThreadCreate() above produced an invalid thread handle. - SB_DCHECK(thread_ != kSbThreadInvalid); - return; -} - -void SimpleThread::Sleep(SbTime microseconds) { - SbThreadSleep(microseconds); -} - -void SimpleThread::SleepMilliseconds(int value) { - return Sleep(value * kSbTimeMillisecond); -} - -void* SimpleThread::ThreadEntryPoint(void* context) { - SimpleThread* this_ptr = static_cast<SimpleThread*>(context); - this_ptr->Run(); - return NULL; -} - -void SimpleThread::Join() { - SB_DCHECK(join_called_.load() == false); - join_called_.store(true); - if (!SbThreadJoin(thread_, NULL)) { - SB_DCHECK(false) << "Could not join thread."; - } -} - -} // namespace win32 -} // namespace shared -} // namespace starboard
diff --git a/src/starboard/shared/win32/simple_thread.h b/src/starboard/shared/win32/simple_thread.h deleted file mode 100644 index 2fd8564..0000000 --- a/src/starboard/shared/win32/simple_thread.h +++ /dev/null
@@ -1,69 +0,0 @@ -/* - * Copyright 2017 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef STARBOARD_SHARED_WIN32_SIMPLE_THREAD_H_ -#define STARBOARD_SHARED_WIN32_SIMPLE_THREAD_H_ - -#include <string> - -#include "starboard/atomic.h" -#include "starboard/thread.h" -#include "starboard/time.h" -#include "starboard/types.h" - -namespace starboard { -namespace shared { -namespace win32 { - -class SimpleThread { - public: - explicit SimpleThread(const std::string& name); - virtual ~SimpleThread(); - - // Subclasses should override the Run method. - virtual void Run() = 0; - - // Signals to the thread to break out of it's loop. - virtual void Cancel() {} - - // Called by the main thread, this will cause Run() to be invoked - // on another thread. - void Start(); - // Destroys the threads resources. If overriding, then please call - // this function as well. - virtual void Join(); - - bool join_called() const { return join_called_.load(); } - - protected: - static void Sleep(SbTime microseconds); - static void SleepMilliseconds(int value); - - private: - static void* ThreadEntryPoint(void* context); - - const std::string name_; - SbThread thread_; - atomic_bool join_called_; - - SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread); -}; - -} // namespace win32 -} // namespace shared -} // namespace starboard - -#endif // STARBOARD_SHARED_WIN32_SIMPLE_THREAD_H_
diff --git a/src/starboard/shared/win32/starboard_main.cc b/src/starboard/shared/win32/starboard_main.cc index ccc3552..a17ed99 100644 --- a/src/starboard/shared/win32/starboard_main.cc +++ b/src/starboard/shared/win32/starboard_main.cc
@@ -22,11 +22,27 @@ #include <cstdio> #include "starboard/shared/starboard/audio_sink/audio_sink_internal.h" +#include "starboard/shared/starboard/command_line.h" +#include "starboard/shared/starboard/net_log.h" #include "starboard/shared/win32/application_win32.h" #include "starboard/shared/win32/thread_private.h" +using starboard::shared::starboard::CommandLine; +using starboard::shared::starboard::kNetLogCommandSwitchWait; +using starboard::shared::starboard::NetLogFlushThenClose; +using starboard::shared::starboard::NetLogWaitForClientConnected; using starboard::shared::win32::ApplicationWin32; +namespace { + +void WaitForNetLogIfNecessary(const CommandLine& cmd_line) { + if (cmd_line.HasSwitch(kNetLogCommandSwitchWait)) { + NetLogWaitForClientConnected(); + } +} + +} // namespace. + extern "C" int StarboardMain(int argc, char** argv) { WSAData wsaData; const int kWinSockVersionMajor = 2; @@ -44,14 +60,16 @@ CoInitialize(nullptr); SbAudioSinkPrivate::Initialize(); starboard::shared::win32::RegisterMainThread(); - // TODO: Do this with SbOnce when media is first used instead. HRESULT hr = MFStartup(MF_VERSION); SB_DCHECK(SUCCEEDED(hr)); + WaitForNetLogIfNecessary(CommandLine(argc, argv)); ApplicationWin32 application; // This will run the message loop. const int main_return_value = application.Run(argc, argv); + NetLogFlushThenClose(); + MFShutdown(); WSACleanup(); return main_return_value;
diff --git a/src/starboard/shared/win32/system_break_into_debugger.cc b/src/starboard/shared/win32/system_break_into_debugger.cc index 45c9158..4bea27f 100644 --- a/src/starboard/shared/win32/system_break_into_debugger.cc +++ b/src/starboard/shared/win32/system_break_into_debugger.cc
@@ -16,9 +16,12 @@ #include <intrin.h> +#include "starboard/shared/starboard/net_log.h" + void SbSystemBreakIntoDebugger() { // Note: neither DebugBreak() nor ExitProcess() are valid // on some Windows platforms (eg UWP) so we use __debugbreak() // instead. + starboard::shared::starboard::NetLogFlush(); __debugbreak(); }
diff --git a/src/starboard/shared/win32/video_decoder.cc b/src/starboard/shared/win32/video_decoder.cc index f3136b2..0535f11 100644 --- a/src/starboard/shared/win32/video_decoder.cc +++ b/src/starboard/shared/win32/video_decoder.cc
@@ -53,8 +53,13 @@ // Allocate decode targets at the maximum size to allow them to be reused for // all resolutions. This minimizes hitching during resolution changes. +#ifdef ENABLE_VP9_8K_SUPPORT +const int kMaxDecodeTargetWidth = 7680; +const int kMaxDecodeTargetHeight = 4320; +#else // ENABLE_VP9_8K_SUPPORT const int kMaxDecodeTargetWidth = 3840; const int kMaxDecodeTargetHeight = 2160; +#endif // ENABLE_VP9_8K_SUPPOR // This structure is used to facilitate creation of decode targets in the // appropriate graphics context. @@ -66,15 +71,15 @@ scoped_ptr<MediaTransform> CreateVideoTransform(const GUID& decoder_guid, const GUID& input_guid, const GUID& output_guid, const IMFDXGIDeviceManager* device_manager) { - scoped_ptr<MediaTransform> transform(new MediaTransform(decoder_guid)); + scoped_ptr<MediaTransform> media_transform(new MediaTransform(decoder_guid)); - transform->EnableInputThrottle(true); - transform->SendMessage(MFT_MESSAGE_SET_D3D_MANAGER, - ULONG_PTR(device_manager)); + media_transform->EnableInputThrottle(true); + media_transform->SendMessage(MFT_MESSAGE_SET_D3D_MANAGER, + ULONG_PTR(device_manager)); // Tell the decoder to allocate resources for the maximum resolution in // order to minimize hitching on resolution changes. - ComPtr<IMFAttributes> attributes = transform->GetAttributes(); + ComPtr<IMFAttributes> attributes = media_transform->GetAttributes(); CheckResult(attributes->SetUINT32(MF_MT_DECODER_USE_MAX_RESOLUTION, 1)); ComPtr<IMFMediaType> input_type; @@ -91,17 +96,17 @@ kMaxDecodeTargetWidth, kMaxDecodeTargetHeight)); } - transform->SetInputType(input_type); + media_transform->SetInputType(input_type); - transform->SetOutputTypeBySubType(output_guid); + media_transform->SetOutputTypeBySubType(output_guid); - return transform.Pass(); + return media_transform.Pass(); } class VideoFrameImpl : public VideoFrame { public: - VideoFrameImpl(SbMediaTime pts, std::function<void(VideoFrame*)> release_cb) - : VideoFrame(pts), release_cb_(release_cb) { + VideoFrameImpl(SbTime timestamp, std::function<void(VideoFrame*)> release_cb) + : VideoFrame(timestamp), release_cb_(release_cb) { SB_DCHECK(release_cb_); } ~VideoFrameImpl() { release_cb_(this); } @@ -188,6 +193,10 @@ return kMaxOutputSamples; } +size_t VideoDecoder::GetMaxNumberOfCachedFrames() const { + return kMaxOutputSamples; +} + void VideoDecoder::Initialize(const DecoderStatusCB& decoder_status_cb, const ErrorCB& error_cb) { SB_DCHECK(thread_checker_.CalledOnValidThread()); @@ -234,6 +243,7 @@ thread_lock_.Release(); outputs_reset_lock_.Release(); + decoder_status_cb_(kReleaseAllFrames, nullptr); decoder_->Reset(); } @@ -494,7 +504,7 @@ // weak references to the actual sample. LONGLONG win32_sample_time = 0; CheckResult(sample->GetSampleTime(&win32_sample_time)); - SbMediaTime sample_time = ConvertToMediaTime(win32_sample_time); + SbTime sample_time = ConvertToSbTime(win32_sample_time); thread_lock_.Acquire(); thread_outputs_.emplace_back(sample_time, video_area_, sample); @@ -510,7 +520,7 @@ ScopedLock lock(thread_lock_); for (auto iter = thread_outputs_.begin(); iter != thread_outputs_.end(); ++iter) { - if (iter->time == video_frame->pts()) { + if (iter->time == video_frame->timestamp()) { thread_outputs_.erase(iter); break; }
diff --git a/src/starboard/shared/win32/video_decoder.h b/src/starboard/shared/win32/video_decoder.h index 8c41f7e..ffe127e 100644 --- a/src/starboard/shared/win32/video_decoder.h +++ b/src/starboard/shared/win32/video_decoder.h
@@ -57,6 +57,7 @@ const ErrorCB& error_cb) override; size_t GetPrerollFrameCount() const override; SbTime GetPrerollTimeout() const override { return kSbTimeMax; } + size_t GetMaxNumberOfCachedFrames() const override; void WriteInputBuffer(const scoped_refptr<InputBuffer>& input_buffer) override;
diff --git a/src/starboard/shared/win32/win32_audio_decoder.cc b/src/starboard/shared/win32/win32_audio_decoder.cc index 2b690c8..eb83a10 100644 --- a/src/starboard/shared/win32/win32_audio_decoder.cc +++ b/src/starboard/shared/win32/win32_audio_decoder.cc
@@ -93,7 +93,7 @@ DecodedAudioPtr data_ptr( new DecodedAudio(number_of_channels_, sample_type_, audio_frame_fmt_, - ConvertToMediaTime(win32_timestamp), data_size)); + ConvertToSbTime(win32_timestamp), data_size)); std::copy(data, data + data_size, data_ptr->buffer());
diff --git a/src/starboard/starboard_all.gyp b/src/starboard/starboard_all.gyp index 743c161..c6584a0 100644 --- a/src/starboard/starboard_all.gyp +++ b/src/starboard/starboard_all.gyp
@@ -17,7 +17,7 @@ { 'variables': { - 'has_platform_tests%' : '<!(python -c "import os.path; print os.path.isfile(\'<(DEPTH)/<(starboard_path)/starboard_platform_tests.gyp\') & 1 | 0")', + 'has_platform_tests%' : '<!(python ../build/file_exists.py <(DEPTH)/<(starboard_path)/starboard_platform_tests.gyp)', }, 'targets': [ { @@ -35,7 +35,7 @@ '<(DEPTH)/starboard/starboard.gyp:*', ], 'conditions': [ - ['has_platform_tests==1', { + ['has_platform_tests=="True"', { 'dependencies': [ '<(DEPTH)/<(starboard_path)/starboard_platform_tests.gyp:*', ],
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h index 444143e..b6f57e4 100644 --- a/src/starboard/stub/configuration_public.h +++ b/src/starboard/stub/configuration_public.h
@@ -92,6 +92,12 @@ // Whether the current platform supports thread priorities. #define SB_HAS_THREAD_PRIORITY_SUPPORT 0 +// Whether the current platform implements the on screen keyboard interface. +#define SB_HAS_ON_SCREEN_KEYBOARD 0 + +// Whether the current platform uses a media player that relies on a URL. +#define SB_HAS_PLAYER_WITH_URL 0 + // Whether the current platform's thread scheduler will automatically balance // threads between cores, as opposed to systems where threads will only ever run // on the specifically pinned core. @@ -370,6 +376,10 @@ // timestamp that is before the timestamp of the video key frame being appended. #undef SB_HAS_QUIRK_SEEK_TO_KEYFRAME +// The implementation is allowed to support kSbMediaAudioSampleTypeInt16 only +// when this macro is defined. +#undef SB_HAS_QUIRK_SUPPORT_INT16_AUDIO_SAMPLES + // dlmalloc will use the ffs intrinsic if available. Platforms on which this is // not available should define the following quirk. #undef SB_HAS_QUIRK_NO_FFS
diff --git a/src/starboard/stub/gyp_configuration.gypi b/src/starboard/stub/gyp_configuration.gypi index b5c4af5..61373cd 100644 --- a/src/starboard/stub/gyp_configuration.gypi +++ b/src/starboard/stub/gyp_configuration.gypi
@@ -16,6 +16,8 @@ 'target_arch': 'x64', 'target_os': 'linux', + 'javascript_engine': 'v8', + 'cobalt_enable_jit': 1, # No GL drivers available. 'gl_type': 'none',
diff --git a/src/starboard/stub/gyp_configuration.py b/src/starboard/stub/gyp_configuration.py index e97189f..6c3da86 100644 --- a/src/starboard/stub/gyp_configuration.py +++ b/src/starboard/stub/gyp_configuration.py
@@ -32,14 +32,16 @@ def __init__(self, platform): super(PlatformConfig, self).__init__(platform) - goma_supports_compiler = True - self.host_compiler_environment = gyp_utils.GetHostCompilerEnvironment( - goma_supports_compiler) def GetVariables(self, configuration): return super(PlatformConfig, self).GetVariables(configuration, use_clang=1) def GetEnvironmentVariables(self): + if not hasattr(self, 'host_compiler_environment'): + goma_supports_compiler = True + self.host_compiler_environment = gyp_utils.GetHostCompilerEnvironment( + goma_supports_compiler) + env_variables = self.host_compiler_environment env_variables.update({ 'CC': self.host_compiler_environment['CC_host'],
diff --git a/src/starboard/system.h b/src/starboard/system.h index 3a4444e..c330cf1 100644 --- a/src/starboard/system.h +++ b/src/starboard/system.h
@@ -55,8 +55,11 @@ // usable by Starboard applications. kSbSystemPathFontConfigurationDirectory, - // Path to the directory containing the root of the source tree. +#if SB_API_VERSION < SB_PATH_SOURCE_DIR_REMOVED_API_VERSION + // Deprecated and unused. Tests looking for static data should instead look + // in the 'test' subdirectory of kSbSystemPathContentDirectory. kSbSystemPathSourceDirectory, +#endif // SB_API_VERSION < SB_PATH_SOURCE_DIR_REMOVED_API_VERSION // Path to a directory where temporary files can be written. kSbSystemPathTempDirectory, @@ -104,8 +107,10 @@ // User-Agent, say. kSbSystemPropertyPlatformName, +#if SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION // A universally-unique ID for the current user. kSbSystemPropertyPlatformUuid, +#endif // SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION // The Google Speech API key. The platform manufacturer is responsible // for registering a Google Speech API key for their products. In the API
diff --git a/src/starboard/testing/fake_graphics_context_provider.cc b/src/starboard/testing/fake_graphics_context_provider.cc index 69f52e8..089fcf2 100644 --- a/src/starboard/testing/fake_graphics_context_provider.cc +++ b/src/starboard/testing/fake_graphics_context_provider.cc
@@ -14,23 +14,66 @@ #include "starboard/testing/fake_graphics_context_provider.h" +#if defined(ADDRESS_SANITIZER) +// By default, Leak Sanitizer and Address Sanitizer is expected to exist +// together. However, this is not true for all platforms. +// HAS_LEAK_SANTIZIER=0 explicitly removes the Leak Sanitizer from code. +#ifndef HAS_LEAK_SANITIZER +#define HAS_LEAK_SANITIZER 1 +#endif // HAS_LEAK_SANITIZER +#endif // defined(ADDRESS_SANITIZER) + +#if HAS_LEAK_SANITIZER +#include <sanitizer/lsan_interface.h> +#endif // HAS_LEAK_SANITIZER + #include "starboard/configuration.h" +#include "starboard/memory.h" namespace starboard { namespace testing { -FakeGraphicsContextProvider::FakeGraphicsContextProvider() { +namespace { + +#if SB_HAS(GLES2) +EGLint const kAttributeList[] = {EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, + EGL_STENCIL_SIZE, + 0, + EGL_BUFFER_SIZE, + 32, + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT | EGL_PBUFFER_BIT, + EGL_COLOR_BUFFER_TYPE, + EGL_RGB_BUFFER, + EGL_CONFORMANT, + EGL_OPENGL_ES2_BIT, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_NONE}; +#endif // SB_HAS(GLES2) + +} // namespace + +FakeGraphicsContextProvider::FakeGraphicsContextProvider() + : +#if SB_HAS(GLES2) + display_(EGL_NO_DISPLAY), + surface_(EGL_NO_SURFACE), + context_(EGL_NO_CONTEXT), +#endif // SB_HAS(GLES2) + window_(kSbWindowInvalid) { + InitializeWindow(); #if SB_HAS(BLITTER) decoder_target_provider_.device = kSbBlitterInvalidDevice; #elif SB_HAS(GLES2) - decoder_target_provider_.egl_display = NULL; - decoder_target_provider_.egl_context = NULL; - decoder_target_provider_.gles_context_runner = DecodeTargetGlesContextRunner; - decoder_target_provider_.gles_context_runner_context = this; - - decode_target_context_thread_ = SbThreadCreate( - 0, kSbThreadPriorityNormal, kSbThreadNoAffinity, true, "dt_context", - &FakeGraphicsContextProvider::ThreadEntryPoint, this); + InitializeEGL(); #endif // SB_HAS(BLITTER) } @@ -38,7 +81,16 @@ #if SB_HAS(GLES2) functor_queue_.Wake(); SbThreadJoin(decode_target_context_thread_, NULL); + eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + SB_CHECK(EGL_SUCCESS == eglGetError()); + eglDestroyContext(display_, context_); + SB_CHECK(EGL_SUCCESS == eglGetError()); + eglDestroySurface(display_, surface_); + SB_CHECK(EGL_SUCCESS == eglGetError()); + eglTerminate(display_); + SB_CHECK(EGL_SUCCESS == eglGetError()); #endif // SB_HAS(GLES2) + SbWindowDestroy(window_); } #if SB_HAS(GLES2) @@ -72,6 +124,95 @@ } } +#endif // SB_HAS(GLES2) + +void FakeGraphicsContextProvider::InitializeWindow() { + SbWindowOptions window_options; + SbWindowSetDefaultOptions(&window_options); + + window_ = SbWindowCreate(&window_options); + SB_CHECK(SbWindowIsValid(window_)); +} + +#if SB_HAS(GLES2) +void FakeGraphicsContextProvider::InitializeEGL() { + display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); + SB_CHECK(EGL_SUCCESS == eglGetError()); + SB_CHECK(EGL_NO_DISPLAY != display_); + +#if HAS_LEAK_SANITIZER + __lsan_disable(); +#endif // HAS_LEAK_SANITIZER + eglInitialize(display_, NULL, NULL); +#if HAS_LEAK_SANITIZER + __lsan_enable(); +#endif // HAS_LEAK_SANITIZER + SB_CHECK(EGL_SUCCESS == eglGetError()); + + // Some EGL drivers can return a first config that doesn't allow + // eglCreateWindowSurface(), with no differences in EGLConfig attribute values + // from configs that do allow that. To handle that, we have to attempt + // eglCreateWindowSurface() until we find a config that succeeds. + + // First, query how many configs match the given attribute list. + EGLint num_configs = 0; + eglChooseConfig(display_, kAttributeList, NULL, 0, &num_configs); + SB_CHECK(EGL_SUCCESS == eglGetError()); + SB_CHECK(0 != num_configs); + + // Allocate space to receive the matching configs and retrieve them. + EGLConfig* configs = reinterpret_cast<EGLConfig*>( + SbMemoryAllocate(num_configs * sizeof(EGLConfig))); + eglChooseConfig(display_, kAttributeList, configs, num_configs, &num_configs); + SB_CHECK(EGL_SUCCESS == eglGetError()); + + EGLNativeWindowType native_window = + (EGLNativeWindowType)SbWindowGetPlatformHandle(window_); + EGLConfig config = EGLConfig(); + + // Find the first config that successfully allow a window surface to be + // created. + for (int config_number = 0; config_number < num_configs; ++config_number) { + config = configs[config_number]; + surface_ = eglCreateWindowSurface(display_, config, native_window, NULL); + if (EGL_SUCCESS == eglGetError()) + break; + } + SB_DCHECK(surface_ != EGL_NO_SURFACE); + + SbMemoryDeallocate(configs); + + // Create the GLES2 or GLEX3 Context. + EGLint context_attrib_list[] = { + EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE, + }; +#if defined(GLES3_SUPPORTED) + // Attempt to create an OpenGL ES 3.0 context. + context_ = + eglCreateContext(display_, config, EGL_NO_CONTEXT, context_attrib_list); +#endif + if (context_ == EGL_NO_CONTEXT) { + // Create an OpenGL ES 2.0 context. + context_attrib_list[1] = 2; + context_ = + eglCreateContext(display_, config, EGL_NO_CONTEXT, context_attrib_list); + } + SB_CHECK(EGL_SUCCESS == eglGetError()); + SB_CHECK(context_ != EGL_NO_CONTEXT); + + eglMakeCurrent(display_, surface_, surface_, context_); + SB_CHECK(EGL_SUCCESS == eglGetError()); + + decoder_target_provider_.egl_display = display_; + decoder_target_provider_.egl_context = context_; + decoder_target_provider_.gles_context_runner = DecodeTargetGlesContextRunner; + decoder_target_provider_.gles_context_runner_context = this; + + decode_target_context_thread_ = SbThreadCreate( + 0, kSbThreadPriorityNormal, kSbThreadNoAffinity, true, "dt_context", + &FakeGraphicsContextProvider::ThreadEntryPoint, this); +} + void FakeGraphicsContextProvider::ReleaseDecodeTargetOnGlesContextThread( Mutex* mutex, ConditionVariable* condition_variable,
diff --git a/src/starboard/testing/fake_graphics_context_provider.h b/src/starboard/testing/fake_graphics_context_provider.h index fae6c1d..d53f7b7 100644 --- a/src/starboard/testing/fake_graphics_context_provider.h +++ b/src/starboard/testing/fake_graphics_context_provider.h
@@ -18,10 +18,18 @@ #include <functional> #include "starboard/condition_variable.h" +#include "starboard/configuration.h" #include "starboard/decode_target.h" #include "starboard/mutex.h" #include "starboard/queue.h" #include "starboard/thread.h" +#include "starboard/window.h" + +// SB_HAS() is available after starboard/configuration.h is included. +#if SB_HAS(GLES2) +#include <EGL/egl.h> +#include <GLES2/gl2.h> +#endif // SB_HAS(GLES2) namespace starboard { namespace testing { @@ -34,6 +42,7 @@ FakeGraphicsContextProvider(); ~FakeGraphicsContextProvider(); + SbWindow window() { return window_; } SbDecodeTargetGraphicsContextProvider* decoder_target_provider() { #if SB_HAS(BLITTER) || SB_HAS(GLES2) return &decoder_target_provider_; @@ -42,15 +51,21 @@ #endif // SB_HAS(BLITTER) || SB_HAS(GLES2) } -#if SB_HAS(BLITTER) || SB_HAS(GLES2) +#if SB_HAS(GLES2) void ReleaseDecodeTarget(SbDecodeTarget decode_target); -#endif // SB_HAS(BLITTER) || SB_HAS(GLES2) +#endif // SB_HAS(GLES2) private: +#if SB_HAS(GLES2) static void* ThreadEntryPoint(void* context); void RunLoop(); +#endif // SB_HAS(GLES2) + + void InitializeWindow(); #if SB_HAS(GLES2) + void InitializeEGL(); + void ReleaseDecodeTargetOnGlesContextThread( Mutex* mutex, ConditionVariable* condition_variable, @@ -69,11 +84,17 @@ SbDecodeTargetGraphicsContextProvider* graphics_context_provider, SbDecodeTargetGlesContextRunnerTarget target_function, void* target_function_context); -#endif // SB_HAS(GLES2) -#if SB_HAS(BLITTER) || SB_HAS(GLES2) + EGLDisplay display_; + EGLSurface surface_; + EGLContext context_; Queue<std::function<void()>> functor_queue_; SbThread decode_target_context_thread_; +#endif // SB_HAS(GLES2) + + SbWindow window_; + +#if SB_HAS(BLITTER) || SB_HAS(GLES2) SbDecodeTargetGraphicsContextProvider decoder_target_provider_; #endif // SB_HAS(BLITTER) || SB_HAS(GLES2) };
diff --git a/src/starboard/tools/abstract_launcher.py b/src/starboard/tools/abstract_launcher.py index b1171e2..97ffdd0 100644 --- a/src/starboard/tools/abstract_launcher.py +++ b/src/starboard/tools/abstract_launcher.py
@@ -172,19 +172,36 @@ """Kills the launcher. Must be implemented in subclasses.""" pass - def SupportsSuspendResume(): + def SupportsSuspendResume(self): return False + # The Send*() functions are guaranteed to resolve sequences of calls (e.g. + # SendSuspend() followed immediately by SendResume()) in the order that they + # are sent. def SendResume(self): - """Sends resume signal to the launcher's executable.""" + """Sends resume signal to the launcher's executable. + + Raises: + RuntimeError: Resume signal not supported on platform. + """ raise RuntimeError("Resume not supported for this platform.") def SendSuspend(self): - """sends suspend signal to the launcher's executable.""" + """sends suspend signal to the launcher's executable. + + When implementing this function, please coordinate with other functions + sending platform signals(e.g. SendResume()) to ensure Cobalt receives these + signals in the same order they are sent. + + Raises: + RuntimeError: Suspend signal not supported on platform. + """ + raise RuntimeError("Suspend not supported for this platform.") def GetStartupTimeout(self): """Gets the number of seconds to wait before assuming a launcher timeout.""" + return self.startup_timeout_seconds def GetHostAndPortGivenPort(self, port):
diff --git a/src/starboard/tools/app_launcher_packager.py b/src/starboard/tools/app_launcher_packager.py index 6adb724..5c77a9c 100644 --- a/src/starboard/tools/app_launcher_packager.py +++ b/src/starboard/tools/app_launcher_packager.py
@@ -22,16 +22,17 @@ """ import argparse +import logging import os -import platform import shutil import sys -import threading +import _env # pylint: disable=unused-import from paths import REPOSITORY_ROOT from paths import THIRD_PARTY_ROOT sys.path.append(THIRD_PARTY_ROOT) import jinja2 +import starboard.tools.platform def _MakeDir(d): @@ -46,16 +47,15 @@ os.mkdir(d) -class FileCopier(threading.Thread): - """Threaded class to copy a file.""" +def _IsOutDir(source_root, d): + """Check if d is under source_root/out directory. - def __init__(self, source, dest): - threading.Thread.__init__(self) - self.source = source - self.dest = dest - - def run(self): - shutil.copy2(self.source, self.dest) + Args: + source_root: Absolute path to the root of the files to be copied. + d: Directory to be checked. + """ + out_dir = os.path.join(source_root, 'out') + return out_dir in d def CopyPythonFiles(source_root, dest_root): @@ -67,23 +67,23 @@ source_root: Absolute path to the root of files to be copied. dest_root: Destination into which files will be copied. """ - copies = {} + logging.info('Searching in ' + source_root + ' for python files.') + file_list = [] for root, _, files in os.walk(source_root): + # Eliminate any locally built files under the out directory. + if _IsOutDir(source_root, root): + continue for f in files: if f.endswith('.py'): source_file = os.path.join(root, f) dest_file = source_file.replace(source_root, dest_root) - copies[source_file] = dest_file + file_list.append((source_file, dest_file)) - file_copiers = [] - for source, dest in copies.iteritems(): + logging.info('Starting copy of ' + str(len(file_list)) + ' python files.') + for (source, dest) in file_list: _MakeDir(os.path.dirname(dest)) - copier = FileCopier(source, dest) - file_copiers.append(copier) - copier.start() - - for copier in file_copiers: - copier.join() + shutil.copy2(source, dest) + logging.info('Copy of python files finished.') def WritePlatformsInfo(repo_root, dest_root): @@ -101,37 +101,59 @@ dest_root: Absolute path to the root of the new repository into which platforms' information to be written. """ + logging.info('Baking platform info files.') current_file = os.path.abspath(__file__) current_dir = os.path.dirname(current_file) dest_dir = current_dir.replace(repo_root, dest_root) platforms_map = {} - for p in platform.GetAll(): - platform_path = platform.Get(p).path.replace(repo_root, dest_root) + for p in starboard.tools.platform.GetAll(): + platform_path = os.path.relpath( + starboard.tools.platform.Get(p).path, repo_root) platforms_map[p] = platform_path template = jinja2.Template( open(os.path.join(current_dir, 'platform.py.template')).read()) with open(os.path.join(dest_dir, 'platform.py'), 'w+') as f: template.stream(platforms_map=platforms_map).dump(f, encoding='utf-8') + logging.info('Finished baking in platform info files.') -def main(): +def CopyAppLauncherTools(repo_root, dest_root): + if os.path.isdir(dest_root): + shutil.rmtree(dest_root) + + CopyPythonFiles(repo_root, dest_root) + WritePlatformsInfo(repo_root, dest_root) + + # Create an extra zip file of the app launcher package so that users have + # the option of downloading a single file which is much faster, especially + # on x20. + logging.info('Creating a zip file of the app launcher package.') + + # Make a zip that has the same name as the dest_root. Then the zip file + # and dest_root are guaranteed to be on the same file system under the + # same parent, so that moving the zip file to dest_root is optimized. + app_launcher_zip_file = shutil.make_archive(dest_root, 'zip', dest_root) + dest_zip = os.path.join(dest_root, 'app_launcher.zip') + if os.path.isfile(dest_zip): + os.unlink(dest_zip) + shutil.move(app_launcher_zip_file, dest_zip) + + +def main(command_args): + logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser() parser.add_argument( '-d', '--destination_root', required=True, - help='The path to the root of the destination folder into which the' + help='The path to the root of the destination folder into which the ' 'python scripts are packaged.') - args = parser.parse_args() - - dest_root = args.destination_root - - CopyPythonFiles(REPOSITORY_ROOT, dest_root) - WritePlatformsInfo(REPOSITORY_ROOT, dest_root) + args = parser.parse_args(command_args) + CopyAppLauncherTools(REPOSITORY_ROOT, args.destination_root) return 0 if __name__ == '__main__': - sys.exit(main()) + sys.exit(main(sys.argv[1:]))
diff --git a/src/starboard/tools/build.py b/src/starboard/tools/build.py index c25a7f9..fd2af48 100644 --- a/src/starboard/tools/build.py +++ b/src/starboard/tools/build.py
@@ -296,7 +296,8 @@ logging.debug('Loading platform configuration for "%s".', platform_name) if platform.IsValid(platform_name): platform_path = platform.Get(platform_name).path - module_path = os.path.join(platform_path, 'gyp_configuration.py') + module_path = os.path.join(paths.REPOSITORY_ROOT, platform_path, + 'gyp_configuration.py') if not _ModuleLoaded('platform_module', module_path): platform_module = imp.load_source('platform_module', module_path) else:
diff --git a/src/starboard/tools/net_log.py b/src/starboard/tools/net_log.py new file mode 100644 index 0000000..f6d4dc5 --- /dev/null +++ b/src/starboard/tools/net_log.py
@@ -0,0 +1,143 @@ +#!/usr/bin/env python +# +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Example script for getting the Net log.""" + +from __future__ import print_function + +import argparse +import pprint +import socket +import sys +import time +import threading + +class NetLog: + """ + Uses a non-blocking socket to establish a connection with a + NetLog and then will allow the log to be fetched. + """ + def __init__(self, host, port): + self.host = host + self.port = port + self.server_socket = None + + def FetchLog(self): + if not self.server_socket: + self.server_socket = self._TryCreateSocketConnection() + if not self.server_socket: + return None + try: + log = self._TryRead() + if log is None: + self.server_socket = None + else: + return log + except socket.error as (err_no, err_str): + print(err_no, err_str) + self.server_socket = None + return None + + # Private members + def _TryRead(self): + try: + result = self.server_socket.recv(1024) + # An empty string is a flag that the connection has closed. + if len(result) == 0: + return None + + return result + except socket.error as (err_no, err_str): + if err_no == 10035: # Data not ready yet. + return '' + else: + raise + + def _TryCreateSocketConnection(self): + try: + print("Waiting for connection to " + str(self.host) + ':' + str(self.port)) + + server_socket = socket.create_connection((self.host, self.port), timeout = 1) + server_socket.setblocking(0) + return server_socket + except socket.timeout as err: + return None + except socket.error as err: + return None + + +class NetLogThread(threading.Thread): + """Threaded version of NetLog""" + def __init__(self, host, port): + super(NetLogThread, self).__init__() + self.web_log = NetLog(host, port) + self.alive = True + self.log_mutex = threading.Lock() + self.log = [] + self.start() + + def join(self): + self.alive = False + return super(NetLogThread, self).join() + + def GetLog(self): + with self.log_mutex: + log_copy = self.log + self.log = [] + return ''.join(log_copy) + + def run(self): + while self.alive: + new_log = self.web_log.FetchLog() + if new_log: + with self.log_mutex: + self.log.extend(new_log) + +def TestNetLog(host, port): + print("Started...") + web_log = NetLog(host, port) + + while True: + log = web_log.FetchLog() + if log: + print(log, end='') + time.sleep(.1) + + +def main(argv): + parser = argparse.ArgumentParser(description = 'Connects to the weblog.') + parser.add_argument('--host', type=str, required = False, + default = 'localhost', + help = "Example localhost or 1.2.3.4") + parser.add_argument('--port', type=int, required = False, default = '49353') + args = parser.parse_args(argv) + + thread = NetLogThread(args.host, args.port) + + try: + while True: + print(thread.GetLog(), end='') + time.sleep(.1) + except KeyboardInterrupt as ki: + print("\nUser canceled.") + pass + + print("Waiting to join...") + thread.join() + return 0 + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
diff --git a/src/starboard/tools/platform.py.template b/src/starboard/tools/platform.py.template index f1d9d1a..f4f2560 100644 --- a/src/starboard/tools/platform.py.template +++ b/src/starboard/tools/platform.py.template
@@ -16,7 +16,7 @@ """Functionality to enumerate and represent starboard ports.""" import os - +import _env from starboard.tools import environment # The name->platform path mapping.
diff --git a/src/starboard/tools/testing/test_runner.py b/src/starboard/tools/testing/test_runner.py index cc6056e..21bb179 100755 --- a/src/starboard/tools/testing/test_runner.py +++ b/src/starboard/tools/testing/test_runner.py
@@ -38,6 +38,7 @@ _TESTS_FAILED_REGEX = r"\[ FAILED \] (.*) tests?, listed below:" _SINGLE_TEST_FAILED_REGEX = r"\[ FAILED \] (.*)" + def _FilterTests(target_list, filters, config_name): """Returns a Mapping of test targets -> filtered tests.""" @@ -80,6 +81,10 @@ self.stop_event = threading.Event() self.reader_thread = threading.Thread(target=self._ReadLines) + # Don't allow this thread to block sys.exit() when ctrl+c is pressed. + # Otherwise it will hang forever waiting for the pipe to close. + self.reader_thread.daemon = True + def _ReadLines(self): """Continuously reads and stores lines of test output.""" while not self.stop_event.is_set(): @@ -167,7 +172,8 @@ """Runs unit tests.""" def __init__(self, platform, config, device_id, single_target, - target_params, out_directory, application_name=None): + target_params, out_directory, application_name=None, + dry_run=False): self.platform = platform self.config = config self.device_id = device_id @@ -176,6 +182,7 @@ self._platform_config = build.GetPlatformConfig(platform) self._app_config = self._platform_config.GetApplicationConfiguration( application_name) + self.dry_run = dry_run self.threads = [] # If a particular test binary has been provided, configure only that one. @@ -263,6 +270,8 @@ self.platform, self.config) args_list = ["ninja", "-C", build_dir] + if self.dry_run: + args_list.append("-n") args_list.extend([ "{}_deploy".format(test_name) for test_name in self.test_targets]) if ninja_flags: @@ -315,19 +324,26 @@ self.threads.append(test_launcher) self.threads.append(test_reader) - sys.stdout.write("Starting {}\n".format(target_name)) + if self.dry_run: + sys.stdout.write( + "{} {}\n".format(target_name, test_params) if test_params + else "{}\n".format(target_name)) + write_pipe.close() + read_pipe.close() - test_reader.Start() - test_launcher.Start() + else: + sys.stdout.write("Starting {}\n".format(target_name)) + test_reader.Start() + test_launcher.Start() - # If there are actives threads during a ctrl+c exit, they will join here. - test_launcher.Join() - write_pipe.close() + # Wait for the launcher to exit then close the write pipe, which will + # cause the reader to exit. + test_launcher.Join() + write_pipe.close() - # Do not join the reader thread until the launcher has finished and the - # write pipe has been closed, otherwise the thread could hang. - test_reader.Join() - read_pipe.close() + # Only after closing the write pipe, wait for the reader to exit. + test_reader.Join() + read_pipe.close() output = test_reader.GetLines() @@ -397,12 +413,16 @@ Returns: True if the test run succeeded, False if not. """ + if self.dry_run: + print "\n{} TOTAL TEST TARGETS".format(len(results)) + return True + total_run_count = 0 total_passed_count = 0 total_failed_count = 0 # If the number of run tests from a test binary cannot be - # determined, assume an error occured while running it. + # determined, assume an error occurred while running it. error = False print "\nTEST RUN COMPLETE. RESULTS BELOW:\n" @@ -503,6 +523,11 @@ " If both the \"--build\" and \"--run\" flags are not" " provided, this is the default.") arg_parser.add_argument( + "-n", + "--dry_run", + action="store_true", + help="Specifies to show what would be done without actually doing it.") + arg_parser.add_argument( "-t", "--target_name", help="Name of executable target.") @@ -526,7 +551,7 @@ runner = TestRunner(args.platform, args.config, args.device_id, args.target_name, target_params, args.out_directory, - args.application_name) + args.application_name, args.dry_run) def Abort(signum, frame): del signum, frame # Unused. @@ -544,6 +569,9 @@ build_success = True run_success = True + if args.dry_run: + sys.stderr.write("=== Dry run ===\n") + if args.build: build_success = runner.BuildAllTests(args.ninja_flags) # If the build fails, don't try to run the tests.
diff --git a/src/starboard/win/shared/configuration_public.h b/src/starboard/win/shared/configuration_public.h index 8cb9a9f..73294a3 100644 --- a/src/starboard/win/shared/configuration_public.h +++ b/src/starboard/win/shared/configuration_public.h
@@ -124,6 +124,9 @@ // Whether the current platform has speech recognizer. #define SB_HAS_SPEECH_RECOGNIZER 0 +// Whether the current platform has a DRM session closed callback. +#define SB_HAS_DRM_SESSION_CLOSED 1 + #if !defined(__WCHAR_MAX__) #include <wchar.h> #define __WCHAR_MAX__ WCHAR_MAX
diff --git a/src/starboard/win/shared/gyp_configuration.gypi b/src/starboard/win/shared/gyp_configuration.gypi index 67b1224..ca05ba4 100644 --- a/src/starboard/win/shared/gyp_configuration.gypi +++ b/src/starboard/win/shared/gyp_configuration.gypi
@@ -28,7 +28,11 @@ # possible busy-loops on unrendered submissions. 'cobalt_minimum_frame_time_in_milliseconds': '1', - 'fallback_splash_screen_url%': 'h5vcc-embedded://youtube_splash_screen.html', + 'cobalt_splash_screen_file': '<(DEPTH)/cobalt/browser/splash_screen/youtube_splash_screen.html', + 'fallback_splash_screen_url': 'file:///cobalt/browser/splash_screen/youtube_splash_screen.html', + + # win-win32-lib does not link this binary successfully today. + 'build_snapshot_app_stats': 0, # Platform-specific implementations to compile into cobalt. 'cobalt_platform_dependencies': [
diff --git a/src/starboard/win/shared/starboard_platform.gypi b/src/starboard/win/shared/starboard_platform.gypi index b96d6cd..611c9d8 100644 --- a/src/starboard/win/shared/starboard_platform.gypi +++ b/src/starboard/win/shared/starboard_platform.gypi
@@ -26,6 +26,7 @@ '<(DEPTH)/starboard/shared/win32/log_file_impl.h', '<(DEPTH)/starboard/shared/win32/log_raw.cc', '<(DEPTH)/starboard/shared/win32/log_raw_format.cc', + '<(DEPTH)/starboard/shared/win32/media_is_audio_supported.cc', '<(DEPTH)/starboard/shared/win32/playready_license.cc', '<(DEPTH)/starboard/shared/win32/starboard_main.cc', '<(DEPTH)/starboard/shared/win32/system_clear_platform_error.cc', @@ -75,14 +76,11 @@ '<(DEPTH)/starboard/shared/win32/media_common.h', '<(DEPTH)/starboard/shared/win32/media_foundation_utils.cc', '<(DEPTH)/starboard/shared/win32/media_foundation_utils.h', - '<(DEPTH)/starboard/shared/win32/media_is_audio_supported.cc', '<(DEPTH)/starboard/shared/win32/media_is_video_supported.cc', '<(DEPTH)/starboard/shared/win32/media_is_supported.cc', '<(DEPTH)/starboard/shared/win32/media_transform.cc', '<(DEPTH)/starboard/shared/win32/media_transform.h', '<(DEPTH)/starboard/shared/win32/player_components_impl.cc', - '<(DEPTH)/starboard/shared/win32/simple_thread.cc', - '<(DEPTH)/starboard/shared/win32/simple_thread.h', '<(DEPTH)/starboard/shared/win32/video_decoder.cc', '<(DEPTH)/starboard/shared/win32/video_decoder.h', '<(DEPTH)/starboard/shared/win32/win32_audio_decoder.cc', @@ -92,7 +90,6 @@ '<(DEPTH)/starboard/shared/starboard/media/codec_util.cc', '<(DEPTH)/starboard/shared/starboard/media/codec_util.h', '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc', - '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc', '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc', '<(DEPTH)/starboard/shared/starboard/media/media_util.cc', '<(DEPTH)/starboard/shared/starboard/media/media_util.h', @@ -140,7 +137,16 @@ '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc', '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h', ], + + 'win32_shared_misc_files': [ + '<(DEPTH)/starboard/common/thread.cc', + '<(DEPTH)/starboard/common/thread.h', + '<(DEPTH)/starboard/shared/starboard/net_log.cc', + '<(DEPTH)/starboard/shared/starboard/net_log.h', + ], + 'starboard_platform_dependent_files': [ + '<@(win32_shared_misc_files)', '<@(win32_media_player_files)', '<@(win32_shared_drm_files)', '<@(win32_shared_media_player_files)', @@ -409,8 +415,11 @@ # This must be defined when building Starboard, and must not when # building Starboard client code. 'STARBOARD_IMPLEMENTATION', - # We assume most modern Windows PCs can handle 4k H264. - 'ENABLE_H264_4K_SUPPORT' + # We assume most modern Windows PCs can handle 4k H264 and 8k VP9. + # The latter is additionally gated on Media Foundation acceleration + # support during runtime anyway. + 'ENABLE_H264_4K_SUPPORT', + 'ENABLE_VP9_8K_SUPPORT', ], 'dependencies': [ 'convert_i18n_data',
diff --git a/src/starboard/win/shared/system_get_path.cc b/src/starboard/win/shared/system_get_path.cc index d6db634..b9c491d 100644 --- a/src/starboard/win/shared/system_get_path.cc +++ b/src/starboard/win/shared/system_get_path.cc
@@ -111,11 +111,6 @@ return GetRelativeDirectory("\\content\\data", out_path, path_size); } -bool GetSourceRootPath(char* out_path, int path_size) { - return GetRelativeDirectory("\\content\\dir_source_root", - out_path, path_size); -} - bool GetCachePath(char* out_path, int path_size) { return GetRelativeDirectory("\\content\\cache", out_path, path_size); @@ -168,8 +163,6 @@ return GetExecutablePath(out_path, path_size); case kSbSystemPathTempDirectory: return CreateAndGetTempPath(out_path, path_size); - case kSbSystemPathSourceDirectory: - return GetSourceRootPath(out_path, path_size); case kSbSystemPathCacheDirectory: return GetCachePath(out_path, path_size); case kSbSystemPathFontConfigurationDirectory:
diff --git a/src/starboard/win/win32/lib/gyp_configuration.gypi b/src/starboard/win/win32/lib/gyp_configuration.gypi index 1c51d34..b5dbd3b 100644 --- a/src/starboard/win/win32/lib/gyp_configuration.gypi +++ b/src/starboard/win/win32/lib/gyp_configuration.gypi
@@ -21,6 +21,12 @@ 'final_executable_type': 'shared_library', 'default_renderer_options_dependency': '<(DEPTH)/cobalt/renderer/rasterizer/lib/lib.gyp:external_rasterizer', 'sb_enable_lib': 1, + + # We turn off V-Sync and throttling in Cobalt so we can let the client + # decide if they want those features. + 'cobalt_egl_swap_interval': 0, + 'cobalt_minimum_frame_time_in_milliseconds': 0, + 'enable_map_to_mesh': 1, 'angle_build_winrt': 0, 'winrt': 0,
diff --git a/src/starboard/win/win32/starboard_platform.gypi b/src/starboard/win/win32/starboard_platform.gypi index 4988d9c..225e479 100644 --- a/src/starboard/win/win32/starboard_platform.gypi +++ b/src/starboard/win/win32/starboard_platform.gypi
@@ -22,6 +22,7 @@ 'thread_types_public.h', '<(DEPTH)/starboard/shared/starboard/localized_strings.cc', '<(DEPTH)/starboard/shared/starboard/localized_strings.cc', + '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc', '<(DEPTH)/starboard/shared/starboard/queue_application.cc', '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc', '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
diff --git a/src/starboard/window.h b/src/starboard/window.h index 258fde7..1d91e79 100644 --- a/src/starboard/window.h +++ b/src/starboard/window.h
@@ -137,9 +137,26 @@ // kSbEventOnScreenKeyboardInvalidTicket. #define kSbEventOnScreenKeyboardInvalidTicket (-1) +// Defines a rectangle via a point |(x, y)| and a size |(width, height)|. This +// structure is used as output for SbWindowGetOnScreenKeyboardBoundingRect. +typedef struct SbWindowRect { + float x; + float y; + float width; + float height; +} SbWindowRect; + // Determine if the on screen keyboard is shown. SB_EXPORT bool SbWindowIsOnScreenKeyboardShown(SbWindow window); +// Get the rectangle of the on screen keyboard in screen pixel coordinates. +// Return |true| if successful. Return |false| if the on screen keyboard is not +// showing. If the function returns |false|, then |rect| will not have been +// modified. +SB_EXPORT +bool SbWindowGetOnScreenKeyboardBoundingRect(SbWindow window, + SbWindowRect* bounding_rect); + // Notify the system that |keepFocus| has been set for the OnScreenKeyboard. // |keepFocus| true indicates that the user may not navigate focus off of the // OnScreenKeyboard via input; focus may only be moved via events sent by the
diff --git a/src/third_party/QR-Code-generator/Readme.markdown b/src/third_party/QR-Code-generator/Readme.markdown new file mode 100644 index 0000000..7b1e7cc --- /dev/null +++ b/src/third_party/QR-Code-generator/Readme.markdown
@@ -0,0 +1,195 @@ +QR Code generator library +========================= + + +Introduction +------------ + +This project aims to be the best, clearest QR Code generator library in multiple languages. The primary goals are flexible options and absolute correctness. Secondary goals are compact implementation size and good documentation comments. + +Home page with live JavaScript demo, extensive descriptions, and competitor comparisons: [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) + + +Features +-------- + +Core features: + +* Available in 6 programming languages, all with nearly equal functionality: Java, JavaScript, Python, C++, C, Rust +* Significantly shorter code but more documentation comments compared to competing libraries +* Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard +* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), `BufferedImage` raster bitmap (Java only), HTML5 canvas (JavaScript only) +* Encodes numeric and special-alphanumeric text in less space than general text +* Open source code under the permissive MIT License + +Manual parameters: + +* User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data +* User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one +* User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number +* User can create a list of data segments manually and add ECI segments (all languages except C) + +Optional advanced features (Java only): + +* Encodes Japanese Unicode text in kanji mode to save a lot of space compared to UTF-8 bytes +* Computes optimal segment mode switching for text with mixed numeric/alphanumeric/general parts + + +Examples +-------- + +Java language: + + import java.awt.image.BufferedImage; + import java.io.File; + import javax.imageio.ImageIO; + import io.nayuki.qrcodegen.*; + + // Simple operation + QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); + BufferedImage img = qr0.toImage(4, 10); + ImageIO.write(img, "png", new File("qr-code.png")); + + // Manual operation + List<QrSegment> segs = QrSegment.makeSegments("3141592653589793238462643383"); + QrCode qr1 = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, 5, 5, 2, false); + for (int y = 0; y < qr1.size; y++) { + for (int x = 0; x < qr1.size; x++) { + (... paint qr1.getModule(x, y) ...) + } + } + +JavaScript language: + + // Name abbreviated for the sake of these examples here + var QRC = qrcodegen.QrCode; + + // Simple operation + var qr0 = QRC.encodeText("Hello, world!", QRC.Ecc.MEDIUM); + var svg = qr0.toSvgString(4); + + // Manual operation + var segs = qrcodegen.QrSegment.makeSegments("3141592653589793238462643383"); + var qr1 = QRC.encodeSegments(segs, QRC.Ecc.HIGH, 5, 5, 2, false); + for (var y = 0; y < qr1.size; y++) { + for (var x = 0; x < qr1.size; x++) { + (... paint qr1.getModule(x, y) ...) + } + } + +Python language: + + from qrcodegen import * + + # Simple operation + qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM) + svg = qr0.to_svg_str(4) + + # Manual operation + segs = QrSegment.make_segments("3141592653589793238462643383") + qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False) + for y in range(qr1.get_size()): + for x in range(qr1.get_size()): + (... paint qr1.get_module(x, y) ...) + +C++ language: + + #include <string> + #include <vector> + #include "QrCode.hpp" + using namespace qrcodegen; + + // Simple operation + QrCode qr0 = QrCode::encodeText("Hello, world!", QrCode::Ecc::MEDIUM); + std::string svg = qr0.toSvgString(4); + + // Manual operation + std::vector<QrSegment> segs = + QrSegment::makeSegments("3141592653589793238462643383"); + QrCode qr1 = QrCode::encodeSegments( + segs, QrCode::Ecc::HIGH, 5, 5, 2, false); + for (int y = 0; y < qr1.getSize(); y++) { + for (int x = 0; x < qr1.getSize(); x++) { + (... paint qr1.getModule(x, y) ...) + } + } + +C language: + + #include <stdbool.h> + #include <stdint.h> + #include "qrcodegen.h" + + // Text data + uint8_t qr0[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok = qrcodegen_encodeText("Hello, world!", + tempBuffer, qr0, qrcodegen_Ecc_MEDIUM, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, + qrcodegen_Mask_AUTO, true); + if (!ok) + return; + + int size = qrcodegen_getSize(qr0); + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + (... paint qrcodegen_getModule(qr0, x, y) ...) + } + } + + // Binary data + uint8_t dataAndTemp[qrcodegen_BUFFER_LEN_FOR_VERSION(7)] + = {0xE3, 0x81, 0x82}; + uint8_t qr1[qrcodegen_BUFFER_LEN_FOR_VERSION(7)]; + ok = qrcodegen_encodeBinary(dataAndTemp, 3, qr1, + qrcodegen_Ecc_HIGH, 2, 7, qrcodegen_Mask_4, false); + +Rust language: + + extern crate qrcodegen; + use qrcodegen::QrCode; + use qrcodegen::QrCodeEcc; + use qrcodegen::QrSegment; + + // Simple operation + let qr0 = QrCode::encode_text("Hello, world!", + QrCodeEcc::Medium).unwrap(); + let svg = qr0.to_svg_string(4); + + // Manual operation + let chrs: Vec<char> = "3141592653589793238462643383".chars().collect(); + let segs = QrSegment::make_segments(&chrs); + let qr1 = QrCode::encode_segments_advanced( + &segs, QrCodeEcc::High, 5, 5, Some(2), false).unwrap(); + for y in 0 .. qr1.size() { + for x in 0 .. qr1.size() { + (... paint qr1.get_module(x, y) ...) + } + } + +More information about QR Code technology and this library's design can be found on the project home page. + + +License +------- + +Copyright © 2017 Project Nayuki. (MIT License) +[https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +* The Software is provided "as is", without warranty of any kind, express or + implied, including but not limited to the warranties of merchantability, + fitness for a particular purpose and noninfringement. In no event shall the + authors or copyright holders be liable for any claim, damages or other + liability, whether in an action of contract, tort or otherwise, arising from, + out of or in connection with the Software or the use or other dealings in the + Software.
diff --git a/src/third_party/QR-Code-generator/c/Makefile b/src/third_party/QR-Code-generator/c/Makefile new file mode 100644 index 0000000..a33f8d2 --- /dev/null +++ b/src/third_party/QR-Code-generator/c/Makefile
@@ -0,0 +1,75 @@ +# +# Makefile for QR Code generator (C) +# +# Copyright (c) Project Nayuki. (MIT License) +# https://www.nayuki.io/page/qr-code-generator-library +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + + +# ---- Configuration options ---- + +# External/implicit variables: +# - CC: The C compiler, such as gcc or clang. +# - CFLAGS: Any extra user-specified compiler flags (can be blank). + +# Mandatory compiler flags +CFLAGS += -std=c99 +# Diagnostics. Adding '-fsanitize=address' is helpful for most versions of Clang and newer versions of GCC. +CFLAGS += -Wall -fsanitize=undefined +# Optimization level +CFLAGS += -O1 + + +# ---- Controlling make ---- + +# Clear default suffix rules +.SUFFIXES: + +# Don't delete object files +.SECONDARY: + +# Stuff concerning goals +.DEFAULT_GOAL = all +.PHONY: all clean + + +# ---- Targets to build ---- + +LIBSRC = qrcodegen +LIBFILE = libqrcodegen.so +MAINS = qrcodegen-demo qrcodegen-test qrcodegen-worker + +# Build all binaries +all: $(LIBFILE) $(MAINS) + +# Delete build output +clean: + rm -f -- $(LIBFILE) $(MAINS) + +# Shared library +$(LIBFILE): $(LIBSRC:=.c) $(LIBSRC:=.h) + $(CC) $(CFLAGS) -fPIC -shared -o $@ $(LIBSRC:=.c) + +# Executable files +%: %.c $(LIBFILE) + $(CC) $(CFLAGS) -o $@ $^ + +# Special executable +qrcodegen-test: qrcodegen-test.c $(LIBSRC:=.c) $(LIBSRC:=.h) + $(CC) $(CFLAGS) -DQRCODEGEN_TEST -o $@ $< $(LIBSRC:=.c)
diff --git a/src/third_party/QR-Code-generator/c/qrcodegen-demo.c b/src/third_party/QR-Code-generator/c/qrcodegen-demo.c new file mode 100644 index 0000000..c06e2f7 --- /dev/null +++ b/src/third_party/QR-Code-generator/c/qrcodegen-demo.c
@@ -0,0 +1,310 @@ +/* + * QR Code generator demo (C) + * + * Run this command-line program with no arguments. The program + * computes a demonstration QR Codes and print it to the console. + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "qrcodegen.h" + + +// Function prototypes +static void doBasicDemo(void); +static void doVarietyDemo(void); +static void doSegmentDemo(void); +static void doMaskDemo(void); +static void printQr(const uint8_t qrcode[]); + + +// The main application program. +int main(void) { + doBasicDemo(); + doVarietyDemo(); + doSegmentDemo(); + doMaskDemo(); + return EXIT_SUCCESS; +} + + + +/*---- Demo suite ----*/ + +// Creates a single QR Code, then prints it to the console. +static void doBasicDemo(void) { + const char *text = "Hello, world!"; // User-supplied text + enum qrcodegen_Ecc errCorLvl = qrcodegen_Ecc_LOW; // Error correction level + + // Make and print the QR Code symbol + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok = qrcodegen_encodeText(text, tempBuffer, qrcode, errCorLvl, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); + if (ok) + printQr(qrcode); +} + + +// Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console. +static void doVarietyDemo(void) { + { // Numeric mode encoding (3.33 bits per digit) + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok = qrcodegen_encodeText("314159265358979323846264338327950288419716939937510", tempBuffer, qrcode, + qrcodegen_Ecc_MEDIUM, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); + if (ok) + printQr(qrcode); + } + + { // Alphanumeric mode encoding (5.5 bits per character) + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok = qrcodegen_encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", tempBuffer, qrcode, + qrcodegen_Ecc_HIGH, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); + if (ok) + printQr(qrcode); + } + + { // Unicode text as UTF-8 + const char *text = "\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4"; + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok = qrcodegen_encodeText(text, tempBuffer, qrcode, + qrcodegen_Ecc_QUARTILE, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); + if (ok) + printQr(qrcode); + } + + { // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) + const char *text = + "Alice was beginning to get very tired of sitting by her sister on the bank, " + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " + "a White Rabbit with pink eyes ran close by her."; + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok = qrcodegen_encodeText(text, tempBuffer, qrcode, + qrcodegen_Ecc_HIGH, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); + if (ok) + printQr(qrcode); + } +} + + +// Creates QR Codes with manually specified segments for better compactness. +static void doSegmentDemo(void) { + { // Illustration "silver" + const char *silver0 = "THE SQUARE ROOT OF 2 IS 1."; + const char *silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok; + { + char *concat = calloc(strlen(silver0) + strlen(silver1) + 1, sizeof(char)); + strcat(concat, silver0); + strcat(concat, silver1); + ok = qrcodegen_encodeText(concat, tempBuffer, qrcode, qrcodegen_Ecc_LOW, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); + if (ok) + printQr(qrcode); + free(concat); + } + { + uint8_t *segBuf0 = malloc(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, strlen(silver0)) * sizeof(uint8_t)); + uint8_t *segBuf1 = malloc(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, strlen(silver1)) * sizeof(uint8_t)); + struct qrcodegen_Segment segs[] = { + qrcodegen_makeAlphanumeric(silver0, segBuf0), + qrcodegen_makeNumeric(silver1, segBuf1), + }; + ok = qrcodegen_encodeSegments(segs, sizeof(segs) / sizeof(segs[0]), qrcodegen_Ecc_LOW, tempBuffer, qrcode); + free(segBuf0); + free(segBuf1); + if (ok) + printQr(qrcode); + } + } + + { // Illustration "golden" + const char *golden0 = "Golden ratio \xCF\x86 = 1."; + const char *golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; + const char *golden2 = "......"; + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok; + { + char *concat = calloc(strlen(golden0) + strlen(golden1) + strlen(golden2) + 1, sizeof(char)); + strcat(concat, golden0); + strcat(concat, golden1); + strcat(concat, golden2); + ok = qrcodegen_encodeText(concat, tempBuffer, qrcode, qrcodegen_Ecc_LOW, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); + if (ok) + printQr(qrcode); + free(concat); + } + { + uint8_t *bytes = malloc(strlen(golden0) * sizeof(uint8_t)); + for (size_t i = 0, len = strlen(golden0); i < len; i++) + bytes[i] = (uint8_t)golden0[i]; + uint8_t *segBuf0 = malloc(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_BYTE, strlen(golden0)) * sizeof(uint8_t)); + uint8_t *segBuf1 = malloc(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, strlen(golden1)) * sizeof(uint8_t)); + uint8_t *segBuf2 = malloc(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, strlen(golden2)) * sizeof(uint8_t)); + struct qrcodegen_Segment segs[] = { + qrcodegen_makeBytes(bytes, strlen(golden0), segBuf0), + qrcodegen_makeNumeric(golden1, segBuf1), + qrcodegen_makeAlphanumeric(golden2, segBuf2), + }; + free(bytes); + ok = qrcodegen_encodeSegments(segs, sizeof(segs) / sizeof(segs[0]), qrcodegen_Ecc_LOW, tempBuffer, qrcode); + free(segBuf0); + free(segBuf1); + free(segBuf2); + if (ok) + printQr(qrcode); + } + } + + { // Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok; + { + const char *madoka = // Encoded in UTF-8 + "\xE3\x80\x8C\xE9\xAD\x94\xE6\xB3\x95\xE5" + "\xB0\x91\xE5\xA5\xB3\xE3\x81\xBE\xE3\x81" + "\xA9\xE3\x81\x8B\xE2\x98\x86\xE3\x83\x9E" + "\xE3\x82\xAE\xE3\x82\xAB\xE3\x80\x8D\xE3" + "\x81\xA3\xE3\x81\xA6\xE3\x80\x81\xE3\x80" + "\x80\xD0\x98\xD0\x90\xD0\x98\xE3\x80\x80" + "\xEF\xBD\x84\xEF\xBD\x85\xEF\xBD\x93\xEF" + "\xBD\x95\xE3\x80\x80\xCE\xBA\xCE\xB1\xEF" + "\xBC\x9F"; + ok = qrcodegen_encodeText(madoka, tempBuffer, qrcode, qrcodegen_Ecc_LOW, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); + if (ok) + printQr(qrcode); + } + { + const int kanjiChars[] = { // Kanji mode encoding (13 bits per character) + 0x0035, 0x1002, 0x0FC0, 0x0AED, 0x0AD7, + 0x015C, 0x0147, 0x0129, 0x0059, 0x01BD, + 0x018D, 0x018A, 0x0036, 0x0141, 0x0144, + 0x0001, 0x0000, 0x0249, 0x0240, 0x0249, + 0x0000, 0x0104, 0x0105, 0x0113, 0x0115, + 0x0000, 0x0208, 0x01FF, 0x0008, + }; + size_t len = sizeof(kanjiChars) / sizeof(kanjiChars[0]); + uint8_t *segBuf = calloc(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_KANJI, len), sizeof(uint8_t)); + struct qrcodegen_Segment seg; + seg.mode = qrcodegen_Mode_KANJI; + seg.numChars = len; + seg.bitLength = 0; + for (size_t i = 0; i < len; i++) { + for (int j = 12; j >= 0; j--, seg.bitLength++) + segBuf[seg.bitLength >> 3] |= ((kanjiChars[i] >> j) & 1) << (7 - (seg.bitLength & 7)); + } + seg.data = segBuf; + ok = qrcodegen_encodeSegments(&seg, 1, qrcodegen_Ecc_LOW, tempBuffer, qrcode); + free(segBuf); + if (ok) + printQr(qrcode); + } + } +} + + +// Creates QR Codes with the same size and contents but different mask patterns. +static void doMaskDemo(void) { + { // Project Nayuki URL + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok; + + ok = qrcodegen_encodeText("https://www.nayuki.io/", tempBuffer, qrcode, + qrcodegen_Ecc_HIGH, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true); + if (ok) + printQr(qrcode); + + ok = qrcodegen_encodeText("https://www.nayuki.io/", tempBuffer, qrcode, + qrcodegen_Ecc_HIGH, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_3, true); + if (ok) + printQr(qrcode); + } + + { // Chinese text as UTF-8 + const char *text = + "\xE7\xB6\xAD\xE5\x9F\xBA\xE7\x99\xBE\xE7\xA7\x91\xEF\xBC\x88\x57\x69\x6B\x69\x70" + "\x65\x64\x69\x61\xEF\xBC\x8C\xE8\x81\x86\xE8\x81\xBD\x69\x2F\xCB\x8C\x77\xC9\xAA" + "\x6B\xE1\xB5\xBB\xCB\x88\x70\x69\xCB\x90\x64\x69\x2E\xC9\x99\x2F\xEF\xBC\x89\xE6" + "\x98\xAF\xE4\xB8\x80\xE5\x80\x8B\xE8\x87\xAA\xE7\x94\xB1\xE5\x85\xA7\xE5\xAE\xB9" + "\xE3\x80\x81\xE5\x85\xAC\xE9\x96\x8B\xE7\xB7\xA8\xE8\xBC\xAF\xE4\xB8\x94\xE5\xA4" + "\x9A\xE8\xAA\x9E\xE8\xA8\x80\xE7\x9A\x84\xE7\xB6\xB2\xE8\xB7\xAF\xE7\x99\xBE\xE7" + "\xA7\x91\xE5\x85\xA8\xE6\x9B\xB8\xE5\x8D\x94\xE4\xBD\x9C\xE8\xA8\x88\xE7\x95\xAB"; + uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; + uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; + bool ok; + + ok = qrcodegen_encodeText(text, tempBuffer, qrcode, + qrcodegen_Ecc_MEDIUM, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_0, true); + if (ok) + printQr(qrcode); + + ok = qrcodegen_encodeText(text, tempBuffer, qrcode, + qrcodegen_Ecc_MEDIUM, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_1, true); + if (ok) + printQr(qrcode); + + ok = qrcodegen_encodeText(text, tempBuffer, qrcode, + qrcodegen_Ecc_MEDIUM, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_5, true); + if (ok) + printQr(qrcode); + + ok = qrcodegen_encodeText(text, tempBuffer, qrcode, + qrcodegen_Ecc_MEDIUM, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_7, true); + if (ok) + printQr(qrcode); + } +} + + + +/*---- Utilities ----*/ + +// Prints the given QR Code to the console. +static void printQr(const uint8_t qrcode[]) { + int size = qrcodegen_getSize(qrcode); + int border = 4; + for (int y = -border; y < size + border; y++) { + for (int x = -border; x < size + border; x++) { + fputs((qrcodegen_getModule(qrcode, x, y) ? "##" : " "), stdout); + } + fputs("\n", stdout); + } + fputs("\n", stdout); +}
diff --git a/src/third_party/QR-Code-generator/c/qrcodegen-test.c b/src/third_party/QR-Code-generator/c/qrcodegen-test.c new file mode 100644 index 0000000..4f9c5c0 --- /dev/null +++ b/src/third_party/QR-Code-generator/c/qrcodegen-test.c
@@ -0,0 +1,1081 @@ +/* + * QR Code generator test suite (C) + * + * When compiling this program, the library qrcodegen.c needs QRCODEGEN_TEST + * to be defined. Run this command line program with no arguments. + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include <assert.h> +#include <limits.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "qrcodegen.h" + +#define ARRAY_LENGTH(name) (sizeof(name) / sizeof(name[0])) + +#ifndef __cplusplus + #define MALLOC(num, type) malloc((num) * sizeof(type)) +#else + #define MALLOC(num, type) static_cast<type*>(malloc((num) * sizeof(type))) +#endif + + +// Global variables +static int numTestCases = 0; + + +// Prototypes of private functions under test +extern const int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; +extern const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; +void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); +void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); +int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); +int getNumRawDataModules(int version); +void calcReedSolomonGenerator(int degree, uint8_t result[]); +void calcReedSolomonRemainder(const uint8_t data[], int dataLen, const uint8_t generator[], int degree, uint8_t result[]); +uint8_t finiteFieldMultiply(uint8_t x, uint8_t y); +void initializeFunctionModules(int version, uint8_t qrcode[]); +int getAlignmentPatternPositions(int version, uint8_t result[7]); +bool getModule(const uint8_t qrcode[], int x, int y); +void setModule(uint8_t qrcode[], int x, int y, bool isBlack); +void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack); +int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); +int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); + + +/*---- Test cases ----*/ + +static void testAppendBitsToBuffer(void) { + { + uint8_t buf[1] = {0}; + int bitLen = 0; + appendBitsToBuffer(0, 0, buf, &bitLen); + assert(bitLen == 0); + assert(buf[0] == 0); + appendBitsToBuffer(1, 1, buf, &bitLen); + assert(bitLen == 1); + assert(buf[0] == 0x80); + appendBitsToBuffer(0, 1, buf, &bitLen); + assert(bitLen == 2); + assert(buf[0] == 0x80); + appendBitsToBuffer(5, 3, buf, &bitLen); + assert(bitLen == 5); + assert(buf[0] == 0xA8); + appendBitsToBuffer(6, 3, buf, &bitLen); + assert(bitLen == 8); + assert(buf[0] == 0xAE); + numTestCases++; + } + { + uint8_t buf[6] = {0}; + int bitLen = 0; + appendBitsToBuffer(16942, 16, buf, &bitLen); + assert(bitLen == 16); + assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x00 && buf[3] == 0x00 && buf[4] == 0x00 && buf[5] == 0x00); + appendBitsToBuffer(10, 7, buf, &bitLen); + assert(bitLen == 23); + assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x14 && buf[3] == 0x00 && buf[4] == 0x00 && buf[5] == 0x00); + appendBitsToBuffer(15, 4, buf, &bitLen); + assert(bitLen == 27); + assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x15 && buf[3] == 0xE0 && buf[4] == 0x00 && buf[5] == 0x00); + appendBitsToBuffer(26664, 15, buf, &bitLen); + assert(bitLen == 42); + assert(buf[0] == 0x42 && buf[1] == 0x2E && buf[2] == 0x15 && buf[3] == 0xFA && buf[4] == 0x0A && buf[5] == 0x00); + numTestCases++; + } +} + + +// Ported from the Java version of the code. +static uint8_t *appendErrorCorrectionReference(const uint8_t *data, int version, enum qrcodegen_Ecc ecl) { + // Calculate parameter numbers + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK[(int)ecl][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockLen = rawCodewords / numBlocks; + + // Split data into blocks and append ECC to each block + uint8_t **blocks = MALLOC(numBlocks, uint8_t*); + uint8_t *generator = MALLOC(blockEccLen, uint8_t); + calcReedSolomonGenerator(blockEccLen, generator); + for (int i = 0, k = 0; i < numBlocks; i++) { + uint8_t *block = MALLOC(shortBlockLen + 1, uint8_t); + int blockDataLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); + memcpy(block, &data[k], blockDataLen * sizeof(uint8_t)); + calcReedSolomonRemainder(&data[k], blockDataLen, generator, blockEccLen, &block[shortBlockLen + 1 - blockEccLen]); + k += blockDataLen; + blocks[i] = block; + } + free(generator); + + // Interleave (not concatenate) the bytes from every block into a single sequence + uint8_t *result = MALLOC(rawCodewords, uint8_t); + for (int i = 0, k = 0; i < shortBlockLen + 1; i++) { + for (int j = 0; j < numBlocks; j++) { + // Skip the padding byte in short blocks + if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) { + result[k] = blocks[j][i]; + k++; + } + } + } + for (int i = 0; i < numBlocks; i++) + free(blocks[i]); + free(blocks); + return result; +} + + +static void testAppendErrorCorrection(void) { + for (int version = 1; version <= 40; version++) { + for (int ecl = 0; ecl < 4; ecl++) { + int dataLen = getNumDataCodewords(version, (enum qrcodegen_Ecc)ecl); + uint8_t *pureData = MALLOC(dataLen, uint8_t); + for (int i = 0; i < dataLen; i++) + pureData[i] = rand() % 256; + uint8_t *expectOutput = appendErrorCorrectionReference(pureData, version, (enum qrcodegen_Ecc)ecl); + + int dataAndEccLen = getNumRawDataModules(version) / 8; + uint8_t *paddedData = MALLOC(dataAndEccLen, uint8_t); + memcpy(paddedData, pureData, dataLen * sizeof(uint8_t)); + uint8_t *actualOutput = MALLOC(dataAndEccLen, uint8_t); + appendErrorCorrection(paddedData, version, (enum qrcodegen_Ecc)ecl, actualOutput); + + assert(memcmp(actualOutput, expectOutput, dataAndEccLen * sizeof(uint8_t)) == 0); + free(pureData); + free(expectOutput); + free(paddedData); + free(actualOutput); + numTestCases++; + } + } +} + + +static void testGetNumDataCodewords(void) { + const int cases[][3] = { + { 3, 1, 44}, + { 3, 2, 34}, + { 3, 3, 26}, + { 6, 0, 136}, + { 7, 0, 156}, + { 9, 0, 232}, + { 9, 1, 182}, + {12, 3, 158}, + {15, 0, 523}, + {16, 2, 325}, + {19, 3, 341}, + {21, 0, 932}, + {22, 0, 1006}, + {22, 1, 782}, + {22, 3, 442}, + {24, 0, 1174}, + {24, 3, 514}, + {28, 0, 1531}, + {30, 3, 745}, + {32, 3, 845}, + {33, 0, 2071}, + {33, 3, 901}, + {35, 0, 2306}, + {35, 1, 1812}, + {35, 2, 1286}, + {36, 3, 1054}, + {37, 3, 1096}, + {39, 1, 2216}, + {40, 1, 2334}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + const int *tc = cases[i]; + assert(getNumDataCodewords(tc[0], (enum qrcodegen_Ecc)tc[1]) == tc[2]); + numTestCases++; + } +} + + +static void testGetNumRawDataModules(void) { + const int cases[][2] = { + { 1, 208}, + { 2, 359}, + { 3, 567}, + { 6, 1383}, + { 7, 1568}, + {12, 3728}, + {15, 5243}, + {18, 7211}, + {22, 10068}, + {26, 13652}, + {32, 19723}, + {37, 25568}, + {40, 29648}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + const int *tc = cases[i]; + assert(getNumRawDataModules(tc[0]) == tc[1]); + numTestCases++; + } +} + + +static void testCalcReedSolomonGenerator(void) { + uint8_t generator[30]; + + calcReedSolomonGenerator(1, generator); + assert(generator[0] == 0x01); + numTestCases++; + + calcReedSolomonGenerator(2, generator); + assert(generator[0] == 0x03); + assert(generator[1] == 0x02); + numTestCases++; + + calcReedSolomonGenerator(5, generator); + assert(generator[0] == 0x1F); + assert(generator[1] == 0xC6); + assert(generator[2] == 0x3F); + assert(generator[3] == 0x93); + assert(generator[4] == 0x74); + numTestCases++; + + calcReedSolomonGenerator(30, generator); + assert(generator[ 0] == 0xD4); + assert(generator[ 1] == 0xF6); + assert(generator[ 5] == 0xC0); + assert(generator[12] == 0x16); + assert(generator[13] == 0xD9); + assert(generator[20] == 0x12); + assert(generator[27] == 0x6A); + assert(generator[29] == 0x96); + numTestCases++; +} + + +static void testCalcReedSolomonRemainder(void) { + { + uint8_t data[1]; + uint8_t generator[3]; + uint8_t remainder[ARRAY_LENGTH(generator)]; + calcReedSolomonGenerator(ARRAY_LENGTH(generator), generator); + calcReedSolomonRemainder(data, 0, generator, ARRAY_LENGTH(generator), remainder); + assert(remainder[0] == 0); + assert(remainder[1] == 0); + assert(remainder[2] == 0); + numTestCases++; + } + { + uint8_t data[2] = {0, 1}; + uint8_t generator[4]; + uint8_t remainder[ARRAY_LENGTH(generator)]; + calcReedSolomonGenerator(ARRAY_LENGTH(generator), generator); + calcReedSolomonRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); + assert(remainder[0] == generator[0]); + assert(remainder[1] == generator[1]); + assert(remainder[2] == generator[2]); + assert(remainder[3] == generator[3]); + numTestCases++; + } + { + uint8_t data[5] = {0x03, 0x3A, 0x60, 0x12, 0xC7}; + uint8_t generator[5]; + uint8_t remainder[ARRAY_LENGTH(generator)]; + calcReedSolomonGenerator(ARRAY_LENGTH(generator), generator); + calcReedSolomonRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); + assert(remainder[0] == 0xCB); + assert(remainder[1] == 0x36); + assert(remainder[2] == 0x16); + assert(remainder[3] == 0xFA); + assert(remainder[4] == 0x9D); + numTestCases++; + } + { + uint8_t data[43] = { + 0x38, 0x71, 0xDB, 0xF9, 0xD7, 0x28, 0xF6, 0x8E, 0xFE, 0x5E, + 0xE6, 0x7D, 0x7D, 0xB2, 0xA5, 0x58, 0xBC, 0x28, 0x23, 0x53, + 0x14, 0xD5, 0x61, 0xC0, 0x20, 0x6C, 0xDE, 0xDE, 0xFC, 0x79, + 0xB0, 0x8B, 0x78, 0x6B, 0x49, 0xD0, 0x1A, 0xAD, 0xF3, 0xEF, + 0x52, 0x7D, 0x9A, + }; + uint8_t generator[30]; + uint8_t remainder[ARRAY_LENGTH(generator)]; + calcReedSolomonGenerator(ARRAY_LENGTH(generator), generator); + calcReedSolomonRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); + assert(remainder[ 0] == 0xCE); + assert(remainder[ 1] == 0xF0); + assert(remainder[ 2] == 0x31); + assert(remainder[ 3] == 0xDE); + assert(remainder[ 8] == 0xE1); + assert(remainder[12] == 0xCA); + assert(remainder[17] == 0xE3); + assert(remainder[19] == 0x85); + assert(remainder[20] == 0x50); + assert(remainder[24] == 0xBE); + assert(remainder[29] == 0xB3); + numTestCases++; + } +} + + +static void testFiniteFieldMultiply(void) { + const uint8_t cases[][3] = { + {0x00, 0x00, 0x00}, + {0x01, 0x01, 0x01}, + {0x02, 0x02, 0x04}, + {0x00, 0x6E, 0x00}, + {0xB2, 0xDD, 0xE6}, + {0x41, 0x11, 0x25}, + {0xB0, 0x1F, 0x11}, + {0x05, 0x75, 0xBC}, + {0x52, 0xB5, 0xAE}, + {0xA8, 0x20, 0xA4}, + {0x0E, 0x44, 0x9F}, + {0xD4, 0x13, 0xA0}, + {0x31, 0x10, 0x37}, + {0x6C, 0x58, 0xCB}, + {0xB6, 0x75, 0x3E}, + {0xFF, 0xFF, 0xE2}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + const uint8_t *tc = cases[i]; + assert(finiteFieldMultiply(tc[0], tc[1]) == tc[2]); + numTestCases++; + } +} + + +static void testInitializeFunctionModulesEtc(void) { + for (int ver = 1; ver <= 40; ver++) { + uint8_t *qrcode = MALLOC(qrcodegen_BUFFER_LEN_FOR_VERSION(ver), uint8_t); + assert(qrcode != NULL); + initializeFunctionModules(ver, qrcode); + + int size = qrcodegen_getSize(qrcode); + if (ver == 1) + assert(size == 21); + else if (ver == 40) + assert(size == 177); + else + assert(size == ver * 4 + 17); + + bool hasWhite = false; + bool hasBlack = false; + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + bool color = qrcodegen_getModule(qrcode, x, y); + if (color) + hasBlack = true; + else + hasWhite = true; + } + } + assert(hasWhite && hasBlack); + free(qrcode); + numTestCases++; + } +} + + +static void testGetAlignmentPatternPositions(void) { + const int cases[][9] = { + { 1, 0, -1, -1, -1, -1, -1, -1, -1}, + { 2, 2, 6, 18, -1, -1, -1, -1, -1}, + { 3, 2, 6, 22, -1, -1, -1, -1, -1}, + { 6, 2, 6, 34, -1, -1, -1, -1, -1}, + { 7, 3, 6, 22, 38, -1, -1, -1, -1}, + { 8, 3, 6, 24, 42, -1, -1, -1, -1}, + {16, 4, 6, 26, 50, 74, -1, -1, -1}, + {25, 5, 6, 32, 58, 84, 110, -1, -1}, + {32, 6, 6, 34, 60, 86, 112, 138, -1}, + {33, 6, 6, 30, 58, 86, 114, 142, -1}, + {39, 7, 6, 26, 54, 82, 110, 138, 166}, + {40, 7, 6, 30, 58, 86, 114, 142, 170}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + const int *tc = cases[i]; + uint8_t pos[7]; + int num = getAlignmentPatternPositions(tc[0], pos); + assert(num == tc[1]); + for (int j = 0; j < num; j++) + assert(pos[j] == tc[2 + j]); + numTestCases++; + } +} + + +static void testGetSetModule(void) { + uint8_t qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(23)]; + initializeFunctionModules(23, qrcode); + int size = qrcodegen_getSize(qrcode); + + for (int y = 0; y < size; y++) { // Clear all to white + for (int x = 0; x < size; x++) + setModule(qrcode, x, y, false); + } + for (int y = 0; y < size; y++) { // Check all white + for (int x = 0; x < size; x++) + assert(qrcodegen_getModule(qrcode, x, y) == false); + } + for (int y = 0; y < size; y++) { // Set all to black + for (int x = 0; x < size; x++) + setModule(qrcode, x, y, true); + } + for (int y = 0; y < size; y++) { // Check all black + for (int x = 0; x < size; x++) + assert(qrcodegen_getModule(qrcode, x, y) == true); + } + + // Set some out of bounds modules to white + setModuleBounded(qrcode, -1, -1, false); + setModuleBounded(qrcode, -1, 0, false); + setModuleBounded(qrcode, 0, -1, false); + setModuleBounded(qrcode, size, 5, false); + setModuleBounded(qrcode, 72, size, false); + setModuleBounded(qrcode, size, size, false); + for (int y = 0; y < size; y++) { // Check all black + for (int x = 0; x < size; x++) + assert(qrcodegen_getModule(qrcode, x, y) == true); + } + + // Set some modules to white + setModule(qrcode, 3, 8, false); + setModule(qrcode, 61, 49, false); + for (int y = 0; y < size; y++) { // Check most black + for (int x = 0; x < size; x++) { + bool white = (x == 3 && y == 8) || (x == 61 && y == 49); + assert(qrcodegen_getModule(qrcode, x, y) != white); + } + } + numTestCases++; +} + + +static void testGetSetModuleRandomly(void) { + uint8_t qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(1)]; + initializeFunctionModules(1, qrcode); + int size = qrcodegen_getSize(qrcode); + + bool modules[21][21]; + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) + modules[y][x] = qrcodegen_getModule(qrcode, x, y); + } + + long trials = 100000; + for (long i = 0; i < trials; i++) { + int x = rand() % (size * 2) - size / 2; + int y = rand() % (size * 2) - size / 2; + bool isInBounds = 0 <= x && x < size && 0 <= y && y < size; + bool oldColor = isInBounds && modules[y][x]; + if (isInBounds) + assert(getModule(qrcode, x, y) == oldColor); + assert(qrcodegen_getModule(qrcode, x, y) == oldColor); + + bool newColor = rand() % 2 == 0; + if (isInBounds) + modules[y][x] = newColor; + if (isInBounds && rand() % 2 == 0) + setModule(qrcode, x, y, newColor); + else + setModuleBounded(qrcode, x, y, newColor); + } + numTestCases++; +} + + +static void testIsAlphanumeric(void) { + struct TestCase { + bool answer; + const char *text; + }; + const struct TestCase cases[] = { + {true, ""}, + {true, "0"}, + {true, "A"}, + {false, "a"}, + {true, " "}, + {true, "."}, + {true, "*"}, + {false, ","}, + {false, "|"}, + {false, "@"}, + {true, "XYZ"}, + {false, "XYZ!"}, + {true, "79068"}, + {true, "+123 ABC$"}, + {false, "\x01"}, + {false, "\x7F"}, + {false, "\x80"}, + {false, "\xC0"}, + {false, "\xFF"}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + assert(qrcodegen_isAlphanumeric(cases[i].text) == cases[i].answer); + numTestCases++; + } +} + + +static void testIsNumeric(void) { + struct TestCase { + bool answer; + const char *text; + }; + const struct TestCase cases[] = { + {true, ""}, + {true, "0"}, + {false, "A"}, + {false, "a"}, + {false, " "}, + {false, "."}, + {false, "*"}, + {false, ","}, + {false, "|"}, + {false, "@"}, + {false, "XYZ"}, + {false, "XYZ!"}, + {true, "79068"}, + {false, "+123 ABC$"}, + {false, "\x01"}, + {false, "\x7F"}, + {false, "\x80"}, + {false, "\xC0"}, + {false, "\xFF"}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + assert(qrcodegen_isNumeric(cases[i].text) == cases[i].answer); + numTestCases++; + } +} + + +static void testCalcSegmentBufferSize(void) { + { + const size_t cases[][2] = { + {0, 0}, + {1, 1}, + {2, 1}, + {3, 2}, + {4, 2}, + {5, 3}, + {6, 3}, + {1472, 614}, + {2097, 874}, + {5326, 2220}, + {9828, 4095}, + {9829, 4096}, + {9830, 4096}, + {9831, SIZE_MAX}, + {9832, SIZE_MAX}, + {12000, SIZE_MAX}, + {28453, SIZE_MAX}, + {55555, SIZE_MAX}, + {SIZE_MAX / 6, SIZE_MAX}, + {SIZE_MAX / 4, SIZE_MAX}, + {SIZE_MAX / 2, SIZE_MAX}, + {SIZE_MAX / 1, SIZE_MAX}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, cases[i][0]) == cases[i][1]); + numTestCases++; + } + } + { + const size_t cases[][2] = { + {0, 0}, + {1, 1}, + {2, 2}, + {3, 3}, + {4, 3}, + {5, 4}, + {6, 5}, + {1472, 1012}, + {2097, 1442}, + {5326, 3662}, + {5955, 4095}, + {5956, 4095}, + {5957, 4096}, + {5958, SIZE_MAX}, + {5959, SIZE_MAX}, + {12000, SIZE_MAX}, + {28453, SIZE_MAX}, + {55555, SIZE_MAX}, + {SIZE_MAX / 10, SIZE_MAX}, + {SIZE_MAX / 8, SIZE_MAX}, + {SIZE_MAX / 5, SIZE_MAX}, + {SIZE_MAX / 2, SIZE_MAX}, + {SIZE_MAX / 1, SIZE_MAX}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, cases[i][0]) == cases[i][1]); + numTestCases++; + } + } + { + const size_t cases[][2] = { + {0, 0}, + {1, 1}, + {2, 2}, + {3, 3}, + {1472, 1472}, + {2097, 2097}, + {4094, 4094}, + {4095, 4095}, + {4096, SIZE_MAX}, + {4097, SIZE_MAX}, + {5957, SIZE_MAX}, + {12000, SIZE_MAX}, + {28453, SIZE_MAX}, + {55555, SIZE_MAX}, + {SIZE_MAX / 16 + 1, SIZE_MAX}, + {SIZE_MAX / 14, SIZE_MAX}, + {SIZE_MAX / 9, SIZE_MAX}, + {SIZE_MAX / 7, SIZE_MAX}, + {SIZE_MAX / 4, SIZE_MAX}, + {SIZE_MAX / 3, SIZE_MAX}, + {SIZE_MAX / 2, SIZE_MAX}, + {SIZE_MAX / 1, SIZE_MAX}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_BYTE, cases[i][0]) == cases[i][1]); + numTestCases++; + } + } + { + const size_t cases[][2] = { + {0, 0}, + {1, 2}, + {2, 4}, + {3, 5}, + {1472, 2392}, + {2097, 3408}, + {2519, 4094}, + {2520, 4095}, + {2521, SIZE_MAX}, + {5957, SIZE_MAX}, + {2522, SIZE_MAX}, + {12000, SIZE_MAX}, + {28453, SIZE_MAX}, + {55555, SIZE_MAX}, + {SIZE_MAX / 13 + 1, SIZE_MAX}, + {SIZE_MAX / 12, SIZE_MAX}, + {SIZE_MAX / 9, SIZE_MAX}, + {SIZE_MAX / 4, SIZE_MAX}, + {SIZE_MAX / 3, SIZE_MAX}, + {SIZE_MAX / 2, SIZE_MAX}, + {SIZE_MAX / 1, SIZE_MAX}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_KANJI, cases[i][0]) == cases[i][1]); + numTestCases++; + } + } + { + assert(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ECI, 0) == 3); + numTestCases++; + } +} + + +static void testCalcSegmentBitLength(void) { + { + const int cases[][2] = { + {0, 0}, + {1, 4}, + {2, 7}, + {3, 10}, + {4, 14}, + {5, 17}, + {6, 20}, + {1472, 4907}, + {2097, 6990}, + {5326, 17754}, + {9828, 32760}, + {9829, 32764}, + {9830, 32767}, + {9831, -1}, + {9832, -1}, + {12000, -1}, + {28453, -1}, + {INT_MAX / 3, -1}, + {INT_MAX / 2, -1}, + {INT_MAX / 1, -1}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + assert(calcSegmentBitLength(qrcodegen_Mode_NUMERIC, cases[i][0]) == cases[i][1]); + numTestCases++; + } + } + { + const int cases[][2] = { + {0, 0}, + {1, 6}, + {2, 11}, + {3, 17}, + {4, 22}, + {5, 28}, + {6, 33}, + {1472, 8096}, + {2097, 11534}, + {5326, 29293}, + {5955, 32753}, + {5956, 32758}, + {5957, 32764}, + {5958, -1}, + {5959, -1}, + {12000, -1}, + {28453, -1}, + {INT_MAX / 5, -1}, + {INT_MAX / 4, -1}, + {INT_MAX / 3, -1}, + {INT_MAX / 2, -1}, + {INT_MAX / 1, -1}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + assert(calcSegmentBitLength(qrcodegen_Mode_ALPHANUMERIC, cases[i][0]) == cases[i][1]); + numTestCases++; + } + } + { + const int cases[][2] = { + {0, 0}, + {1, 8}, + {2, 16}, + {3, 24}, + {1472, 11776}, + {2097, 16776}, + {4094, 32752}, + {4095, 32760}, + {4096, -1}, + {4097, -1}, + {5957, -1}, + {12000, -1}, + {28453, -1}, + {INT_MAX / 8 + 1, -1}, + {INT_MAX / 7, -1}, + {INT_MAX / 6, -1}, + {INT_MAX / 5, -1}, + {INT_MAX / 4, -1}, + {INT_MAX / 3, -1}, + {INT_MAX / 2, -1}, + {INT_MAX / 1, -1}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + assert(calcSegmentBitLength(qrcodegen_Mode_BYTE, cases[i][0]) == cases[i][1]); + numTestCases++; + } + } + { + const int cases[][2] = { + {0, 0}, + {1, 13}, + {2, 26}, + {3, 39}, + {1472, 19136}, + {2097, 27261}, + {2519, 32747}, + {2520, 32760}, + {2521, -1}, + {5957, -1}, + {2522, -1}, + {12000, -1}, + {28453, -1}, + {INT_MAX / 13 + 1, -1}, + {INT_MAX / 12, -1}, + {INT_MAX / 9, -1}, + {INT_MAX / 4, -1}, + {INT_MAX / 3, -1}, + {INT_MAX / 2, -1}, + {INT_MAX / 1, -1}, + }; + for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { + assert(calcSegmentBitLength(qrcodegen_Mode_KANJI, cases[i][0]) == cases[i][1]); + numTestCases++; + } + } + { + assert(calcSegmentBitLength(qrcodegen_Mode_ECI, 0) == 24); + numTestCases++; + } +} + + +static void testMakeBytes(void) { + { + struct qrcodegen_Segment seg = qrcodegen_makeBytes(NULL, 0, NULL); + assert(seg.mode == qrcodegen_Mode_BYTE); + assert(seg.numChars == 0); + assert(seg.bitLength == 0); + numTestCases++; + } + { + const uint8_t data[] = {0x00}; + uint8_t buf[1]; + struct qrcodegen_Segment seg = qrcodegen_makeBytes(data, 1, buf); + assert(seg.numChars == 1); + assert(seg.bitLength == 8); + assert(seg.data[0] == 0x00); + numTestCases++; + } + { + const uint8_t data[] = {0xEF, 0xBB, 0xBF}; + uint8_t buf[3]; + struct qrcodegen_Segment seg = qrcodegen_makeBytes(data, 3, buf); + assert(seg.numChars == 3); + assert(seg.bitLength == 24); + assert(seg.data[0] == 0xEF); + assert(seg.data[1] == 0xBB); + assert(seg.data[2] == 0xBF); + numTestCases++; + } +} + + +static void testMakeNumeric(void) { + { + struct qrcodegen_Segment seg = qrcodegen_makeNumeric("", NULL); + assert(seg.mode == qrcodegen_Mode_NUMERIC); + assert(seg.numChars == 0); + assert(seg.bitLength == 0); + numTestCases++; + } + { + uint8_t buf[1]; + struct qrcodegen_Segment seg = qrcodegen_makeNumeric("9", buf); + assert(seg.numChars == 1); + assert(seg.bitLength == 4); + assert(seg.data[0] == 0x90); + numTestCases++; + } + { + uint8_t buf[1]; + struct qrcodegen_Segment seg = qrcodegen_makeNumeric("81", buf); + assert(seg.numChars == 2); + assert(seg.bitLength == 7); + assert(seg.data[0] == 0xA2); + numTestCases++; + } + { + uint8_t buf[2]; + struct qrcodegen_Segment seg = qrcodegen_makeNumeric("673", buf); + assert(seg.numChars == 3); + assert(seg.bitLength == 10); + assert(seg.data[0] == 0xA8); + assert(seg.data[1] == 0x40); + numTestCases++; + } + { + uint8_t buf[5]; + struct qrcodegen_Segment seg = qrcodegen_makeNumeric("3141592653", buf); + assert(seg.numChars == 10); + assert(seg.bitLength == 34); + assert(seg.data[0] == 0x4E); + assert(seg.data[1] == 0x89); + assert(seg.data[2] == 0xF4); + assert(seg.data[3] == 0x24); + assert(seg.data[4] == 0xC0); + numTestCases++; + } +} + + +static void testMakeAlphanumeric(void) { + { + struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("", NULL); + assert(seg.mode == qrcodegen_Mode_ALPHANUMERIC); + assert(seg.numChars == 0); + assert(seg.bitLength == 0); + numTestCases++; + } + { + uint8_t buf[1]; + struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("A", buf); + assert(seg.numChars == 1); + assert(seg.bitLength == 6); + assert(seg.data[0] == 0x28); + numTestCases++; + } + { + uint8_t buf[2]; + struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("%:", buf); + assert(seg.numChars == 2); + assert(seg.bitLength == 11); + assert(seg.data[0] == 0xDB); + assert(seg.data[1] == 0x40); + numTestCases++; + } + { + uint8_t buf[3]; + struct qrcodegen_Segment seg = qrcodegen_makeAlphanumeric("Q R", buf); + assert(seg.numChars == 3); + assert(seg.bitLength == 17); + assert(seg.data[0] == 0x96); + assert(seg.data[1] == 0xCD); + assert(seg.data[2] == 0x80); + numTestCases++; + } +} + + +static void testMakeEci(void) { + { + uint8_t buf[1]; + struct qrcodegen_Segment seg = qrcodegen_makeEci(127, buf); + assert(seg.mode == qrcodegen_Mode_ECI); + assert(seg.numChars == 0); + assert(seg.bitLength == 8); + assert(seg.data[0] == 0x7F); + numTestCases++; + } + { + uint8_t buf[2]; + struct qrcodegen_Segment seg = qrcodegen_makeEci(10345, buf); + assert(seg.numChars == 0); + assert(seg.bitLength == 16); + assert(seg.data[0] == 0xA8); + assert(seg.data[1] == 0x69); + numTestCases++; + } + { + uint8_t buf[3]; + struct qrcodegen_Segment seg = qrcodegen_makeEci(999999, buf); + assert(seg.numChars == 0); + assert(seg.bitLength == 24); + assert(seg.data[0] == 0xCF); + assert(seg.data[1] == 0x42); + assert(seg.data[2] == 0x3F); + numTestCases++; + } +} + + +static void testGetTotalBits(void) { + { + assert(getTotalBits(NULL, 0, 1) == 0); + numTestCases++; + assert(getTotalBits(NULL, 0, 40) == 0); + numTestCases++; + } + { + struct qrcodegen_Segment segs[] = { + {qrcodegen_Mode_BYTE, 3, NULL, 24}, + }; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 2) == 36); + numTestCases++; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 10) == 44); + numTestCases++; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 39) == 44); + numTestCases++; + } + { + struct qrcodegen_Segment segs[] = { + {qrcodegen_Mode_ECI, 0, NULL, 8}, + {qrcodegen_Mode_NUMERIC, 7, NULL, 24}, + {qrcodegen_Mode_ALPHANUMERIC, 1, NULL, 6}, + {qrcodegen_Mode_KANJI, 4, NULL, 52}, + }; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 9) == 133); + numTestCases++; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 21) == 139); + numTestCases++; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 27) == 145); + numTestCases++; + } + { + struct qrcodegen_Segment segs[] = { + {qrcodegen_Mode_BYTE, 4093, NULL, 32744}, + }; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 1) == -1); + numTestCases++; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 10) == 32764); + numTestCases++; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 27) == 32764); + numTestCases++; + } + { + struct qrcodegen_Segment segs[] = { + {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824}, + {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824}, + {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824}, + {qrcodegen_Mode_NUMERIC, 2047, NULL, 6824}, + {qrcodegen_Mode_NUMERIC, 1617, NULL, 5390}, + }; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 1) == -1); + numTestCases++; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 10) == 32766); + numTestCases++; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 27) == -1); + numTestCases++; + } + { + struct qrcodegen_Segment segs[] = { + {qrcodegen_Mode_KANJI, 255, NULL, 3315}, + {qrcodegen_Mode_KANJI, 255, NULL, 3315}, + {qrcodegen_Mode_KANJI, 255, NULL, 3315}, + {qrcodegen_Mode_KANJI, 255, NULL, 3315}, + {qrcodegen_Mode_KANJI, 255, NULL, 3315}, + {qrcodegen_Mode_KANJI, 255, NULL, 3315}, + {qrcodegen_Mode_KANJI, 255, NULL, 3315}, + {qrcodegen_Mode_KANJI, 255, NULL, 3315}, + {qrcodegen_Mode_KANJI, 255, NULL, 3315}, + {qrcodegen_Mode_ALPHANUMERIC, 511, NULL, 2811}, + }; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 9) == 32767); + numTestCases++; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 26) == -1); + numTestCases++; + assert(getTotalBits(segs, ARRAY_LENGTH(segs), 40) == -1); + numTestCases++; + } +} + + +/*---- Main runner ----*/ + +int main(void) { + srand(time(NULL)); + testAppendBitsToBuffer(); + testAppendErrorCorrection(); + testGetNumDataCodewords(); + testGetNumRawDataModules(); + testCalcReedSolomonGenerator(); + testCalcReedSolomonRemainder(); + testFiniteFieldMultiply(); + testInitializeFunctionModulesEtc(); + testGetAlignmentPatternPositions(); + testGetSetModule(); + testGetSetModuleRandomly(); + testIsAlphanumeric(); + testIsNumeric(); + testCalcSegmentBufferSize(); + testCalcSegmentBitLength(); + testMakeBytes(); + testMakeNumeric(); + testMakeAlphanumeric(); + testMakeEci(); + testGetTotalBits(); + printf("All %d test cases passed\n", numTestCases); + return EXIT_SUCCESS; +}
diff --git a/src/third_party/QR-Code-generator/c/qrcodegen-worker.c b/src/third_party/QR-Code-generator/c/qrcodegen-worker.c new file mode 100644 index 0000000..9fa6433 --- /dev/null +++ b/src/third_party/QR-Code-generator/c/qrcodegen-worker.c
@@ -0,0 +1,116 @@ +/* + * QR Code generator test worker (C) + * + * This program reads data and encoding parameters from standard input and writes + * QR Code bitmaps to standard output. The I/O format is one integer per line. + * Run with no command line arguments. The program is intended for automated + * batch testing of end-to-end functionality of this QR Code generator library. + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "qrcodegen.h" + +#ifndef __cplusplus + #define MALLOC(num, type) malloc((num) * sizeof(type)) +#else + #define MALLOC(num, type) static_cast<type*>(malloc((num) * sizeof(type))) +#endif + + +int main(void) { + while (true) { + + // Read data length or exit + int length; + if (scanf("%d", &length) != 1) + return EXIT_FAILURE; + if (length == -1) + break; + + // Read data bytes + bool isAscii = true; + uint8_t *data = MALLOC(length, uint8_t); + if (data == NULL) { + perror("malloc"); + return EXIT_FAILURE; + } + for (int i = 0; i < length; i++) { + int b; + if (scanf("%d", &b) != 1) + return EXIT_FAILURE; + data[i] = (uint8_t)b; + isAscii &= 0 < b && b < 128; + } + + // Read encoding parameters + int errCorLvl, minVersion, maxVersion, mask, boostEcl; + if (scanf("%d %d %d %d %d", &errCorLvl, &minVersion, &maxVersion, &mask, &boostEcl) != 5) + return EXIT_FAILURE; + + // Allocate memory for QR Code + int bufferLen = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); + uint8_t *qrcode = MALLOC(bufferLen, uint8_t); + uint8_t *tempBuffer = MALLOC(bufferLen, uint8_t); + if (qrcode == NULL || tempBuffer == NULL) { + perror("malloc"); + return EXIT_FAILURE; + } + + // Try to make QR Code symbol + bool ok; + if (isAscii) { + char *text = MALLOC(length + 1, char); + for (int i = 0; i < length; i++) + text[i] = (char)data[i]; + text[length] = '\0'; + ok = qrcodegen_encodeText(text, tempBuffer, qrcode, (enum qrcodegen_Ecc)errCorLvl, + minVersion, maxVersion, (enum qrcodegen_Mask)mask, boostEcl == 1); + free(text); + } else if (length <= bufferLen) { + memcpy(tempBuffer, data, length * sizeof(data[0])); + ok = qrcodegen_encodeBinary(tempBuffer, (size_t)length, qrcode, (enum qrcodegen_Ecc)errCorLvl, + minVersion, maxVersion, (enum qrcodegen_Mask)mask, boostEcl == 1); + } else + ok = false; + free(data); + free(tempBuffer); + + if (ok) { + // Print grid of modules + int size = qrcodegen_getSize(qrcode); + printf("%d\n", (size - 17) / 4); + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) + printf("%d\n", qrcodegen_getModule(qrcode, x, y) ? 1 : 0); + } + } else + printf("-1\n"); + free(qrcode); + fflush(stdout); + } + return EXIT_SUCCESS; +}
diff --git a/src/third_party/QR-Code-generator/c/qrcodegen.c b/src/third_party/QR-Code-generator/c/qrcodegen.c new file mode 100644 index 0000000..d448f91 --- /dev/null +++ b/src/third_party/QR-Code-generator/c/qrcodegen.c
@@ -0,0 +1,1024 @@ +/* + * QR Code generator library (C) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include <assert.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include "qrcodegen.h" + +#ifndef QRCODEGEN_TEST + #define testable static // Keep functions private +#else + // Expose private functions + #ifndef __cplusplus + #define testable + #else + // Needed for const variables because they are treated as implicitly 'static' in C++ + #define testable extern + #endif +#endif + + +/*---- Forward declarations for private functions ----*/ + +// Regarding all public and private functions defined in this source file: +// - They require all pointer/array arguments to be not null. +// - They only read input scalar/array arguments, write to output pointer/array +// arguments, and return scalar values; they are "pure" functions. +// - They don't read mutable global variables or write to any global variables. +// - They don't perform I/O, read the clock, print to console, etc. +// - They allocate a small and constant amount of stack memory. +// - They don't allocate or free any memory on the heap. +// - They don't recurse or mutually recurse. All the code +// could be inlined into the top-level public functions. +// - They run in at most quadratic time with respect to input arguments. +// Most functions run in linear time, and some in constant time. +// There are no unbounded loops or non-obvious termination conditions. +// - They are completely thread-safe if the caller does not give the +// same writable buffer to concurrent calls to these functions. + +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); + +testable void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); +testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); +testable int getNumRawDataModules(int version); + +testable void calcReedSolomonGenerator(int degree, uint8_t result[]); +testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, + const uint8_t generator[], int degree, uint8_t result[]); +testable uint8_t finiteFieldMultiply(uint8_t x, uint8_t y); + +testable void initializeFunctionModules(int version, uint8_t qrcode[]); +static void drawWhiteFunctionModules(uint8_t qrcode[], int version); +static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]); +testable int getAlignmentPatternPositions(int version, uint8_t result[7]); +static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]); + +static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]); +static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask); +static long getPenaltyScore(const uint8_t qrcode[]); + +testable bool getModule(const uint8_t qrcode[], int x, int y); +testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack); +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack); + +testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); +testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); +static int numCharCountBits(enum qrcodegen_Mode mode, int version); + + + +/*---- Private tables of constants ----*/ + +// For checking text and encoding segments. +static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + +// For generating error correction codes. +testable const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +// For generating error correction codes. +testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + +// For automatic mask pattern selection. +static const int PENALTY_N1 = 3; +static const int PENALTY_N2 = 3; +static const int PENALTY_N3 = 40; +static const int PENALTY_N4 = 10; + + + +/*---- High-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + + size_t textLen = strlen(text); + if (textLen == 0) + return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); + size_t bufLen = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); + + struct qrcodegen_Segment seg; + if (qrcodegen_isNumeric(text)) { + if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, textLen) > bufLen) + goto fail; + seg = qrcodegen_makeNumeric(text, tempBuffer); + } else if (qrcodegen_isAlphanumeric(text)) { + if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, textLen) > bufLen) + goto fail; + seg = qrcodegen_makeAlphanumeric(text, tempBuffer); + } else { + if (textLen > bufLen) + goto fail; + for (size_t i = 0; i < textLen; i++) + tempBuffer[i] = (uint8_t)text[i]; + seg.mode = qrcodegen_Mode_BYTE; + seg.bitLength = calcSegmentBitLength(seg.mode, textLen); + if (seg.bitLength == -1) + goto fail; + seg.numChars = (int)textLen; + seg.data = tempBuffer; + } + return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); + +fail: + qrcode[0] = 0; // Set size to invalid value for safety + return false; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + + struct qrcodegen_Segment seg; + seg.mode = qrcodegen_Mode_BYTE; + seg.bitLength = calcSegmentBitLength(seg.mode, dataLen); + if (seg.bitLength == -1) { + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + seg.numChars = (int)dataLen; + seg.data = dataAndTemp; + return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, dataAndTemp, qrcode); +} + + +// Appends the given sequence of bits to the given byte-based bit buffer, increasing the bit length. +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen) { + assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0); + for (int i = numBits - 1; i >= 0; i--, (*bitLen)++) + buffer[*bitLen >> 3] |= ((val >> i) & 1) << (7 - (*bitLen & 7)); +} + + + +/*---- Error correction code generation functions ----*/ + +// Appends error correction bytes to each block of the given data array, then interleaves bytes +// from the blocks and stores them in the result array. data[0 : rawCodewords - totalEcc] contains +// the input data. data[rawCodewords - totalEcc : rawCodewords] is used as a temporary work area +// and will be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. +testable void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { + // Calculate parameter numbers + assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK[(int)ecl][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int dataLen = rawCodewords - blockEccLen * numBlocks; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; + + // Split data into blocks and append ECC after all data + uint8_t generator[30]; + calcReedSolomonGenerator(blockEccLen, generator); + for (int i = 0, j = dataLen, k = 0; i < numBlocks; i++) { + int blockLen = shortBlockDataLen; + if (i >= numShortBlocks) + blockLen++; + calcReedSolomonRemainder(&data[k], blockLen, generator, blockEccLen, &data[j]); + j += blockEccLen; + k += blockLen; + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + for (int i = 0, k = 0; i < numBlocks; i++) { + for (int j = 0, l = i; j < shortBlockDataLen; j++, k++, l += numBlocks) + result[l] = data[k]; + if (i >= numShortBlocks) + k++; + } + for (int i = numShortBlocks, k = (numShortBlocks + 1) * shortBlockDataLen, l = numBlocks * shortBlockDataLen; + i < numBlocks; i++, k += shortBlockDataLen + 1, l++) + result[l] = data[k]; + for (int i = 0, k = dataLen; i < numBlocks; i++) { + for (int j = 0, l = dataLen + i; j < blockEccLen; j++, k++, l += numBlocks) + result[l] = data[k]; + } +} + + +// Returns the number of 8-bit codewords that can be used for storing data (not ECC), +// for the given version number and error correction level. The result is in the range [9, 2956]. +testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { + int v = version, e = (int)ecl; + assert(0 <= e && e < 4 && qrcodegen_VERSION_MIN <= v && v <= qrcodegen_VERSION_MAX); + return getNumRawDataModules(v) / 8 - ECC_CODEWORDS_PER_BLOCK[e][v] * NUM_ERROR_CORRECTION_BLOCKS[e][v]; +} + + +// Returns the number of data bits that can be stored in a QR Code of the given version number, after +// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. +// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. +testable int getNumRawDataModules(int version) { + assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int result = (16 * version + 128) * version + 64; + if (version >= 2) { + int numAlign = version / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (version >= 7) + result -= 18 * 2; // Subtract version information + } + return result; +} + + + +/*---- Reed-Solomon ECC generator functions ----*/ + +// Calculates the Reed-Solomon generator polynomial of the given degree, storing in result[0 : degree]. +testable void calcReedSolomonGenerator(int degree, uint8_t result[]) { + // Start with the monomial x^0 + assert(1 <= degree && degree <= 30); + memset(result, 0, degree * sizeof(result[0])); + result[degree - 1] = 1; + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (int j = 0; j < degree; j++) { + result[j] = finiteFieldMultiply(result[j], root); + if (j + 1 < degree) + result[j] ^= result[j + 1]; + } + root = finiteFieldMultiply(root, 0x02); + } +} + + +// Calculates the remainder of the polynomial data[0 : dataLen] when divided by the generator[0 : degree], where all +// polynomials are in big endian and the generator has an implicit leading 1 term, storing the result in result[0 : degree]. +testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, + const uint8_t generator[], int degree, uint8_t result[]) { + + // Perform polynomial division + assert(1 <= degree && degree <= 30); + memset(result, 0, degree * sizeof(result[0])); + for (int i = 0; i < dataLen; i++) { + uint8_t factor = data[i] ^ result[0]; + memmove(&result[0], &result[1], (degree - 1) * sizeof(result[0])); + result[degree - 1] = 0; + for (int j = 0; j < degree; j++) + result[j] ^= finiteFieldMultiply(generator[j], factor); + } +} + + +// Returns the product of the two given field elements modulo GF(2^8/0x11D). +// All inputs are valid. This could be implemented as a 256*256 lookup table. +testable uint8_t finiteFieldMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + uint8_t z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + return z; +} + + + +/*---- Drawing function modules ----*/ + +// Clears the given QR Code grid with white modules for the given +// version's size, then marks every function module as black. +testable void initializeFunctionModules(int version, uint8_t qrcode[]) { + // Initialize QR Code + int qrsize = version * 4 + 17; + memset(qrcode, 0, ((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0])); + qrcode[0] = (uint8_t)qrsize; + + // Fill horizontal and vertical timing patterns + fillRectangle(6, 0, 1, qrsize, qrcode); + fillRectangle(0, 6, qrsize, 1, qrcode); + + // Fill 3 finder patterns (all corners except bottom right) and format bits + fillRectangle(0, 0, 9, 9, qrcode); + fillRectangle(qrsize - 8, 0, 8, 9, qrcode); + fillRectangle(0, qrsize - 8, 9, 8, qrcode); + + // Fill numerous alignment patterns + uint8_t alignPatPos[7] = {0}; + int numAlign = getAlignmentPatternPositions(version, alignPatPos); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) + continue; // Skip the three finder corners + else + fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode); + } + } + + // Fill version blocks + if (version >= 7) { + fillRectangle(qrsize - 11, 0, 3, 6, qrcode); + fillRectangle(0, qrsize - 11, 6, 3, qrcode); + } +} + + +// Draws white function modules and possibly some black modules onto the given QR Code, without changing +// non-function modules. This does not draw the format bits. This requires all function modules to be previously +// marked black (namely by initializeFunctionModules()), because this may skip redrawing black function modules. +static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { + // Draw horizontal and vertical timing patterns + int qrsize = qrcodegen_getSize(qrcode); + for (int i = 7; i < qrsize - 7; i += 2) { + setModule(qrcode, 6, i, false); + setModule(qrcode, i, 6, false); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + int dist = abs(i); + if (abs(j) > dist) + dist = abs(j); + if (dist == 2 || dist == 4) { + setModuleBounded(qrcode, 3 + j, 3 + i, false); + setModuleBounded(qrcode, qrsize - 4 + j, 3 + i, false); + setModuleBounded(qrcode, 3 + j, qrsize - 4 + i, false); + } + } + } + + // Draw numerous alignment patterns + uint8_t alignPatPos[7] = {0}; + int numAlign = getAlignmentPatternPositions(version, alignPatPos); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) + continue; // Skip the three finder corners + else { + for (int k = -1; k <= 1; k++) { + for (int l = -1; l <= 1; l++) + setModule(qrcode, alignPatPos[i] + l, alignPatPos[j] + k, k == 0 && l == 0); + } + } + } + } + + // Draw version blocks + if (version >= 7) { + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long data = (long)version << 12 | rem; // uint18 + assert(data >> 18 == 0); + + // Draw two copies + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 3; j++) { + int k = qrsize - 11 + j; + setModule(qrcode, k, i, (data & 1) != 0); + setModule(qrcode, i, k, (data & 1) != 0); + data >>= 1; + } + } + } +} + + +// Draws two copies of the format bits (with its own error correction code) based +// on the given mask and error correction level. This always draws all modules of +// the format bits, unlike drawWhiteFunctionModules() which might skip black modules. +static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { + // Calculate error correction code and pack bits + assert(0 <= (int)mask && (int)mask <= 7); + int data = -1; // Dummy value + switch (ecl) { + case qrcodegen_Ecc_LOW : data = 1; break; + case qrcodegen_Ecc_MEDIUM : data = 0; break; + case qrcodegen_Ecc_QUARTILE: data = 3; break; + case qrcodegen_Ecc_HIGH : data = 2; break; + default: assert(false); + } + data = data << 3 | (int)mask; // ecl-derived value is uint2, mask is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + data = data << 10 | rem; + data ^= 0x5412; // uint15 + assert(data >> 15 == 0); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setModule(qrcode, 8, i, ((data >> i) & 1) != 0); + setModule(qrcode, 8, 7, ((data >> 6) & 1) != 0); + setModule(qrcode, 8, 8, ((data >> 7) & 1) != 0); + setModule(qrcode, 7, 8, ((data >> 8) & 1) != 0); + for (int i = 9; i < 15; i++) + setModule(qrcode, 14 - i, 8, ((data >> i) & 1) != 0); + + // Draw second copy + int qrsize = qrcodegen_getSize(qrcode); + for (int i = 0; i <= 7; i++) + setModule(qrcode, qrsize - 1 - i, 8, ((data >> i) & 1) != 0); + for (int i = 8; i < 15; i++) + setModule(qrcode, 8, qrsize - 15 + i, ((data >> i) & 1) != 0); + setModule(qrcode, 8, qrsize - 8, true); +} + + +// Calculates the positions of alignment patterns in ascending order for the given version number, +// storing them to the given array and returning an array length in the range [0, 7]. +testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { + if (version == 1) + return 0; + int numAlign = version / 7 + 2; + int step; + if (version != 32) { + // ceil((size - 13) / (2*numAlign - 2)) * 2 + step = (version * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; + } else // C-C-C-Combo breaker! + step = 26; + for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) + result[i] = pos; + result[0] = 6; + return numAlign; +} + + +// Sets every pixel in the range [left : left + width] * [top : top + height] to black. +static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]) { + for (int dy = 0; dy < height; dy++) { + for (int dx = 0; dx < width; dx++) + setModule(qrcode, left + dx, top + dy, true); + } +} + + + +/*---- Drawing data modules and masking ----*/ + +// Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of +// the QR Code to be black at function modules and white at codeword modules (including unused remainder bits). +static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { + int qrsize = qrcodegen_getSize(qrcode); + int i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = qrsize - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < qrsize; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + int x = right - j; // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate + if (!getModule(qrcode, x, y) && i < dataLen * 8) { + bool black = ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0; + setModule(qrcode, x, y, black); + i++; + } + // If there are any remainder bits (0 to 7), they are already + // set to 0/false/white when the grid of modules was initialized + } + } + } + assert(i == dataLen * 8); +} + + +// XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical +// properties, calling applyMask(..., m) twice with the same value is equivalent to no change at all. +// This means it is possible to apply a mask, undo it, and try another mask. Note that a final +// well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). +static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask) { + assert(0 <= (int)mask && (int)mask <= 7); // Disallows qrcodegen_Mask_AUTO + int qrsize = qrcodegen_getSize(qrcode); + for (int y = 0; y < qrsize; y++) { + for (int x = 0; x < qrsize; x++) { + if (getModule(functionModules, x, y)) + continue; + bool invert = false; // Dummy value + switch ((int)mask) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: assert(false); + } + bool val = getModule(qrcode, x, y); + setModule(qrcode, x, y, val ^ invert); + } + } +} + + +// Calculates and returns the penalty score based on state of the given QR Code's current modules. +// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. +static long getPenaltyScore(const uint8_t qrcode[]) { + int qrsize = qrcodegen_getSize(qrcode); + long result = 0; + + // Adjacent modules in row having same color + for (int y = 0; y < qrsize; y++) { + bool colorX = false; + for (int x = 0, runX = -1; x < qrsize; x++) { + if (x == 0 || getModule(qrcode, x, y) != colorX) { + colorX = getModule(qrcode, x, y); + runX = 1; + } else { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } + } + } + // Adjacent modules in column having same color + for (int x = 0; x < qrsize; x++) { + bool colorY = false; + for (int y = 0, runY = -1; y < qrsize; y++) { + if (y == 0 || getModule(qrcode, x, y) != colorY) { + colorY = getModule(qrcode, x, y); + runY = 1; + } else { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } + } + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < qrsize - 1; y++) { + for (int x = 0; x < qrsize - 1; x++) { + bool color = getModule(qrcode, x, y); + if ( color == getModule(qrcode, x + 1, y) && + color == getModule(qrcode, x, y + 1) && + color == getModule(qrcode, x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Finder-like pattern in rows + for (int y = 0; y < qrsize; y++) { + for (int x = 0, bits = 0; x < qrsize; x++) { + bits = ((bits << 1) & 0x7FF) | (getModule(qrcode, x, y) ? 1 : 0); + if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + // Finder-like pattern in columns + for (int x = 0; x < qrsize; x++) { + for (int y = 0, bits = 0; y < qrsize; y++) { + bits = ((bits << 1) & 0x7FF) | (getModule(qrcode, x, y) ? 1 : 0); + if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + + // Balance of black and white modules + int black = 0; + for (int y = 0; y < qrsize; y++) { + for (int x = 0; x < qrsize; x++) { + if (getModule(qrcode, x, y)) + black++; + } + } + int total = qrsize * qrsize; + // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + for (int k = 0; black*20L < (9L-k)*total || black*20L > (11L+k)*total; k++) + result += PENALTY_N4; + return result; +} + + + +/*---- Basic QR Code information ----*/ + +// Public function - see documentation comment in header file. +int qrcodegen_getSize(const uint8_t qrcode[]) { + assert(qrcode != NULL); + int result = qrcode[0]; + assert((qrcodegen_VERSION_MIN * 4 + 17) <= result + && result <= (qrcodegen_VERSION_MAX * 4 + 17)); + return result; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y) { + assert(qrcode != NULL); + int qrsize = qrcode[0]; + return (0 <= x && x < qrsize && 0 <= y && y < qrsize) && getModule(qrcode, x, y); +} + + +// Gets the module at the given coordinates, which must be in bounds. +testable bool getModule(const uint8_t qrcode[], int x, int y) { + int qrsize = qrcode[0]; + assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); + int index = y * qrsize + x; + int bitIndex = index & 7; + int byteIndex = (index >> 3) + 1; + return ((qrcode[byteIndex] >> bitIndex) & 1) != 0; +} + + +// Sets the module at the given coordinates, which must be in bounds. +testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack) { + int qrsize = qrcode[0]; + assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); + int index = y * qrsize + x; + int bitIndex = index & 7; + int byteIndex = (index >> 3) + 1; + if (isBlack) + qrcode[byteIndex] |= 1 << bitIndex; + else + qrcode[byteIndex] &= (1 << bitIndex) ^ 0xFF; +} + + +// Sets the module at the given coordinates, doing nothing if out of bounds. +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack) { + int qrsize = qrcode[0]; + if (0 <= x && x < qrsize && 0 <= y && y < qrsize) + setModule(qrcode, x, y, isBlack); +} + + + +/*---- Segment handling ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_isAlphanumeric(const char *text) { + assert(text != NULL); + for (; *text != '\0'; text++) { + if (strchr(ALPHANUMERIC_CHARSET, *text) == NULL) + return false; + } + return true; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_isNumeric(const char *text) { + assert(text != NULL); + for (; *text != '\0'; text++) { + if (*text < '0' || *text > '9') + return false; + } + return true; +} + + +// Public function - see documentation comment in header file. +size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars) { + int temp = calcSegmentBitLength(mode, numChars); + if (temp == -1) + return SIZE_MAX; + assert(0 <= temp && temp <= INT16_MAX); + return ((size_t)temp + 7) / 8; +} + + +// Returns the number of data bits needed to represent a segment +// containing the given number of characters using the given mode. Notes: +// - Returns -1 on failure, i.e. numChars > INT16_MAX or +// the number of needed bits exceeds INT16_MAX (i.e. 32767). +// - Otherwise, all valid results are in the range [0, INT16_MAX]. +// - For byte mode, numChars measures the number of bytes, not Unicode code points. +// - For ECI mode, numChars must be 0, and the worst-case number of bits is returned. +// An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. +testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { + const int LIMIT = INT16_MAX; // Can be configured as high as INT_MAX + if (numChars > (unsigned int)LIMIT) + return -1; + int n = (int)numChars; + + int result = -2; + if (mode == qrcodegen_Mode_NUMERIC) { + // n * 3 + ceil(n / 3) + if (n > LIMIT / 3) + goto overflow; + result = n * 3; + int temp = n / 3 + (n % 3 == 0 ? 0 : 1); + if (temp > LIMIT - result) + goto overflow; + result += temp; + } else if (mode == qrcodegen_Mode_ALPHANUMERIC) { + // n * 5 + ceil(n / 2) + if (n > LIMIT / 5) + goto overflow; + result = n * 5; + int temp = n / 2 + n % 2; + if (temp > LIMIT - result) + goto overflow; + result += temp; + } else if (mode == qrcodegen_Mode_BYTE) { + if (n > LIMIT / 8) + goto overflow; + result = n * 8; + } else if (mode == qrcodegen_Mode_KANJI) { + if (n > LIMIT / 13) + goto overflow; + result = n * 13; + } else if (mode == qrcodegen_Mode_ECI && numChars == 0) + result = 3 * 8; + assert(0 <= result && result <= LIMIT); + return result; +overflow: + return -1; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]) { + assert(data != NULL || len == 0); + struct qrcodegen_Segment result; + result.mode = qrcodegen_Mode_BYTE; + result.bitLength = calcSegmentBitLength(result.mode, len); + assert(result.bitLength != -1); + result.numChars = (int)len; + if (len > 0) + memcpy(buf, data, len * sizeof(buf[0])); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]) { + assert(digits != NULL); + struct qrcodegen_Segment result; + size_t len = strlen(digits); + result.mode = qrcodegen_Mode_NUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != -1); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *digits != '\0'; digits++) { + char c = *digits; + assert('0' <= c && c <= '9'); + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + appendBitsToBuffer(accumData, 10, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + appendBitsToBuffer(accumData, accumCount * 3 + 1, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]) { + assert(text != NULL); + struct qrcodegen_Segment result; + size_t len = strlen(text); + result.mode = qrcodegen_Mode_ALPHANUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != -1); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *text != '\0'; text++) { + const char *temp = strchr(ALPHANUMERIC_CHARSET, *text); + assert(temp != NULL); + accumData = accumData * 45 + (temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + appendBitsToBuffer(accumData, 11, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + appendBitsToBuffer(accumData, 6, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { + struct qrcodegen_Segment result; + result.mode = qrcodegen_Mode_ECI; + result.numChars = 0; + result.bitLength = 0; + if (0 <= assignVal && assignVal < (1 << 7)) { + memset(buf, 0, 1 * sizeof(buf[0])); + appendBitsToBuffer(assignVal, 8, buf, &result.bitLength); + } else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + memset(buf, 0, 2 * sizeof(buf[0])); + appendBitsToBuffer(2, 2, buf, &result.bitLength); + appendBitsToBuffer(assignVal, 14, buf, &result.bitLength); + } else if ((1 << 14) <= assignVal && assignVal < 1000000L) { + memset(buf, 0, 3 * sizeof(buf[0])); + appendBitsToBuffer(6, 3, buf, &result.bitLength); + appendBitsToBuffer(assignVal >> 10, 11, buf, &result.bitLength); + appendBitsToBuffer(assignVal & 0x3FF, 10, buf, &result.bitLength); + } else + assert(false); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { + return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, -1, true, tempBuffer, qrcode); +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { + assert(segs != NULL || len == 0); + assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); + assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = getTotalBits(segs, len, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + } + assert(dataUsedBits != -1); + + // Increase the error correction level while the data still fits in the current version number + for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) + ecl = (enum qrcodegen_Ecc)i; + } + + // Create the data bit string by concatenating all segments + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); + int bitLen = 0; + for (size_t i = 0; i < len; i++) { + const struct qrcodegen_Segment *seg = &segs[i]; + unsigned int modeBits = 0; // Dummy value + switch (seg->mode) { + case qrcodegen_Mode_NUMERIC : modeBits = 0x1; break; + case qrcodegen_Mode_ALPHANUMERIC: modeBits = 0x2; break; + case qrcodegen_Mode_BYTE : modeBits = 0x4; break; + case qrcodegen_Mode_KANJI : modeBits = 0x8; break; + case qrcodegen_Mode_ECI : modeBits = 0x7; break; + default: assert(false); + } + appendBitsToBuffer(modeBits, 4, qrcode, &bitLen); + appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); + for (int j = 0; j < seg->bitLength; j++) + appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); + } + + // Add terminator and pad up to a byte if applicable + int terminatorBits = dataCapacityBits - bitLen; + if (terminatorBits > 4) + terminatorBits = 4; + appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); + appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); + + // Pad with alternate bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + appendBitsToBuffer(padByte, 8, qrcode, &bitLen); + assert(bitLen % 8 == 0); + + // Draw function and data codeword modules + appendErrorCorrection(qrcode, version, ecl, tempBuffer); + initializeFunctionModules(version, qrcode); + drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); + drawWhiteFunctionModules(qrcode, version); + initializeFunctionModules(version, tempBuffer); + + // Handle masking + if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + drawFormatBits(ecl, (enum qrcodegen_Mask)i, qrcode); + applyMask(tempBuffer, qrcode, (enum qrcodegen_Mask)i); + long penalty = getPenaltyScore(qrcode); + if (penalty < minPenalty) { + mask = (enum qrcodegen_Mask)i; + minPenalty = penalty; + } + applyMask(tempBuffer, qrcode, (enum qrcodegen_Mask)i); // Undoes the mask due to XOR + } + } + assert(0 <= (int)mask && (int)mask <= 7); + drawFormatBits(ecl, mask, qrcode); + applyMask(tempBuffer, qrcode, mask); + return true; +} + + +// Returns the number of bits needed to encode the given list of segments at the given version. +// The result is in the range [0, 32767] if successful. Otherwise, -1 is returned if any segment +// has more characters than allowed by that segment's mode's character count field at the version, +// or if the actual answer exceeds INT16_MAX. +testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) { + assert(segs != NULL || len == 0); + assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int result = 0; + for (size_t i = 0; i < len; i++) { + int numChars = segs[i].numChars; + int bitLength = segs[i].bitLength; + assert(0 <= numChars && numChars <= INT16_MAX); + assert(0 <= bitLength && bitLength <= INT16_MAX); + int ccbits = numCharCountBits(segs[i].mode, version); + assert(0 <= ccbits && ccbits <= 16); + // Fail if segment length value doesn't fit in the length field's bit-width + if (numChars >= (1L << ccbits)) + return -1; + long temp = 4L + ccbits + bitLength; + if (temp > INT16_MAX - result) + return -1; + result += temp; + } + assert(0 <= result && result <= INT16_MAX); + return result; +} + + +// Returns the bit width of the segment character count field for the +// given mode at the given version number. The result is in the range [0, 16]. +static int numCharCountBits(enum qrcodegen_Mode mode, int version) { + assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int i = -1; // Dummy value + if ( 1 <= version && version <= 9) i = 0; + else if (10 <= version && version <= 26) i = 1; + else if (27 <= version && version <= 40) i = 2; + else assert(false); + + switch (mode) { + case qrcodegen_Mode_NUMERIC : { static const int temp[] = {10, 12, 14}; return temp[i]; } + case qrcodegen_Mode_ALPHANUMERIC: { static const int temp[] = { 9, 11, 13}; return temp[i]; } + case qrcodegen_Mode_BYTE : { static const int temp[] = { 8, 16, 16}; return temp[i]; } + case qrcodegen_Mode_KANJI : { static const int temp[] = { 8, 10, 12}; return temp[i]; } + case qrcodegen_Mode_ECI : return 0; + default: assert(false); + } + return -1; // Dummy value +}
diff --git a/src/third_party/QR-Code-generator/c/qrcodegen.h b/src/third_party/QR-Code-generator/c/qrcodegen.h new file mode 100644 index 0000000..899feae --- /dev/null +++ b/src/third_party/QR-Code-generator/c/qrcodegen.h
@@ -0,0 +1,267 @@ +/* + * QR Code generator library (C) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + + +/*---- Enum and struct types----*/ + +/* + * The error correction level used in a QR Code symbol. + */ +enum qrcodegen_Ecc { + qrcodegen_Ecc_LOW = 0, + qrcodegen_Ecc_MEDIUM, + qrcodegen_Ecc_QUARTILE, + qrcodegen_Ecc_HIGH, +}; + + +/* + * The mask pattern used in a QR Code symbol. + */ +enum qrcodegen_Mask { + // A special value to tell the QR Code encoder to + // automatically select an appropriate mask pattern + qrcodegen_Mask_AUTO = -1, + // The eight actual mask patterns + qrcodegen_Mask_0 = 0, + qrcodegen_Mask_1, + qrcodegen_Mask_2, + qrcodegen_Mask_3, + qrcodegen_Mask_4, + qrcodegen_Mask_5, + qrcodegen_Mask_6, + qrcodegen_Mask_7, +}; + + +/* + * The mode field of a segment. + */ +enum qrcodegen_Mode { + qrcodegen_Mode_NUMERIC, + qrcodegen_Mode_ALPHANUMERIC, + qrcodegen_Mode_BYTE, + qrcodegen_Mode_KANJI, + qrcodegen_Mode_ECI, +}; + + +/* + * A segment of user/application data that a QR Code symbol can convey. + * Each segment has a mode, a character count, and character/general data that is + * already encoded as a sequence of bits. The maximum allowed bit length is 32767, + * because even the largest QR Code (version 40) has only 31329 modules. + */ +struct qrcodegen_Segment { + // The mode indicator for this segment. + enum qrcodegen_Mode mode; + + // The length of this segment's unencoded data. Always in the range [0, 32767]. + // For numeric, alphanumeric, and kanji modes, this measures in Unicode code points. + // For byte mode, this measures in bytes (raw binary data, text in UTF-8, or other encodings). + // For ECI mode, this is always zero. + int numChars; + + // The data bits of this segment, packed in bitwise big endian. + // Can be null if the bit length is zero. + uint8_t *data; + + // The number of valid data bits used in the buffer. Requires + // 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8. + int bitLength; +}; + + + +/*---- Macro constants and functions ----*/ + +// The minimum and maximum defined QR Code version numbers for Model 2. +#define qrcodegen_VERSION_MIN 1 +#define qrcodegen_VERSION_MAX 40 + +// Calculates the number of bytes needed to store any QR Code up to and including the given version number, +// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' +// can store any single QR Code from version 1 to 25, inclusive. +// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX. +#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1) + +// The worst-case number of bytes needed to store one QR Code, up to and including +// version 40. This value equals 3918, which is just under 4 kilobytes. +// Use this more convenient value to avoid calculating tighter memory bounds for buffers. +#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX) + + + +/*---- Functions to generate QR Codes ----*/ + +/* + * Encodes the given text string to a QR Code symbol, returning true if encoding succeeded. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * - The input text must be encoded in UTF-8 and contain no NULs. + * - The variables ecl and mask must correspond to enum constant values. + * - Requires 1 <= minVersion <= maxVersion <= 40. + * - The arrays tempBuffer and qrcode must each have a length + * of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion). + * - After the function returns, tempBuffer contains no useful data. + * - If successful, the resulting QR Code may use numeric, + * alphanumeric, or byte mode to encode the text. + * - In the most optimistic case, a QR Code at version 40 with low ECC + * can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string + * up to 4296 characters, or any digit string up to 7089 characters. + * These numbers represent the hard upper limit of the QR Code standard. + * - Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + */ +bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); + + +/* + * Encodes the given binary data to a QR Code symbol, returning true if encoding succeeded. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * - The input array range dataAndTemp[0 : dataLen] should normally be + * valid UTF-8 text, but is not required by the QR Code standard. + * - The variables ecl and mask must correspond to enum constant values. + * - Requires 1 <= minVersion <= maxVersion <= 40. + * - The arrays dataAndTemp and qrcode must each have a length + * of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion). + * - After the function returns, the contents of dataAndTemp may have changed, + * and does not represent useful data anymore. + * - If successful, the resulting QR Code will use byte mode to encode the data. + * - In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte + * sequence up to length 2953. This is the hard upper limit of the QR Code standard. + * - Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + */ +bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); + + +/* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + */ +bool qrcodegen_isAlphanumeric(const char *text); + + +/* + * Tests whether the given string can be encoded as a segment in numeric mode. + */ +bool qrcodegen_isNumeric(const char *text); + + +/* + * Returns the number of bytes (uint8_t) needed for the data buffer of a segment + * containing the given number of characters using the given mode. Notes: + * - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or + * the number of needed bits exceeds INT16_MAX (i.e. 32767). + * - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096. + * - It is okay for the user to allocate more bytes for the buffer than needed. + * - For byte mode, numChars measures the number of bytes, not Unicode code points. + * - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned. + * An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. + */ +size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars); + + +/* + * Returns a segment representing the given binary data encoded in byte mode. + */ +struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); + + +/* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]); + + +/* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]); + + +/* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ +struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); + + +/* + * Renders a QR Code symbol representing the given data segments at the given error correction + * level or higher. The smallest possible QR Code version is automatically chosen for the output. + * Returns true if QR Code creation succeeded, or false if the data is too long to fit in any version. + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. + * This function is considered to be lower level than simply encoding text or binary data. + * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will + * result in them being clobbered, but the QR Code output will still be correct. + * But the qrcode array must not overlap tempBuffer or any segment's data buffer. + */ +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Renders a QR Code symbol representing the given data segments with the given encoding parameters. + * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. + * The smallest possible QR Code version within the given range is automatically chosen for the output. + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. + * This function is considered to be lower level than simply encoding text or binary data. + * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will + * result in them being clobbered, but the QR Code output will still be correct. + * But the qrcode array must not overlap tempBuffer or any segment's data buffer. + */ +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/*---- Functions to extract raw data from QR Codes ----*/ + +/* + * Returns the side length of the given QR Code, assuming that encoding succeeded. + * The result is in the range [21, 177]. Note that the length of the array buffer + * is related to the side length - every 'uint8_t qrcode[]' must have length at least + * qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1). + */ +int qrcodegen_getSize(const uint8_t qrcode[]); + + +/* + * Returns the color of the module (pixel) at the given coordinates, which is either + * false for white or true for black. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (white) is returned. + */ +bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y);
diff --git a/src/third_party/QR-Code-generator/cpp/BitBuffer.cpp b/src/third_party/QR-Code-generator/cpp/BitBuffer.cpp new file mode 100644 index 0000000..d6e8cb2 --- /dev/null +++ b/src/third_party/QR-Code-generator/cpp/BitBuffer.cpp
@@ -0,0 +1,48 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include "BitBuffer.hpp" + + +namespace qrcodegen { + +BitBuffer::BitBuffer() + : std::vector<bool>() {} + + +std::vector<std::uint8_t> BitBuffer::getBytes() const { + std::vector<std::uint8_t> result(size() / 8 + (size() % 8 == 0 ? 0 : 1)); + for (std::size_t i = 0; i < size(); i++) + result[i >> 3] |= (*this)[i] ? 1 << (7 - (i & 7)) : 0; + return result; +} + + +void BitBuffer::appendBits(std::uint32_t val, int len) { + if (len < 0 || len > 31 || val >> len != 0) + throw "Value out of range"; + for (int i = len - 1; i >= 0; i--) // Append bit by bit + this->push_back(((val >> i) & 1) != 0); +} + +}
diff --git a/src/third_party/QR-Code-generator/cpp/BitBuffer.hpp b/src/third_party/QR-Code-generator/cpp/BitBuffer.hpp new file mode 100644 index 0000000..be00ee4 --- /dev/null +++ b/src/third_party/QR-Code-generator/cpp/BitBuffer.hpp
@@ -0,0 +1,57 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include <cstdint> +#include <vector> + + +namespace qrcodegen { + +/* + * An appendable sequence of bits (0's and 1's). + */ +class BitBuffer final : public std::vector<bool> { + + /*---- Constructor ----*/ + + // Creates an empty bit buffer (length 0). + public: BitBuffer(); + + + + /*---- Methods ----*/ + + // Packs this buffer's bits into bytes in big endian, + // padding with '0' bit values, and returns the new vector. + public: std::vector<std::uint8_t> getBytes() const; + + + // Appends the given number of low bits of the given value + // to this sequence. Requires 0 <= val < 2^len. + public: void appendBits(std::uint32_t val, int len); + +}; + +}
diff --git a/src/third_party/QR-Code-generator/cpp/Makefile b/src/third_party/QR-Code-generator/cpp/Makefile new file mode 100644 index 0000000..151a4da --- /dev/null +++ b/src/third_party/QR-Code-generator/cpp/Makefile
@@ -0,0 +1,66 @@ +# +# Makefile for QR Code generator (C++) +# +# Copyright (c) Project Nayuki. (MIT License) +# https://www.nayuki.io/page/qr-code-generator-library +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + + +# ---- Configuration options ---- + +# External/implicit variables: +# - CXX: The C++ compiler, such as g++ or clang++. +# - CXXFLAGS: Any extra user-specified compiler flags (can be blank). + +# Mandatory compiler flags +CXXFLAGS += -std=c++11 +# Diagnostics. Adding '-fsanitize=address' is helpful for most versions of Clang and newer versions of GCC. +CXXFLAGS += -Wall -fsanitize=undefined +# Optimization level +CXXFLAGS += -O1 + + +# ---- Controlling make ---- + +# Clear default suffix rules +.SUFFIXES: + +# Don't delete object files +.SECONDARY: + +# Stuff concerning goals +.DEFAULT_GOAL = all +.PHONY: all clean + + +# ---- Targets to build ---- + +LIBSRC = BitBuffer QrCode QrSegment +MAINS = QrCodeGeneratorDemo QrCodeGeneratorWorker + +# Build all binaries +all: $(MAINS) + +# Delete build output +clean: + rm -f -- $(MAINS) + +# Executable files +%: %.cpp $(LIBSRC:=.cpp) $(LIBSRC:=.hpp) + $(CXX) $(CXXFLAGS) -o $@ $< $(LIBSRC:=.cpp)
diff --git a/src/third_party/QR-Code-generator/cpp/QrCode.cpp b/src/third_party/QR-Code-generator/cpp/QrCode.cpp new file mode 100644 index 0000000..75a5473 --- /dev/null +++ b/src/third_party/QR-Code-generator/cpp/QrCode.cpp
@@ -0,0 +1,616 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include <algorithm> +#include <climits> +#include <cstddef> +#include <cstdlib> +#include <sstream> +#include <utility> +#include "BitBuffer.hpp" +#include "QrCode.hpp" + +using std::int8_t; +using std::uint8_t; +using std::size_t; +using std::vector; + + +namespace qrcodegen { + +int QrCode::getFormatBits(Ecc ecl) { + switch (ecl) { + case Ecc::LOW : return 1; + case Ecc::MEDIUM : return 0; + case Ecc::QUARTILE: return 3; + case Ecc::HIGH : return 2; + default: throw "Assertion error"; + } +} + + +QrCode QrCode::encodeText(const char *text, Ecc ecl) { + vector<QrSegment> segs = QrSegment::makeSegments(text); + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeBinary(const vector<uint8_t> &data, Ecc ecl) { + vector<QrSegment> segs{QrSegment::makeBytes(data)}; + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeSegments(const vector<QrSegment> &segs, Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl) { + if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) + throw "Invalid value"; + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = QrSegment::getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) // All versions in the range could not fit the given data + throw "Data too long"; + } + if (dataUsedBits == -1) + throw "Assertion error"; + + // Increase the error correction level while the data still fits in the current version number + for (Ecc newEcl : vector<Ecc>{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + } + + // Create the data bit string by concatenating all segments + size_t dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + BitBuffer bb; + for (const QrSegment &seg : segs) { + bb.appendBits(seg.getMode().getModeBits(), 4); + bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version)); + bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); + } + + // Add terminator and pad up to a byte if applicable + bb.appendBits(0, std::min<size_t>(4, dataCapacityBits - bb.size())); + bb.appendBits(0, (8 - bb.size() % 8) % 8); + + // Pad with alternate bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + if (bb.size() % 8 != 0) + throw "Assertion error"; + + // Create the QR Code symbol + return QrCode(version, ecl, bb.getBytes(), mask); +} + + +QrCode::QrCode(int ver, Ecc ecl, const vector<uint8_t> &dataCodewords, int mask) : + // Initialize fields + version(ver), + size(MIN_VERSION <= ver && ver <= MAX_VERSION ? ver * 4 + 17 : -1), // Avoid signed overflow undefined behavior + errorCorrectionLevel(ecl), + modules(size, vector<bool>(size)), // Entirely white grid + isFunction(size, vector<bool>(size)) { + + // Check arguments + if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) + throw "Value out of range"; + + // Draw function patterns, draw all codewords, do masking + drawFunctionPatterns(); + const vector<uint8_t> allCodewords = appendErrorCorrection(dataCodewords); + drawCodewords(allCodewords); + this->mask = handleConstructorMasking(mask); +} + + +int QrCode::getVersion() const { + return version; +} + + +int QrCode::getSize() const { + return size; +} + + +QrCode::Ecc QrCode::getErrorCorrectionLevel() const { + return errorCorrectionLevel; +} + + +int QrCode::getMask() const { + return mask; +} + + +bool QrCode::getModule(int x, int y) const { + return 0 <= x && x < size && 0 <= y && y < size && module(x, y); +} + + +std::string QrCode::toSvgString(int border) const { + if (border < 0) + throw "Border must be non-negative"; + if (border > INT_MAX / 2 || border * 2 > INT_MAX - size) + throw "Border too large"; + + std::ostringstream sb; + sb << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + sb << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"; + sb << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 "; + sb << (size + border * 2) << " " << (size + border * 2) << "\" stroke=\"none\">\n"; + sb << "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n"; + sb << "\t<path d=\""; + bool head = true; + for (int y = -border; y < size + border; y++) { + for (int x = -border; x < size + border; x++) { + if (getModule(x, y)) { + if (head) + head = false; + else + sb << " "; + sb << "M" << (x + border) << "," << (y + border) << "h1v1h-1z"; + } + } + } + sb << "\" fill=\"#000000\"/>\n"; + sb << "</svg>\n"; + return sb.str(); +} + + +void QrCode::drawFunctionPatterns() { + // Draw horizontal and vertical timing patterns + for (int i = 0; i < size; i++) { + setFunctionModule(6, i, i % 2 == 0); + setFunctionModule(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + drawFinderPattern(3, 3); + drawFinderPattern(size - 4, 3); + drawFinderPattern(3, size - 4); + + // Draw numerous alignment patterns + const vector<int> alignPatPos = getAlignmentPatternPositions(version); + int numAlign = alignPatPos.size(); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) + continue; // Skip the three finder corners + else + drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); + } + } + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); +} + + +void QrCode::drawFormatBits(int mask) { + // Calculate error correction code and pack bits + int data = getFormatBits(errorCorrectionLevel) << 3 | mask; // errCorrLvl is uint2, mask is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + data = data << 10 | rem; + data ^= 0x5412; // uint15 + if (data >> 15 != 0) + throw "Assertion error"; + + // Draw first copy + for (int i = 0; i <= 5; i++) + setFunctionModule(8, i, ((data >> i) & 1) != 0); + setFunctionModule(8, 7, ((data >> 6) & 1) != 0); + setFunctionModule(8, 8, ((data >> 7) & 1) != 0); + setFunctionModule(7, 8, ((data >> 8) & 1) != 0); + for (int i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, ((data >> i) & 1) != 0); + + // Draw second copy + for (int i = 0; i <= 7; i++) + setFunctionModule(size - 1 - i, 8, ((data >> i) & 1) != 0); + for (int i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, ((data >> i) & 1) != 0); + setFunctionModule(8, size - 8, true); +} + + +void QrCode::drawVersion() { + if (version < 7) + return; + + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long data = (long)version << 12 | rem; // uint18 + if (data >> 18 != 0) + throw "Assertion error"; + + // Draw two copies + for (int i = 0; i < 18; i++) { + bool bit = ((data >> i) & 1) != 0; + int a = size - 11 + i % 3, b = i / 3; + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } +} + + +void QrCode::drawFinderPattern(int x, int y) { + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + int dist = std::max(std::abs(i), std::abs(j)); // Chebyshev/infinity norm + int xx = x + j, yy = y + i; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } +} + + +void QrCode::drawAlignmentPattern(int x, int y) { + for (int i = -2; i <= 2; i++) { + for (int j = -2; j <= 2; j++) + setFunctionModule(x + j, y + i, std::max(std::abs(i), std::abs(j)) != 1); + } +} + + +void QrCode::setFunctionModule(int x, int y, bool isBlack) { + modules.at(y).at(x) = isBlack; + isFunction.at(y).at(x) = true; +} + + +bool QrCode::module(int x, int y) const { + return modules.at(y).at(x); +} + + +vector<uint8_t> QrCode::appendErrorCorrection(const vector<uint8_t> &data) const { + if (data.size() != static_cast<unsigned int>(getNumDataCodewords(version, errorCorrectionLevel))) + throw "Invalid argument"; + + // Calculate parameter numbers + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(errorCorrectionLevel)][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK[static_cast<int>(errorCorrectionLevel)][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockLen = rawCodewords / numBlocks; + + // Split data into blocks and append ECC to each block + vector<vector<uint8_t> > blocks; + const ReedSolomonGenerator rs(blockEccLen); + for (int i = 0, k = 0; i < numBlocks; i++) { + vector<uint8_t> dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); + k += dat.size(); + const vector<uint8_t> ecc = rs.getRemainder(dat); + if (i < numShortBlocks) + dat.push_back(0); + dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); + blocks.push_back(std::move(dat)); + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + vector<uint8_t> result; + for (int i = 0; static_cast<unsigned int>(i) < blocks.at(0).size(); i++) { + for (int j = 0; static_cast<unsigned int>(j) < blocks.size(); j++) { + // Skip the padding byte in short blocks + if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) + result.push_back(blocks.at(j).at(i)); + } + } + if (result.size() != static_cast<unsigned int>(rawCodewords)) + throw "Assertion error"; + return result; +} + + +void QrCode::drawCodewords(const vector<uint8_t> &data) { + if (data.size() != static_cast<unsigned int>(getNumRawDataModules(version) / 8)) + throw "Invalid argument"; + + size_t i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + int x = right - j; // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + int y = upward ? size - 1 - vert : vert; // Actual y coordinate + if (!isFunction.at(y).at(x) && i < data.size() * 8) { + modules.at(y).at(x) = ((data.at(i >> 3) >> (7 - (i & 7))) & 1) != 0; + i++; + } + // If there are any remainder bits (0 to 7), they are already + // set to 0/false/white when the grid of modules was initialized + } + } + } + if (static_cast<unsigned int>(i) != data.size() * 8) + throw "Assertion error"; +} + + +void QrCode::applyMask(int mask) { + if (mask < 0 || mask > 7) + throw "Mask value out of range"; + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + bool invert; + switch (mask) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: throw "Assertion error"; + } + modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); + } + } +} + + +int QrCode::handleConstructorMasking(int mask) { + if (mask == -1) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + drawFormatBits(i); + applyMask(i); + long penalty = getPenaltyScore(); + if (penalty < minPenalty) { + mask = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + if (mask < 0 || mask > 7) + throw "Assertion error"; + drawFormatBits(mask); // Overwrite old format bits + applyMask(mask); // Apply the final choice of mask + return mask; // The caller shall assign this value to the final-declared field +} + + +long QrCode::getPenaltyScore() const { + long result = 0; + + // Adjacent modules in row having same color + for (int y = 0; y < size; y++) { + bool colorX = false; + for (int x = 0, runX = -1; x < size; x++) { + if (x == 0 || module(x, y) != colorX) { + colorX = module(x, y); + runX = 1; + } else { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } + } + } + // Adjacent modules in column having same color + for (int x = 0; x < size; x++) { + bool colorY = false; + for (int y = 0, runY = -1; y < size; y++) { + if (y == 0 || module(x, y) != colorY) { + colorY = module(x, y); + runY = 1; + } else { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } + } + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + bool color = module(x, y); + if ( color == module(x + 1, y) && + color == module(x, y + 1) && + color == module(x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Finder-like pattern in rows + for (int y = 0; y < size; y++) { + for (int x = 0, bits = 0; x < size; x++) { + bits = ((bits << 1) & 0x7FF) | (module(x, y) ? 1 : 0); + if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + // Finder-like pattern in columns + for (int x = 0; x < size; x++) { + for (int y = 0, bits = 0; y < size; y++) { + bits = ((bits << 1) & 0x7FF) | (module(x, y) ? 1 : 0); + if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + + // Balance of black and white modules + int black = 0; + for (const vector<bool> &row : modules) { + for (bool color : row) { + if (color) + black++; + } + } + int total = size * size; + // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + for (int k = 0; black*20L < (9L-k)*total || black*20L > (11L+k)*total; k++) + result += PENALTY_N4; + return result; +} + + +vector<int> QrCode::getAlignmentPatternPositions(int ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw "Version number out of range"; + else if (ver == 1) + return vector<int>(); + else { + int numAlign = ver / 7 + 2; + int step; + if (ver != 32) { + // ceil((size - 13) / (2*numAlign - 2)) * 2 + step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; + } else // C-C-C-Combo breaker! + step = 26; + + vector<int> result; + for (int i = 0, pos = ver * 4 + 10; i < numAlign - 1; i++, pos -= step) + result.insert(result.begin(), pos); + result.insert(result.begin(), 6); + return result; + } +} + + +int QrCode::getNumRawDataModules(int ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw "Version number out of range"; + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 18 * 2; // Subtract version information + } + return result; +} + + +int QrCode::getNumDataCodewords(int ver, Ecc ecl) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw "Version number out of range"; + return getNumRawDataModules(ver) / 8 + - ECC_CODEWORDS_PER_BLOCK[static_cast<int>(ecl)][ver] + * NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(ecl)][ver]; +} + + +/*---- Tables of constants ----*/ + +const int QrCode::PENALTY_N1 = 3; +const int QrCode::PENALTY_N2 = 3; +const int QrCode::PENALTY_N3 = 40; +const int QrCode::PENALTY_N4 = 10; + + +const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + + +QrCode::ReedSolomonGenerator::ReedSolomonGenerator(int degree) : + coefficients() { + if (degree < 1 || degree > 255) + throw "Degree out of range"; + + // Start with the monomial x^0 + coefficients.resize(degree); + coefficients.at(degree - 1) = 1; + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (size_t j = 0; j < coefficients.size(); j++) { + coefficients.at(j) = multiply(coefficients.at(j), root); + if (j + 1 < coefficients.size()) + coefficients.at(j) ^= coefficients.at(j + 1); + } + root = multiply(root, 0x02); + } +} + + +vector<uint8_t> QrCode::ReedSolomonGenerator::getRemainder(const vector<uint8_t> &data) const { + // Compute the remainder by performing polynomial division + vector<uint8_t> result(coefficients.size()); + for (uint8_t b : data) { + uint8_t factor = b ^ result.at(0); + result.erase(result.begin()); + result.push_back(0); + for (size_t j = 0; j < result.size(); j++) + result.at(j) ^= multiply(coefficients.at(j), factor); + } + return result; +} + + +uint8_t QrCode::ReedSolomonGenerator::multiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + if (z >> 8 != 0) + throw "Assertion error"; + return static_cast<uint8_t>(z); +} + +}
diff --git a/src/third_party/QR-Code-generator/cpp/QrCode.hpp b/src/third_party/QR-Code-generator/cpp/QrCode.hpp new file mode 100644 index 0000000..14a3f61 --- /dev/null +++ b/src/third_party/QR-Code-generator/cpp/QrCode.hpp
@@ -0,0 +1,306 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include <cstdint> +#include <string> +#include <vector> +#include "QrSegment.hpp" + + +namespace qrcodegen { + +/* + * Represents an immutable square grid of black and white cells for a QR Code symbol, and + * provides static functions to create a QR Code from user-supplied textual or binary data. + * This class covers the QR Code model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and only 3 character encoding modes. + */ +class QrCode final { + + /*---- Public helper enumeration ----*/ + + /* + * Represents the error correction level used in a QR Code symbol. + */ + public: enum class Ecc { + // Constants declared in ascending order of error protection. + LOW = 0, MEDIUM = 1, QUARTILE = 2, HIGH = 3 + }; + + + // Returns a value in the range 0 to 3 (unsigned 2-bit integer). + private: static int getFormatBits(Ecc ecl); + + + + /*---- Public static factory functions ----*/ + + /* + * Returns a QR Code symbol representing the specified Unicode text string at the specified error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer + * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible + * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than + * the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeText(const char *text, Ecc ecl); + + + /* + * Returns a QR Code symbol representing the given binary data string at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeBinary(const std::vector<std::uint8_t> &data, Ecc ecl); + + + /* + * Returns a QR Code symbol representing the given data segments with the given encoding parameters. + * The smallest possible QR Code version within the given range is automatically chosen for the output. + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. + * This function is considered to be lower level than simply encoding text or binary data. + */ + public: static QrCode encodeSegments(const std::vector<QrSegment> &segs, Ecc ecl, + int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters + + + + /*---- Public constants ----*/ + + public: static constexpr int MIN_VERSION = 1; + public: static constexpr int MAX_VERSION = 40; + + + + /*---- Instance fields ----*/ + + // Immutable scalar parameters + + /* This QR Code symbol's version number, which is always between 1 and 40 (inclusive). */ + private: int version; + + /* The width and height of this QR Code symbol, measured in modules. + * Always equal to version × 4 + 17, in the range 21 to 177. */ + private: int size; + + /* The error correction level used in this QR Code symbol. */ + private: Ecc errorCorrectionLevel; + + /* The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + * Note that even if a constructor was called with automatic masking requested + * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ + private: int mask; + + // Private grids of modules/pixels (conceptually immutable) + private: std::vector<std::vector<bool> > modules; // The modules of this QR Code symbol (false = white, true = black) + private: std::vector<std::vector<bool> > isFunction; // Indicates function modules that are not subjected to masking + + + + /*---- Constructors ----*/ + + /* + * Creates a new QR Code symbol with the given version number, error correction level, binary data array, + * and mask number. This is a cumbersome low-level constructor that should not be invoked directly by the user. + * To go one level up, see the encodeSegments() function. + */ + public: QrCode(int ver, Ecc ecl, const std::vector<std::uint8_t> &dataCodewords, int mask); + + + + /*---- Public instance methods ----*/ + + public: int getVersion() const; + + + public: int getSize() const; + + + public: Ecc getErrorCorrectionLevel() const; + + + public: int getMask() const; + + + /* + * Returns the color of the module (pixel) at the given coordinates, which is either + * false for white or true for black. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (white) is returned. + */ + public: bool getModule(int x, int y) const; + + + /* + * Based on the given number of border modules to add as padding, this returns a + * string whose contents represents an SVG XML file that depicts this QR Code symbol. + * Note that Unix newlines (\n) are always used, regardless of the platform. + */ + public: std::string toSvgString(int border) const; + + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + private: void drawFunctionPatterns(); + + + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. + private: void drawFormatBits(int mask); + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field (which only has an effect for 7 <= version <= 40). + private: void drawVersion(); + + + // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + private: void drawFinderPattern(int x, int y); + + + // Draws a 5*5 alignment pattern, with the center module at (x, y). + private: void drawAlignmentPattern(int x, int y); + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in range. + private: void setFunctionModule(int x, int y, bool isBlack); + + + // Returns the color of the module at the given coordinates, which must be in range. + private: bool module(int x, int y) const; + + + /*---- Private helper methods for constructor: Codewords and masking ----*/ + + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. + private: std::vector<std::uint8_t> appendErrorCorrection(const std::vector<std::uint8_t> &data) const; + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code symbol. Function modules need to be marked off before this is called. + private: void drawCodewords(const std::vector<std::uint8_t> &data); + + + // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical + // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. + // This means it is possible to apply a mask, undo it, and try another mask. Note that a final + // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + private: void applyMask(int mask); + + + // A messy helper function for the constructors. This QR Code must be in an unmasked state when this + // method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed. + // This method applies and returns the actual mask chosen, from 0 to 7. + private: int handleConstructorMasking(int mask); + + + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + private: long getPenaltyScore() const; + + + + /*---- Private static helper functions ----*/ + + // Returns a set of positions of the alignment patterns in ascending order. These positions are + // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). + // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. + private: static std::vector<int> getAlignmentPatternPositions(int ver); + + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + private: static int getNumRawDataModules(int ver); + + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. + private: static int getNumDataCodewords(int ver, Ecc ecl); + + + /*---- Private tables of constants ----*/ + + // For use in getPenaltyScore(), when evaluating which mask is best. + private: static const int PENALTY_N1; + private: static const int PENALTY_N2; + private: static const int PENALTY_N3; + private: static const int PENALTY_N4; + + private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; + private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; + + + + /*---- Private helper class ----*/ + + /* + * Computes the Reed-Solomon error correction codewords for a sequence of data codewords + * at a given degree. Objects are immutable, and the state only depends on the degree. + * This class exists because each data block in a QR Code shares the same the divisor polynomial. + */ + private: class ReedSolomonGenerator final { + + /*-- Immutable field --*/ + + // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which + // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + private: std::vector<std::uint8_t> coefficients; + + + /*-- Constructor --*/ + + /* + * Creates a Reed-Solomon ECC generator for the given degree. This could be implemented + * as a lookup table over all possible parameter values, instead of as an algorithm. + */ + public: explicit ReedSolomonGenerator(int degree); + + + /*-- Method --*/ + + /* + * Computes and returns the Reed-Solomon error correction codewords for the given + * sequence of data codewords. The returned object is always a new byte array. + * This method does not alter this object's state (because it is immutable). + */ + public: std::vector<std::uint8_t> getRemainder(const std::vector<std::uint8_t> &data) const; + + + /*-- Static function --*/ + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). + // All inputs are valid. This could be implemented as a 256*256 lookup table. + private: static std::uint8_t multiply(std::uint8_t x, std::uint8_t y); + + }; + +}; + +}
diff --git a/src/third_party/QR-Code-generator/cpp/QrCodeGeneratorDemo.cpp b/src/third_party/QR-Code-generator/cpp/QrCodeGeneratorDemo.cpp new file mode 100644 index 0000000..5c78b51 --- /dev/null +++ b/src/third_party/QR-Code-generator/cpp/QrCodeGeneratorDemo.cpp
@@ -0,0 +1,199 @@ +/* + * QR Code generator demo (C++) + * + * Run this command-line program with no arguments. The program computes a bunch of demonstration + * QR Codes and prints them to the console. Also, the SVG code for one QR Code is printed as a sample. + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <string> +#include <vector> +#include "BitBuffer.hpp" +#include "QrCode.hpp" + +using std::uint8_t; +using qrcodegen::QrCode; +using qrcodegen::QrSegment; + + +// Function prototypes +static void doBasicDemo(); +static void doVarietyDemo(); +static void doSegmentDemo(); +static void doMaskDemo(); +static void printQr(const QrCode &qr); + + +// The main application program. +int main() { + doBasicDemo(); + doVarietyDemo(); + doSegmentDemo(); + doMaskDemo(); + return EXIT_SUCCESS; +} + + + +/*---- Demo suite ----*/ + +// Creates a single QR Code, then prints it to the console. +static void doBasicDemo() { + const char *text = "Hello, world!"; // User-supplied text + const QrCode::Ecc errCorLvl = QrCode::Ecc::LOW; // Error correction level + + // Make and print the QR Code symbol + const QrCode qr = QrCode::encodeText(text, errCorLvl); + printQr(qr); + std::cout << qr.toSvgString(4) << std::endl; +} + + +// Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console. +static void doVarietyDemo() { + // Numeric mode encoding (3.33 bits per digit) + const QrCode qr1 = QrCode::encodeText("314159265358979323846264338327950288419716939937510", QrCode::Ecc::MEDIUM); + printQr(qr1); + + // Alphanumeric mode encoding (5.5 bits per character) + const QrCode qr2 = QrCode::encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode::Ecc::HIGH); + printQr(qr2); + + // Unicode text as UTF-8 + const QrCode qr3 = QrCode::encodeText("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4", QrCode::Ecc::QUARTILE); + printQr(qr3); + + // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) + const QrCode qr4 = QrCode::encodeText( + "Alice was beginning to get very tired of sitting by her sister on the bank, " + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " + "a White Rabbit with pink eyes ran close by her.", QrCode::Ecc::HIGH); + printQr(qr4); +} + + +// Creates QR Codes with manually specified segments for better compactness. +static void doSegmentDemo() { + // Illustration "silver" + const char *silver0 = "THE SQUARE ROOT OF 2 IS 1."; + const char *silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; + const QrCode qr0 = QrCode::encodeText( + (std::string(silver0) + silver1).c_str(), + QrCode::Ecc::LOW); + printQr(qr0); + + const QrCode qr1 = QrCode::encodeSegments( + {QrSegment::makeAlphanumeric(silver0), QrSegment::makeNumeric(silver1)}, + QrCode::Ecc::LOW); + printQr(qr1); + + // Illustration "golden" + const char *golden0 = "Golden ratio \xCF\x86 = 1."; + const char *golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; + const char *golden2 = "......"; + const QrCode qr2 = QrCode::encodeText( + (std::string(golden0) + golden1 + golden2).c_str(), + QrCode::Ecc::LOW); + printQr(qr2); + + std::vector<uint8_t> bytes(golden0, golden0 + std::strlen(golden0)); + const QrCode qr3 = QrCode::encodeSegments( + {QrSegment::makeBytes(bytes), QrSegment::makeNumeric(golden1), QrSegment::makeAlphanumeric(golden2)}, + QrCode::Ecc::LOW); + printQr(qr3); + + // Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + const char *madoka = // Encoded in UTF-8 + "\xE3\x80\x8C\xE9\xAD\x94\xE6\xB3\x95\xE5" + "\xB0\x91\xE5\xA5\xB3\xE3\x81\xBE\xE3\x81" + "\xA9\xE3\x81\x8B\xE2\x98\x86\xE3\x83\x9E" + "\xE3\x82\xAE\xE3\x82\xAB\xE3\x80\x8D\xE3" + "\x81\xA3\xE3\x81\xA6\xE3\x80\x81\xE3\x80" + "\x80\xD0\x98\xD0\x90\xD0\x98\xE3\x80\x80" + "\xEF\xBD\x84\xEF\xBD\x85\xEF\xBD\x93\xEF" + "\xBD\x95\xE3\x80\x80\xCE\xBA\xCE\xB1\xEF" + "\xBC\x9F"; + const QrCode qr4 = QrCode::encodeText(madoka, QrCode::Ecc::LOW); + printQr(qr4); + + const std::vector<int> kanjiChars{ // Kanji mode encoding (13 bits per character) + 0x0035, 0x1002, 0x0FC0, 0x0AED, 0x0AD7, + 0x015C, 0x0147, 0x0129, 0x0059, 0x01BD, + 0x018D, 0x018A, 0x0036, 0x0141, 0x0144, + 0x0001, 0x0000, 0x0249, 0x0240, 0x0249, + 0x0000, 0x0104, 0x0105, 0x0113, 0x0115, + 0x0000, 0x0208, 0x01FF, 0x0008, + }; + qrcodegen::BitBuffer bb; + for (int c : kanjiChars) + bb.appendBits(c, 13); + const QrCode qr5 = QrCode::encodeSegments( + {QrSegment(QrSegment::Mode::KANJI, kanjiChars.size(), bb)}, + QrCode::Ecc::LOW); + printQr(qr5); +} + + +// Creates QR Codes with the same size and contents but different mask patterns. +static void doMaskDemo() { + // Project Nayuki URL + std::vector<QrSegment> segs0 = QrSegment::makeSegments("https://www.nayuki.io/"); + printQr(QrCode::encodeSegments(segs0, QrCode::Ecc::HIGH, QrCode::MIN_VERSION, QrCode::MAX_VERSION, -1, true)); // Automatic mask + printQr(QrCode::encodeSegments(segs0, QrCode::Ecc::HIGH, QrCode::MIN_VERSION, QrCode::MAX_VERSION, 3, true)); // Force mask 3 + + // Chinese text as UTF-8 + std::vector<QrSegment> segs1 = QrSegment::makeSegments( + "\xE7\xB6\xAD\xE5\x9F\xBA\xE7\x99\xBE\xE7\xA7\x91\xEF\xBC\x88\x57\x69\x6B\x69\x70" + "\x65\x64\x69\x61\xEF\xBC\x8C\xE8\x81\x86\xE8\x81\xBD\x69\x2F\xCB\x8C\x77\xC9\xAA" + "\x6B\xE1\xB5\xBB\xCB\x88\x70\x69\xCB\x90\x64\x69\x2E\xC9\x99\x2F\xEF\xBC\x89\xE6" + "\x98\xAF\xE4\xB8\x80\xE5\x80\x8B\xE8\x87\xAA\xE7\x94\xB1\xE5\x85\xA7\xE5\xAE\xB9" + "\xE3\x80\x81\xE5\x85\xAC\xE9\x96\x8B\xE7\xB7\xA8\xE8\xBC\xAF\xE4\xB8\x94\xE5\xA4" + "\x9A\xE8\xAA\x9E\xE8\xA8\x80\xE7\x9A\x84\xE7\xB6\xB2\xE8\xB7\xAF\xE7\x99\xBE\xE7" + "\xA7\x91\xE5\x85\xA8\xE6\x9B\xB8\xE5\x8D\x94\xE4\xBD\x9C\xE8\xA8\x88\xE7\x95\xAB"); + printQr(QrCode::encodeSegments(segs1, QrCode::Ecc::MEDIUM, QrCode::MIN_VERSION, QrCode::MAX_VERSION, 0, true)); // Force mask 0 + printQr(QrCode::encodeSegments(segs1, QrCode::Ecc::MEDIUM, QrCode::MIN_VERSION, QrCode::MAX_VERSION, 1, true)); // Force mask 1 + printQr(QrCode::encodeSegments(segs1, QrCode::Ecc::MEDIUM, QrCode::MIN_VERSION, QrCode::MAX_VERSION, 5, true)); // Force mask 5 + printQr(QrCode::encodeSegments(segs1, QrCode::Ecc::MEDIUM, QrCode::MIN_VERSION, QrCode::MAX_VERSION, 7, true)); // Force mask 7 +} + + + +/*---- Utilities ----*/ + +// Prints the given QR Code to the console. +static void printQr(const QrCode &qr) { + int border = 4; + for (int y = -border; y < qr.getSize() + border; y++) { + for (int x = -border; x < qr.getSize() + border; x++) { + std::cout << (qr.getModule(x, y) ? "##" : " "); + } + std::cout << std::endl; + } + std::cout << std::endl; +}
diff --git a/src/third_party/QR-Code-generator/cpp/QrCodeGeneratorWorker.cpp b/src/third_party/QR-Code-generator/cpp/QrCodeGeneratorWorker.cpp new file mode 100644 index 0000000..2e14607 --- /dev/null +++ b/src/third_party/QR-Code-generator/cpp/QrCodeGeneratorWorker.cpp
@@ -0,0 +1,104 @@ +/* + * QR Code generator test worker (C++) + * + * This program reads data and encoding parameters from standard input and writes + * QR Code bitmaps to standard output. The I/O format is one integer per line. + * Run with no command line arguments. The program is intended for automated + * batch testing of end-to-end functionality of this QR Code generator library. + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <vector> +#include "QrCode.hpp" + +using qrcodegen::QrCode; +using qrcodegen::QrSegment; + + +static const std::vector<QrCode::Ecc> ECC_LEVELS{ + QrCode::Ecc::LOW, + QrCode::Ecc::MEDIUM, + QrCode::Ecc::QUARTILE, + QrCode::Ecc::HIGH, +}; + + +int main() { + while (true) { + + // Read data length or exit + int length; + std::cin >> length; + if (length == -1) + break; + + // Read data bytes + bool isAscii = true; + std::vector<uint8_t> data; + for (int i = 0; i < length; i++) { + int b; + std::cin >> b; + data.push_back(static_cast<uint8_t>(b)); + isAscii &= 0 < b && b < 128; + } + + // Read encoding parameters + int errCorLvl, minVersion, maxVersion, mask, boostEcl; + std::cin >> errCorLvl; + std::cin >> minVersion; + std::cin >> maxVersion; + std::cin >> mask; + std::cin >> boostEcl; + + // Make list of segments + std::vector<QrSegment> segs; + if (isAscii) { + std::vector<char> text(data.cbegin(), data.cend()); + text.push_back('\0'); + segs = QrSegment::makeSegments(text.data()); + } else + segs.push_back(QrSegment::makeBytes(data)); + + try { // Try to make QR Code symbol + const QrCode qr = QrCode::encodeSegments(segs, + ECC_LEVELS.at(errCorLvl), minVersion, maxVersion, mask, boostEcl == 1); + // Print grid of modules + std::cout << qr.getVersion() << std::endl; + for (int y = 0; y < qr.getSize(); y++) { + for (int x = 0; x < qr.getSize(); x++) + std::cout << (qr.getModule(x, y) ? 1 : 0) << std::endl; + } + + } catch (const char *msg) { + if (strcmp(msg, "Data too long") != 0) { + std::cerr << msg << std::endl; + return EXIT_FAILURE; + } + std::cout << -1 << std::endl; + } + std::cout << std::flush; + } + return EXIT_SUCCESS; +}
diff --git a/src/third_party/QR-Code-generator/cpp/QrSegment.cpp b/src/third_party/QR-Code-generator/cpp/QrSegment.cpp new file mode 100644 index 0000000..f711461 --- /dev/null +++ b/src/third_party/QR-Code-generator/cpp/QrSegment.cpp
@@ -0,0 +1,228 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include <climits> +#include <cstring> +#include <utility> +#include "QrSegment.hpp" + +using std::uint8_t; +using std::vector; + + +namespace qrcodegen { + +QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : + modeBits(mode) { + numBitsCharCount[0] = cc0; + numBitsCharCount[1] = cc1; + numBitsCharCount[2] = cc2; +} + + +int QrSegment::Mode::getModeBits() const { + return modeBits; +} + + +int QrSegment::Mode::numCharCountBits(int ver) const { + if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; + else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; + else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; + else throw "Version number out of range"; +} + + +const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); +const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); +const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); +const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); +const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); + + + +QrSegment QrSegment::makeBytes(const vector<uint8_t> &data) { + if (data.size() > INT_MAX) + throw "Data too long"; + BitBuffer bb; + for (uint8_t b : data) + bb.appendBits(b, 8); + return QrSegment(Mode::BYTE, static_cast<int>(data.size()), std::move(bb)); +} + + +QrSegment QrSegment::makeNumeric(const char *digits) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *digits != '\0'; digits++, charCount++) { + char c = *digits; + if (c < '0' || c > '9') + throw "String contains non-numeric characters"; + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + bb.appendBits(accumData, 10); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + bb.appendBits(accumData, accumCount * 3 + 1); + return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); +} + + +QrSegment QrSegment::makeAlphanumeric(const char *text) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *text != '\0'; text++, charCount++) { + const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); + if (temp == nullptr) + throw "String contains unencodable characters in alphanumeric mode"; + accumData = accumData * 45 + (temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + bb.appendBits(accumData, 11); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + bb.appendBits(accumData, 6); + return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); +} + + +vector<QrSegment> QrSegment::makeSegments(const char *text) { + // Select the most efficient segment encoding automatically + vector<QrSegment> result; + if (*text == '\0'); // Leave result empty + else if (isNumeric(text)) + result.push_back(makeNumeric(text)); + else if (isAlphanumeric(text)) + result.push_back(makeAlphanumeric(text)); + else { + vector<uint8_t> bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast<uint8_t>(*text)); + result.push_back(makeBytes(bytes)); + } + return result; +} + + +QrSegment QrSegment::makeEci(long assignVal) { + BitBuffer bb; + if (0 <= assignVal && assignVal < (1 << 7)) + bb.appendBits(assignVal, 8); + else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(assignVal, 14); + } else if ((1 << 14) <= assignVal && assignVal < 1000000L) { + bb.appendBits(6, 3); + bb.appendBits(assignVal, 21); + } else + throw "ECI assignment value out of range"; + return QrSegment(Mode::ECI, 0, std::move(bb)); +} + + +QrSegment::QrSegment(Mode md, int numCh, const std::vector<bool> &dt) : + mode(md), + numChars(numCh), + data(dt) { + if (numCh < 0) + throw "Invalid value"; +} + + +QrSegment::QrSegment(Mode md, int numCh, std::vector<bool> &&dt) : + mode(md), + numChars(numCh), + data(std::move(dt)) { + if (numCh < 0) + throw "Invalid value"; +} + + +int QrSegment::getTotalBits(const vector<QrSegment> &segs, int version) { + if (version < 1 || version > 40) + throw "Version number out of range"; + int result = 0; + for (const QrSegment &seg : segs) { + int ccbits = seg.mode.numCharCountBits(version); + // Fail if segment length value doesn't fit in the length field's bit-width + if (seg.numChars >= (1L << ccbits)) + return -1; + if (4 + ccbits > INT_MAX - result) + return -1; + result += 4 + ccbits; + if (seg.data.size() > static_cast<unsigned int>(INT_MAX - result)) + return -1; + result += static_cast<int>(seg.data.size()); + } + return result; +} + + +bool QrSegment::isAlphanumeric(const char *text) { + for (; *text != '\0'; text++) { + if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) + return false; + } + return true; +} + + +bool QrSegment::isNumeric(const char *text) { + for (; *text != '\0'; text++) { + char c = *text; + if (c < '0' || c > '9') + return false; + } + return true; +} + + +QrSegment::Mode QrSegment::getMode() const { + return mode; +} + + +int QrSegment::getNumChars() const { + return numChars; +} + + +const std::vector<bool> &QrSegment::getData() const { + return data; +} + + +const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + +}
diff --git a/src/third_party/QR-Code-generator/cpp/QrSegment.hpp b/src/third_party/QR-Code-generator/cpp/QrSegment.hpp new file mode 100644 index 0000000..2b9eb66 --- /dev/null +++ b/src/third_party/QR-Code-generator/cpp/QrSegment.hpp
@@ -0,0 +1,186 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include <cstdint> +#include <vector> +#include "BitBuffer.hpp" + + +namespace qrcodegen { + +/* + * Represents a character string to be encoded in a QR Code symbol. Each segment has + * a mode, and a sequence of characters that is already encoded as a sequence of bits. + * Instances of this class are immutable. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +class QrSegment final { + + /*---- Public helper enumeration ----*/ + + /* + * The mode field of a segment. Immutable. Provides methods to retrieve closely related values. + */ + public: class Mode final { + + /*-- Constants --*/ + + public: static const Mode NUMERIC; + public: static const Mode ALPHANUMERIC; + public: static const Mode BYTE; + public: static const Mode KANJI; + public: static const Mode ECI; + + + /*-- Fields --*/ + + private: int modeBits; + + private: int numBitsCharCount[3]; + + + /*-- Constructor --*/ + + private: Mode(int mode, int cc0, int cc1, int cc2); + + + /*-- Methods --*/ + + /* + * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). + */ + public: int getModeBits() const; + + /* + * (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. + */ + public: int numCharCountBits(int ver) const; + + }; + + + + /*---- Public static factory functions ----*/ + + /* + * Returns a segment representing the given binary data encoded in byte mode. + */ + public: static QrSegment makeBytes(const std::vector<std::uint8_t> &data); + + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ + public: static QrSegment makeNumeric(const char *digits); + + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static QrSegment makeAlphanumeric(const char *text); + + + /* + * Returns a list of zero or more segments to represent the given text string. + * The result may use various segment modes and switch modes to optimize the length of the bit stream. + */ + public: static std::vector<QrSegment> makeSegments(const char *text); + + + /* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ + public: static QrSegment makeEci(long assignVal); + + + /*---- Public static helper functions ----*/ + + /* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + */ + public: static bool isAlphanumeric(const char *text); + + + /* + * Tests whether the given string can be encoded as a segment in numeric mode. + */ + public: static bool isNumeric(const char *text); + + + + /*---- Instance fields ----*/ + + /* The mode indicator for this segment. */ + private: Mode mode; + + /* The length of this segment's unencoded data, measured in characters. Always zero or positive. */ + private: int numChars; + + /* The data bits of this segment. */ + private: std::vector<bool> data; + + + /*---- Constructors ----*/ + + /* + * Creates a new QR Code data segment with the given parameters and data. + */ + public: QrSegment(Mode md, int numCh, const std::vector<bool> &dt); + + + /* + * Creates a new QR Code data segment with the given parameters and data. + */ + public: QrSegment(Mode md, int numCh, std::vector<bool> &&dt); + + + /*---- Methods ----*/ + + public: Mode getMode() const; + + + public: int getNumChars() const; + + + public: const std::vector<bool> &getData() const; + + + // Package-private helper function. + public: static int getTotalBits(const std::vector<QrSegment> &segs, int version); + + + /*---- Private constant ----*/ + + /* The set of all legal characters in alphanumeric mode, where each character value maps to the index in the string. */ + private: static const char *ALPHANUMERIC_CHARSET; + +}; + +}
diff --git a/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/BitBuffer.java b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/BitBuffer.java new file mode 100644 index 0000000..f43cc12 --- /dev/null +++ b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/BitBuffer.java
@@ -0,0 +1,133 @@ +/* + * QR Code generator library (Java) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import java.util.BitSet; +import java.util.Objects; + + +/** + * An appendable sequence of bits (0's and 1's). + */ +public final class BitBuffer implements Cloneable { + + /*---- Fields ----*/ + + private BitSet data; + + private int bitLength; + + + + /*---- Constructor ----*/ + + /** + * Constructs an empty bit buffer (length 0). + */ + public BitBuffer() { + data = new BitSet(); + bitLength = 0; + } + + + + /*---- Methods ----*/ + + /** + * Returns the length of this sequence, which is a non-negative value. + * @return the length of this sequence + */ + public int bitLength() { + return bitLength; + } + + + /** + * Returns the bit at the specified index, yielding 0 or 1. + * @param index the index to get the bit at + * @return the bit at the specified index + * @throws IndexOutOfBoundsException if index < 0 or index ≥ bitLength + */ + public int getBit(int index) { + if (index < 0 || index >= bitLength) + throw new IndexOutOfBoundsException(); + return data.get(index) ? 1 : 0; + } + + + /** + * Packs this buffer's bits into bytes in big endian, + * padding with '0' bit values, and returns the new array. + * @return this sequence as a new array of bytes (not {@code null}) + */ + public byte[] getBytes() { + byte[] result = new byte[(bitLength + 7) / 8]; + for (int i = 0; i < bitLength; i++) + result[i >>> 3] |= data.get(i) ? 1 << (7 - (i & 7)) : 0; + return result; + } + + + /** + * Appends the specified number of low bits of the specified value + * to this sequence. Requires 0 ≤ val < 2<sup>len</sup>. + * @param val the value to append + * @param len the number of low bits in the value to take + */ + public void appendBits(int val, int len) { + if (len < 0 || len > 31 || val >>> len != 0) + throw new IllegalArgumentException("Value out of range"); + for (int i = len - 1; i >= 0; i--, bitLength++) // Append bit by bit + data.set(bitLength, ((val >>> i) & 1) != 0); + } + + + /** + * Appends the bit data of the specified segment to this bit buffer. + * @param seg the segment whose data to append (not {@code null}) + * @throws NullPointerException if the segment is {@code null} + */ + public void appendData(QrSegment seg) { + Objects.requireNonNull(seg); + BitBuffer bb = seg.data; + for (int i = 0; i < bb.bitLength; i++, bitLength++) // Append bit by bit + data.set(bitLength, bb.data.get(i)); + } + + + /** + * Returns a copy of this bit buffer object. + * @return a copy of this bit buffer object + */ + public BitBuffer clone() { + try { + BitBuffer result = (BitBuffer)super.clone(); + result.data = (BitSet)result.data.clone(); + return result; + } catch (CloneNotSupportedException e) { + throw new AssertionError(e); + } + } + +}
diff --git a/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrCode.java b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrCode.java new file mode 100644 index 0000000..5f64ca2 --- /dev/null +++ b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrCode.java
@@ -0,0 +1,849 @@ +/* + * QR Code generator library (Java) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import java.awt.image.BufferedImage; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + + +/** + * Represents an immutable square grid of black and white cells for a QR Code symbol, and + * provides static functions to create a QR Code from user-supplied textual or binary data. + * <p>This class covers the QR Code model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and only 3 character encoding modes.</p> + */ +public final class QrCode { + + /*---- Public static factory functions ----*/ + + /** + * Returns a QR Code symbol representing the specified Unicode text string at the specified error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer + * Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible + * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the + * ecl argument if it can be done without increasing the version. + * @param text the text to be encoded, which can be any Unicode string + * @param ecl the error correction level to use (will be boosted) + * @return a QR Code representing the text + * @throws NullPointerException if the text or error correction level is {@code null} + * @throws IllegalArgumentException if the text fails to fit in the largest version QR Code, which means it is too long + */ + public static QrCode encodeText(String text, Ecc ecl) { + Objects.requireNonNull(text); + Objects.requireNonNull(ecl); + List<QrSegment> segs = QrSegment.makeSegments(text); + return encodeSegments(segs, ecl); + } + + + /** + * Returns a QR Code symbol representing the specified binary data string at the specified error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + * @param data the binary data to encode + * @param ecl the error correction level to use (will be boosted) + * @return a QR Code representing the binary data + * @throws NullPointerException if the data or error correction level is {@code null} + * @throws IllegalArgumentException if the data fails to fit in the largest version QR Code, which means it is too long + */ + public static QrCode encodeBinary(byte[] data, Ecc ecl) { + Objects.requireNonNull(data); + Objects.requireNonNull(ecl); + QrSegment seg = QrSegment.makeBytes(data); + return encodeSegments(Arrays.asList(seg), ecl); + } + + + /** + * Returns a QR Code symbol representing the specified data segments at the specified error correction + * level or higher. The smallest possible QR Code version is automatically chosen for the output. + * <p>This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. + * This function is considered to be lower level than simply encoding text or binary data.</p> + * @param segs the segments to encode + * @param ecl the error correction level to use (will be boosted) + * @return a QR Code representing the segments + * @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null} + * @throws IllegalArgumentException if the data is too long to fit in the largest version QR Code at the ECL + */ + public static QrCode encodeSegments(List<QrSegment> segs, Ecc ecl) { + return encodeSegments(segs, ecl, MIN_VERSION, MAX_VERSION, -1, true); + } + + + /** + * Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. + * The smallest possible QR Code version within the specified range is automatically chosen for the output. + * <p>This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. + * This function is considered to be lower level than simply encoding text or binary data.</p> + * @param segs the segments to encode + * @param ecl the error correction level to use (may be boosted) + * @param minVersion the minimum allowed version of the QR symbol (at least 1) + * @param maxVersion the maximum allowed version of the QR symbol (at most 40) + * @param mask the mask pattern to use, which is either -1 for automatic choice or from 0 to 7 for fixed choice + * @param boostEcl increases the error correction level if it can be done without increasing the version number + * @return a QR Code representing the segments + * @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null} + * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated, or if mask + * < −1 or mask > 7, or if the data is too long to fit in a QR Code at maxVersion at the ECL + */ + public static QrCode encodeSegments(List<QrSegment> segs, Ecc ecl, int minVersion, int maxVersion, int mask, boolean boostEcl) { + Objects.requireNonNull(segs); + Objects.requireNonNull(ecl); + if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) + throw new IllegalArgumentException("Invalid value"); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = QrSegment.getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) // All versions in the range could not fit the given data + throw new IllegalArgumentException("Data too long"); + } + if (dataUsedBits == -1) + throw new AssertionError(); + + // Increase the error correction level while the data still fits in the current version number + for (Ecc newEcl : Ecc.values()) { + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + } + + // Create the data bit string by concatenating all segments + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + BitBuffer bb = new BitBuffer(); + for (QrSegment seg : segs) { + bb.appendBits(seg.mode.modeBits, 4); + bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); + bb.appendData(seg); + } + + // Add terminator and pad up to a byte if applicable + bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength())); + bb.appendBits(0, (8 - bb.bitLength() % 8) % 8); + + // Pad with alternate bytes until data capacity is reached + for (int padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + if (bb.bitLength() % 8 != 0) + throw new AssertionError(); + + // Create the QR Code symbol + return new QrCode(version, ecl, bb.getBytes(), mask); + } + + + + /*---- Public constants ----*/ + + public static final int MIN_VERSION = 1; + public static final int MAX_VERSION = 40; + + + + /*---- Instance fields ----*/ + + // Public immutable scalar parameters + + /** This QR Code symbol's version number, which is always between 1 and 40 (inclusive). */ + public final int version; + + /** The width and height of this QR Code symbol, measured in modules. + * Always equal to version × 4 + 17, in the range 21 to 177. */ + public final int size; + + /** The error correction level used in this QR Code symbol. Never {@code null}. */ + public final Ecc errorCorrectionLevel; + + /** The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + * Note that even if a constructor was called with automatic masking requested + * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ + public final int mask; + + // Private grids of modules/pixels (conceptually immutable) + private boolean[][] modules; // The modules of this QR Code symbol (false = white, true = black) + private boolean[][] isFunction; // Indicates function modules that are not subjected to masking + + + + /*---- Constructors ----*/ + + /** + * Creates a new QR Code symbol with the specified version number, error correction level, binary data array, and mask number. + * <p>This is a cumbersome low-level constructor that should not be invoked directly by the user. + * To go one level up, see the {@link #encodeSegments(List,Ecc)} function.</p> + * @param ver the version number to use, which must be in the range 1 to 40, inclusive + * @param ecl the error correction level to use + * @param dataCodewords the raw binary user data to encode + * @param mask the mask pattern to use, which is either -1 for automatic choice or from 0 to 7 for fixed choice + * @throws NullPointerException if the byte array or error correction level is {@code null} + * @throws IllegalArgumentException if the version or mask value is out of range + */ + public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int mask) { + // Check arguments + Objects.requireNonNull(ecl); + if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) + throw new IllegalArgumentException("Value out of range"); + Objects.requireNonNull(dataCodewords); + + // Initialize fields + version = ver; + size = ver * 4 + 17; + errorCorrectionLevel = ecl; + modules = new boolean[size][size]; // Entirely white grid + isFunction = new boolean[size][size]; + + // Draw function patterns, draw all codewords, do masking + drawFunctionPatterns(); + byte[] allCodewords = appendErrorCorrection(dataCodewords); + drawCodewords(allCodewords); + this.mask = handleConstructorMasking(mask); + } + + + + /*---- Public instance methods ----*/ + + /** + * Returns the color of the module (pixel) at the specified coordinates, which is either + * false for white or true for black. The top left corner has the coordinates (x=0, y=0). + * If the specified coordinates are out of bounds, then false (white) is returned. + * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge + * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge + * @return the module's color, which is either false (white) or true (black) + */ + public boolean getModule(int x, int y) { + return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; + } + + + /** + * Returns a new image object representing this QR Code, with the specified module scale and number + * of border modules. For example, the arguments scale=10, border=4 means to pad the QR Code symbol + * with 4 white border modules on all four edges, then use 10*10 pixels to represent each module. + * The resulting image only contains the hex colors 000000 and FFFFFF. + * @param scale the module scale factor, which must be positive + * @param border the number of border modules to add, which must be non-negative + * @return an image representing this QR Code, with padding and scaling + * @throws IllegalArgumentException if the scale or border is out of range + */ + public BufferedImage toImage(int scale, int border) { + if (scale <= 0 || border < 0) + throw new IllegalArgumentException("Value out of range"); + if (border > Integer.MAX_VALUE / 2 || size + border * 2L > Integer.MAX_VALUE / scale) + throw new IllegalArgumentException("Scale or border too large"); + + BufferedImage result = new BufferedImage((size + border * 2) * scale, (size + border * 2) * scale, BufferedImage.TYPE_INT_RGB); + for (int y = 0; y < result.getHeight(); y++) { + for (int x = 0; x < result.getWidth(); x++) { + boolean val = getModule(x / scale - border, y / scale - border); + result.setRGB(x, y, val ? 0x000000 : 0xFFFFFF); + } + } + return result; + } + + + /** + * Based on the specified number of border modules to add as padding, this returns a + * string whose contents represents an SVG XML file that depicts this QR Code symbol. + * Note that Unix newlines (\n) are always used, regardless of the platform. + * @param border the number of border modules to add, which must be non-negative + * @return a string representing this QR Code as an SVG document + */ + public String toSvgString(int border) { + if (border < 0) + throw new IllegalArgumentException("Border must be non-negative"); + if (size + border * 2L > Integer.MAX_VALUE) + throw new IllegalArgumentException("Border too large"); + + StringBuilder sb = new StringBuilder(); + sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + sb.append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + sb.append(String.format( + "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 %1$d %1$d\" stroke=\"none\">\n", + size + border * 2)); + sb.append("\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n"); + sb.append("\t<path d=\""); + boolean head = true; + for (int y = -border; y < size + border; y++) { + for (int x = -border; x < size + border; x++) { + if (getModule(x, y)) { + if (head) + head = false; + else + sb.append(" "); + sb.append(String.format("M%d,%dh1v1h-1z", x + border, y + border)); + } + } + } + sb.append("\" fill=\"#000000\"/>\n"); + sb.append("</svg>\n"); + return sb.toString(); + } + + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + private void drawFunctionPatterns() { + // Draw horizontal and vertical timing patterns + for (int i = 0; i < size; i++) { + setFunctionModule(6, i, i % 2 == 0); + setFunctionModule(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + drawFinderPattern(3, 3); + drawFinderPattern(size - 4, 3); + drawFinderPattern(3, size - 4); + + // Draw numerous alignment patterns + int[] alignPatPos = getAlignmentPatternPositions(version); + int numAlign = alignPatPos.length; + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + if (i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0) + continue; // Skip the three finder corners + else + drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); + } + } + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); + } + + + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. + private void drawFormatBits(int mask) { + // Calculate error correction code and pack bits + int data = errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >>> 9) * 0x537); + data = data << 10 | rem; + data ^= 0x5412; // uint15 + if (data >>> 15 != 0) + throw new AssertionError(); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setFunctionModule(8, i, ((data >>> i) & 1) != 0); + setFunctionModule(8, 7, ((data >>> 6) & 1) != 0); + setFunctionModule(8, 8, ((data >>> 7) & 1) != 0); + setFunctionModule(7, 8, ((data >>> 8) & 1) != 0); + for (int i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, ((data >>> i) & 1) != 0); + + // Draw second copy + for (int i = 0; i <= 7; i++) + setFunctionModule(size - 1 - i, 8, ((data >>> i) & 1) != 0); + for (int i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, ((data >>> i) & 1) != 0); + setFunctionModule(8, size - 8, true); + } + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field (which only has an effect for 7 <= version <= 40). + private void drawVersion() { + if (version < 7) + return; + + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); + int data = version << 12 | rem; // uint18 + if (data >>> 18 != 0) + throw new AssertionError(); + + // Draw two copies + for (int i = 0; i < 18; i++) { + boolean bit = ((data >>> i) & 1) != 0; + int a = size - 11 + i % 3, b = i / 3; + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } + } + + + // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + private void drawFinderPattern(int x, int y) { + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + int dist = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm + int xx = x + j, yy = y + i; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } + } + + + // Draws a 5*5 alignment pattern, with the center module at (x, y). + private void drawAlignmentPattern(int x, int y) { + for (int i = -2; i <= 2; i++) { + for (int j = -2; j <= 2; j++) + setFunctionModule(x + j, y + i, Math.max(Math.abs(i), Math.abs(j)) != 1); + } + } + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in range. + private void setFunctionModule(int x, int y, boolean isBlack) { + modules[y][x] = isBlack; + isFunction[y][x] = true; + } + + + /*---- Private helper methods for constructor: Codewords and masking ----*/ + + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. + private byte[] appendErrorCorrection(byte[] data) { + if (data.length != getNumDataCodewords(version, errorCorrectionLevel)) + throw new IllegalArgumentException(); + + // Calculate parameter numbers + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal()][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK[errorCorrectionLevel.ordinal()][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockLen = rawCodewords / numBlocks; + + // Split data into blocks and append ECC to each block + byte[][] blocks = new byte[numBlocks][]; + ReedSolomonGenerator rs = new ReedSolomonGenerator(blockEccLen); + for (int i = 0, k = 0; i < numBlocks; i++) { + byte[] dat = Arrays.copyOfRange(data, k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); + byte[] block = Arrays.copyOf(dat, shortBlockLen + 1); + k += dat.length; + byte[] ecc = rs.getRemainder(dat); + System.arraycopy(ecc, 0, block, block.length - blockEccLen, ecc.length); + blocks[i] = block; + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + byte[] result = new byte[rawCodewords]; + for (int i = 0, k = 0; i < blocks[0].length; i++) { + for (int j = 0; j < blocks.length; j++) { + // Skip the padding byte in short blocks + if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) { + result[k] = blocks[j][i]; + k++; + } + } + } + return result; + } + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code symbol. Function modules need to be marked off before this is called. + private void drawCodewords(byte[] data) { + Objects.requireNonNull(data); + if (data.length != getNumRawDataModules(version) / 8) + throw new IllegalArgumentException(); + + int i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + int x = right - j; // Actual x coordinate + boolean upward = ((right + 1) & 2) == 0; + int y = upward ? size - 1 - vert : vert; // Actual y coordinate + if (!isFunction[y][x] && i < data.length * 8) { + modules[y][x] = ((data[i >>> 3] >>> (7 - (i & 7))) & 1) != 0; + i++; + } + // If there are any remainder bits (0 to 7), they are already + // set to 0/false/white when the grid of modules was initialized + } + } + } + if (i != data.length * 8) + throw new AssertionError(); + } + + + // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical + // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. + // This means it is possible to apply a mask, undo it, and try another mask. Note that a final + // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + private void applyMask(int mask) { + if (mask < 0 || mask > 7) + throw new IllegalArgumentException("Mask value out of range"); + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + boolean invert; + switch (mask) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: throw new AssertionError(); + } + modules[y][x] ^= invert & !isFunction[y][x]; + } + } + } + + + // A messy helper function for the constructors. This QR Code must be in an unmasked state when this + // method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed. + // This method applies and returns the actual mask chosen, from 0 to 7. + private int handleConstructorMasking(int mask) { + if (mask == -1) { // Automatically choose best mask + int minPenalty = Integer.MAX_VALUE; + for (int i = 0; i < 8; i++) { + drawFormatBits(i); + applyMask(i); + int penalty = getPenaltyScore(); + if (penalty < minPenalty) { + mask = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + if (mask < 0 || mask > 7) + throw new AssertionError(); + drawFormatBits(mask); // Overwrite old format bits + applyMask(mask); // Apply the final choice of mask + return mask; // The caller shall assign this value to the final-declared field + } + + + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + private int getPenaltyScore() { + int result = 0; + + // Adjacent modules in row having same color + for (int y = 0; y < size; y++) { + boolean colorX = false; + for (int x = 0, runX = 0; x < size; x++) { + if (x == 0 || modules[y][x] != colorX) { + colorX = modules[y][x]; + runX = 1; + } else { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } + } + } + // Adjacent modules in column having same color + for (int x = 0; x < size; x++) { + boolean colorY = false; + for (int y = 0, runY = 0; y < size; y++) { + if (y == 0 || modules[y][x] != colorY) { + colorY = modules[y][x]; + runY = 1; + } else { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } + } + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + boolean color = modules[y][x]; + if ( color == modules[y][x + 1] && + color == modules[y + 1][x] && + color == modules[y + 1][x + 1]) + result += PENALTY_N2; + } + } + + // Finder-like pattern in rows + for (int y = 0; y < size; y++) { + for (int x = 0, bits = 0; x < size; x++) { + bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); + if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + // Finder-like pattern in columns + for (int x = 0; x < size; x++) { + for (int y = 0, bits = 0; y < size; y++) { + bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); + if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + + // Balance of black and white modules + int black = 0; + for (boolean[] row : modules) { + for (boolean color : row) { + if (color) + black++; + } + } + int total = size * size; + // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + for (int k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) + result += PENALTY_N4; + return result; + } + + + + /*---- Private static helper functions ----*/ + + // Returns a set of positions of the alignment patterns in ascending order. These positions are + // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). + // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. + private static int[] getAlignmentPatternPositions(int ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw new IllegalArgumentException("Version number out of range"); + else if (ver == 1) + return new int[]{}; + else { + int numAlign = ver / 7 + 2; + int step; + if (ver != 32) { + // ceil((size - 13) / (2*numAlign - 2)) * 2 + step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; + } else // C-C-C-Combo breaker! + step = 26; + + int[] result = new int[numAlign]; + result[0] = 6; + for (int i = result.length - 1, pos = ver * 4 + 10; i >= 1; i--, pos -= step) + result[i] = pos; + return result; + } + } + + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + private static int getNumRawDataModules(int ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw new IllegalArgumentException("Version number out of range"); + + int size = ver * 4 + 17; + int result = size * size; // Number of modules in the whole QR symbol square + result -= 64 * 3; // Subtract the three finders with separators + result -= 15 * 2 + 1; // Subtract the format information and black module + result -= (size - 16) * 2; // Subtract the timing patterns + // The five lines above are equivalent to: int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (numAlign - 1) * (numAlign - 1) * 25; // Subtract alignment patterns not overlapping with timing patterns + result -= (numAlign - 2) * 2 * 20; // Subtract alignment patterns that overlap with timing patterns + // The two lines above are equivalent to: result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 18 * 2; // Subtract version information + } + return result; + } + + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. + static int getNumDataCodewords(int ver, Ecc ecl) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw new IllegalArgumentException("Version number out of range"); + return getNumRawDataModules(ver) / 8 + - ECC_CODEWORDS_PER_BLOCK[ecl.ordinal()][ver] + * NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal()][ver]; + } + + + /*---- Private tables of constants ----*/ + + // For use in getPenaltyScore(), when evaluating which mask is best. + private static final int PENALTY_N1 = 3; + private static final int PENALTY_N2 = 3; + private static final int PENALTY_N3 = 40; + private static final int PENALTY_N4 = 10; + + + private static final byte[][] ECC_CODEWORDS_PER_BLOCK = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High + }; + + private static final byte[][] NUM_ERROR_CORRECTION_BLOCKS = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High + }; + + + + /*---- Public helper enumeration ----*/ + + /** + * Represents the error correction level used in a QR Code symbol. + */ + public enum Ecc { + // These enum constants must be declared in ascending order of error protection, + // for the sake of the implicit ordinal() method and values() function. + LOW(1), MEDIUM(0), QUARTILE(3), HIGH(2); + + // In the range 0 to 3 (unsigned 2-bit integer). + final int formatBits; + + // Constructor. + private Ecc(int fb) { + formatBits = fb; + } + } + + + + /*---- Private helper class ----*/ + + /** + * Computes the Reed-Solomon error correction codewords for a sequence of data codewords + * at a given degree. Objects are immutable, and the state only depends on the degree. + * This class exists because each data block in a QR Code shares the same the divisor polynomial. + */ + private static final class ReedSolomonGenerator { + + /*-- Immutable field --*/ + + // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which + // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + private final byte[] coefficients; + + + /*-- Constructor --*/ + + /** + * Creates a Reed-Solomon ECC generator for the specified degree. This could be implemented + * as a lookup table over all possible parameter values, instead of as an algorithm. + * @param degree the divisor polynomial degree, which must be between 1 and 255 + * @throws IllegalArgumentException if degree < 1 or degree > 255 + */ + public ReedSolomonGenerator(int degree) { + if (degree < 1 || degree > 255) + throw new IllegalArgumentException("Degree out of range"); + + // Start with the monomial x^0 + coefficients = new byte[degree]; + coefficients[degree - 1] = 1; + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + int root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (int j = 0; j < coefficients.length; j++) { + coefficients[j] = (byte)multiply(coefficients[j] & 0xFF, root); + if (j + 1 < coefficients.length) + coefficients[j] ^= coefficients[j + 1]; + } + root = multiply(root, 0x02); + } + } + + + /*-- Method --*/ + + /** + * Computes and returns the Reed-Solomon error correction codewords for the specified + * sequence of data codewords. The returned object is always a new byte array. + * This method does not alter this object's state (because it is immutable). + * @param data the sequence of data codewords + * @return the Reed-Solomon error correction codewords + * @throws NullPointerException if the data is {@code null} + */ + public byte[] getRemainder(byte[] data) { + Objects.requireNonNull(data); + + // Compute the remainder by performing polynomial division + byte[] result = new byte[coefficients.length]; + for (byte b : data) { + int factor = (b ^ result[0]) & 0xFF; + System.arraycopy(result, 1, result, 0, result.length - 1); + result[result.length - 1] = 0; + for (int i = 0; i < result.length; i++) + result[i] ^= multiply(coefficients[i] & 0xFF, factor); + } + return result; + } + + + /*-- Static function --*/ + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result + // are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8. + private static int multiply(int x, int y) { + if (x >>> 8 != 0 || y >>> 8 != 0) + throw new IllegalArgumentException("Byte out of range"); + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >>> 7) * 0x11D); + z ^= ((y >>> i) & 1) * x; + } + if (z >>> 8 != 0) + throw new AssertionError(); + return z; + } + + } + +}
diff --git a/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java new file mode 100644 index 0000000..b05c2b2 --- /dev/null +++ b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java
@@ -0,0 +1,190 @@ +/* + * QR Code generator demo (Java) + * + * Run this command-line program with no arguments. The program creates/overwrites a bunch of + * PNG and SVG files in the current working directory to demonstrate the creation of QR Codes. + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import javax.imageio.ImageIO; + + +public final class QrCodeGeneratorDemo { + + // The main application program. + public static void main(String[] args) throws IOException { + doBasicDemo(); + doVarietyDemo(); + doSegmentDemo(); + doMaskDemo(); + } + + + + /*---- Demo suite ----*/ + + // Creates a single QR Code, then writes it to a PNG file and an SVG file. + private static void doBasicDemo() throws IOException { + String text = "Hello, world!"; // User-supplied Unicode text + QrCode.Ecc errCorLvl = QrCode.Ecc.LOW; // Error correction level + + QrCode qr = QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol + + BufferedImage img = qr.toImage(10, 4); // Convert to bitmap image + File imgFile = new File("hello-world-QR.png"); // File path for output + ImageIO.write(img, "png", imgFile); // Write image to file + + String svg = qr.toSvgString(4); // Convert to SVG XML code + try (Writer out = new OutputStreamWriter( + new FileOutputStream("hello-world-QR.svg"), + StandardCharsets.UTF_8)) { + out.write(svg); // Create/overwrite file and write SVG data + } + } + + + // Creates a variety of QR Codes that exercise different features of the library, and writes each one to file. + private static void doVarietyDemo() throws IOException { + QrCode qr; + + // Numeric mode encoding (3.33 bits per digit) + qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM); + writePng(qr.toImage(13, 1), "pi-digits-QR.png"); + + // Alphanumeric mode encoding (5.5 bits per character) + qr = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode.Ecc.HIGH); + writePng(qr.toImage(10, 2), "alphanumeric-QR.png"); + + // Unicode text as UTF-8 + qr = QrCode.encodeText("こんにちwa、世界! αβγδ", QrCode.Ecc.QUARTILE); + writePng(qr.toImage(10, 3), "unicode-QR.png"); + + // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) + qr = QrCode.encodeText( + "Alice was beginning to get very tired of sitting by her sister on the bank, " + + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " + + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " + + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " + + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " + + "a White Rabbit with pink eyes ran close by her.", QrCode.Ecc.HIGH); + writePng(qr.toImage(6, 10), "alice-wonderland-QR.png"); + } + + + // Creates QR Codes with manually specified segments for better compactness. + private static void doSegmentDemo() throws IOException { + QrCode qr; + List<QrSegment> segs; + + // Illustration "silver" + String silver0 = "THE SQUARE ROOT OF 2 IS 1."; + String silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; + qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW); + writePng(qr.toImage(10, 3), "sqrt2-monolithic-QR.png"); + + segs = Arrays.asList( + QrSegment.makeAlphanumeric(silver0), + QrSegment.makeNumeric(silver1)); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); + writePng(qr.toImage(10, 3), "sqrt2-segmented-QR.png"); + + // Illustration "golden" + String golden0 = "Golden ratio φ = 1."; + String golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; + String golden2 = "......"; + qr = QrCode.encodeText(golden0 + golden1 + golden2, QrCode.Ecc.LOW); + writePng(qr.toImage(8, 5), "phi-monolithic-QR.png"); + + segs = Arrays.asList( + QrSegment.makeBytes(golden0.getBytes(StandardCharsets.UTF_8)), + QrSegment.makeNumeric(golden1), + QrSegment.makeAlphanumeric(golden2)); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); + writePng(qr.toImage(8, 5), "phi-segmented-QR.png"); + + // Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + String madoka = "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?"; + qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); + writePng(qr.toImage(9, 4), "madoka-utf8-QR.png"); + + int[] kanjiChars = { // Kanji mode encoding (13 bits per character) + 0x0035, 0x1002, 0x0FC0, 0x0AED, 0x0AD7, + 0x015C, 0x0147, 0x0129, 0x0059, 0x01BD, + 0x018D, 0x018A, 0x0036, 0x0141, 0x0144, + 0x0001, 0x0000, 0x0249, 0x0240, 0x0249, + 0x0000, 0x0104, 0x0105, 0x0113, 0x0115, + 0x0000, 0x0208, 0x01FF, 0x0008, + }; + BitBuffer bb = new BitBuffer(); + for (int c : kanjiChars) + bb.appendBits(c, 13); + segs = Arrays.asList(new QrSegment(QrSegment.Mode.KANJI, kanjiChars.length, bb)); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); + writePng(qr.toImage(9, 4), "madoka-kanji-QR.png"); + } + + + // Creates QR Codes with the same size and contents but different mask patterns. + private static void doMaskDemo() throws IOException { + QrCode qr; + List<QrSegment> segs; + + // Project Nayuki URL + segs = QrSegment.makeSegments("https://www.nayuki.io/"); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, -1, true); // Automatic mask + writePng(qr.toImage(8, 6), "project-nayuki-automask-QR.png"); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3 + writePng(qr.toImage(8, 6), "project-nayuki-mask3-QR.png"); + + // Chinese text as UTF-8 + segs = QrSegment.makeSegments("維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫"); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 0, true); // Force mask 0 + writePng(qr.toImage(10, 3), "unicode-mask0-QR.png"); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 1, true); // Force mask 1 + writePng(qr.toImage(10, 3), "unicode-mask1-QR.png"); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 5, true); // Force mask 5 + writePng(qr.toImage(10, 3), "unicode-mask5-QR.png"); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7 + writePng(qr.toImage(10, 3), "unicode-mask7-QR.png"); + } + + + + /*---- Utilities ----*/ + + // Helper function to reduce code duplication. + private static void writePng(BufferedImage img, String filepath) throws IOException { + ImageIO.write(img, "png", new File(filepath)); + } + +}
diff --git a/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java new file mode 100644 index 0000000..e9d7ecc --- /dev/null +++ b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java
@@ -0,0 +1,103 @@ +/* + * QR Code generator test worker (Java) + * + * This program reads data and encoding parameters from standard input and writes + * QR Code bitmaps to standard output. The I/O format is one integer per line. + * Run with no command line arguments. The program is intended for automated + * batch testing of end-to-end functionality of this QR Code generator library. + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; + + +public final class QrCodeGeneratorWorker { + + public static void main(String[] args) { + // Set up input stream and start loop + try (Scanner input = new Scanner(System.in, "US-ASCII")) { + input.useDelimiter("\r\n|\n|\r"); + while (processCase(input)); + } + } + + + private static boolean processCase(Scanner input) { + // Read data length or exit + int length = input.nextInt(); + if (length == -1) + return false; + if (length > Short.MAX_VALUE) + throw new RuntimeException(); + + // Read data bytes + boolean isAscii = true; + byte[] data = new byte[length]; + for (int i = 0; i < data.length; i++) { + int b = input.nextInt(); + if (b < 0 || b > 255) + throw new RuntimeException(); + data[i] = (byte)b; + isAscii &= b < 128; + } + + // Read encoding parameters + int errCorLvl = input.nextInt(); + int minVersion = input.nextInt(); + int maxVersion = input.nextInt(); + int mask = input.nextInt(); + int boostEcl = input.nextInt(); + if (!(0 <= errCorLvl && errCorLvl <= 3) || !(-1 <= mask && mask <= 7) || (boostEcl >>> 1) != 0 + || !(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION)) + throw new RuntimeException(); + + // Make segments for encoding + List<QrSegment> segs; + if (isAscii) + segs = QrSegment.makeSegments(new String(data, StandardCharsets.US_ASCII)); + else + segs = Arrays.asList(QrSegment.makeBytes(data)); + + + try { // Try to make QR Code symbol + QrCode qr = QrCode.encodeSegments(segs, QrCode.Ecc.values()[errCorLvl], minVersion, maxVersion, mask, boostEcl != 0); + // Print grid of modules + System.out.println(qr.version); + for (int y = 0; y < qr.size; y++) { + for (int x = 0; x < qr.size; x++) + System.out.println(qr.getModule(x, y) ? 1 : 0); + } + + } catch (IllegalArgumentException e) { + if (!e.getMessage().equals("Data too long")) + throw e; + System.out.println(-1); + } + System.out.flush(); + return true; + } + +}
diff --git a/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrSegment.java b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrSegment.java new file mode 100644 index 0000000..a4e8a2a --- /dev/null +++ b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrSegment.java
@@ -0,0 +1,284 @@ +/* + * QR Code generator library (Java) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; + + +/** + * Represents a character string to be encoded in a QR Code symbol. Each segment has + * a mode, and a sequence of characters that is already encoded as a sequence of bits. + * Instances of this class are immutable. + * <p>This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes.</p> + */ +public final class QrSegment { + + /*---- Static factory functions ----*/ + + /** + * Returns a segment representing the specified binary data encoded in byte mode. + * @param data the binary data + * @return a segment containing the data + * @throws NullPointerException if the array is {@code null} + */ + public static QrSegment makeBytes(byte[] data) { + Objects.requireNonNull(data); + BitBuffer bb = new BitBuffer(); + for (byte b : data) + bb.appendBits(b & 0xFF, 8); + return new QrSegment(Mode.BYTE, data.length, bb); + } + + + /** + * Returns a segment representing the specified string of decimal digits encoded in numeric mode. + * @param digits a string consisting of digits from 0 to 9 + * @return a segment containing the data + * @throws NullPointerException if the string is {@code null} + * @throws IllegalArgumentException if the string contains non-digit characters + */ + public static QrSegment makeNumeric(String digits) { + Objects.requireNonNull(digits); + if (!NUMERIC_REGEX.matcher(digits).matches()) + throw new IllegalArgumentException("String contains non-numeric characters"); + + BitBuffer bb = new BitBuffer(); + int i; + for (i = 0; i + 3 <= digits.length(); i += 3) // Process groups of 3 + bb.appendBits(Integer.parseInt(digits.substring(i, i + 3)), 10); + int rem = digits.length() - i; + if (rem > 0) // 1 or 2 digits remaining + bb.appendBits(Integer.parseInt(digits.substring(i)), rem * 3 + 1); + return new QrSegment(Mode.NUMERIC, digits.length(), bb); + } + + + /** + * Returns a segment representing the specified text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + * @param text a string of text, with only certain characters allowed + * @return a segment containing the data + * @throws NullPointerException if the string is {@code null} + * @throws IllegalArgumentException if the string contains non-encodable characters + */ + public static QrSegment makeAlphanumeric(String text) { + Objects.requireNonNull(text); + if (!ALPHANUMERIC_REGEX.matcher(text).matches()) + throw new IllegalArgumentException("String contains unencodable characters in alphanumeric mode"); + + BitBuffer bb = new BitBuffer(); + int i; + for (i = 0; i + 2 <= text.length(); i += 2) { // Process groups of 2 + int temp = ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45; + temp += ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1)); + bb.appendBits(temp, 11); + } + if (i < text.length()) // 1 character remaining + bb.appendBits(ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6); + return new QrSegment(Mode.ALPHANUMERIC, text.length(), bb); + } + + + /** + * Returns a new mutable list of zero or more segments to represent the specified Unicode text string. + * The result may use various segment modes and switch modes to optimize the length of the bit stream. + * @param text the text to be encoded, which can be any Unicode string + * @return a list of segments containing the text + * @throws NullPointerException if the text is {@code null} + */ + public static List<QrSegment> makeSegments(String text) { + Objects.requireNonNull(text); + + // Select the most efficient segment encoding automatically + List<QrSegment> result = new ArrayList<>(); + if (text.equals("")); // Leave result empty + else if (NUMERIC_REGEX.matcher(text).matches()) + result.add(makeNumeric(text)); + else if (ALPHANUMERIC_REGEX.matcher(text).matches()) + result.add(makeAlphanumeric(text)); + else + result.add(makeBytes(text.getBytes(StandardCharsets.UTF_8))); + return result; + } + + + /** + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the specified assignment value. + * @param assignVal the ECI assignment number (see the AIM ECI specification) + * @return a segment containing the data + * @throws IllegalArgumentException if the value is outside the range [0, 10<sup>6</sup>) + */ + public static QrSegment makeEci(int assignVal) { + BitBuffer bb = new BitBuffer(); + if (0 <= assignVal && assignVal < (1 << 7)) + bb.appendBits(assignVal, 8); + else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(assignVal, 14); + } else if ((1 << 14) <= assignVal && assignVal < 1000000) { + bb.appendBits(6, 3); + bb.appendBits(assignVal, 21); + } else + throw new IllegalArgumentException("ECI assignment value out of range"); + return new QrSegment(Mode.ECI, 0, bb); + } + + + + /*---- Instance fields ----*/ + + /** The mode indicator for this segment. Never {@code null}. */ + public final Mode mode; + + /** The length of this segment's unencoded data, measured in characters. Always zero or positive. */ + public final int numChars; + + /** The data bits of this segment. Accessed through {@link getBits()}. Not {@code null}. */ + final BitBuffer data; + + + /*---- Constructor ----*/ + + /** + * Creates a new QR Code data segment with the specified parameters and data. + * @param md the mode, which is not {@code null} + * @param numCh the data length in characters, which is non-negative + * @param data the data bits of this segment, which is not {@code null} + * @throws NullPointerException if the mode or bit buffer is {@code null} + * @throws IllegalArgumentException if the character count is negative + */ + public QrSegment(Mode md, int numCh, BitBuffer data) { + Objects.requireNonNull(md); + Objects.requireNonNull(data); + if (numCh < 0) + throw new IllegalArgumentException("Invalid value"); + mode = md; + numChars = numCh; + this.data = data.clone(); // Make defensive copy + } + + + /*---- Methods ----*/ + + /** + * Returns the data bits of this segment. + * @return the data bits of this segment (not {@code null}) + */ + public BitBuffer getBits() { + return data.clone(); // Make defensive copy + } + + + // Package-private helper function. + static int getTotalBits(List<QrSegment> segs, int version) { + Objects.requireNonNull(segs); + if (version < 1 || version > 40) + throw new IllegalArgumentException("Version number out of range"); + + long result = 0; + for (QrSegment seg : segs) { + Objects.requireNonNull(seg); + int ccbits = seg.mode.numCharCountBits(version); + // Fail if segment length value doesn't fit in the length field's bit-width + if (seg.numChars >= (1 << ccbits)) + return -1; + result += 4L + ccbits + seg.data.bitLength(); + if (result > Integer.MAX_VALUE) + return -1; + } + return (int)result; + } + + + /*---- Constants ----*/ + + /** Can test whether a string is encodable in numeric mode (such as by using {@link #makeNumeric(String)}). */ + public static final Pattern NUMERIC_REGEX = Pattern.compile("[0-9]*"); + + /** Can test whether a string is encodable in alphanumeric mode (such as by using {@link #makeAlphanumeric(String)}). */ + public static final Pattern ALPHANUMERIC_REGEX = Pattern.compile("[A-Z0-9 $%*+./:-]*"); + + /** The set of all legal characters in alphanumeric mode, where each character value maps to the index in the string. */ + private static final String ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + + + + /*---- Public helper enumeration ----*/ + + /** + * The mode field of a segment. Immutable. Provides methods to retrieve closely related values. + */ + public enum Mode { + + /*-- Constants --*/ + + NUMERIC (0x1, 10, 12, 14), + ALPHANUMERIC(0x2, 9, 11, 13), + BYTE (0x4, 8, 16, 16), + KANJI (0x8, 8, 10, 12), + ECI (0x7, 0, 0, 0); + + + /*-- Fields --*/ + + /** An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. */ + final int modeBits; + + private final int[] numBitsCharCount; + + + /*-- Constructor --*/ + + private Mode(int mode, int... ccbits) { + this.modeBits = mode; + numBitsCharCount = ccbits; + } + + + /*-- Method --*/ + + /** + * Returns the bit width of the segment character count field for this mode object at the specified version number. + * @param ver the version number, which is between 1 to 40, inclusive + * @return the number of bits for the character count, which is between 8 to 16, inclusive + * @throws IllegalArgumentException if the version number is out of range + */ + int numCharCountBits(int ver) { + if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; + else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; + else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; + else throw new IllegalArgumentException("Version number out of range"); + } + + } + +}
diff --git a/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java new file mode 100644 index 0000000..34847e4 --- /dev/null +++ b/src/third_party/QR-Code-generator/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java
@@ -0,0 +1,402 @@ +/* + * QR Code generator library - Optional advanced logic (Java) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import static io.nayuki.qrcodegen.QrSegment.Mode.ALPHANUMERIC; +import static io.nayuki.qrcodegen.QrSegment.Mode.BYTE; +import static io.nayuki.qrcodegen.QrSegment.Mode.NUMERIC; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.Objects; + + +public final class QrSegmentAdvanced { + + /*---- Optimal list of segments encoder ----*/ + + /** + * Returns a new mutable list of zero or more segments to represent the specified Unicode text string. + * The resulting list optimally minimizes the total encoded bit length, subjected to the constraints given + * by the specified {error correction level, minimum version number, maximum version number}, plus the additional + * constraint that the segment modes {NUMERIC, ALPHANUMERIC, BYTE} can be used but KANJI cannot be used. + * <p>This function can be viewed as a significantly more sophisticated and slower replacement + * for {@link QrSegment#makeSegments(String)}, but requiring more input parameters in a way + * that overlaps with {@link QrCode#encodeSegments(List,QrCode.Ecc,int,int,int,boolean)}.</p> + * @param text the text to be encoded, which can be any Unicode string + * @param ecl the error correction level to use + * @param minVersion the minimum allowed version of the QR symbol (at least 1) + * @param maxVersion the maximum allowed version of the QR symbol (at most 40) + * @return a list of segments containing the text, minimizing the bit length with respect to the constraints + * @throws NullPointerException if the data or error correction level is {@code null} + * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated, + * or if the data is too long to fit in a QR Code at maxVersion at the ECL + */ + public static List<QrSegment> makeSegmentsOptimally(String text, QrCode.Ecc ecl, int minVersion, int maxVersion) { + // Check arguments + Objects.requireNonNull(text); + Objects.requireNonNull(ecl); + if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40)) + throw new IllegalArgumentException("Invalid value"); + + // Iterate through version numbers, and make tentative segments + List<QrSegment> segs = null; + for (int version = minVersion; version <= maxVersion; version++) { + if (version == minVersion || version == 10 || version == 27) + segs = makeSegmentsOptimally(text, version); + + // Check if the segments fit + int dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; + int dataUsedBits = QrSegment.getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + return segs; + } + throw new IllegalArgumentException("Data too long"); + } + + + // Returns a list of segments that is optimal for the given text at the given version number. + private static List<QrSegment> makeSegmentsOptimally(String text, int version) { + byte[] data = text.getBytes(StandardCharsets.UTF_8); + int[][] bitCosts = computeBitCosts(data, version); + QrSegment.Mode[] charModes = computeCharacterModes(data, version, bitCosts); + return splitIntoSegments(data, charModes); + } + + + private static int[][] computeBitCosts(byte[] data, int version) { + // Segment header sizes, measured in 1/6 bits + int bytesCost = (4 + BYTE .numCharCountBits(version)) * 6; + int alphnumCost = (4 + ALPHANUMERIC.numCharCountBits(version)) * 6; + int numberCost = (4 + NUMERIC .numCharCountBits(version)) * 6; + + // result[mode][len] is the number of 1/6 bits to encode the first len characters of the text, ending in the mode + int[][] result = new int[3][data.length + 1]; + Arrays.fill(result[1], Integer.MAX_VALUE / 2); + Arrays.fill(result[2], Integer.MAX_VALUE / 2); + result[0][0] = bytesCost; + result[1][0] = alphnumCost; + result[2][0] = numberCost; + + // Calculate the cost table using dynamic programming + for (int i = 0; i < data.length; i++) { + // Encode a character + int j = i + 1; + char c = (char)data[i]; + result[0][j] = result[0][i] + 48; // 8 bits per byte + if (isAlphanumeric(c)) + result[1][j] = result[1][i] + 33; // 5.5 bits per alphanumeric char + if (isNumeric(c)) + result[2][j] = result[2][i] + 20; // 3.33 bits per digit + + // Switch modes, rounding up fractional bits + result[0][j] = Math.min((Math.min(result[1][j], result[2][j]) + 5) / 6 * 6 + bytesCost , result[0][j]); + result[1][j] = Math.min((Math.min(result[2][j], result[0][j]) + 5) / 6 * 6 + alphnumCost, result[1][j]); + result[2][j] = Math.min((Math.min(result[0][j], result[1][j]) + 5) / 6 * 6 + numberCost , result[2][j]); + } + return result; + } + + + private static QrSegment.Mode[] computeCharacterModes(byte[] data, int version, int[][] bitCosts) { + // Segment header sizes, measured in 1/6 bits + int bytesCost = (4 + BYTE .numCharCountBits(version)) * 6; + int alphnumCost = (4 + ALPHANUMERIC.numCharCountBits(version)) * 6; + int numberCost = (4 + NUMERIC .numCharCountBits(version)) * 6; + + // Infer the mode used for last character by taking the minimum + QrSegment.Mode curMode; + int end = bitCosts[0].length - 1; + if (bitCosts[0][end] <= Math.min(bitCosts[1][end], bitCosts[2][end])) + curMode = BYTE; + else if (bitCosts[1][end] <= bitCosts[2][end]) + curMode = ALPHANUMERIC; + else + curMode = NUMERIC; + + // Work backwards to calculate optimal encoding mode for each character + QrSegment.Mode[] result = new QrSegment.Mode[data.length]; + if (data.length == 0) + return result; + result[data.length - 1] = curMode; + for (int i = data.length - 2; i >= 0; i--) { + char c = (char)data[i]; + if (curMode == NUMERIC) { + if (isNumeric(c)) + curMode = NUMERIC; + else if (isAlphanumeric(c) && (bitCosts[1][i] + 33 + 5) / 6 * 6 + numberCost == bitCosts[2][i + 1]) + curMode = ALPHANUMERIC; + else + curMode = BYTE; + } else if (curMode == ALPHANUMERIC) { + if (isNumeric(c) && (bitCosts[2][i] + 20 + 5) / 6 * 6 + alphnumCost == bitCosts[1][i + 1]) + curMode = NUMERIC; + else if (isAlphanumeric(c)) + curMode = ALPHANUMERIC; + else + curMode = BYTE; + } else if (curMode == BYTE) { + if (isNumeric(c) && (bitCosts[2][i] + 20 + 5) / 6 * 6 + bytesCost == bitCosts[0][i + 1]) + curMode = NUMERIC; + else if (isAlphanumeric(c) && (bitCosts[1][i] + 33 + 5) / 6 * 6 + bytesCost == bitCosts[0][i + 1]) + curMode = ALPHANUMERIC; + else + curMode = BYTE; + } else + throw new AssertionError(); + result[i] = curMode; + } + return result; + } + + + private static List<QrSegment> splitIntoSegments(byte[] data, QrSegment.Mode[] charModes) { + List<QrSegment> result = new ArrayList<>(); + if (data.length == 0) + return result; + + // Accumulate run of modes + QrSegment.Mode curMode = charModes[0]; + int start = 0; + for (int i = 1; i < data.length; i++) { + if (charModes[i] != curMode) { + if (curMode == BYTE) + result.add(QrSegment.makeBytes(Arrays.copyOfRange(data, start, i))); + else { + String temp = new String(data, start, i - start, StandardCharsets.US_ASCII); + if (curMode == NUMERIC) + result.add(QrSegment.makeNumeric(temp)); + else if (curMode == ALPHANUMERIC) + result.add(QrSegment.makeAlphanumeric(temp)); + else + throw new AssertionError(); + } + curMode = charModes[i]; + start = i; + } + } + + // Final segment + if (curMode == BYTE) + result.add(QrSegment.makeBytes(Arrays.copyOfRange(data, start, data.length))); + else { + String temp = new String(data, start, data.length - start, StandardCharsets.US_ASCII); + if (curMode == NUMERIC) + result.add(QrSegment.makeNumeric(temp)); + else if (curMode == ALPHANUMERIC) + result.add(QrSegment.makeAlphanumeric(temp)); + else + throw new AssertionError(); + } + return result; + } + + + private static boolean isAlphanumeric(char c) { + return isNumeric(c) || 'A' <= c && c <= 'Z' || " $%*+./:-".indexOf(c) != -1; + } + + private static boolean isNumeric(char c) { + return '0' <= c && c <= '9'; + } + + + /*---- Kanji mode segment encoder ----*/ + + /** + * Returns a segment representing the specified string encoded in kanji mode. + * <p>Note that broadly speaking, the set of encodable characters are {kanji used in Japan, hiragana, katakana, + * Asian punctuation, full-width ASCII}.<br/> + * In particular, non-encodable characters are {normal ASCII, half-width katakana, more extensive Chinese hanzi}. + * @param text the text to be encoded, which must fall in the kanji mode subset of characters + * @return a segment containing the data + * @throws NullPointerException if the string is {@code null} + * @throws IllegalArgumentException if the string contains non-kanji-mode characters + * @see #isEncodableAsKanji(String) + */ + public static QrSegment makeKanjiSegment(String text) { + Objects.requireNonNull(text); + BitBuffer bb = new BitBuffer(); + for (int i = 0; i < text.length(); i++) { + int val = UNICODE_TO_QR_KANJI[text.charAt(i)]; + if (val == -1) + throw new IllegalArgumentException("String contains non-kanji-mode characters"); + bb.appendBits(val, 13); + } + return new QrSegment(QrSegment.Mode.KANJI, text.length(), bb); + } + + + /** + * Tests whether the specified text string can be encoded as a segment in kanji mode. + * <p>Note that broadly speaking, the set of encodable characters are {kanji used in Japan, hiragana, katakana, + * Asian punctuation, full-width ASCII}.<br/> + * In particular, non-encodable characters are {normal ASCII, half-width katakana, more extensive Chinese hanzi}. + * @param text the string to test for encodability + * @return {@code true} if and only if the string can be encoded in kanji mode + * @throws NullPointerException if the string is {@code null} + * @see #makeKanjiSegment(String) + */ + public static boolean isEncodableAsKanji(String text) { + Objects.requireNonNull(text); + for (int i = 0; i < text.length(); i++) { + if (UNICODE_TO_QR_KANJI[text.charAt(i)] == -1) + return false; + } + return true; + } + + + // Data derived from ftp://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT + private static final String PACKED_QR_KANJI_TO_UNICODE = + "MAAwATAC/wz/DjD7/xr/G/8f/wEwmzCcALT/QACo/z7/4/8/MP0w/jCdMJ4wA07dMAUwBjAHMPwgFSAQ/w8AXDAcIBb/XCAmICUgGCAZIBwgHf8I/wkwFDAV/zv/Pf9b/10wCDAJMAowCzAMMA0wDjAPMBAwEf8LIhIAsQDX//8A9/8dImD/HP8eImYiZyIeIjQmQiZA" + + "ALAgMiAzIQP/5f8EAKIAo/8F/wP/Bv8K/yAApyYGJgUlyyXPJc4lxyXGJaEloCWzJbIlvSW8IDswEiGSIZAhkSGTMBP/////////////////////////////IggiCyKGIocigiKDIioiKf////////////////////8iJyIoAKwh0iHUIgAiA///////////////////" + + "//////////8iICKlIxIiAiIHImEiUiJqImsiGiI9Ih0iNSIrIiz//////////////////yErIDAmbyZtJmogICAhALb//////////yXv/////////////////////////////////////////////////xD/Ef8S/xP/FP8V/xb/F/8Y/xn///////////////////8h" + + "/yL/I/8k/yX/Jv8n/yj/Kf8q/yv/LP8t/y7/L/8w/zH/Mv8z/zT/Nf82/zf/OP85/zr///////////////////9B/0L/Q/9E/0X/Rv9H/0j/Sf9K/0v/TP9N/07/T/9Q/1H/Uv9T/1T/Vf9W/1f/WP9Z/1r//////////zBBMEIwQzBEMEUwRjBHMEgwSTBKMEswTDBN" + + "ME4wTzBQMFEwUjBTMFQwVTBWMFcwWDBZMFowWzBcMF0wXjBfMGAwYTBiMGMwZDBlMGYwZzBoMGkwajBrMGwwbTBuMG8wcDBxMHIwczB0MHUwdjB3MHgweTB6MHswfDB9MH4wfzCAMIEwgjCDMIQwhTCGMIcwiDCJMIowizCMMI0wjjCPMJAwkTCSMJP/////////////" + + "////////////////////////MKEwojCjMKQwpTCmMKcwqDCpMKowqzCsMK0wrjCvMLAwsTCyMLMwtDC1MLYwtzC4MLkwujC7MLwwvTC+ML8wwDDBMMIwwzDEMMUwxjDHMMgwyTDKMMswzDDNMM4wzzDQMNEw0jDTMNQw1TDWMNcw2DDZMNow2zDcMN0w3jDf//8w4DDh" + + "MOIw4zDkMOUw5jDnMOgw6TDqMOsw7DDtMO4w7zDwMPEw8jDzMPQw9TD2/////////////////////wORA5IDkwOUA5UDlgOXA5gDmQOaA5sDnAOdA54DnwOgA6EDowOkA6UDpgOnA6gDqf////////////////////8DsQOyA7MDtAO1A7YDtwO4A7kDugO7A7wDvQO+" + + "A78DwAPBA8MDxAPFA8YDxwPIA8n/////////////////////////////////////////////////////////////////////////////////////////////////////////////BBAEEQQSBBMEFAQVBAEEFgQXBBgEGQQaBBsEHAQdBB4EHwQgBCEEIgQjBCQEJQQm" + + "BCcEKAQpBCoEKwQsBC0ELgQv////////////////////////////////////////BDAEMQQyBDMENAQ1BFEENgQ3BDgEOQQ6BDsEPAQ9//8EPgQ/BEAEQQRCBEMERARFBEYERwRIBEkESgRLBEwETQROBE///////////////////////////////////yUAJQIlDCUQ" + + "JRglFCUcJSwlJCU0JTwlASUDJQ8lEyUbJRclIyUzJSslOyVLJSAlLyUoJTclPyUdJTAlJSU4JUL/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "/////////////////////////////////////06cVRZaA5Y/VMBhG2MoWfaQIoR1gxx6UGCqY+FuJWXthGaCppv1aJNXJ2WhYnFbm1nQhnuY9H1ifb6bjmIWfJ+It1uJXrVjCWaXaEiVx5eNZ09O5U8KT01PnVBJVvJZN1nUWgFcCWDfYQ9hcGYTaQVwunVPdXB5+32t" + + "fe+Aw4QOiGOLApBVkHpTO06VTqVX34CykMF4704AWPFuopA4ejKDKIKLnC9RQVNwVL1U4VbgWftfFZjybeuA5IUt////////lmKWcJagl/tUC1PzW4dwz3+9j8KW6FNvnVx6uk4ReJOB/G4mVhhVBGsdhRqcO1nlU6ltZnTclY9WQk6RkEuW8oNPmQxT4VW2WzBfcWYg" + + "ZvNoBGw4bPNtKXRbdsh6Tpg0gvGIW4pgku1tsnWrdsqZxWCmiwGNipWyaY5TrVGG//9XElgwWURbtF72YChjqWP0bL9vFHCOcRRxWXHVcz9+AYJ2gtGFl5BgkludG1hpZbxsWnUlUflZLlllX4Bf3GK8ZfpqKmsna7Rzi3/BiVadLJ0OnsRcoWyWg3tRBFxLYbaBxmh2" + + "cmFOWU/6U3hgaW4pek+X804LUxZO7k9VTz1PoU9zUqBT71YJWQ9awVu2W+F50WaHZ5xntmtMbLNwa3PCeY15vno8e4eCsYLbgwSDd4Pvg9OHZoqyVimMqI/mkE6XHoaKT8Rc6GIRcll1O4Hlgr2G/ozAlsWZE5nVTstPGonjVt5YSljKXvtf62AqYJRgYmHQYhJi0GU5" + + "////////m0FmZmiwbXdwcHVMdoZ9dYKlh/mVi5aOjJ1R8VK+WRZUs1uzXRZhaGmCba94jYTLiFeKcpOnmrhtbJmohtlXo2f/hs6SDlKDVodUBF7TYuFkuWg8aDhru3NyeLp6a4maidKNa48DkO2Vo5aUl2lbZlyzaX2YTZhOY5t7IGor//9qf2i2nA1vX1JyVZ1gcGLs" + + "bTtuB27RhFuJEI9EThScOVP2aRtqOpeEaCpRXHrDhLKR3JOMVludKGgigwWEMXylUgiCxXTmTn5Pg1GgW9JSClLYUudd+1WaWCpZ5luMW5hb215yXnlgo2EfYWNhvmPbZWJn0WhTaPprPmtTbFdvIm+Xb0V0sHUYduN3C3r/e6F8IX3pfzZ/8ICdgmaDnomzisyMq5CE" + + "lFGVk5WRlaKWZZfTmSiCGE44VCtcuF3Mc6l2THc8XKl/640LlsGYEZhUmFhPAU8OU3FVnFZoV/pZR1sJW8RckF4MXn5fzGPuZzpl12XiZx9oy2jE////////al9eMGvFbBdsfXV/eUhbY3oAfQBfvYmPihiMtI13jsyPHZjimg6bPE6AUH1RAFmTW5xiL2KAZOxrOnKg" + + "dZF5R3+ph/uKvItwY6yDypegVAlUA1WraFRqWIpweCdndZ7NU3RbooEahlCQBk4YTkVOx08RU8pUOFuuXxNgJWVR//9nPWxCbHJs43B4dAN6dnquewh9Gnz+fWZl53JbU7tcRV3oYtJi4GMZbiCGWooxjd2S+G8BeaabWk6oTqtOrE+bT6BQ0VFHevZRcVH2U1RTIVN/" + + "U+tVrFiDXOFfN19KYC9gUGBtYx9lWWpLbMFywnLtd++A+IEFggiFTpD3k+GX/5lXmlpO8FHdXC1mgWltXEBm8ml1c4loUHyBUMVS5FdHXf6TJmWkayNrPXQ0eYF5vXtLfcqCuYPMiH+JX4s5j9GR0VQfkoBOXVA2U+VTOnLXc5Z36YLmjq+ZxpnImdJRd2Eahl5VsHp6" + + "UHZb05BHloVOMmrbkedcUVxI////////Y5h6n2yTl3SPYXqqcYqWiHyCaBd+cGhRk2xS8lQbhauKE3+kjs2Q4VNmiIh5QU/CUL5SEVFEVVNXLXPqV4tZUV9iX4RgdWF2YWdhqWOyZDplbGZvaEJuE3Vmej18+31MfZl+S39rgw6DSobNigiKY4tmjv2YGp2PgriPzpvo" + + "//9Sh2IfZINvwJaZaEFQkWsgbHpvVHp0fVCIQIojZwhO9lA5UCZQZVF8UjhSY1WnVw9YBVrMXvphsmH4YvNjcmkcailyfXKscy54FHhvfXl3DICpiYuLGYzijtKQY5N1lnqYVZoTnnhRQ1OfU7Nee18mbhtukHOEc/59Q4I3igCK+pZQTk5QC1PkVHxW+lnRW2Rd8V6r" + + "XydiOGVFZ69uVnLQfMqItIChgOGD8IZOioeN6JI3lseYZ58TTpROkk8NU0hUSVQ+Wi9fjF+hYJ9op2qOdFp4gYqeiqSLd5GQTl6byU6kT3xPr1AZUBZRSVFsUp9SuVL+U5pT41QR////////VA5ViVdRV6JZfVtUW11bj13lXedd9154XoNeml63XxhgUmFMYpdi2GOn" + + "ZTtmAmZDZvRnbWghaJdpy2xfbSptaW4vbp11MnaHeGx6P3zgfQV9GH1efbGAFYADgK+AsYFUgY+CKoNSiEyIYYsbjKKM/JDKkXWScXg/kvyVpJZN//+YBZmZmtidO1JbUqtT91QIWNVi92/gjGqPX565UUtSO1RKVv16QJF3nWCe0nNEbwmBcHURX/1g2pqoctuPvGtk" + + "mANOylbwV2RYvlpaYGhhx2YPZgZoOWixbfd11X06gm6bQk6bT1BTyVUGXW9d5l3uZ/tsmXRzeAKKUJOWiN9XUF6nYytQtVCsUY1nAFTJWF5Zu1uwX2liTWOhaD1rc24IcH2Rx3KAeBV4JnltZY59MIPciMGPCZabUmRXKGdQf2qMoVG0V0KWKlg6aYqAtFSyXQ5X/HiV" + + "nfpPXFJKVItkPmYoZxRn9XqEe1Z9IpMvaFybrXs5UxlRilI3////////W99i9mSuZOZnLWu6hamW0XaQm9ZjTJMGm6t2v2ZSTglQmFPCXHFg6GSSZWNoX3Hmc8p1I3uXfoKGlYuDjNuReJkQZaxmq2uLTtVO1E86T39SOlP4U/JV41bbWOtZy1nJWf9bUFxNXgJeK1/X" + + "YB1jB2UvW1xlr2W9ZehnnWti//9re2wPc0V5SXnBfPh9GX0rgKKBAoHziZaKXoppimaKjIrujMeM3JbMmPxrb06LTzxPjVFQW1db+mFIYwFmQmshbstsu3I+dL111HjBeTqADIAzgeqElI+ebFCef18Pi1idK3r6jvhbjZbrTgNT8Vf3WTFayVukYIluf28Gdb6M6luf" + + "hQB74FByZ/SCnVxhhUp+HoIOUZlcBGNojWZlnHFueT59F4AFix2OypBuhseQqlAfUvpcOmdTcHxyNZFMkciTK4LlW8JfMWD5TjtT1luIYktnMWuKculz4HougWuNo5FSmZZRElPXVGpb/2OIajl9rJcAVtpTzlRo////////W5dcMV3eT+5hAWL+bTJ5wHnLfUJ+TX/S" + + "ge2CH4SQiEaJcouQjnSPL5AxkUuRbJbGkZxOwE9PUUVTQV+TYg5n1GxBbgtzY34mkc2Sg1PUWRlbv23ReV1+LnybWH5xn1H6iFOP8E/KXPtmJXeseuOCHJn/UcZfqmXsaW9riW3z//9ulm9kdv59FF3hkHWRh5gGUeZSHWJAZpFm2W4aXrZ90n9yZviFr4X3ivhSqVPZ" + + "WXNej1+QYFWS5JZkULdRH1LdUyBTR1PsVOhVRlUxVhdZaFm+WjxbtVwGXA9cEVwaXoReil7gX3Bif2KEYttjjGN3ZgdmDGYtZnZnfmiiah9qNWy8bYhuCW5YcTxxJnFndcd3AXhdeQF5ZXnweuB7EXynfTmAloPWhIuFSYhdiPOKH4o8ilSKc4xhjN6RpJJmk36UGJac" + + "l5hOCk4ITh5OV1GXUnBXzlg0WMxbIl44YMVk/mdhZ1ZtRHK2dXN6Y4S4i3KRuJMgVjFX9Jj+////////Yu1pDWuWce1+VIB3gnKJ5pjfh1WPsVw7TzhP4U+1VQdaIFvdW+lfw2FOYy9lsGZLaO5pm214bfF1M3W5dx95XnnmfTOB44KvhaqJqoo6jquPm5Aykd2XB066" + + "TsFSA1h1WOxcC3UaXD2BTooKj8WWY5dteyWKz5gIkWJW81Oo//+QF1Q5V4JeJWOobDRwindhfIt/4IhwkEKRVJMQkxiWj3RemsRdB11pZXBnoo2olttjbmdJaRmDxZgXlsCI/m+EZHpb+E4WcCx1XWYvUcRSNlLiWdNfgWAnYhBlP2V0Zh9mdGjyaBZrY24FcnJ1H3bb" + + "fL6AVljwiP2Jf4qgipOKy5AdkZKXUpdZZYl6DoEGlrteLWDcYhplpWYUZ5B383pNfE1+PoEKjKyNZI3hjl94qVIHYtljpWRCYpiKLXqDe8CKrJbqfXaCDIdJTtlRSFNDU2Bbo1wCXBZd3WImYkdksGgTaDRsyW1FbRdn029ccU5xfWXLen97rX3a////////fkp/qIF6" + + "ghuCOYWmim6Mzo31kHiQd5KtkpGVg5uuUk1VhG84cTZRaHmFflWBs3zOVkxYUVyoY6pm/mb9aVpy2XWPdY55DnlWed98l30gfUSGB4o0ljuQYZ8gUOdSdVPMU+JQCVWqWO5ZT3I9W4tcZFMdYONg82NcY4NjP2O7//9kzWXpZvld42nNaf1vFXHlTol16Xb4epN8333P" + + "fZyAYYNJg1iEbIS8hfuIxY1wkAGQbZOXlxyaElDPWJdhjoHThTWNCJAgT8NQdFJHU3Ngb2NJZ19uLI2zkB9P11xejMplz32aU1KIllF2Y8NbWFtrXApkDWdRkFxO1lkaWSpscIpRVT5YFVmlYPBiU2fBgjVpVZZAmcSaKE9TWAZb/oAQXLFeL1+FYCBhS2I0Zv9s8G7e" + + "gM6Bf4LUiIuMuJAAkC6Wip7bm9tO41PwWSd7LJGNmEyd+W7dcCdTU1VEW4ViWGKeYtNsom/vdCKKF5Q4b8GK/oM4UeeG+FPq////////U+lPRpBUj7BZaoExXf166o+/aNqMN3L4nEhqPYqwTjlTWFYGV2ZixWOiZeZrTm3hbltwrXfteu97qn27gD2AxobLipWTW1bj" + + "WMdfPmWtZpZqgGu1dTeKx1Akd+VXMF8bYGVmemxgdfR6Gn9ugfSHGJBFmbN7yXVcevl7UYTE//+QEHnpepKDNlrhd0BOLU7yW5lf4GK9Zjxn8WzohmuId4o7kU6S85nQahdwJnMqgueEV4yvTgFRRlHLVYtb9V4WXjNegV8UXzVfa1+0YfJjEWaiZx1vbnJSdTp3OoB0" + + "gTmBeId2ir+K3I2FjfOSmpV3mAKc5VLFY1d29GcVbIhzzYzDk66Wc20lWJxpDmnMj/2TmnXbkBpYWmgCY7Rp+09Dbyxn2I+7hSZ9tJNUaT9vcFdqWPdbLH0scipUCpHjnbROrU9OUFxQdVJDjJ5USFgkW5peHV6VXq1e918fYIxitWM6Y9Bor2xAeId5jnoLfeCCR4oC" + + "iuaORJAT////////kLiRLZHYnw5s5WRYZOJldW70doR7G5Bpk9FuulTyX7lkpI9Nj+2SRFF4WGtZKVxVXpdt+36PdRyMvI7imFtwuU8da79vsXUwlvtRTlQQWDVYV1msXGBfkmWXZ1xuIXZ7g9+M7ZAUkP2TTXgleDpSql6mVx9ZdGASUBJRWlGs//9RzVIAVRBYVFhY" + + "WVdblVz2XYtgvGKVZC1ncWhDaLxo33bXbdhub22bcG9xyF9Tddh5d3tJe1R7UnzWfXFSMIRjhWmF5IoOiwSMRo4PkAOQD5QZlnaYLZowldhQzVLVVAxYAlwOYadknm0ed7N65YD0hASQU5KFXOCdB1M/X5dfs22ccnl3Y3m/e+Rr0nLsiq1oA2phUfh6gWk0XEqc9oLr" + + "W8WRSXAeVnhcb2DHZWZsjIxakEGYE1RRZseSDVlIkKNRhU5NUeqFmYsOcFhjepNLaWKZtH4EdXdTV2lgjt+W42xdToxcPF8Qj+lTAozRgImGeV7/ZeVOc1Fl////////WYJcP5fuTvtZil/Nio1v4XmweWJb54RxcytxsV50X/Vje2SaccN8mE5DXvxOS1fcVqJgqW/D" + + "fQ2A/YEzgb+PsomXhqRd9GKKZK2Jh2d3bOJtPnQ2eDRaRn91gq2ZrE/zXsNi3WOSZVdnb3bDckyAzIC6jymRTVANV/lakmiF//9pc3Fkcv2Mt1jyjOCWapAZh3955HfnhClPL1JlU1pizWfPbMp2fXuUfJWCNoWEj+tm3W8gcgZ+G4OrmcGeplH9e7F4cnu4gId7SGro" + + "XmGAjHVRdWBRa5Jibox2epGXmupPEH9wYpx7T5WlnOlWelhZhuSWvE80UiRTSlPNU9teBmQsZZFnf2w+bE5ySHKvc+11VH5BgiyF6Yype8SRxnFpmBKY72M9Zml1anbkeNCFQ4buUypTUVQmWYNeh198YLJiSWJ5YqtlkGvUbMx1snaueJF52H3Lf3eApYirirmMu5B/" + + "l16Y22oLfDhQmVw+X65nh2vYdDV3CX+O////////nztnynoXUzl1i5rtX2aBnYPxgJhfPF/FdWJ7RpA8aGdZ61qbfRB2fossT/VfamoZbDdvAnTieWiIaIpVjHle32PPdcV50oLXkyiS8oSchu2cLVTBX2xljG1ccBWMp4zTmDtlT3T2Tg1O2FfgWStaZlvMUaheA16c" + + "YBZidmV3//9lp2ZubW5yNnsmgVCBmoKZi1yMoIzmjXSWHJZET65kq2tmgh6EYYVqkOhcAWlTmKiEeoVXTw9Sb1+pXkVnDXmPgXmJB4mGbfVfF2JVbLhOz3Jpm5JSBlQ7VnRYs2GkYm5xGllufIl83n0blvBlh4BeThlPdVF1WEBeY15zXwpnxE4mhT2ViZZbfHOYAVD7" + + "WMF2VninUiV3pYURe4ZQT1kJckd7x33oj7qP1JBNT79SyVopXwGXrU/dgheS6lcDY1VraXUriNyPFHpCUt9Yk2FVYgpmrmvNfD+D6VAjT/hTBVRGWDFZSVudXPBc710pXpZisWNnZT5luWcL////////bNVs4XD5eDJ+K4DegrOEDITshwKJEooqjEqQppLSmP2c851s" + + "Tk9OoVCNUlZXSlmoXj1f2F/ZYj9mtGcbZ9Bo0lGSfSGAqoGoiwCMjIy/kn6WMlQgmCxTF1DVU1xYqGSyZzRyZ3dmekaR5lLDbKFrhlgAXkxZVGcsf/tR4XbG//9kaXjom1Seu1fLWblmJ2eaa85U6WnZXlWBnGeVm6pn/pxSaF1Opk/jU8hiuWcrbKuPxE+tfm2ev04H" + + "YWJugG8rhRNUc2cqm0Vd83uVXKxbxoccbkqE0XoUgQhZmXyNbBF3IFLZWSJxIXJfd9uXJ51haQtaf1oYUaVUDVR9Zg5234/3kpic9Fnqcl1uxVFNaMl9v33sl2KeumR4aiGDAlmEW19r23MbdvJ9soAXhJlRMmcontl27mdiUv+ZBVwkYjt8foywVU9gtn0LlYBTAU5f" + + "UbZZHHI6gDaRzl8ld+JThF95fQSFrIozjo2XVmfzha6UU2EJYQhsuXZS////////iu2POFUvT1FRKlLHU8tbpV59YKBhgmPWZwln2m5nbYxzNnM3dTF5UIjVipiQSpCRkPWWxIeNWRVOiE9ZTg6KiY8/mBBQrV58WZZbuV64Y9pj+mTBZtxpSmnYbQtutnGUdSh6r3+K" + + "gACESYTJiYGLIY4KkGWWfZkKYX5ikWsy//9sg210f8x//G3Af4WHuoj4Z2WDsZg8lvdtG31hhD2Rak5xU3VdUGsEb+uFzYYtiadSKVQPXGVnTmiodAZ0g3XiiM+I4ZHMluKWeF+Lc4d6y4ROY6B1ZVKJbUFunHQJdVl4a3ySloZ63J+NT7ZhbmXFhlxOhk6uUNpOIVHM" + + "W+5lmWiBbbxzH3ZCd616HHzngm+K0pB8kc+WdZgYUpt90VArU5hnl23LcdB0M4HojyqWo5xXnp90YFhBbZl9L5heTuRPNk+LUbdSsV26YBxzsnk8gtOSNJa3lvaXCp6Xn2Jmpmt0UhdSo3DIiMJeyWBLYZBvI3FJfD599IBv////////hO6QI5MsVEKbb2rTcImMwo3v" + + "lzJStFpBXspfBGcXaXxplG1qbw9yYnL8e+2AAYB+h0uQzlFtnpN5hICLkzKK1lAtVIyKcWtqjMSBB2DRZ6Cd8k6ZTpicEIprhcGFaGkAbn54l4FV////////////////////////////////////////////////////////////////////////////////////////" + + "/////////////////////////////18MThBOFU4qTjFONk48Tj9OQk5WTlhOgk6FjGtOioISXw1Ojk6eTp9OoE6iTrBOs062Ts5OzU7ETsZOwk7XTt5O7U7fTvdPCU9aTzBPW09dT1dPR092T4hPj0+YT3tPaU9wT5FPb0+GT5ZRGE/UT99Pzk/YT9tP0U/aT9BP5E/l" + + "UBpQKFAUUCpQJVAFTxxP9lAhUClQLE/+T+9QEVAGUENQR2cDUFVQUFBIUFpQVlBsUHhQgFCaUIVQtFCy////////UMlQylCzUMJQ1lDeUOVQ7VDjUO5Q+VD1UQlRAVECURZRFVEUURpRIVE6UTdRPFE7UT9RQFFSUUxRVFFievhRaVFqUW5RgFGCVthRjFGJUY9RkVGT" + + "UZVRllGkUaZRolGpUapRq1GzUbFRslGwUbVRvVHFUclR21HghlVR6VHt//9R8FH1Uf5SBFILUhRSDlInUipSLlIzUjlST1JEUktSTFJeUlRSalJ0UmlSc1J/Un1SjVKUUpJScVKIUpGPqI+nUqxSrVK8UrVSwVLNUtdS3lLjUuaY7VLgUvNS9VL4UvlTBlMIdThTDVMQ" + + "Uw9TFVMaUyNTL1MxUzNTOFNAU0ZTRU4XU0lTTVHWU15TaVNuWRhTe1N3U4JTllOgU6ZTpVOuU7BTtlPDfBKW2VPfZvxx7lPuU+hT7VP6VAFUPVRAVCxULVQ8VC5UNlQpVB1UTlSPVHVUjlRfVHFUd1RwVJJUe1SAVHZUhFSQVIZUx1SiVLhUpVSsVMRUyFSo////////" + + "VKtUwlSkVL5UvFTYVOVU5lUPVRRU/VTuVO1U+lTiVTlVQFVjVUxVLlVcVUVVVlVXVThVM1VdVZlVgFSvVYpVn1V7VX5VmFWeVa5VfFWDValVh1WoVdpVxVXfVcRV3FXkVdRWFFX3VhZV/lX9VhtV+VZOVlBx31Y0VjZWMlY4//9Wa1ZkVi9WbFZqVoZWgFaKVqBWlFaP" + + "VqVWrla2VrRWwla8VsFWw1bAVshWzlbRVtNW11buVvlXAFb/VwRXCVcIVwtXDVcTVxhXFlXHVxxXJlc3VzhXTlc7V0BXT1dpV8BXiFdhV39XiVeTV6BXs1ekV6pXsFfDV8ZX1FfSV9NYClfWV+NYC1gZWB1YclghWGJYS1hwa8BYUlg9WHlYhVi5WJ9Yq1i6WN5Yu1i4" + + "WK5YxVjTWNFY11jZWNhY5VjcWORY31jvWPpY+Vj7WPxY/VkCWQpZEFkbaKZZJVksWS1ZMlk4WT560llVWVBZTllaWVhZYllgWWdZbFlp////////WXhZgVmdT15Pq1mjWbJZxlnoWdxZjVnZWdpaJVofWhFaHFoJWhpaQFpsWklaNVo2WmJaalqaWrxavlrLWsJavVrj" + + "Wtda5lrpWtZa+lr7WwxbC1sWWzJa0FsqWzZbPltDW0VbQFtRW1VbWltbW2VbaVtwW3NbdVt4ZYhbeluA//9bg1umW7hbw1vHW8lb1FvQW+Rb5lviW95b5VvrW/Bb9lvzXAVcB1wIXA1cE1wgXCJcKFw4XDlcQVxGXE5cU1xQXE9bcVxsXG5OYlx2XHlcjFyRXJRZm1yr" + + "XLtctly8XLdcxVy+XMdc2VzpXP1c+lztXYxc6l0LXRVdF11cXR9dG10RXRRdIl0aXRldGF1MXVJdTl1LXWxdc112XYddhF2CXaJdnV2sXa5dvV2QXbddvF3JXc1d013SXdZd213rXfJd9V4LXhpeGV4RXhteNl43XkReQ15AXk5eV15UXl9eYl5kXkdedV52XnqevF5/" + + "XqBewV7CXshe0F7P////////XtZe417dXtpe217iXuFe6F7pXuxe8V7zXvBe9F74Xv5fA18JX11fXF8LXxFfFl8pXy1fOF9BX0hfTF9OXy9fUV9WX1dfWV9hX21fc193X4Nfgl9/X4pfiF+RX4dfnl+ZX5hfoF+oX61fvF/WX/tf5F/4X/Ff3WCzX/9gIWBg//9gGWAQ" + + "YClgDmAxYBtgFWArYCZgD2A6YFpgQWBqYHdgX2BKYEZgTWBjYENgZGBCYGxga2BZYIFgjWDnYINgmmCEYJtglmCXYJJgp2CLYOFguGDgYNNgtF/wYL1gxmC1YNhhTWEVYQZg9mD3YQBg9GD6YQNhIWD7YPFhDWEOYUdhPmEoYSdhSmE/YTxhLGE0YT1hQmFEYXNhd2FY" + + "YVlhWmFrYXRhb2FlYXFhX2FdYVNhdWGZYZZhh2GsYZRhmmGKYZFhq2GuYcxhymHJYfdhyGHDYcZhumHLf3lhzWHmYeNh9mH6YfRh/2H9Yfxh/mIAYghiCWINYgxiFGIb////////Yh5iIWIqYi5iMGIyYjNiQWJOYl5iY2JbYmBiaGJ8YoJiiWJ+YpJik2KWYtRig2KU" + + "Ytdi0WK7Ys9i/2LGZNRiyGLcYsxiymLCYsdim2LJYwxi7mLxYydjAmMIYu9i9WNQYz5jTWQcY09jlmOOY4Bjq2N2Y6Njj2OJY59jtWNr//9jaWO+Y+ljwGPGY+NjyWPSY/ZjxGQWZDRkBmQTZCZkNmUdZBdkKGQPZGdkb2R2ZE5lKmSVZJNkpWSpZIhkvGTaZNJkxWTH" + + "ZLtk2GTCZPFk54IJZOBk4WKsZONk72UsZPZk9GTyZPplAGT9ZRhlHGUFZSRlI2UrZTRlNWU3ZTZlOHVLZUhlVmVVZU1lWGVeZV1lcmV4ZYJlg4uKZZtln2WrZbdlw2XGZcFlxGXMZdJl22XZZeBl4WXxZ3JmCmYDZftnc2Y1ZjZmNGYcZk9mRGZJZkFmXmZdZmRmZ2Zo" + + "Zl9mYmZwZoNmiGaOZolmhGaYZp1mwWa5Zslmvma8////////ZsRmuGbWZtpm4GY/ZuZm6WbwZvVm92cPZxZnHmcmZyeXOGcuZz9nNmdBZzhnN2dGZ15nYGdZZ2NnZGeJZ3BnqWd8Z2pnjGeLZ6ZnoWeFZ7dn72e0Z+xns2fpZ7hn5GfeZ91n4mfuZ7lnzmfGZ+dqnGge" + + "aEZoKWhAaE1oMmhO//9os2graFloY2h3aH9on2iPaK1olGidaJtog2quaLlodGi1aKBoumkPaI1ofmkBaMppCGjYaSJpJmjhaQxozWjUaOdo1Wk2aRJpBGjXaONpJWj5aOBo72koaSppGmkjaSFoxml5aXdpXGl4aWtpVGl+aW5pOWl0aT1pWWkwaWFpXmldaYFpammy" + + "aa5p0Gm/acFp02m+ac5b6GnKad1pu2nDaadqLmmRaaBpnGmVabRp3mnoagJqG2n/awpp+WnyaedqBWmxah5p7WoUaetqCmoSasFqI2oTakRqDGpyajZqeGpHamJqWWpmakhqOGoiapBqjWqgaoRqomqj////////apeGF2q7asNqwmq4arNqrGreatFq32qqatpq6mr7" + + "awWGFmr6axJrFpsxax9rOGs3dtxrOZjua0drQ2tJa1BrWWtUa1trX2tha3hreWt/a4BrhGuDa41rmGuVa55rpGuqa6trr2uya7Frs2u3a7xrxmvLa9Nr32vsa+tr82vv//+evmwIbBNsFGwbbCRsI2xebFVsYmxqbIJsjWyabIFsm2x+bGhsc2ySbJBsxGzxbNNsvWzX" + + "bMVs3WyubLFsvmy6bNts72zZbOptH4hNbTZtK209bThtGW01bTNtEm0MbWNtk21kbVpteW1ZbY5tlW/kbYVt+W4VbgpttW3HbeZtuG3Gbext3m3Mbeht0m3Fbfpt2W3kbdVt6m3ubi1ubm4ubhlucm5fbj5uI25rbitudm5Nbh9uQ246bk5uJG7/bh1uOG6CbqpumG7J" + + "brdu0269bq9uxG6ybtRu1W6PbqVuwm6fb0FvEXBMbuxu+G7+bz9u8m8xbu9vMm7M////////bz5vE273b4Zvem94b4FvgG9vb1tv829tb4JvfG9Yb45vkW/Cb2Zvs2+jb6FvpG+5b8Zvqm/fb9Vv7G/Ub9hv8W/ub9twCXALb/pwEXABcA9v/nAbcBpvdHAdcBhwH3Aw" + + "cD5wMnBRcGNwmXCScK9w8XCscLhws3CucN9wy3Dd//9w2XEJcP1xHHEZcWVxVXGIcWZxYnFMcVZxbHGPcftxhHGVcahxrHHXcblxvnHScclx1HHOceBx7HHncfVx/HH5cf9yDXIQchtyKHItcixyMHIycjtyPHI/ckByRnJLclhydHJ+coJygXKHcpJylnKicqdyuXKy" + + "csNyxnLEcs5y0nLicuBy4XL5cvdQD3MXcwpzHHMWcx1zNHMvcylzJXM+c05zT57Yc1dzanNoc3BzeHN1c3tzenPIc7NzznO7c8Bz5XPuc950onQFdG90JXP4dDJ0OnRVdD90X3RZdEF0XHRpdHB0Y3RqdHZ0fnSLdJ50p3TKdM901HPx////////dOB043TndOl07nTy" + + "dPB08XT4dPd1BHUDdQV1DHUOdQ11FXUTdR51JnUsdTx1RHVNdUp1SXVbdUZ1WnVpdWR1Z3VrdW11eHV2dYZ1h3V0dYp1iXWCdZR1mnWddaV1o3XCdbN1w3W1db11uHW8dbF1zXXKddJ12XXjdd51/nX///91/HYBdfB1+nXydfN2C3YNdgl2H3YndiB2IXYidiR2NHYw" + + "djt2R3ZIdkZ2XHZYdmF2YnZodml2anZndmx2cHZydnZ2eHZ8doB2g3aIdot2jnaWdpN2mXaadrB2tHa4drl2unbCds121nbSdt524Xbldud26oYvdvt3CHcHdwR3KXckdx53JXcmdxt3N3c4d0d3Wndod2t3W3dld393fnd5d453i3eRd6B3nnewd7Z3uXe/d7x3vXe7" + + "d8d3zXfXd9p33Hfjd+53/HgMeBJ5JnggeSp4RXiOeHR4hnh8eJp4jHijeLV4qniveNF4xnjLeNR4vni8eMV4ynjs////////eOd42nj9ePR5B3kSeRF5GXkseSt5QHlgeVd5X3laeVV5U3l6eX95inmdeaefS3mqea55s3m5ebp5yXnVeed57HnheeN6CHoNehh6GXog" + + "eh95gHoxejt6Pno3ekN6V3pJemF6Ynppn516cHp5en16iHqXepV6mHqWeql6yHqw//96tnrFesR6v5CDesd6ynrNes961XrTetl62nrdeuF64nrmeu168HsCew97CnsGezN7GHsZex57NXsoezZ7UHt6ewR7TXsLe0x7RXt1e2V7dHtne3B7cXtse257nXuYe597jXuc" + + "e5p7i3uSe497XXuZe8t7wXvMe897tHvGe9176XwRfBR75nvlfGB8AHwHfBN783v3fBd8DXv2fCN8J3wqfB98N3wrfD18THxDfFR8T3xAfFB8WHxffGR8VnxlfGx8dXyDfJB8pHytfKJ8q3yhfKh8s3yyfLF8rny5fL18wHzFfMJ82HzSfNx84ps7fO988nz0fPZ8+n0G" + + "////////fQJ9HH0VfQp9RX1LfS59Mn0/fTV9Rn1zfVZ9Tn1yfWh9bn1PfWN9k32JfVt9j319fZt9un2ufaN9tX3Hfb19q349faJ9r33cfbh9n32wfdh93X3kfd59+33yfeF+BX4KfiN+IX4SfjF+H34Jfgt+In5GfmZ+O341fjl+Q343//9+Mn46fmd+XX5Wfl5+WX5a" + + "fnl+an5pfnx+e36DfdV+fY+ufn9+iH6Jfox+kn6QfpN+lH6Wfo5+m36cfzh/On9Ff0x/TX9Of1B/UX9Vf1R/WH9ff2B/aH9pf2d/eH+Cf4Z/g3+If4d/jH+Uf55/nX+af6N/r3+yf7l/rn+2f7iLcX/Ff8Z/yn/Vf9R/4X/mf+l/83/5mNyABoAEgAuAEoAYgBmAHIAh" + + "gCiAP4A7gEqARoBSgFiAWoBfgGKAaIBzgHKAcIB2gHmAfYB/gISAhoCFgJuAk4CagK1RkICsgNuA5YDZgN2AxIDagNaBCYDvgPGBG4EpgSOBL4FL////////louBRoE+gVOBUYD8gXGBboFlgWaBdIGDgYiBioGAgYKBoIGVgaSBo4FfgZOBqYGwgbWBvoG4gb2BwIHC" + + "gbqByYHNgdGB2YHYgciB2oHfgeCB54H6gfuB/oIBggKCBYIHggqCDYIQghaCKYIrgjiCM4JAglmCWIJdglqCX4Jk//+CYoJogmqCa4IugnGCd4J4gn6CjYKSgquCn4K7gqyC4YLjgt+C0oL0gvOC+oOTgwOC+4L5gt6DBoLcgwmC2YM1gzSDFoMygzGDQIM5g1CDRYMv" + + "gyuDF4MYg4WDmoOqg5+DooOWgyODjoOHg4qDfIO1g3ODdYOgg4mDqIP0hBOD64POg/2EA4PYhAuDwYP3hAeD4IPyhA2EIoQgg72EOIUGg/uEbYQqhDyFWoSEhHeEa4SthG6EgoRphEaELIRvhHmENYTKhGKEuYS/hJ+E2YTNhLuE2oTQhMGExoTWhKGFIYT/hPSFF4UY" + + "hSyFH4UVhRSE/IVAhWOFWIVI////////hUGGAoVLhVWFgIWkhYiFkYWKhaiFbYWUhZuF6oWHhZyFd4V+hZCFyYW6hc+FuYXQhdWF3YXlhdyF+YYKhhOGC4X+hfqGBoYihhqGMIY/hk1OVYZUhl+GZ4ZxhpOGo4aphqqGi4aMhraGr4bEhsaGsIbJiCOGq4bUht6G6Ybs" + + "//+G34bbhu+HEocGhwiHAIcDhvuHEYcJhw2G+YcKhzSHP4c3hzuHJYcphxqHYIdfh3iHTIdOh3SHV4doh26HWYdTh2OHaogFh6KHn4eCh6+Hy4e9h8CH0JbWh6uHxIezh8eHxoe7h++H8ofgiA+IDYf+h/aH94gOh9KIEYgWiBWIIoghiDGINog5iCeIO4hEiEKIUohZ" + + "iF6IYohriIGIfoieiHWIfYi1iHKIgoiXiJKIroiZiKKIjYikiLCIv4ixiMOIxIjUiNiI2YjdiPmJAoj8iPSI6IjyiQSJDIkKiROJQ4keiSWJKokriUGJRIk7iTaJOIlMiR2JYIle////////iWaJZIltiWqJb4l0iXeJfomDiYiJiomTiZiJoYmpiaaJrImvibKJuom9" + + "ib+JwInaidyJ3YnnifSJ+IoDihaKEIoMihuKHYolijaKQYpbilKKRopIinyKbYpsimKKhYqCioSKqIqhipGKpYqmipqKo4rEis2KworaiuuK84rn//+K5IrxixSK4IriiveK3orbiwyLB4saiuGLFosQixeLIIszl6uLJosriz6LKItBi0yLT4tOi0mLVotbi1qLa4tf" + + "i2yLb4t0i32LgIuMi46LkouTi5aLmYuajDqMQYw/jEiMTIxOjFCMVYxijGyMeIx6jIKMiYyFjIqMjYyOjJSMfIyYYh2MrYyqjL2MsoyzjK6MtozIjMGM5IzjjNqM/Yz6jPuNBI0FjQqNB40PjQ2NEJ9OjROMzY0UjRaNZ41tjXGNc42BjZmNwo2+jbqNz43ajdaNzI3b" + + "jcuN6o3rjd+N4438jgiOCY3/jh2OHo4Qjh+OQo41jjCONI5K////////jkeOSY5MjlCOSI5ZjmSOYI4qjmOOVY52jnKOfI6BjoeOhY6EjouOio6TjpGOlI6ZjqqOoY6sjrCOxo6xjr6OxY7IjsuO247jjvyO+47rjv6PCo8FjxWPEo8ZjxOPHI8fjxuPDI8mjzOPO485" + + "j0WPQo8+j0yPSY9Gj06PV49c//+PYo9jj2SPnI+fj6OPrY+vj7eP2o/lj+KP6o/vkIeP9JAFj/mP+pARkBWQIZANkB6QFpALkCeQNpA1kDmP+JBPkFCQUZBSkA6QSZA+kFaQWJBekGiQb5B2lqiQcpCCkH2QgZCAkIqQiZCPkKiQr5CxkLWQ4pDkYkiQ25ECkRKRGZEy" + + "kTCRSpFWkViRY5FlkWmRc5FykYuRiZGCkaKRq5GvkaqRtZG0kbqRwJHBkcmRy5HQkdaR35HhkduR/JH1kfaSHpH/khSSLJIVkhGSXpJXkkWSSZJkkkiSlZI/kkuSUJKckpaSk5KbklqSz5K5kreS6ZMPkvqTRJMu////////kxmTIpMakyOTOpM1kzuTXJNgk3yTbpNW" + + "k7CTrJOtk5STuZPWk9eT6JPlk9iTw5Pdk9CTyJPklBqUFJQTlAOUB5QQlDaUK5Q1lCGUOpRBlFKURJRblGCUYpRelGqSKZRwlHWUd5R9lFqUfJR+lIGUf5WClYeVipWUlZaVmJWZ//+VoJWolaeVrZW8lbuVuZW+lcpv9pXDlc2VzJXVldSV1pXcleGV5ZXiliGWKJYu" + + "li+WQpZMlk+WS5Z3llyWXpZdll+WZpZylmyWjZaYlpWWl5aqlqeWsZaylrCWtJa2lriWuZbOlsuWyZbNiU2W3JcNltWW+ZcElwaXCJcTlw6XEZcPlxaXGZcklyqXMJc5lz2XPpdEl0aXSJdCl0mXXJdgl2SXZpdoUtKXa5dxl3mXhZd8l4GXepeGl4uXj5eQl5yXqJem" + + "l6OXs5e0l8OXxpfIl8uX3Jftn0+X8nrfl/aX9ZgPmAyYOJgkmCGYN5g9mEaYT5hLmGuYb5hw////////mHGYdJhzmKqYr5ixmLaYxJjDmMaY6ZjrmQOZCZkSmRSZGJkhmR2ZHpkkmSCZLJkumT2ZPplCmUmZRZlQmUuZUZlSmUyZVZmXmZiZpZmtma6ZvJnfmduZ3ZnY" + + "mdGZ7ZnumfGZ8pn7mfiaAZoPmgWZ4poZmiuaN5pFmkKaQJpD//+aPppVmk2aW5pXml+aYpplmmSaaZprmmqarZqwmryawJrPmtGa05rUmt6a35rimuOa5prvmuua7pr0mvGa95r7mwabGJsamx+bIpsjmyWbJ5somymbKpsumy+bMptEm0ObT5tNm06bUZtYm3Sbk5uD" + + "m5GblpuXm5+boJuom7SbwJvKm7mbxpvPm9Gb0pvjm+Kb5JvUm+GcOpvym/Gb8JwVnBScCZwTnAycBpwInBKcCpwEnC6cG5wlnCScIZwwnEecMpxGnD6cWpxgnGecdpx4nOec7JzwnQmdCJzrnQOdBp0qnSadr50jnR+dRJ0VnRKdQZ0/nT6dRp1I////////nV2dXp1k" + + "nVGdUJ1ZnXKdiZ2Hnaudb516nZqdpJ2pnbKdxJ3BnbuduJ26ncadz53Cndmd0534nead7Z3vnf2eGp4bnh6edZ55nn2egZ6InouejJ6SnpWekZ6dnqWeqZ64nqqerZdhnsyezp7PntCe1J7cnt6e3Z7gnuWe6J7v//+e9J72nvee+Z77nvye/Z8Hnwh2t58VnyGfLJ8+" + + "n0qfUp9Un2OfX59gn2GfZp9nn2yfap93n3Kfdp+Vn5yfoFgvaceQWXRkUdxxmf//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + + "/////////////////////////////////////////////w=="; + + + private static short[] UNICODE_TO_QR_KANJI = new short[65536]; + + static { // Unpack the Shift JIS table into a more computation-friendly form + Arrays.fill(UNICODE_TO_QR_KANJI, (short)-1); + byte[] bytes = Base64.getDecoder().decode(PACKED_QR_KANJI_TO_UNICODE); + for (int i = 0; i < bytes.length; i += 2) { + int j = ((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF); + if (j == 0xFFFF) + continue; + if (UNICODE_TO_QR_KANJI[j] != -1) + throw new AssertionError(); + UNICODE_TO_QR_KANJI[j] = (short)(i / 2); + } + } + +}
diff --git a/src/third_party/QR-Code-generator/javascript/qrcodegen-demo.js b/src/third_party/QR-Code-generator/javascript/qrcodegen-demo.js new file mode 100644 index 0000000..e75e689 --- /dev/null +++ b/src/third_party/QR-Code-generator/javascript/qrcodegen-demo.js
@@ -0,0 +1,154 @@ +/* + * QR Code generator demo (JavaScript) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +"use strict"; + + +function redrawQrCode() { + // Show/hide rows based on bitmap/vector image output + var bitmapOutput = document.getElementById("output-format-bitmap").checked; + var scaleRow = document.getElementById("scale-row"); + var svgXmlRow = document.getElementById("svg-xml-row"); + if (bitmapOutput) { + scaleRow.style.removeProperty("display"); + svgXmlRow.style.display = "none"; + } else { + scaleRow.style.display = "none"; + svgXmlRow.style.removeProperty("display"); + } + var svgXml = document.getElementById("svg-xml-output"); + svgXml.value = ""; + + // Reset output images in case of early termination + var canvas = document.getElementById("qrcode-canvas"); + var svg = document.getElementById("qrcode-svg"); + canvas.style.display = "none"; + svg.style.display = "none"; + + // Returns a QrCode.Ecc object based on the radio buttons in the HTML form. + function getInputErrorCorrectionLevel() { + if (document.getElementById("errcorlvl-medium").checked) + return qrcodegen.QrCode.Ecc.MEDIUM; + else if (document.getElementById("errcorlvl-quartile").checked) + return qrcodegen.QrCode.Ecc.QUARTILE; + else if (document.getElementById("errcorlvl-high").checked) + return qrcodegen.QrCode.Ecc.HIGH; + else // In case no radio button is depressed + return qrcodegen.QrCode.Ecc.LOW; + } + + // Get form inputs and compute QR Code + var ecl = getInputErrorCorrectionLevel(); + var text = document.getElementById("text-input").value; + var segs = qrcodegen.QrSegment.makeSegments(text); + var minVer = parseInt(document.getElementById("version-min-input").value, 10); + var maxVer = parseInt(document.getElementById("version-max-input").value, 10); + var mask = parseInt(document.getElementById("mask-input").value, 10); + var boostEcc = document.getElementById("boost-ecc-input").checked; + var qr = qrcodegen.QrCode.encodeSegments(segs, ecl, minVer, maxVer, mask, boostEcc); + + // Draw image output + var border = parseInt(document.getElementById("border-input").value, 10); + if (border < 0 || border > 100) + return; + if (bitmapOutput) { + var scale = parseInt(document.getElementById("scale-input").value, 10); + if (scale <= 0 || scale > 30) + return; + qr.drawCanvas(scale, border, canvas); + canvas.style.removeProperty("display"); + } else { + var code = qr.toSvgString(border); + svg.setAttribute("viewBox", / viewBox="([^"]*)"/.exec(code)[1]); + svg.querySelector("path").setAttribute("d", / d="([^"]*)"/.exec(code)[1]); + svg.style.removeProperty("display"); + svgXml.value = qr.toSvgString(border); + } + + + // Returns a string to describe the given list of segments. + function describeSegments(segs) { + if (segs.length == 0) + return "none"; + else if (segs.length == 1) { + var mode = segs[0].mode; + var Mode = qrcodegen.QrSegment.Mode; + if (mode == Mode.NUMERIC ) return "numeric"; + if (mode == Mode.ALPHANUMERIC) return "alphanumeric"; + if (mode == Mode.BYTE ) return "byte"; + if (mode == Mode.KANJI ) return "kanji"; + return "unknown"; + } else + return "multiple"; + } + + // Returns the number of Unicode code points in the given UTF-16 string. + function countUnicodeChars(str) { + var result = 0; + for (var i = 0; i < str.length; i++, result++) { + var c = str.charCodeAt(i); + if (c < 0xD800 || c >= 0xE000) + continue; + else if (0xD800 <= c && c < 0xDC00) { // High surrogate + i++; + var d = str.charCodeAt(i); + if (0xDC00 <= d && d < 0xE000) // Low surrogate + continue; + } + throw "Invalid UTF-16 string"; + } + return result; + } + + // Show the QR Code symbol's statistics as a string + var stats = "QR Code version = " + qr.version + ", "; + stats += "mask pattern = " + qr.mask + ", "; + stats += "character count = " + countUnicodeChars(text) + ",\n"; + stats += "encoding mode = " + describeSegments(segs) + ", "; + stats += "error correction = level " + "LMQH".charAt(qr.errorCorrectionLevel.ordinal) + ", "; + stats += "data bits = " + qrcodegen.QrSegment.getTotalBits(segs, qr.version) + "."; + var elem = document.getElementById("statistics-output"); + while (elem.firstChild != null) + elem.removeChild(elem.firstChild); + elem.appendChild(document.createTextNode(stats)); +} + + +function handleVersionMinMax(which) { + var minElem = document.getElementById("version-min-input"); + var maxElem = document.getElementById("version-max-input"); + var minVal = parseInt(minElem.value, 10); + var maxVal = parseInt(maxElem.value, 10); + minVal = Math.max(Math.min(minVal, qrcodegen.QrCode.MAX_VERSION), qrcodegen.QrCode.MIN_VERSION); + maxVal = Math.max(Math.min(maxVal, qrcodegen.QrCode.MAX_VERSION), qrcodegen.QrCode.MIN_VERSION); + if (which == "min" && minVal > maxVal) + maxVal = minVal; + else if (which == "max" && maxVal < minVal) + minVal = maxVal; + minElem.value = minVal.toString(); + maxElem.value = maxVal.toString(); + redrawQrCode(); +} + + +redrawQrCode();
diff --git a/src/third_party/QR-Code-generator/javascript/qrcodegen-js-demo.html b/src/third_party/QR-Code-generator/javascript/qrcodegen-js-demo.html new file mode 100644 index 0000000..0b1ba15 --- /dev/null +++ b/src/third_party/QR-Code-generator/javascript/qrcodegen-js-demo.html
@@ -0,0 +1,121 @@ +<!-- + - QR Code generator demo (HTML+JavaScript) + - + - Copyright (c) Project Nayuki. (MIT License) + - https://www.nayuki.io/page/qr-code-generator-library + - + - Permission is hereby granted, free of charge, to any person obtaining a copy of + - this software and associated documentation files (the "Software"), to deal in + - the Software without restriction, including without limitation the rights to + - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + - the Software, and to permit persons to whom the Software is furnished to do so, + - subject to the following conditions: + - * The above copyright notice and this permission notice shall be included in + - all copies or substantial portions of the Software. + - * The Software is provided "as is", without warranty of any kind, express or + - implied, including but not limited to the warranties of merchantability, + - fitness for a particular purpose and noninfringement. In no event shall the + - authors or copyright holders be liable for any claim, damages or other + - liability, whether in an action of contract, tort or otherwise, arising from, + - out of or in connection with the Software or the use or other dealings in the + - Software. + --> +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>QR Code generator library demo (JavaScript)</title> + <style type="text/css"> + html { + font-family: sans-serif; + } + td { + vertical-align: top; + padding-top: 0.2em; + padding-bottom: 0.2em; + } + td:first-child { + white-space: pre; + } + input[type=radio] + label, input[type=checkbox] + label { + margin-left: 0.1em; + margin-right: 0.7em; + } + </style> +</head> + +<body> +<h1>QR Code generator demo library (JavaScript)</h1> +<form action="#" method="get" onsubmit="return false;"> + <table class="noborder" style="width:100%"> + <tbody> + <tr> + <td><strong>Text string:</strong></td> + <td style="width:100%"><textarea placeholder="Enter your text to be put into the QR Code" id="text-input" style="width:100%; max-width:30em; height:5em; font-family:inherit" oninput="redrawQrCode();"></textarea></td> + </tr> + <tr> + <td><strong>QR Code:</strong></td> + <td> + <canvas id="qrcode-canvas" style="padding:1em; background-color:#E8E8E8"></canvas> + <svg id="qrcode-svg" style="width:30em; height:30em; padding:1em; background-color:#E8E8E8"> + <rect width="100%" height="100%" fill="#FFFFFF" stroke-width="0"></rect> + <path d="" fill="#000000" stroke-width="0"></path> + </svg> + </td> + </tr> + <tr> + <td><strong>Error correction:</strong></td> + <td> + <input type="radio" name="errcorlvl" id="errcorlvl-low" onchange="redrawQrCode();" checked="checked"><label for="errcorlvl-low">Low</label> + <input type="radio" name="errcorlvl" id="errcorlvl-medium" onchange="redrawQrCode();"><label for="errcorlvl-medium">Medium</label> + <input type="radio" name="errcorlvl" id="errcorlvl-quartile" onchange="redrawQrCode();"><label for="errcorlvl-quartile">Quartile</label> + <input type="radio" name="errcorlvl" id="errcorlvl-high" onchange="redrawQrCode();"><label for="errcorlvl-high">High</label> + </td> + </tr> + <tr> + <td>Output format:</td> + <td> + <input type="radio" name="output-format" id="output-format-bitmap" onchange="redrawQrCode();" checked="checked"><label for="output-format-bitmap">Bitmap</label> + <input type="radio" name="output-format" id="output-format-vector" onchange="redrawQrCode();"><label for="output-format-vector">Vector</label> + </td> + </tr> + <tr> + <td>Border:</td> + <td><input type="number" value="4" min="0" max="100" step="1" id="border-input" style="width:4em" oninput="redrawQrCode();"> modules</td> + </tr> + <tr id="scale-row"> + <td>Scale:</td> + <td><input type="number" value="8" min="1" max="30" step="1" id="scale-input" style="width:4em" oninput="redrawQrCode();"> pixels per module</td> + </tr> + <tr> + <td>Version range:</td> + <td>Minimum = <input type="number" value="1" min="1" max="40" step="1" id="version-min-input" style="width:4em" oninput="handleVersionMinMax('min');">, maximum = <input type="number" value="40" min="1" max="40" step="1" id="version-max-input" style="width:4em" oninput="handleVersionMinMax('max');"></td> + </tr> + <tr> + <td>Mask pattern:</td> + <td><input type="number" value="-1" min="-1" max="7" step="1" id="mask-input" style="width:4em" oninput="redrawQrCode();"> (−1 for automatic, 0 to 7 for manual)</td> + </tr> + <tr> + <td>Boost ECC:</td> + <td><input type="checkbox" checked="checked" id="boost-ecc-input" onchange="redrawQrCode();"><label for="boost-ecc-input">Increase <abbr title="error-correcting code">ECC</abbr> level within same version</label></td> + </tr> + <tr> + <td>Statistics:</td> + <td id="statistics-output" style="white-space:pre"></td> + </tr> + <tr id="svg-xml-row"> + <td>SVG XML code:</td> + <td> + <textarea id="svg-xml-output" readonly="readonly" style="width:100%; max-width:50em; height:15em; font-family:monospace"></textarea> + </td> + </tr> + </tbody> + </table> +</form> +<script type="application/javascript" src="qrcodegen.js"></script> +<script type="application/javascript" src="qrcodegen-demo.js"></script> + +<hr> +<p>Copyright © Project Nayuki – <a href="https://www.nayuki.io/page/qr-code-generator-library">https://www.nayuki.io/page/qr-code-generator-library</a></p> +</body> +</html>
diff --git a/src/third_party/QR-Code-generator/javascript/qrcodegen.js b/src/third_party/QR-Code-generator/javascript/qrcodegen.js new file mode 100644 index 0000000..19ae3a8 --- /dev/null +++ b/src/third_party/QR-Code-generator/javascript/qrcodegen.js
@@ -0,0 +1,1004 @@ +/* + * QR Code generator library (JavaScript) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +"use strict"; + + +/* + * Module "qrcodegen", public members: + * - Class QrCode: + * - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode + * - Function encodeBinary(list<byte> data, QrCode.Ecc ecl) -> QrCode + * - Function encodeSegments(list<QrSegment> segs, QrCode.Ecc ecl, + * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode + * - Constants int MIN_VERSION, MAX_VERSION + * - Constructor QrCode(list<int> datacodewords, int mask, int version, QrCode.Ecc ecl) + * - Fields int version, size, mask + * - Field QrCode.Ecc errorCorrectionLevel + * - Method getModule(int x, int y) -> bool + * - Method drawCanvas(int scale, int border, HTMLCanvasElement canvas) -> void + * - Method toSvgString(int border) -> str + * - Enum Ecc: + * - Constants LOW, MEDIUM, QUARTILE, HIGH + * - Field int ordinal + * - Class QrSegment: + * - Function makeBytes(list<int> data) -> QrSegment + * - Function makeNumeric(str data) -> QrSegment + * - Function makeAlphanumeric(str data) -> QrSegment + * - Function makeSegments(str text) -> list<QrSegment> + * - Function makeEci(int assignVal) -> QrSegment + * - Constructor QrSegment(QrSegment.Mode mode, int numChars, list<int> bitData) + * - Field QrSegment.Mode mode + * - Field int numChars + * - Method getBits() -> list<int> + * - Constants RegExp NUMERIC_REGEX, ALPHANUMERIC_REGEX + * - Enum Mode: + * - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI + */ +var qrcodegen = new function() { + + /*---- QR Code symbol class ----*/ + + /* + * A class that represents an immutable square grid of black and white cells for a QR Code symbol, + * with associated static functions to create a QR Code from user-supplied textual or binary data. + * This class covers the QR Code model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels. + * This constructor creates a new QR Code symbol with the given version number, error correction level, binary data array, + * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor + * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. + */ + this.QrCode = function(datacodewords, mask, version, errCorLvl) { + + /*---- Constructor ----*/ + + // Check arguments and handle simple scalar fields + if (mask < -1 || mask > 7) + throw "Mask value out of range"; + if (version < MIN_VERSION || version > MAX_VERSION) + throw "Version value out of range"; + var size = version * 4 + 17; + + // Initialize both grids to be size*size arrays of Boolean false + var row = []; + for (var i = 0; i < size; i++) + row.push(false); + var modules = []; + var isFunction = []; + for (var i = 0; i < size; i++) { + modules.push(row.slice()); + isFunction.push(row.slice()); + } + + // Handle grid fields, draw function patterns, draw all codewords + drawFunctionPatterns(); + var allCodewords = appendErrorCorrection(datacodewords); + drawCodewords(allCodewords); + + // Handle masking + if (mask == -1) { // Automatically choose best mask + var minPenalty = Infinity; + for (var i = 0; i < 8; i++) { + drawFormatBits(i); + applyMask(i); + var penalty = getPenaltyScore(); + if (penalty < minPenalty) { + mask = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + if (mask < 0 || mask > 7) + throw "Assertion error"; + drawFormatBits(mask); // Overwrite old format bits + applyMask(mask); // Apply the final choice of mask + + + /*---- Read-only instance properties ----*/ + + // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). + Object.defineProperty(this, "version", {value:version}); + + // The width and height of this QR Code symbol, measured in modules. + // Always equal to version * 4 + 17, in the range 21 to 177. + Object.defineProperty(this, "size", {value:size}); + + // The error correction level used in this QR Code symbol. + Object.defineProperty(this, "errorCorrectionLevel", {value:errCorLvl}); + + // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + // Note that even if the constructor was called with automatic masking requested + // (mask = -1), the resulting object will still have a mask value between 0 and 7. + Object.defineProperty(this, "mask", {value:mask}); + + + /*---- Accessor methods ----*/ + + // (Public) Returns the color of the module (pixel) at the given coordinates, which is either + // false for white or true for black. The top left corner has the coordinates (x=0, y=0). + // If the given coordinates are out of bounds, then false (white) is returned. + this.getModule = function(x, y) { + return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; + }; + + // (Package-private) Tests whether the module at the given coordinates is a function module (true) or not (false). + // The top left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then false is returned. + // The JavaScript version of this library has this method because it is impossible to access private variables of another object. + this.isFunctionModule = function(x, y) { + if (0 <= x && x < size && 0 <= y && y < size) + return isFunction[y][x]; + else + return false; // Infinite border + }; + + + /*---- Public instance methods ----*/ + + // Draws this QR Code symbol with the given module scale and number of modules onto the given HTML canvas element. + // The canvas will be resized to a width and height of (this.size + border * 2) * scale. The painted image will be purely + // black and white with no transparent regions. The scale must be a positive integer, and the border must be a non-negative integer. + this.drawCanvas = function(scale, border, canvas) { + if (scale <= 0 || border < 0) + throw "Value out of range"; + var width = (size + border * 2) * scale; + canvas.width = width; + canvas.height = width; + var ctx = canvas.getContext("2d"); + for (var y = -border; y < size + border; y++) { + for (var x = -border; x < size + border; x++) { + ctx.fillStyle = this.getModule(x, y) ? "#000000" : "#FFFFFF"; + ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); + } + } + }; + + // Based on the given number of border modules to add as padding, this returns a + // string whose contents represents an SVG XML file that depicts this QR Code symbol. + // Note that Unix newlines (\n) are always used, regardless of the platform. + this.toSvgString = function(border) { + if (border < 0) + throw "Border must be non-negative"; + var result = '<?xml version="1.0" encoding="UTF-8"?>\n'; + result += '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'; + result += '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 ' + + (size + border * 2) + ' ' + (size + border * 2) + '" stroke="none">\n'; + result += '\t<rect width="100%" height="100%" fill="#FFFFFF"/>\n'; + result += '\t<path d="'; + var head = true; + for (var y = -border; y < size + border; y++) { + for (var x = -border; x < size + border; x++) { + if (this.getModule(x, y)) { + if (head) + head = false; + else + result += " "; + result += "M" + (x + border) + "," + (y + border) + "h1v1h-1z"; + } + } + } + result += '" fill="#000000"/>\n'; + result += '</svg>\n'; + return result; + }; + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + function drawFunctionPatterns() { + // Draw horizontal and vertical timing patterns + for (var i = 0; i < size; i++) { + setFunctionModule(6, i, i % 2 == 0); + setFunctionModule(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + drawFinderPattern(3, 3); + drawFinderPattern(size - 4, 3); + drawFinderPattern(3, size - 4); + + // Draw numerous alignment patterns + var alignPatPos = QrCode.getAlignmentPatternPositions(version); + var numAlign = alignPatPos.length; + for (var i = 0; i < numAlign; i++) { + for (var j = 0; j < numAlign; j++) { + if (i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0) + continue; // Skip the three finder corners + else + drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); + } + } + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); + } + + + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. + function drawFormatBits(mask) { + // Calculate error correction code and pack bits + var data = errCorLvl.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 + var rem = data; + for (var i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >>> 9) * 0x537); + data = data << 10 | rem; + data ^= 0x5412; // uint15 + if (data >>> 15 != 0) + throw "Assertion error"; + + // Draw first copy + for (var i = 0; i <= 5; i++) + setFunctionModule(8, i, ((data >>> i) & 1) != 0); + setFunctionModule(8, 7, ((data >>> 6) & 1) != 0); + setFunctionModule(8, 8, ((data >>> 7) & 1) != 0); + setFunctionModule(7, 8, ((data >>> 8) & 1) != 0); + for (var i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, ((data >>> i) & 1) != 0); + + // Draw second copy + for (var i = 0; i <= 7; i++) + setFunctionModule(size - 1 - i, 8, ((data >>> i) & 1) != 0); + for (var i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, ((data >>> i) & 1) != 0); + setFunctionModule(8, size - 8, true); + } + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field (which only has an effect for 7 <= version <= 40). + function drawVersion() { + if (version < 7) + return; + + // Calculate error correction code and pack bits + var rem = version; // version is uint6, in the range [7, 40] + for (var i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); + var data = version << 12 | rem; // uint18 + if (data >>> 18 != 0) + throw "Assertion error"; + + // Draw two copies + for (var i = 0; i < 18; i++) { + var bit = ((data >>> i) & 1) != 0; + var a = size - 11 + i % 3, b = Math.floor(i / 3); + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } + } + + + // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + function drawFinderPattern(x, y) { + for (var i = -4; i <= 4; i++) { + for (var j = -4; j <= 4; j++) { + var dist = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm + var xx = x + j, yy = y + i; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } + } + + + // Draws a 5*5 alignment pattern, with the center module at (x, y). + function drawAlignmentPattern(x, y) { + for (var i = -2; i <= 2; i++) { + for (var j = -2; j <= 2; j++) + setFunctionModule(x + j, y + i, Math.max(Math.abs(i), Math.abs(j)) != 1); + } + } + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in range. + function setFunctionModule(x, y, isBlack) { + modules[y][x] = isBlack; + isFunction[y][x] = true; + } + + + /*---- Private helper methods for constructor: Codewords and masking ----*/ + + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. + function appendErrorCorrection(data) { + if (data.length != QrCode.getNumDataCodewords(version, errCorLvl)) + throw "Invalid argument"; + + // Calculate parameter numbers + var numBlocks = QrCode.NUM_ERROR_CORRECTION_BLOCKS[errCorLvl.ordinal][version]; + var blockEccLen = QrCode.ECC_CODEWORDS_PER_BLOCK[errCorLvl.ordinal][version]; + var rawCodewords = Math.floor(QrCode.getNumRawDataModules(version) / 8); + var numShortBlocks = numBlocks - rawCodewords % numBlocks; + var shortBlockLen = Math.floor(rawCodewords / numBlocks); + + // Split data into blocks and append ECC to each block + var blocks = []; + var rs = new ReedSolomonGenerator(blockEccLen); + for (var i = 0, k = 0; i < numBlocks; i++) { + var dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); + k += dat.length; + var ecc = rs.getRemainder(dat); + if (i < numShortBlocks) + dat.push(0); + ecc.forEach(function(b) { + dat.push(b); + }); + blocks.push(dat); + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + var result = []; + for (var i = 0; i < blocks[0].length; i++) { + for (var j = 0; j < blocks.length; j++) { + // Skip the padding byte in short blocks + if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) + result.push(blocks[j][i]); + } + } + if (result.length != rawCodewords) + throw "Assertion error"; + return result; + } + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code symbol. Function modules need to be marked off before this is called. + function drawCodewords(data) { + if (data.length != Math.floor(QrCode.getNumRawDataModules(version) / 8)) + throw "Invalid argument"; + var i = 0; // Bit index into the data + // Do the funny zigzag scan + for (var right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (var vert = 0; vert < size; vert++) { // Vertical counter + for (var j = 0; j < 2; j++) { + var x = right - j; // Actual x coordinate + var upward = ((right + 1) & 2) == 0; + var y = upward ? size - 1 - vert : vert; // Actual y coordinate + if (!isFunction[y][x] && i < data.length * 8) { + modules[y][x] = ((data[i >>> 3] >>> (7 - (i & 7))) & 1) != 0; + i++; + } + // If there are any remainder bits (0 to 7), they are already + // set to 0/false/white when the grid of modules was initialized + } + } + } + if (i != data.length * 8) + throw "Assertion error"; + } + + + // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical + // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. + // This means it is possible to apply a mask, undo it, and try another mask. Note that a final + // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + function applyMask(mask) { + if (mask < 0 || mask > 7) + throw "Mask value out of range"; + for (var y = 0; y < size; y++) { + for (var x = 0; x < size; x++) { + var invert; + switch (mask) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (Math.floor(x / 3) + Math.floor(y / 2)) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: throw "Assertion error"; + } + modules[y][x] ^= invert & !isFunction[y][x]; + } + } + } + + + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + function getPenaltyScore() { + var result = 0; + + // Adjacent modules in row having same color + for (var y = 0; y < size; y++) { + for (var x = 0, runX, colorX; x < size; x++) { + if (x == 0 || modules[y][x] != colorX) { + colorX = modules[y][x]; + runX = 1; + } else { + runX++; + if (runX == 5) + result += QrCode.PENALTY_N1; + else if (runX > 5) + result++; + } + } + } + // Adjacent modules in column having same color + for (var x = 0; x < size; x++) { + for (var y = 0, runY, colorY; y < size; y++) { + if (y == 0 || modules[y][x] != colorY) { + colorY = modules[y][x]; + runY = 1; + } else { + runY++; + if (runY == 5) + result += QrCode.PENALTY_N1; + else if (runY > 5) + result++; + } + } + } + + // 2*2 blocks of modules having same color + for (var y = 0; y < size - 1; y++) { + for (var x = 0; x < size - 1; x++) { + var color = modules[y][x]; + if ( color == modules[y][x + 1] && + color == modules[y + 1][x] && + color == modules[y + 1][x + 1]) + result += QrCode.PENALTY_N2; + } + } + + // Finder-like pattern in rows + for (var y = 0; y < size; y++) { + for (var x = 0, bits = 0; x < size; x++) { + bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); + if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += QrCode.PENALTY_N3; + } + } + // Finder-like pattern in columns + for (var x = 0; x < size; x++) { + for (var y = 0, bits = 0; y < size; y++) { + bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); + if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += QrCode.PENALTY_N3; + } + } + + // Balance of black and white modules + var black = 0; + modules.forEach(function(row) { + row.forEach(function(color) { + if (color) + black++; + }); + }); + var total = size * size; + // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + for (var k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) + result += QrCode.PENALTY_N4; + return result; + } + }; + + + /*---- Public static factory functions for QrCode ----*/ + + /* + * Returns a QR Code symbol representing the specified Unicode text string at the specified error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer + * Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible + * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the + * ecl argument if it can be done without increasing the version. + */ + this.QrCode.encodeText = function(text, ecl) { + var segs = qrcodegen.QrSegment.makeSegments(text); + return this.encodeSegments(segs, ecl); + }; + + + /* + * Returns a QR Code symbol representing the given binary data string at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + */ + this.QrCode.encodeBinary = function(data, ecl) { + var seg = qrcodegen.QrSegment.makeBytes(data); + return this.encodeSegments([seg], ecl); + }; + + + /* + * Returns a QR Code symbol representing the given data segments with the given encoding parameters. + * The smallest possible QR Code version within the given range is automatically chosen for the output. + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. + * This function is considered to be lower level than simply encoding text or binary data. + */ + this.QrCode.encodeSegments = function(segs, ecl, minVersion, maxVersion, mask, boostEcl) { + if (minVersion == undefined) minVersion = MIN_VERSION; + if (maxVersion == undefined) maxVersion = MAX_VERSION; + if (mask == undefined) mask = -1; + if (boostEcl == undefined) boostEcl = true; + if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) + throw "Invalid value"; + + // Find the minimal version number to use + var version, dataUsedBits; + for (version = minVersion; ; version++) { + var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = qrcodegen.QrSegment.getTotalBits(segs, version); + if (dataUsedBits != null && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) // All versions in the range could not fit the given data + throw "Data too long"; + } + + // Increase the error correction level while the data still fits in the current version number + [this.Ecc.MEDIUM, this.Ecc.QUARTILE, this.Ecc.HIGH].forEach(function(newEcl) { + if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + }); + + // Create the data bit string by concatenating all segments + var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; + var bb = new BitBuffer(); + segs.forEach(function(seg) { + bb.appendBits(seg.mode.modeBits, 4); + bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); + seg.getBits().forEach(function(bit) { + bb.push(bit); + }); + }); + + // Add terminator and pad up to a byte if applicable + bb.appendBits(0, Math.min(4, dataCapacityBits - bb.length)); + bb.appendBits(0, (8 - bb.length % 8) % 8); + + // Pad with alternate bytes until data capacity is reached + for (var padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + if (bb.length % 8 != 0) + throw "Assertion error"; + + // Create the QR Code symbol + return new this(bb.getBytes(), mask, version, ecl); + }; + + + /*---- Public constants for QrCode ----*/ + + var MIN_VERSION = 1; + var MAX_VERSION = 40; + Object.defineProperty(this.QrCode, "MIN_VERSION", {value:MIN_VERSION}); + Object.defineProperty(this.QrCode, "MAX_VERSION", {value:MAX_VERSION}); + + + /*---- Private static helper functions QrCode ----*/ + + var QrCode = {}; // Private object to assign properties to. Not the same object as 'this.QrCode'. + + + // Returns a sequence of positions of the alignment patterns in ascending order. These positions are + // used on both the x and y axes. Each value in the resulting sequence is in the range [0, 177). + // This stateless pure function could be implemented as table of 40 variable-length lists of integers. + QrCode.getAlignmentPatternPositions = function(ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw "Version number out of range"; + else if (ver == 1) + return []; + else { + var size = ver * 4 + 17; + var numAlign = Math.floor(ver / 7) + 2; + var step; + if (ver != 32) + step = Math.ceil((size - 13) / (2 * numAlign - 2)) * 2; + else // C-C-C-Combo breaker! + step = 26; + + var result = [6]; + for (var i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.splice(1, 0, pos); + return result; + } + }; + + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + QrCode.getNumRawDataModules = function(ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw "Version number out of range"; + var result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + var numAlign = Math.floor(ver / 7) + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 18 * 2; // Subtract version information + } + return result; + }; + + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. + QrCode.getNumDataCodewords = function(ver, ecl) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw "Version number out of range"; + return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - + QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * + QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; + }; + + + /*---- Private tables of constants for QrCode ----*/ + + // For use in getPenaltyScore(), when evaluating which mask is best. + QrCode.PENALTY_N1 = 3; + QrCode.PENALTY_N2 = 3; + QrCode.PENALTY_N3 = 40; + QrCode.PENALTY_N4 = 10; + + QrCode.ECC_CODEWORDS_PER_BLOCK = [ + // Version: (note that index 0 is for padding, and is set to an illegal value) + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + [null, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Low + [null, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28], // Medium + [null, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Quartile + [null, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // High + ]; + + QrCode.NUM_ERROR_CORRECTION_BLOCKS = [ + // Version: (note that index 0 is for padding, and is set to an illegal value) + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + [null, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25], // Low + [null, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49], // Medium + [null, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68], // Quartile + [null, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81], // High + ]; + + + /*---- Public helper enumeration ----*/ + + /* + * Represents the error correction level used in a QR Code symbol. + */ + this.QrCode.Ecc = { + // Constants declared in ascending order of error protection + LOW : new Ecc(0, 1), + MEDIUM : new Ecc(1, 0), + QUARTILE: new Ecc(2, 3), + HIGH : new Ecc(3, 2), + }; + + + // Private constructor. + function Ecc(ord, fb) { + // (Public) In the range 0 to 3 (unsigned 2-bit integer) + Object.defineProperty(this, "ordinal", {value:ord}); + + // (Package-private) In the range 0 to 3 (unsigned 2-bit integer) + Object.defineProperty(this, "formatBits", {value:fb}); + } + + + + /*---- Data segment class ----*/ + + /* + * A public class that represents a character string to be encoded in a QR Code symbol. + * Each segment has a mode, and a sequence of characters that is already encoded as + * a sequence of bits. Instances of this class are immutable. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ + this.QrSegment = function(mode, numChars, bitData) { + if (numChars < 0 || !(mode instanceof Mode)) + throw "Invalid argument"; + bitData = bitData.slice(); // Make defensive copy + + // The mode indicator for this segment. + Object.defineProperty(this, "mode", {value:mode}); + + // The length of this segment's unencoded data, measured in characters. Always zero or positive. + Object.defineProperty(this, "numChars", {value:numChars}); + + // Returns a copy of all bits, which is an array of 0s and 1s. + this.getBits = function() { + return bitData.slice(); // Make defensive copy + }; + }; + + + /*---- Public static factory functions for QrSegment ----*/ + + /* + * Returns a segment representing the given binary data encoded in byte mode. + */ + this.QrSegment.makeBytes = function(data) { + var bb = new BitBuffer(); + data.forEach(function(b) { + bb.appendBits(b, 8); + }); + return new this(this.Mode.BYTE, data.length, bb); + }; + + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ + this.QrSegment.makeNumeric = function(digits) { + if (!this.NUMERIC_REGEX.test(digits)) + throw "String contains non-numeric characters"; + var bb = new BitBuffer(); + var i; + for (i = 0; i + 3 <= digits.length; i += 3) // Process groups of 3 + bb.appendBits(parseInt(digits.substr(i, 3), 10), 10); + var rem = digits.length - i; + if (rem > 0) // 1 or 2 digits remaining + bb.appendBits(parseInt(digits.substring(i), 10), rem * 3 + 1); + return new this(this.Mode.NUMERIC, digits.length, bb); + }; + + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + this.QrSegment.makeAlphanumeric = function(text) { + if (!this.ALPHANUMERIC_REGEX.test(text)) + throw "String contains unencodable characters in alphanumeric mode"; + var bb = new BitBuffer(); + var i; + for (i = 0; i + 2 <= text.length; i += 2) { // Process groups of 2 + var temp = QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45; + temp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1)); + bb.appendBits(temp, 11); + } + if (i < text.length) // 1 character remaining + bb.appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6); + return new this(this.Mode.ALPHANUMERIC, text.length, bb); + }; + + + /* + * Returns a new mutable list of zero or more segments to represent the given Unicode text string. + * The result may use various segment modes and switch modes to optimize the length of the bit stream. + */ + this.QrSegment.makeSegments = function(text) { + // Select the most efficient segment encoding automatically + if (text == "") + return []; + else if (this.NUMERIC_REGEX.test(text)) + return [this.makeNumeric(text)]; + else if (this.ALPHANUMERIC_REGEX.test(text)) + return [this.makeAlphanumeric(text)]; + else + return [this.makeBytes(toUtf8ByteArray(text))]; + }; + + + /* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ + this.QrSegment.makeEci = function(assignVal) { + var bb = new BitBuffer(); + if (0 <= assignVal && assignVal < (1 << 7)) + bb.appendBits(assignVal, 8); + else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(assignVal, 14); + } else if ((1 << 14) <= assignVal && assignVal < 1000000) { + bb.appendBits(6, 3); + bb.appendBits(assignVal, 21); + } else + throw "ECI assignment value out of range"; + return new this(this.Mode.ECI, 0, bb); + }; + + + // Package-private helper function. + this.QrSegment.getTotalBits = function(segs, version) { + if (version < MIN_VERSION || version > MAX_VERSION) + throw "Version number out of range"; + var result = 0; + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + var ccbits = seg.mode.numCharCountBits(version); + // Fail if segment length value doesn't fit in the length field's bit-width + if (seg.numChars >= (1 << ccbits)) + return null; + result += 4 + ccbits + seg.getBits().length; + } + return result; + }; + + + /*---- Constants for QrSegment ----*/ + + var QrSegment = {}; // Private object to assign properties to. Not the same object as 'this.QrSegment'. + + // (Public) Can test whether a string is encodable in numeric mode (such as by using QrSegment.makeNumeric()). + this.QrSegment.NUMERIC_REGEX = /^[0-9]*$/; + + // (Public) Can test whether a string is encodable in alphanumeric mode (such as by using QrSegment.makeAlphanumeric()). + this.QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/; + + // (Private) The set of all legal characters in alphanumeric mode, where each character value maps to the index in the string. + QrSegment.ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + + + /*---- Public helper enumeration ----*/ + + /* + * Represents the mode field of a segment. Immutable. + */ + this.QrSegment.Mode = { // Constants + NUMERIC : new Mode(0x1, [10, 12, 14]), + ALPHANUMERIC: new Mode(0x2, [ 9, 11, 13]), + BYTE : new Mode(0x4, [ 8, 16, 16]), + KANJI : new Mode(0x8, [ 8, 10, 12]), + ECI : new Mode(0x7, [ 0, 0, 0]), + }; + + + // Private constructor. + function Mode(mode, ccbits) { + // (Package-private) An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. + Object.defineProperty(this, "modeBits", {value:mode}); + + // (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. + this.numCharCountBits = function(ver) { + if ( 1 <= ver && ver <= 9) return ccbits[0]; + else if (10 <= ver && ver <= 26) return ccbits[1]; + else if (27 <= ver && ver <= 40) return ccbits[2]; + else throw "Version number out of range"; + }; + } + + + + /*---- Private helper functions and classes ----*/ + + // Returns a new array of bytes representing the given string encoded in UTF-8. + function toUtf8ByteArray(str) { + str = encodeURI(str); + var result = []; + for (var i = 0; i < str.length; i++) { + if (str.charAt(i) != "%") + result.push(str.charCodeAt(i)); + else { + result.push(parseInt(str.substr(i + 1, 2), 16)); + i += 2; + } + } + return result; + } + + + + /* + * A private helper class that computes the Reed-Solomon error correction codewords for a sequence of + * data codewords at a given degree. Objects are immutable, and the state only depends on the degree. + * This class exists because each data block in a QR Code shares the same the divisor polynomial. + * This constructor creates a Reed-Solomon ECC generator for the given degree. This could be implemented + * as a lookup table over all possible parameter values, instead of as an algorithm. + */ + function ReedSolomonGenerator(degree) { + if (degree < 1 || degree > 255) + throw "Degree out of range"; + + // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which + // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + var coefficients = []; + + // Start with the monomial x^0 + for (var i = 0; i < degree - 1; i++) + coefficients.push(0); + coefficients.push(1); + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + var root = 1; + for (var i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (var j = 0; j < coefficients.length; j++) { + coefficients[j] = ReedSolomonGenerator.multiply(coefficients[j], root); + if (j + 1 < coefficients.length) + coefficients[j] ^= coefficients[j + 1]; + } + root = ReedSolomonGenerator.multiply(root, 0x02); + } + + // Computes and returns the Reed-Solomon error correction codewords for the given + // sequence of data codewords. The returned object is always a new byte array. + // This method does not alter this object's state (because it is immutable). + this.getRemainder = function(data) { + // Compute the remainder by performing polynomial division + var result = coefficients.map(function() { return 0; }); + data.forEach(function(b) { + var factor = b ^ result.shift(); + result.push(0); + for (var i = 0; i < result.length; i++) + result[i] ^= ReedSolomonGenerator.multiply(coefficients[i], factor); + }); + return result; + }; + } + + // This static function returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and + // result are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8. + ReedSolomonGenerator.multiply = function(x, y) { + if (x >>> 8 != 0 || y >>> 8 != 0) + throw "Byte out of range"; + // Russian peasant multiplication + var z = 0; + for (var i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >>> 7) * 0x11D); + z ^= ((y >>> i) & 1) * x; + } + if (z >>> 8 != 0) + throw "Assertion error"; + return z; + }; + + + + /* + * A private helper class that represents an appendable sequence of bits. + * This constructor creates an empty bit buffer (length 0). + */ + function BitBuffer() { + + // Packs this buffer's bits into bytes in big endian, + // padding with '0' bit values, and returns the new array. + this.getBytes = function() { + var result = []; + while (result.length * 8 < this.length) + result.push(0); + this.forEach(function(bit, i) { + result[i >>> 3] |= bit << (7 - (i & 7)); + }); + return result; + }; + + // Appends the given number of low bits of the given value + // to this sequence. Requires 0 <= val < 2^len. + this.appendBits = function(val, len) { + if (len < 0 || len > 31 || val >>> len != 0) + throw "Value out of range"; + for (var i = len - 1; i >= 0; i--) // Append bit by bit + this.push((val >>> i) & 1); + }; + } + + BitBuffer.prototype = Object.create(Array.prototype); + +};
diff --git a/src/third_party/QR-Code-generator/python/qrcodegen-batch-test.py b/src/third_party/QR-Code-generator/python/qrcodegen-batch-test.py new file mode 100644 index 0000000..fa92e52 --- /dev/null +++ b/src/third_party/QR-Code-generator/python/qrcodegen-batch-test.py
@@ -0,0 +1,137 @@ +# +# QR Code generator batch test (Python 3) +# +# Runs various versions of the QR Code generator test worker as subprocesses, +# feeds each one the same random input, and compares their output for equality. +# +# Copyright (c) Project Nayuki. (MIT License) +# https://www.nayuki.io/page/qr-code-generator-library +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + +from __future__ import print_function +import itertools, random, subprocess, sys, time +if sys.version_info.major < 3: + raise RuntimeError("Requires Python 3+") + + +CHILD_PROGRAMS = [ + ["python2", "../python/qrcodegen-worker.py"], # Python 2 program + ["python3", "../python/qrcodegen-worker.py"], # Python 3 program + ["java", "-cp", "../java", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program + ["../c/qrcodegen-worker"], # C program + ["../cpp/QrCodeGeneratorWorker"], # C++ program + ["../rust/target/debug/examples/qrcodegen-worker"], # Rust program +] + + +subprocs = [] + +def main(): + # Launch workers + global subprocs + try: + for args in CHILD_PROGRAMS: + subprocs.append(subprocess.Popen(args, universal_newlines=True, + stdin=subprocess.PIPE, stdout=subprocess.PIPE)) + except FileNotFoundError: + write_all(-1) + raise + + # Check if any died + time.sleep(0.3) + if any(proc.poll() is not None for proc in subprocs): + for proc in subprocs: + if proc.poll() is None: + print(-1, file=proc.stdin) + proc.stdin.flush() + sys.exit("Error: One or more workers failed to start") + + # Do tests + for i in itertools.count(): + print("Trial {}: ".format(i), end="") + do_trial() + print() + + +def do_trial(): + mode = random.randrange(4) + if mode == 0: # Numeric + length = round((2 * 7089) ** random.random()) + data = [random.randrange(48, 58) for _ in range(length)] + elif mode == 1: # Alphanumeric + length = round((2 * 4296) ** random.random()) + data = [ord(random.choice("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")) for _ in range(length)] + elif mode == 2: # ASCII + length = round((2 * 2953) ** random.random()) + data = [random.randrange(128) for _ in range(length)] + elif mode == 3: # Byte + length = round((2 * 2953) ** random.random()) + data = [random.randrange(256) for _ in range(length)] + else: + raise AssertionError() + + write_all(length) + for b in data: + write_all(b) + + errcorlvl = random.randrange(4) + minversion = random.randint(1, 40) + maxversion = random.randint(1, 40) + if minversion > maxversion: + minversion, maxversion = maxversion, minversion + mask = -1 + if random.random() < 0.5: + mask = random.randrange(8) + boostecl = int(random.random() < 0.2) + print("mode={} len={} ecl={} minv={} maxv={} mask={} boost={}".format(mode, length, errcorlvl, minversion, maxversion, mask, boostecl), end="") + + write_all(errcorlvl) + write_all(minversion) + write_all(maxversion) + write_all(mask) + write_all(boostecl) + flush_all() + + version = read_verify() + print(" version={}".format(version), end="") + if version == -1: + return + size = version * 4 + 17 + for _ in range(size**2): + read_verify() + + +def write_all(val): + for proc in subprocs: + print(val, file=proc.stdin) + +def flush_all(): + for proc in subprocs: + proc.stdin.flush() + +def read_verify(): + val = subprocs[0].stdout.readline().rstrip("\r\n") + for proc in subprocs[1 : ]: + if proc.stdout.readline().rstrip("\r\n") != val: + raise ValueError("Mismatch") + return int(val) + + +if __name__ == "__main__": + main()
diff --git a/src/third_party/QR-Code-generator/python/qrcodegen-demo.py b/src/third_party/QR-Code-generator/python/qrcodegen-demo.py new file mode 100644 index 0000000..50e975e --- /dev/null +++ b/src/third_party/QR-Code-generator/python/qrcodegen-demo.py
@@ -0,0 +1,186 @@ +# +# QR Code generator demo (Python 2, 3) +# +# Run this command-line program with no arguments. The program computes a bunch of demonstration +# QR Codes and prints them to the console. Also, the SVG code for one QR Code is printed as a sample. +# +# Copyright (c) Project Nayuki. (MIT License) +# https://www.nayuki.io/page/qr-code-generator-library +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + +from __future__ import print_function +from qrcodegen import QrCode, QrSegment + + +def main(): + """The main application program.""" + do_basic_demo() + do_variety_demo() + do_segment_demo() + do_mask_demo() + + + +# ---- Demo suite ---- + +def do_basic_demo(): + """Creates a single QR Code, then prints it to the console.""" + text = u"Hello, world!" # User-supplied Unicode text + errcorlvl = QrCode.Ecc.LOW # Error correction level + + # Make and print the QR Code symbol + qr = QrCode.encode_text(text, errcorlvl) + print_qr(qr) + print(qr.to_svg_str(4)) + + +def do_variety_demo(): + """Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console.""" + + # Numeric mode encoding (3.33 bits per digit) + qr = QrCode.encode_text("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM) + print_qr(qr) + + # Alphanumeric mode encoding (5.5 bits per character) + qr = QrCode.encode_text("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode.Ecc.HIGH) + print_qr(qr) + + # Unicode text as UTF-8 + qr = QrCode.encode_text(u"\u3053\u3093\u306B\u3061\u0077\u0061\u3001\u4E16\u754C\uFF01\u0020\u03B1\u03B2\u03B3\u03B4", QrCode.Ecc.QUARTILE) + print_qr(qr) + + # Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) + qr = QrCode.encode_text( + "Alice was beginning to get very tired of sitting by her sister on the bank, " + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " + "a White Rabbit with pink eyes ran close by her.", QrCode.Ecc.HIGH) + print_qr(qr) + + +def do_segment_demo(): + """Creates QR Codes with manually specified segments for better compactness.""" + + # Illustration "silver" + silver0 = "THE SQUARE ROOT OF 2 IS 1." + silver1 = "41421356237309504880168872420969807856967187537694807317667973799" + qr = QrCode.encode_text(silver0 + silver1, QrCode.Ecc.LOW) + print_qr(qr) + + segs = [ + QrSegment.make_alphanumeric(silver0), + QrSegment.make_numeric(silver1)] + qr = QrCode.encode_segments(segs, QrCode.Ecc.LOW) + print_qr(qr) + + # Illustration "golden" + golden0 = u"Golden ratio \u03C6 = 1." + golden1 = u"6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374" + golden2 = u"......" + qr = QrCode.encode_text(golden0 + golden1 + golden2, QrCode.Ecc.LOW) + print_qr(qr) + + segs = [ + QrSegment.make_bytes(golden0.encode("UTF-8")), + QrSegment.make_numeric(golden1), + QrSegment.make_alphanumeric(golden2)] + qr = QrCode.encode_segments(segs, QrCode.Ecc.LOW) + print_qr(qr) + + # Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + madoka = u"\u300C\u9B54\u6CD5\u5C11\u5973\u307E\u3069\u304B\u2606\u30DE\u30AE\u30AB\u300D\u3063\u3066\u3001\u3000\u0418\u0410\u0418\u3000\uFF44\uFF45\uFF53\uFF55\u3000\u03BA\u03B1\uFF1F" + qr = QrCode.encode_text(madoka, QrCode.Ecc.LOW) + print_qr(qr) + + kanjiCharBits = [ # Kanji mode encoding (13 bits per character) + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, + 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + ] + segs = [QrSegment(QrSegment.Mode.KANJI, len(kanjiCharBits) // 13, kanjiCharBits)] + qr = QrCode.encode_segments(segs, QrCode.Ecc.LOW) + print_qr(qr) + + +def do_mask_demo(): + """Creates QR Codes with the same size and contents but different mask patterns.""" + + # Project Nayuki URL + segs = QrSegment.make_segments("https://www.nayuki.io/") + print_qr(QrCode.encode_segments(segs, QrCode.Ecc.HIGH, mask=-1)) # Automatic mask + print_qr(QrCode.encode_segments(segs, QrCode.Ecc.HIGH, mask=3)) # Force mask 3 + + # Chinese text as UTF-8 + segs = QrSegment.make_segments( + u"\u7DAD\u57FA\u767E\u79D1\uFF08\u0057\u0069\u006B\u0069\u0070\u0065\u0064\u0069\u0061\uFF0C" + "\u8046\u807D\u0069\u002F\u02CC\u0077\u026A\u006B\u1D7B\u02C8\u0070\u0069\u02D0\u0064\u0069" + "\u002E\u0259\u002F\uFF09\u662F\u4E00\u500B\u81EA\u7531\u5167\u5BB9\u3001\u516C\u958B\u7DE8" + "\u8F2F\u4E14\u591A\u8A9E\u8A00\u7684\u7DB2\u8DEF\u767E\u79D1\u5168\u66F8\u5354\u4F5C\u8A08" + "\u756B") + print_qr(QrCode.encode_segments(segs, QrCode.Ecc.MEDIUM, mask=0)) # Force mask 0 + print_qr(QrCode.encode_segments(segs, QrCode.Ecc.MEDIUM, mask=1)) # Force mask 1 + print_qr(QrCode.encode_segments(segs, QrCode.Ecc.MEDIUM, mask=5)) # Force mask 5 + print_qr(QrCode.encode_segments(segs, QrCode.Ecc.MEDIUM, mask=7)) # Force mask 7 + + + +# ---- Utilities ---- + +def print_qr(qrcode): + """Prints the given QrCode object to the console.""" + border = 4 + for y in range(-border, qrcode.get_size() + border): + for x in range(-border, qrcode.get_size() + border): + print(u"\u2588 "[1 if qrcode.get_module(x,y) else 0] * 2, end="") + print() + print() + + +# Run the main program +if __name__ == "__main__": + main()
diff --git a/src/third_party/QR-Code-generator/python/qrcodegen-worker.py b/src/third_party/QR-Code-generator/python/qrcodegen-worker.py new file mode 100644 index 0000000..c52548d --- /dev/null +++ b/src/third_party/QR-Code-generator/python/qrcodegen-worker.py
@@ -0,0 +1,87 @@ +# +# QR Code generator test worker (Python 2, 3) +# +# This program reads data and encoding parameters from standard input and writes +# QR Code bitmaps to standard output. The I/O format is one integer per line. +# Run with no command line arguments. The program is intended for automated +# batch testing of end-to-end functionality of this QR Code generator library. +# +# Copyright (c) Project Nayuki. (MIT License) +# https://www.nayuki.io/page/qr-code-generator-library +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + +from __future__ import print_function +import sys +import qrcodegen +py3 = sys.version_info.major >= 3 + + +def read_int(): + return int((input if py3 else raw_input)()) + + +def main(): + while True: + + # Read data or exit + length = read_int() + if length == -1: + break + data = [read_int() for _ in range(length)] + + # Read encoding parameters + errcorlvl = read_int() + minversion = read_int() + maxversion = read_int() + mask = read_int() + boostecl = read_int() + + # Make segments for encoding + if all((b < 128) for b in data): # Is ASCII + segs = qrcodegen.QrSegment.make_segments("".join(chr(b) for b in data)) + elif py3: + segs = [qrcodegen.QrSegment.make_bytes(bytes(data))] + else: + segs = [qrcodegen.QrSegment.make_bytes("".join(chr(b) for b in data))] + + try: # Try to make QR Code symbol + qr = qrcodegen.QrCode.encode_segments(segs, ECC_LEVELS[errcorlvl], minversion, maxversion, mask, boostecl != 0) + # Print grid of modules + print(qr.get_version()) + for y in range(qr.get_size()): + for x in range(qr.get_size()): + print(1 if qr.get_module(x, y) else 0) + + except ValueError as e: + if e.args[0] != "Data too long": + raise + print(-1) + sys.stdout.flush() + + +ECC_LEVELS = ( + qrcodegen.QrCode.Ecc.LOW, + qrcodegen.QrCode.Ecc.MEDIUM, + qrcodegen.QrCode.Ecc.QUARTILE, + qrcodegen.QrCode.Ecc.HIGH, +) + + +if __name__ == "__main__": + main()
diff --git a/src/third_party/QR-Code-generator/python/qrcodegen.py b/src/third_party/QR-Code-generator/python/qrcodegen.py new file mode 100644 index 0000000..9a5b32c --- /dev/null +++ b/src/third_party/QR-Code-generator/python/qrcodegen.py
@@ -0,0 +1,839 @@ +# +# QR Code generator library (Python 2, 3) +# +# Copyright (c) Project Nayuki. (MIT License) +# https://www.nayuki.io/page/qr-code-generator-library +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + +import itertools, re, sys + + +""" +This module "qrcodegen", public members: +- Class QrCode: + - Function encode_text(str text, QrCode.Ecc ecl) -> QrCode + - Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode + - Function encode_segments(list<QrSegment> segs, QrCode.Ecc ecl, + int minversion=1, int maxversion=40, mask=-1, boostecl=true) -> QrCode + - Constants int MIN_VERSION, MAX_VERSION + - Constructor QrCode(bytes datacodewords, int mask, int version, QrCode.Ecc ecl) + - Method get_version() -> int + - Method get_size() -> int + - Method get_error_correction_level() -> QrCode.Ecc + - Method get_mask() -> int + - Method get_module(int x, int y) -> bool + - Method to_svg_str(int border) -> str + - Enum Ecc: + - Constants LOW, MEDIUM, QUARTILE, HIGH + - Field int ordinal +- Class QrSegment: + - Function make_bytes(bytes data) -> QrSegment + - Function make_numeric(str digits) -> QrSegment + - Function make_alphanumeric(str text) -> QrSegment + - Function make_segments(str text) -> list<QrSegment> + - Function make_eci(int assignval) -> QrSegment + - Constructor QrSegment(QrSegment.Mode mode, int numch, list<int> bitdata) + - Method get_mode() -> QrSegment.Mode + - Method get_num_chars() -> int + - Method get_bits() -> list<int> + - Constants regex NUMERIC_REGEX, ALPHANUMERIC_REGEX + - Enum Mode: + - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI +""" + + +# ---- QR Code symbol class ---- + +class QrCode(object): + """Represents an immutable square grid of black or white cells for a QR Code symbol. This class covers the + QR Code model 2 specification, supporting all versions (sizes) from 1 to 40, all 4 error correction levels.""" + + # ---- Public static factory functions ---- + + @staticmethod + def encode_text(text, ecl): + """Returns a QR Code symbol representing the specified Unicode text string at the specified error correction level. + As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer + Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible + QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the + ecl argument if it can be done without increasing the version.""" + segs = QrSegment.make_segments(text) + return QrCode.encode_segments(segs, ecl) + + + @staticmethod + def encode_binary(data, ecl): + """Returns a QR Code symbol representing the given binary data string at the given error correction level. + This function always encodes using the binary segment mode, not any text mode. The maximum number of + bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.""" + if not isinstance(data, (bytes, bytearray)): + raise TypeError("Byte string/list expected") + return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl) + + + @staticmethod + def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True): + """Returns a QR Code symbol representing the given data segments with the given encoding parameters. + The smallest possible QR Code version within the given range is automatically chosen for the output. + This function allows the user to create a custom sequence of segments that switches + between modes (such as alphanumeric and binary) to encode text more efficiently. + This function is considered to be lower level than simply encoding text or binary data.""" + + if not (QrCode.MIN_VERSION <= minversion <= maxversion <= QrCode.MAX_VERSION) or not (-1 <= mask <= 7): + raise ValueError("Invalid value") + + # Find the minimal version number to use + for version in range(minversion, maxversion + 1): + datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available + datausedbits = QrSegment.get_total_bits(segs, version) + if datausedbits is not None and datausedbits <= datacapacitybits: + break # This version number is found to be suitable + if version >= maxversion: # All versions in the range could not fit the given data + raise ValueError("Data too long") + if datausedbits is None: + raise AssertionError() + + # Increase the error correction level while the data still fits in the current version number + for newecl in (QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH): + if boostecl and datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8: + ecl = newecl + + # Create the data bit string by concatenating all segments + datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 + bb = _BitBuffer() + for seg in segs: + bb.append_bits(seg.get_mode().get_mode_bits(), 4) + bb.append_bits(seg.get_num_chars(), seg.get_mode().num_char_count_bits(version)) + bb.extend(seg._bitdata) + + # Add terminator and pad up to a byte if applicable + bb.append_bits(0, min(4, datacapacitybits - len(bb))) + bb.append_bits(0, -len(bb) % 8) # Note: Python's modulo on negative numbers behaves better than C family languages + + # Pad with alternate bytes until data capacity is reached + for padbyte in itertools.cycle((0xEC, 0x11)): + if len(bb) >= datacapacitybits: + break + bb.append_bits(padbyte, 8) + assert len(bb) % 8 == 0 + + # Create the QR Code symbol + return QrCode(bb.get_bytes(), mask, version, ecl) + + + # ---- Public constants ---- + + MIN_VERSION = 1 + MAX_VERSION = 40 + + + # ---- Constructor ---- + + def __init__(self, datacodewords, mask, version, errcorlvl): + """Creates a new QR Code symbol with the given version number, error correction level, binary data array, + and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor + that should not be invoked directly by the user. To go one level up, see the QrCode.encode_segments() function.""" + + # Check arguments and handle simple scalar fields + if not (-1 <= mask <= 7): + raise ValueError("Mask value out of range") + if not (QrCode.MIN_VERSION <= version <= QrCode.MAX_VERSION): + raise ValueError("Version value out of range") + if not isinstance(errcorlvl, QrCode.Ecc): + raise TypeError("QrCode.Ecc expected") + self._version = version + self._errcorlvl = errcorlvl + self._size = version * 4 + 17 + + if len(datacodewords) != QrCode._get_num_data_codewords(version, errcorlvl): + raise ValueError("Invalid array length") + # Initialize grids of modules + self._modules = [[False] * self._size for _ in range(self._size)] # The modules of the QR symbol; start with entirely white grid + self._isfunction = [[False] * self._size for _ in range(self._size)] # Indicates function modules that are not subjected to masking + # Draw function patterns, draw all codewords + self._draw_function_patterns() + allcodewords = self._append_error_correction(datacodewords) + self._draw_codewords(allcodewords) + + # Handle masking + if mask == -1: # Automatically choose best mask + minpenalty = 1 << 32 + for i in range(8): + self._draw_format_bits(i) + self._apply_mask(i) + penalty = self._get_penalty_score() + if penalty < minpenalty: + mask = i + minpenalty = penalty + self._apply_mask(i) # Undoes the mask due to XOR + assert 0 <= mask <= 7 + self._draw_format_bits(mask) # Overwrite old format bits + self._apply_mask(mask) # Apply the final choice of mask + self._mask = mask + + + # ---- Accessor methods ---- + + def get_version(self): + """Returns this QR Code symbol's version number, which is always between 1 and 40 (inclusive).""" + return self._version + + def get_size(self): + """Returns the width and height of this QR Code symbol, measured in modules. + Always equal to version * 4 + 17, in the range 21 to 177.""" + return self._size + + def get_error_correction_level(self): + """Returns the error correction level used in this QR Code symbol.""" + return self._errcorlvl + + def get_mask(self): + """Returns the mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + Note that even if a constructor was called with automatic masking requested + (mask = -1), the resulting object will still have a mask value between 0 and 7.""" + return self._mask + + def get_module(self, x, y): + """Returns the color of the module (pixel) at the given coordinates, which is either + False for white or True for black. The top left corner has the coordinates (x=0, y=0). + If the given coordinates are out of bounds, then False (white) is returned.""" + return (0 <= x < self._size) and (0 <= y < self._size) and self._modules[y][x] + + + # ---- Public instance methods ---- + + def to_svg_str(self, border): + """Based on the given number of border modules to add as padding, this returns a + string whose contents represents an SVG XML file that depicts this QR Code symbol.""" + if border < 0: + raise ValueError("Border must be non-negative") + parts = [] + for y in range(-border, self._size + border): + for x in range(-border, self._size + border): + if self.get_module(x, y): + parts.append("M{},{}h1v1h-1z".format(x + border, y + border)) + return """<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 {0} {0}" stroke="none"> + <rect width="100%" height="100%" fill="#FFFFFF"/> + <path d="{1}" fill="#000000"/> +</svg> +""".format(self._size + border * 2, " ".join(parts)) + + + # ---- Private helper methods for constructor: Drawing function modules ---- + + def _draw_function_patterns(self): + # Draw horizontal and vertical timing patterns + for i in range(self._size): + self._set_function_module(6, i, i % 2 == 0) + self._set_function_module(i, 6, i % 2 == 0) + + # Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + self._draw_finder_pattern(3, 3) + self._draw_finder_pattern(self._size - 4, 3) + self._draw_finder_pattern(3, self._size - 4) + + # Draw numerous alignment patterns + alignpatpos = QrCode._get_alignment_pattern_positions(self._version) + numalign = len(alignpatpos) + skips = ((0, 0), (0, numalign - 1), (numalign - 1, 0)) # Skip the three finder corners + for i in range(numalign): + for j in range(numalign): + if (i, j) not in skips: + self._draw_alignment_pattern(alignpatpos[i], alignpatpos[j]) + + # Draw configuration data + self._draw_format_bits(0) # Dummy mask value; overwritten later in the constructor + self._draw_version() + + + def _draw_format_bits(self, mask): + """Draws two copies of the format bits (with its own error correction code) + based on the given mask and this object's error correction level field.""" + # Calculate error correction code and pack bits + data = self._errcorlvl.formatbits << 3 | mask # errCorrLvl is uint2, mask is uint3 + rem = data + for _ in range(10): + rem = (rem << 1) ^ ((rem >> 9) * 0x537) + data = data << 10 | rem + data ^= 0x5412 # uint15 + assert data >> 15 == 0 + + # Draw first copy + for i in range(0, 6): + self._set_function_module(8, i, (data >> i) & 1 != 0) + self._set_function_module(8, 7, (data >> 6) & 1 != 0) + self._set_function_module(8, 8, (data >> 7) & 1 != 0) + self._set_function_module(7, 8, (data >> 8) & 1 != 0) + for i in range(9, 15): + self._set_function_module(14 - i, 8, (data >> i) & 1 != 0) + + # Draw second copy + for i in range(0, 8): + self._set_function_module(self._size - 1 - i, 8, (data >> i) & 1 != 0) + for i in range(8, 15): + self._set_function_module(8, self._size - 15 + i, (data >> i) & 1 != 0) + self._set_function_module(8, self._size - 8, True) + + + def _draw_version(self): + """Draws two copies of the version bits (with its own error correction code), + based on this object's version field (which only has an effect for 7 <= version <= 40).""" + if self._version < 7: + return + + # Calculate error correction code and pack bits + rem = self._version # version is uint6, in the range [7, 40] + for _ in range(12): + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25) + data = self._version << 12 | rem # uint18 + assert data >> 18 == 0 + + # Draw two copies + for i in range(18): + bit = (data >> i) & 1 != 0 + a, b = self._size - 11 + i % 3, i // 3 + self._set_function_module(a, b, bit) + self._set_function_module(b, a, bit) + + + def _draw_finder_pattern(self, x, y): + """Draws a 9*9 finder pattern including the border separator, with the center module at (x, y).""" + for i in range(-4, 5): + for j in range(-4, 5): + xx, yy = x + j, y + i + if (0 <= xx < self._size) and (0 <= yy < self._size): + # Chebyshev/infinity norm + self._set_function_module(xx, yy, max(abs(i), abs(j)) not in (2, 4)) + + + def _draw_alignment_pattern(self, x, y): + """Draws a 5*5 alignment pattern, with the center module at (x, y).""" + for i in range(-2, 3): + for j in range(-2, 3): + self._set_function_module(x + j, y + i, max(abs(i), abs(j)) != 1) + + + def _set_function_module(self, x, y, isblack): + """Sets the color of a module and marks it as a function module. + Only used by the constructor. Coordinates must be in range.""" + assert type(isblack) is bool + self._modules[y][x] = isblack + self._isfunction[y][x] = True + + + # ---- Private helper methods for constructor: Codewords and masking ---- + + def _append_error_correction(self, data): + """Returns a new byte string representing the given data with the appropriate error correction + codewords appended to it, based on this object's version and error correction level.""" + version = self._version + assert len(data) == QrCode._get_num_data_codewords(version, self._errcorlvl) + + # Calculate parameter numbers + numblocks = QrCode._NUM_ERROR_CORRECTION_BLOCKS[self._errcorlvl.ordinal][version] + blockecclen = QrCode._ECC_CODEWORDS_PER_BLOCK[self._errcorlvl.ordinal][version] + rawcodewords = QrCode._get_num_raw_data_modules(version) // 8 + numshortblocks = numblocks - rawcodewords % numblocks + shortblocklen = rawcodewords // numblocks + + # Split data into blocks and append ECC to each block + blocks = [] + rs = _ReedSolomonGenerator(blockecclen) + k = 0 + for i in range(numblocks): + dat = data[k : k + shortblocklen - blockecclen + (0 if i < numshortblocks else 1)] + k += len(dat) + ecc = rs.get_remainder(dat) + if i < numshortblocks: + dat.append(0) + dat.extend(ecc) + blocks.append(dat) + assert k == len(data) + + # Interleave (not concatenate) the bytes from every block into a single sequence + result = [] + for i in range(len(blocks[0])): + for (j, blk) in enumerate(blocks): + # Skip the padding byte in short blocks + if i != shortblocklen - blockecclen or j >= numshortblocks: + result.append(blk[i]) + assert len(result) == rawcodewords + return result + + + def _draw_codewords(self, data): + """Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + data area of this QR Code symbol. Function modules need to be marked off before this is called.""" + assert len(data) == QrCode._get_num_raw_data_modules(self._version) // 8 + + i = 0 # Bit index into the data + # Do the funny zigzag scan + for right in range(self._size - 1, 0, -2): # Index of right column in each column pair + if right <= 6: + right -= 1 + for vert in range(self._size): # Vertical counter + for j in range(2): + x = right - j # Actual x coordinate + upward = (right + 1) & 2 == 0 + y = (self._size - 1 - vert) if upward else vert # Actual y coordinate + if not self._isfunction[y][x] and i < len(data) * 8: + self._modules[y][x] = (data[i >> 3] >> (7 - (i & 7))) & 1 != 0 + i += 1 + # If there are any remainder bits (0 to 7), they are already + # set to 0/false/white when the grid of modules was initialized + assert i == len(data) * 8 + + + def _apply_mask(self, mask): + """XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical + properties, calling applyMask(m) twice with the same value is equivalent to no change at all. + This means it is possible to apply a mask, undo it, and try another mask. Note that a final + well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.).""" + if not (0 <= mask <= 7): + raise ValueError("Mask value out of range") + masker = QrCode._MASK_PATTERNS[mask] + for y in range(self._size): + for x in range(self._size): + self._modules[y][x] ^= (masker(x, y) == 0) and (not self._isfunction[y][x]) + + + def _get_penalty_score(self): + """Calculates and returns the penalty score based on state of this QR Code's current modules. + This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.""" + result = 0 + size = self._size + modules = self._modules + + # Adjacent modules in row having same color + for y in range(size): + for x in range(size): + if x == 0 or modules[y][x] != colorx: + colorx = modules[y][x] + runx = 1 + else: + runx += 1 + if runx == 5: + result += QrCode._PENALTY_N1 + elif runx > 5: + result += 1 + # Adjacent modules in column having same color + for x in range(size): + for y in range(size): + if y == 0 or modules[y][x] != colory: + colory = modules[y][x] + runy = 1 + else: + runy += 1 + if runy == 5: + result += QrCode._PENALTY_N1 + elif runy > 5: + result += 1 + + # 2*2 blocks of modules having same color + for y in range(size - 1): + for x in range(size - 1): + if modules[y][x] == modules[y][x + 1] == modules[y + 1][x] == modules[y + 1][x + 1]: + result += QrCode._PENALTY_N2 + + # Finder-like pattern in rows + for y in range(size): + bits = 0 + for x in range(size): + bits = ((bits << 1) & 0x7FF) | (1 if modules[y][x] else 0) + if x >= 10 and bits in (0x05D, 0x5D0): # Needs 11 bits accumulated + result += QrCode._PENALTY_N3 + # Finder-like pattern in columns + for x in range(size): + bits = 0 + for y in range(size): + bits = ((bits << 1) & 0x7FF) | (1 if modules[y][x] else 0) + if y >= 10 and bits in (0x05D, 0x5D0): # Needs 11 bits accumulated + result += QrCode._PENALTY_N3 + + # Balance of black and white modules + black = sum((1 if cell else 0) for row in modules for cell in row) + total = size**2 + # Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + for k in itertools.count(): + if (9-k)*total <= black*20 <= (11+k)*total: + break + result += QrCode._PENALTY_N4 + return result + + + # ---- Private static helper functions ---- + + @staticmethod + def _get_alignment_pattern_positions(ver): + """Returns a sequence of positions of the alignment patterns in ascending order. These positions are + used on both the x and y axes. Each value in the resulting sequence is in the range [0, 177). + This stateless pure function could be implemented as table of 40 variable-length lists of integers.""" + if not (QrCode.MIN_VERSION <= ver <= QrCode.MAX_VERSION): + raise ValueError("Version number out of range") + elif ver == 1: + return [] + else: + numalign = ver // 7 + 2 + if ver != 32: + # ceil((size - 13) / (2*numalign - 2)) * 2 + step = (ver * 4 + numalign * 2 + 1) // (2 * numalign - 2) * 2 + else: # C-C-C-Combo breaker! + step = 26 + result = [6] + pos = ver * 4 + 10 + for _ in range(numalign - 1): + result.insert(1, pos) + pos -= step + return result + + + @staticmethod + def _get_num_raw_data_modules(ver): + """Returns the number of data bits that can be stored in a QR Code of the given version number, after + all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.""" + if not (QrCode.MIN_VERSION <= ver <= QrCode.MAX_VERSION): + raise ValueError("Version number out of range") + result = (16 * ver + 128) * ver + 64 + if ver >= 2: + numalign = ver // 7 + 2 + result -= (25 * numalign - 10) * numalign - 55 + if ver >= 7: + result -= 18 * 2 # Subtract version information + return result + + + @staticmethod + def _get_num_data_codewords(ver, ecl): + """Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + QR Code of the given version number and error correction level, with remainder bits discarded. + This stateless pure function could be implemented as a (40*4)-cell lookup table.""" + if not (QrCode.MIN_VERSION <= ver <= QrCode.MAX_VERSION): + raise ValueError("Version number out of range") + return QrCode._get_num_raw_data_modules(ver) // 8 \ + - QrCode._ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] \ + * QrCode._NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver] + + + # ---- Private tables of constants ---- + + # For use in getPenaltyScore(), when evaluating which mask is best. + _PENALTY_N1 = 3 + _PENALTY_N2 = 3 + _PENALTY_N3 = 40 + _PENALTY_N4 = 10 + + _ECC_CODEWORDS_PER_BLOCK = ( + # Version: (note that index 0 is for padding, and is set to an illegal value) + # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + (None, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30), # Low + (None, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28), # Medium + (None, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30), # Quartile + (None, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30)) # High + + _NUM_ERROR_CORRECTION_BLOCKS = ( + # Version: (note that index 0 is for padding, and is set to an illegal value) + # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + (None, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25), # Low + (None, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49), # Medium + (None, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68), # Quartile + (None, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81)) # High + + _MASK_PATTERNS = ( + (lambda x, y: (x + y) % 2 ), + (lambda x, y: y % 2 ), + (lambda x, y: x % 3 ), + (lambda x, y: (x + y) % 3 ), + (lambda x, y: (x // 3 + y // 2) % 2 ), + (lambda x, y: x * y % 2 + x * y % 3 ), + (lambda x, y: (x * y % 2 + x * y % 3) % 2 ), + (lambda x, y: ((x + y) % 2 + x * y % 3) % 2), + ) + + + # ---- Public helper enumeration ---- + + class Ecc(object): + """Represents the error correction level used in a QR Code symbol.""" + # Private constructor + def __init__(self, i, fb): + self.ordinal = i # (Public) In the range 0 to 3 (unsigned 2-bit integer) + self.formatbits = fb # (Package-private) In the range 0 to 3 (unsigned 2-bit integer) + + # Public constants. Create them outside the class. + Ecc.LOW = Ecc(0, 1) + Ecc.MEDIUM = Ecc(1, 0) + Ecc.QUARTILE = Ecc(2, 3) + Ecc.HIGH = Ecc(3, 2) + + + +# ---- Data segment class ---- + +class QrSegment(object): + """Represents a character string to be encoded in a QR Code symbol. Each segment has + a mode, and a sequence of characters that is already encoded as a sequence of bits. + Instances of this class are immutable. + This segment class imposes no length restrictions, but QR Codes have restrictions. + Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + Any segment longer than this is meaningless for the purpose of generating QR Codes.""" + + # ---- Public static factory functions ---- + + @staticmethod + def make_bytes(data): + """Returns a segment representing the given binary data encoded in byte mode.""" + py3 = sys.version_info.major >= 3 + if (py3 and isinstance(data, str)) or (not py3 and isinstance(data, unicode)): + raise TypeError("Byte string/list expected") + if not py3 and isinstance(data, str): + data = bytearray(data) + bb = _BitBuffer() + for b in data: + bb.append_bits(b, 8) + return QrSegment(QrSegment.Mode.BYTE, len(data), bb) + + + @staticmethod + def make_numeric(digits): + """Returns a segment representing the given string of decimal digits encoded in numeric mode.""" + if QrSegment.NUMERIC_REGEX.match(digits) is None: + raise ValueError("String contains non-numeric characters") + bb = _BitBuffer() + for i in range(0, len(digits) - 2, 3): # Process groups of 3 + bb.append_bits(int(digits[i : i + 3]), 10) + rem = len(digits) % 3 + if rem > 0: # 1 or 2 digits remaining + bb.append_bits(int(digits[-rem : ]), rem * 3 + 1) + return QrSegment(QrSegment.Mode.NUMERIC, len(digits), bb) + + + @staticmethod + def make_alphanumeric(text): + """Returns a segment representing the given text string encoded in alphanumeric mode. + The characters allowed are: 0 to 9, A to Z (uppercase only), space, + dollar, percent, asterisk, plus, hyphen, period, slash, colon.""" + if QrSegment.ALPHANUMERIC_REGEX.match(text) is None: + raise ValueError("String contains unencodable characters in alphanumeric mode") + bb = _BitBuffer() + for i in range(0, len(text) - 1, 2): # Process groups of 2 + temp = QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i]] * 45 + temp += QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i + 1]] + bb.append_bits(temp, 11) + if len(text) % 2 > 0: # 1 character remaining + bb.append_bits(QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[-1]], 6) + return QrSegment(QrSegment.Mode.ALPHANUMERIC, len(text), bb) + + + @staticmethod + def make_segments(text): + """Returns a new mutable list of zero or more segments to represent the given Unicode text string. + The result may use various segment modes and switch modes to optimize the length of the bit stream.""" + if not (isinstance(text, str) or (sys.version_info.major < 3 and isinstance(text, unicode))): + raise TypeError("Text string expected") + + # Select the most efficient segment encoding automatically + if text == "": + return [] + elif QrSegment.NUMERIC_REGEX.match(text) is not None: + return [QrSegment.make_numeric(text)] + elif QrSegment.ALPHANUMERIC_REGEX.match(text) is not None: + return [QrSegment.make_alphanumeric(text)] + else: + return [QrSegment.make_bytes(text.encode("UTF-8"))] + + + @staticmethod + def make_eci(assignval): + """Returns a segment representing an Extended Channel Interpretation + (ECI) designator with the given assignment value.""" + bb = _BitBuffer() + if 0 <= assignval < (1 << 7): + bb.append_bits(assignval, 8) + elif (1 << 7) <= assignval < (1 << 14): + bb.append_bits(2, 2) + bb.append_bits(assignval, 14) + elif (1 << 14) <= assignval < 1000000: + bb.append_bits(6, 3) + bb.append_bits(assignval, 21) + else: + raise ValueError("ECI assignment value out of range") + return QrSegment(QrSegment.Mode.ECI, 0, bb) + + + # ---- Constructor ---- + + def __init__(self, mode, numch, bitdata): + if numch < 0 or not isinstance(mode, QrSegment.Mode): + raise ValueError() + self._mode = mode + self._numchars = numch + self._bitdata = list(bitdata) # Make defensive copy + + + # ---- Accessor methods ---- + + def get_mode(self): + return self._mode + + def get_num_chars(self): + return self._numchars + + def get_bits(self): + return list(self._bitdata) # Make defensive copy + + + # Package-private helper function. + @staticmethod + def get_total_bits(segs, version): + if not (QrCode.MIN_VERSION <= version <= QrCode.MAX_VERSION): + raise ValueError("Version number out of range") + result = 0 + for seg in segs: + ccbits = seg.get_mode().num_char_count_bits(version) + # Fail if segment length value doesn't fit in the length field's bit-width + if seg.get_num_chars() >= (1 << ccbits): + return None + result += 4 + ccbits + len(seg._bitdata) + return result + + + # ---- Constants ---- + + # (Public) Can test whether a string is encodable in numeric mode (such as by using make_numeric()) + NUMERIC_REGEX = re.compile(r"[0-9]*\Z") + + # (Public) Can test whether a string is encodable in alphanumeric mode (such as by using make_alphanumeric()) + ALPHANUMERIC_REGEX = re.compile(r"[A-Z0-9 $%*+./:-]*\Z") + + # (Private) Dictionary of "0"->0, "A"->10, "$"->37, etc. + _ALPHANUMERIC_ENCODING_TABLE = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")} + + + # ---- Public helper enumeration ---- + + class Mode(object): + """The mode field of a segment. Immutable.""" + + # Private constructor + def __init__(self, modebits, charcounts): + self._modebits = modebits + self._charcounts = charcounts + + # Package-private method + def get_mode_bits(self): + """Returns an unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object.""" + return self._modebits + + # Package-private method + def num_char_count_bits(self, ver): + """Returns the bit width of the segment character count field for this mode object at the given version number.""" + if 1 <= ver <= 9: return self._charcounts[0] + elif 10 <= ver <= 26: return self._charcounts[1] + elif 27 <= ver <= 40: return self._charcounts[2] + else: raise ValueError("Version number out of range") + + # Public constants. Create them outside the class. + Mode.NUMERIC = Mode(0x1, (10, 12, 14)) + Mode.ALPHANUMERIC = Mode(0x2, ( 9, 11, 13)) + Mode.BYTE = Mode(0x4, ( 8, 16, 16)) + Mode.KANJI = Mode(0x8, ( 8, 10, 12)) + Mode.ECI = Mode(0x7, ( 0, 0, 0)) + + + +# ---- Private helper classes ---- + +class _ReedSolomonGenerator(object): + """Computes the Reed-Solomon error correction codewords for a sequence of data codewords + at a given degree. Objects are immutable, and the state only depends on the degree. + This class exists because each data block in a QR Code shares the same the divisor polynomial.""" + + def __init__(self, degree): + """Creates a Reed-Solomon ECC generator for the given degree. This could be implemented + as a lookup table over all possible parameter values, instead of as an algorithm.""" + if degree < 1 or degree > 255: + raise ValueError("Degree out of range") + + # Start with the monomial x^0 + self.coefficients = [0] * (degree - 1) + [1] + + # Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + # drop the highest term, and store the rest of the coefficients in order of descending powers. + # Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + root = 1 + for _ in range(degree): # Unused variable i + # Multiply the current product by (x - r^i) + for j in range(degree): + self.coefficients[j] = _ReedSolomonGenerator._multiply(self.coefficients[j], root) + if j + 1 < degree: + self.coefficients[j] ^= self.coefficients[j + 1] + root = _ReedSolomonGenerator._multiply(root, 0x02) + + + def get_remainder(self, data): + """Computes and returns the Reed-Solomon error correction codewords for the given + sequence of data codewords. The returned object is always a new byte list. + This method does not alter this object's state (because it is immutable).""" + # Compute the remainder by performing polynomial division + result = [0] * len(self.coefficients) + for b in data: + factor = b ^ result.pop(0) + result.append(0) + for i in range(len(result)): + result[i] ^= _ReedSolomonGenerator._multiply(self.coefficients[i], factor) + return result + + + @staticmethod + def _multiply(x, y): + """Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result + are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8.""" + if x >> 8 != 0 or y >> 8 != 0: + raise ValueError("Byte out of range") + # Russian peasant multiplication + z = 0 + for i in reversed(range(8)): + z = (z << 1) ^ ((z >> 7) * 0x11D) + z ^= ((y >> i) & 1) * x + assert z >> 8 == 0 + return z + + + +class _BitBuffer(list): + """An appendable sequence of bits (0's and 1's).""" + + def get_bytes(self): + """Packs this buffer's bits into bytes in big endian, + padding with '0' bit values, and returns the new list.""" + result = [0] * ((len(self) + 7) // 8) + for (i, bit) in enumerate(self): + result[i >> 3] |= bit << (7 - (i & 7)) + return result + + def append_bits(self, val, n): + """Appends the given number of low bits of the given value + to this sequence. Requires 0 <= val < 2^n.""" + if n < 0 or val >> n != 0: + raise ValueError("Value out of range") + self.extend(((val >> i) & 1) for i in reversed(range(n)))
diff --git a/src/third_party/QR-Code-generator/python/setup.cfg b/src/third_party/QR-Code-generator/python/setup.cfg new file mode 100644 index 0000000..2a9acf1 --- /dev/null +++ b/src/third_party/QR-Code-generator/python/setup.cfg
@@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1
diff --git a/src/third_party/QR-Code-generator/python/setup.py b/src/third_party/QR-Code-generator/python/setup.py new file mode 100644 index 0000000..6026451 --- /dev/null +++ b/src/third_party/QR-Code-generator/python/setup.py
@@ -0,0 +1,113 @@ +# +# QR Code generator Distutils script (Python 2, 3) +# +# Copyright (c) Project Nayuki. (MIT License) +# https://www.nayuki.io/page/qr-code-generator-library +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + +import setuptools + + +setuptools.setup( + name = "qrcodegen", + description = "High quality QR Code generator library for Python 2 and 3", + version = "1.2.0", + platforms = "OS Independent", + license = "MIT License", + + author = "Project Nayuki", + author_email = "me@nayuki.io", + url = "https://www.nayuki.io/page/qr-code-generator-library", + + classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + "Topic :: Multimedia :: Graphics", + "Topic :: Software Development :: Libraries :: Python Modules", + ], + + long_description = """========================= +QR Code generator library +========================= + + +Introduction +------------ + +This project aims to be the best, clearest QR Code generator library. The primary goals are flexible options and absolute correctness. Secondary goals are compact implementation size and good documentation comments. + +Home page with live JavaScript demo, extensive descriptions, and competitor comparisons: https://www.nayuki.io/page/qr-code-generator-library + + +Features +-------- + +Core features: + +* Available in 6 programming languages, all with nearly equal functionality: Java, JavaScript, Python, C++, C, Rust +* Significantly shorter code but more documentation comments compared to competing libraries +* Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard +* Output formats: Raw modules/pixels of the QR symbol, SVG XML string +* Encodes numeric and special-alphanumeric text in less space than general text +* Open source code under the permissive MIT License + +Manual parameters: + +* User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data +* User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one +* User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number +* User can create a list of data segments manually and add ECI segments + + +Usage +----- + +Install this package by downloading the source code ZIP file from PyPI_, or by running ``pip install qrcodegen``. + +Examples: + + from qrcodegen import * + + # Simple operation + qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM) + svg = qr0.to_svg_str(4) + + # Manual operation + segs = QrSegment.make_segments("3141592653589793238462643383") + qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False) + border = 4 + for y in range(-border, qr1.get_size() + border): + for x in range(-border, qr1.get_size() + border): + color = qr1.get_module(x, y) # False for white, True for black + # (... paint the module onto pixels ...) + +More complete set of examples: https://github.com/nayuki/QR-Code-generator/blob/master/python/qrcodegen-demo.py . + +API documentation is in the source file itself, with a summary comment at the top: https://github.com/nayuki/QR-Code-generator/blob/master/python/qrcodegen.py . + +.. _PyPI: https://pypi.python.org/pypi/qrcodegen""", + + py_modules = ["qrcodegen"], +)
diff --git a/src/third_party/QR-Code-generator/rust/Cargo.toml b/src/third_party/QR-Code-generator/rust/Cargo.toml new file mode 100644 index 0000000..4fe927c --- /dev/null +++ b/src/third_party/QR-Code-generator/rust/Cargo.toml
@@ -0,0 +1,11 @@ +[package] +name = "qrcodegen" +version = "1.2.1" +authors = ["Project Nayuki"] +description = "High-quality QR Code generator library" +homepage = "https://www.nayuki.io/page/qr-code-generator-library" +repository = "https://github.com/nayuki/QR-Code-generator" +readme = "Readme.markdown" +keywords = ["qr-code", "barcode", "encoder", "image"] +categories = ["encoding", "multimedia::images"] +license = "MIT"
diff --git a/src/third_party/QR-Code-generator/rust/Readme.markdown b/src/third_party/QR-Code-generator/rust/Readme.markdown new file mode 100644 index 0000000..fd8cb50 --- /dev/null +++ b/src/third_party/QR-Code-generator/rust/Readme.markdown
@@ -0,0 +1,57 @@ +QR Code generator library +========================= + + +Introduction +------------ + +This project aims to be the best, clearest QR Code generator library. The primary goals are flexible options and absolute correctness. Secondary goals are compact implementation size and good documentation comments. + +Home page with live JavaScript demo, extensive descriptions, and competitor comparisons: https://www.nayuki.io/page/qr-code-generator-library + + +Features +-------- + +Core features: + +* Available in 6 programming languages, all with nearly equal functionality: Java, JavaScript, Python, C++, C, Rust +* Significantly shorter code but more documentation comments compared to competing libraries +* Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard +* Output formats: Raw modules/pixels of the QR symbol, SVG XML string +* Encodes numeric and special-alphanumeric text in less space than general text +* Open source code under the permissive MIT License + +Manual parameters: + +* User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data +* User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one +* User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number +* User can create a list of data segments manually and add ECI segments + + +Examples +-------- + + extern crate qrcodegen; + use qrcodegen::QrCode; + use qrcodegen::QrCodeEcc; + use qrcodegen::QrSegment; + + // Simple operation + let qr0 = QrCode::encode_text("Hello, world!", + QrCodeEcc::Medium).unwrap(); + let svg = qr0.to_svg_string(4); + + // Manual operation + let chrs: Vec<char> = "3141592653589793238462643383".chars().collect(); + let segs = QrSegment::make_segments(&chrs); + let qr1 = QrCode::encode_segments_advanced( + &segs, QrCodeEcc::High, 5, 5, Some(2), false).unwrap(); + for y in 0 .. qr1.size() { + for x in 0 .. qr1.size() { + (... paint qr1.get_module(x, y) ...) + } + } + +More complete set of examples: https://github.com/nayuki/QR-Code-generator/blob/master/rust/examples/qrcodegen-demo.rs .
diff --git a/src/third_party/QR-Code-generator/rust/examples/qrcodegen-demo.rs b/src/third_party/QR-Code-generator/rust/examples/qrcodegen-demo.rs new file mode 100644 index 0000000..1fa2d3f --- /dev/null +++ b/src/third_party/QR-Code-generator/rust/examples/qrcodegen-demo.rs
@@ -0,0 +1,184 @@ +/* + * QR Code generator demo (Rust) + * + * Run this command-line program with no arguments. The program computes a bunch of demonstration + * QR Codes and prints them to the console. Also, the SVG code for one QR Code is printed as a sample. + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +extern crate qrcodegen; +use qrcodegen::Mask; +use qrcodegen::QrCode; +use qrcodegen::QrCodeEcc; +use qrcodegen::QrSegment; +use qrcodegen::QrCode_MAX_VERSION; +use qrcodegen::QrCode_MIN_VERSION; + + +// The main application program. +fn main() { + do_basic_demo(); + do_variety_demo(); + do_segment_demo(); + do_mask_demo(); +} + + + +/*---- Demo suite ----*/ + +// Creates a single QR Code, then prints it to the console. +fn do_basic_demo() { + let text: &'static str = "Hello, world!"; // User-supplied Unicode text + let errcorlvl: QrCodeEcc = QrCodeEcc::Low; // Error correction level + + // Make and print the QR Code symbol + let qr: QrCode = QrCode::encode_text(text, errcorlvl).unwrap(); + print_qr(&qr); + println!("{}", qr.to_svg_string(4)); +} + + +// Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console. +fn do_variety_demo() { + // Numeric mode encoding (3.33 bits per digit) + let qr = QrCode::encode_text("314159265358979323846264338327950288419716939937510", QrCodeEcc::Medium).unwrap(); + print_qr(&qr); + + // Alphanumeric mode encoding (5.5 bits per character) + let qr = QrCode::encode_text("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCodeEcc::High).unwrap(); + print_qr(&qr); + + // Unicode text as UTF-8 + let qr = QrCode::encode_text("こんにちwa、世界! αβγδ", QrCodeEcc::Quartile).unwrap(); + print_qr(&qr); + + // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) + let qr = QrCode::encode_text(concat!( + "Alice was beginning to get very tired of sitting by her sister on the bank, ", + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, ", + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice ", + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, ", + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a ", + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly ", + "a White Rabbit with pink eyes ran close by her."), QrCodeEcc::High).unwrap(); + print_qr(&qr); +} + + +// Creates QR Codes with manually specified segments for better compactness. +fn do_segment_demo() { + // Illustration "silver" + let silver0 = "THE SQUARE ROOT OF 2 IS 1."; + let silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; + let qr = QrCode::encode_text(&[silver0, silver1].concat(), QrCodeEcc::Low).unwrap(); + print_qr(&qr); + + let segs = vec![ + QrSegment::make_alphanumeric(&to_chars(silver0)), + QrSegment::make_numeric(&to_chars(silver1)), + ]; + let qr = QrCode::encode_segments(&segs, QrCodeEcc::Low).unwrap(); + print_qr(&qr); + + // Illustration "golden" + let golden0 = "Golden ratio φ = 1."; + let golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; + let golden2 = "......"; + let qr = QrCode::encode_text(&[golden0, golden1, golden2].concat(), QrCodeEcc::Low).unwrap(); + print_qr(&qr); + + let segs = vec![ + QrSegment::make_bytes(golden0.as_bytes()), + QrSegment::make_numeric(&to_chars(golden1)), + QrSegment::make_alphanumeric(&to_chars(golden2)), + ]; + let qr = QrCode::encode_segments(&segs, QrCodeEcc::Low).unwrap(); + print_qr(&qr); + + // Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + let madoka = "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?"; + let qr = QrCode::encode_text(madoka, QrCodeEcc::Low).unwrap(); + print_qr(&qr); + + let kanjichars: Vec<u32> = vec![ // Kanji mode encoding (13 bits per character) + 0x0035, 0x1002, 0x0FC0, 0x0AED, 0x0AD7, + 0x015C, 0x0147, 0x0129, 0x0059, 0x01BD, + 0x018D, 0x018A, 0x0036, 0x0141, 0x0144, + 0x0001, 0x0000, 0x0249, 0x0240, 0x0249, + 0x0000, 0x0104, 0x0105, 0x0113, 0x0115, + 0x0000, 0x0208, 0x01FF, 0x0008, + ]; + let mut bb = qrcodegen::BitBuffer(Vec::new()); + for c in &kanjichars { + bb.append_bits(*c, 13); + } + let segs = vec![ + QrSegment::new(qrcodegen::QrSegmentMode::Kanji, kanjichars.len(), bb.0), + ]; + let qr = QrCode::encode_segments(&segs, QrCodeEcc::Low).unwrap(); + print_qr(&qr); +} + + +// Creates QR Codes with the same size and contents but different mask patterns. +fn do_mask_demo() { + // Project Nayuki URL + let segs = QrSegment::make_segments(&to_chars("https://www.nayuki.io/")); + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true).unwrap(); // Automatic mask + print_qr(&qr); + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(3)), true).unwrap(); // Force mask 3 + print_qr(&qr); + + // Chinese text as UTF-8 + let segs = QrSegment::make_segments(&to_chars("維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫")); + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(0)), true).unwrap(); // Force mask 0 + print_qr(&qr); + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(1)), true).unwrap(); // Force mask 1 + print_qr(&qr); + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(5)), true).unwrap(); // Force mask 5 + print_qr(&qr); + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(7)), true).unwrap(); // Force mask 7 + print_qr(&qr); +} + + + +/*---- Utilities ----*/ + +// Prints the given QrCode object to the console. +fn print_qr(qr: &QrCode) { + let border: i32 = 4; + for y in -border .. qr.size() + border { + for x in -border .. qr.size() + border { + let c: char = if qr.get_module(x, y) { '█' } else { ' ' }; + print!("{0}{0}", c); + } + println!(); + } + println!(); +} + + +// Converts the given borrowed string slice to a new character vector. +fn to_chars(text: &str) -> Vec<char> { + text.chars().collect() +}
diff --git a/src/third_party/QR-Code-generator/rust/examples/qrcodegen-worker.rs b/src/third_party/QR-Code-generator/rust/examples/qrcodegen-worker.rs new file mode 100644 index 0000000..5537f19 --- /dev/null +++ b/src/third_party/QR-Code-generator/rust/examples/qrcodegen-worker.rs
@@ -0,0 +1,117 @@ +/* + * QR Code generator test worker (Rust) + * + * This program reads data and encoding parameters from standard input and writes + * QR Code bitmaps to standard output. The I/O format is one integer per line. + * Run with no command line arguments. The program is intended for automated + * batch testing of end-to-end functionality of this QR Code generator library. + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +extern crate qrcodegen; +use qrcodegen::Mask; +use qrcodegen::QrCode; +use qrcodegen::QrCodeEcc; +use qrcodegen::QrSegment; +use qrcodegen::Version; + + +fn main() { + loop { + + // Read data length or exit + let length: i16 = read_int(); + if length == -1 { + break; + } + + // Read data bytes + let mut data = Vec::<u8>::with_capacity(length as usize); + for _ in 0 .. length { + let b: i16 = read_int(); + assert_eq!((b as u8) as i16, b, "Byte value out of range"); + data.push(b as u8); + } + let isascii: bool = data.iter().all(|b| *b < 128); + + // Read encoding parameters + let errcorlvl = read_int(); + let minversion = read_int(); + let maxversion = read_int(); + let mask = read_int(); + let boostecl = read_int(); + assert!(0 <= errcorlvl && errcorlvl <= 3); + assert!((qrcodegen::QrCode_MIN_VERSION.value() as i16) <= minversion + && minversion <= maxversion + && maxversion <= (qrcodegen::QrCode_MAX_VERSION.value() as i16)); + assert!(-1 <= mask && mask <= 7); + assert!(boostecl >> 1 == 0); + + // Make segments for encoding + let segs: Vec<QrSegment>; + if isascii { + let chrs: Vec<char> = std::str::from_utf8(&data).unwrap().chars().collect(); + segs = QrSegment::make_segments(&chrs); + } else { + segs = vec![QrSegment::make_bytes(&data)]; + } + + // Try to make QR Code symbol + let msk = if mask == -1 { None } else { Some(Mask::new(mask as u8)) }; + match QrCode::encode_segments_advanced(&segs, ECC_LEVELS[errcorlvl as usize], + Version::new(minversion as u8), Version::new(maxversion as u8), msk, boostecl != 0) { + + Some(qr) => { + // Print grid of modules + println!("{}", qr.version().value()); + for y in 0 .. qr.size() { + for x in 0 .. qr.size() { + println!("{}", qr.get_module(x, y) as i8); + } + } + }, + None => println!("-1"), + } + use std::io::Write; + std::io::stdout().flush().unwrap(); + } +} + + +fn read_int() -> i16 { + let mut line = String::new(); + std::io::stdin().read_line(&mut line).unwrap(); + let mut chrs: Vec<char> = line.chars().collect(); + assert_eq!(chrs.pop().unwrap(), '\n'); + let line: String = chrs.iter().cloned().collect(); + match line.parse::<i16>() { + Ok(x) => x, + Err(_) => panic!("Invalid number"), + } +} + + +static ECC_LEVELS: [QrCodeEcc; 4] = [ + QrCodeEcc::Low, + QrCodeEcc::Medium, + QrCodeEcc::Quartile, + QrCodeEcc::High, +];
diff --git a/src/third_party/QR-Code-generator/rust/src/lib.rs b/src/third_party/QR-Code-generator/rust/src/lib.rs new file mode 100644 index 0000000..d4b41ae --- /dev/null +++ b/src/third_party/QR-Code-generator/rust/src/lib.rs
@@ -0,0 +1,1117 @@ +/* + * QR Code generator library (Rust) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + + +/*---- QrCode functionality ----*/ + +// Represents an immutable square grid of black and white cells for a QR Code symbol, and +// provides static functions to create a QR Code from user-supplied textual or binary data. +// This struct covers the QR Code model 2 specification, supporting all versions (sizes) +// from 1 to 40, all 4 error correction levels, and only 3 character encoding modes. +pub struct QrCode { + + // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). + version: Version, + + // The width and height of this QR Code symbol, measured in modules. + // Always equal to version × 4 + 17, in the range 21 to 177. + size: i32, + + // The error correction level used in this QR Code symbol. + errorcorrectionlevel: QrCodeEcc, + + // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + // Note that even if a constructor was called with automatic masking requested + // (mask = -1), the resulting object will still have a mask value between 0 and 7. + mask: Mask, + + // The modules of this QR Code symbol (false = white, true = black) + modules: Vec<bool>, + + // Indicates function modules that are not subjected to masking + isfunction: Vec<bool>, + +} + + +impl QrCode { + + /*---- Public static factory functions ----*/ + + // Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + // As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode + // code points (not UTF-8 code units) if the low error correction level is used. The smallest possible + // QR Code version is automatically chosen for the output. The ECC level of the result may be higher than + // the ecl argument if it can be done without increasing the version. Returns a wrapped QrCode if successful, + // or None if the data is too long to fit in any version at the given ECC level. + pub fn encode_text(text: &str, ecl: QrCodeEcc) -> Option<QrCode> { + let chrs: Vec<char> = text.chars().collect(); + let segs: Vec<QrSegment> = QrSegment::make_segments(&chrs); + QrCode::encode_segments(&segs, ecl) + } + + + // Returns a QR Code symbol representing the given binary data string at the given error correction level. + // This function always encodes using the binary segment mode, not any text mode. The maximum number of + // bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + // The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + // Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. + pub fn encode_binary(data: &[u8], ecl: QrCodeEcc) -> Option<QrCode> { + let segs: Vec<QrSegment> = vec![QrSegment::make_bytes(data)]; + QrCode::encode_segments(&segs, ecl) + } + + + // Returns a QR Code symbol representing the given data segments at the given error correction + // level or higher. The smallest possible QR Code version is automatically chosen for the output. + // This function allows the user to create a custom sequence of segments that switches + // between modes (such as alphanumeric and binary) to encode text more efficiently. + // This function is considered to be lower level than simply encoding text or binary data. + // Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. + pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Option<QrCode> { + QrCode::encode_segments_advanced(segs, ecl, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true) + } + + + // Returns a QR Code symbol representing the given data segments with the given encoding parameters. + // The smallest possible QR Code version within the given range is automatically chosen for the output. + // This function allows the user to create a custom sequence of segments that switches + // between modes (such as alphanumeric and binary) to encode text more efficiently. + // This function is considered to be lower level than simply encoding text or binary data. + // Returns a wrapped QrCode if successful, or None if the data is too long to fit + // in any version in the given range at the given ECC level. + pub fn encode_segments_advanced(segs: &[QrSegment], mut ecl: QrCodeEcc, + minversion: Version, maxversion: Version, mask: Option<Mask>, boostecl: bool) -> Option<QrCode> { + assert!(minversion.value() <= maxversion.value(), "Invalid value"); + + // Find the minimal version number to use + let mut version = minversion; + let datausedbits: usize; + loop { + // Number of data bits available + let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; + if let Some(n) = QrSegment::get_total_bits(segs, version) { + if n <= datacapacitybits { + datausedbits = n; + break; // This version number is found to be suitable + } + } + if version.value() >= maxversion.value() { // All versions in the range could not fit the given data + return None; + } + version = Version::new(version.value() + 1); + } + + // Increase the error correction level while the data still fits in the current version number + for newecl in &[QrCodeEcc::Medium, QrCodeEcc::Quartile, QrCodeEcc::High] { + if boostecl && datausedbits <= QrCode::get_num_data_codewords(version, *newecl) * 8 { + ecl = *newecl; + } + } + + // Create the data bit string by concatenating all segments + let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; + let mut bb = BitBuffer(Vec::new()); + for seg in segs { + bb.append_bits(seg.mode.mode_bits(), 4); + bb.append_bits(seg.numchars as u32, seg.mode.num_char_count_bits(version)); + bb.0.extend_from_slice(&seg.data); + } + + // Add terminator and pad up to a byte if applicable + let numzerobits = std::cmp::min(4, datacapacitybits - bb.0.len()); + bb.append_bits(0, numzerobits as u8); + let numzerobits = bb.0.len().wrapping_neg() & 7; + bb.append_bits(0, numzerobits as u8); + + // Pad with alternate bytes until data capacity is reached + let mut padbyte: u32 = 0xEC; + while bb.0.len() < datacapacitybits { + bb.append_bits(padbyte, 8); + padbyte ^= 0xEC ^ 0x11; + } + assert_eq!(bb.0.len() % 8, 0, "Assertion error"); + + let mut bytes = vec![0u8; bb.0.len() / 8]; + for (i, bit) in bb.0.iter().enumerate() { + bytes[i >> 3] |= (*bit as u8) << (7 - (i & 7)); + } + + // Create the QR Code symbol + Some(QrCode::encode_codewords(version, ecl, &bytes, mask)) + } + + + /*---- Constructors ----*/ + + // Creates a new QR Code symbol with the given version number, error correction level, + // binary data array, and mask number. This is a cumbersome low-level constructor that + // should not be invoked directly by the user. To go one level up, see the encode_segments() function. + pub fn encode_codewords(ver: Version, ecl: QrCodeEcc, datacodewords: &[u8], mask: Option<Mask>) -> QrCode { + // Initialize fields + let size: usize = (ver.value() as usize) * 4 + 17; + let mut result = QrCode { + version: ver, + size: size as i32, + mask: Mask::new(0), // Dummy value + errorcorrectionlevel: ecl, + modules: vec![false; size * size], // Entirely white grid + isfunction: vec![false; size * size], + }; + + // Draw function patterns, draw all codewords, do masking + result.draw_function_patterns(); + let allcodewords: Vec<u8> = result.append_error_correction(datacodewords); + result.draw_codewords(&allcodewords); + result.handle_constructor_masking(mask); + result + } + + + // Returns this QR Code's version, in the range [1, 40]. + pub fn version(&self) -> Version { + self.version + } + + + // Returns this QR Code's size, in the range [21, 177]. + pub fn size(&self) -> i32 { + self.size + } + + + // Returns this QR Code's error correction level. + pub fn error_correction_level(&self) -> QrCodeEcc { + self.errorcorrectionlevel + } + + + // Returns this QR Code's mask, in the range [0, 7]. + pub fn mask(&self) -> Mask { + self.mask + } + + + // Returns the color of the module (pixel) at the given coordinates, which is either + // false for white or true for black. The top left corner has the coordinates (x=0, y=0). + // If the given coordinates are out of bounds, then 0 (white) is returned. + pub fn get_module(&self, x: i32, y: i32) -> bool { + 0 <= x && x < self.size && 0 <= y && y < self.size && self.module(x, y) + } + + + // Returns the color of the module at the given coordinates, which must be in bounds. + fn module(&self, x: i32, y: i32) -> bool { + self.modules[(y * self.size + x) as usize] + } + + + // Returns a mutable reference to the module's color at the given coordinates, which must be in bounds. + fn module_mut(&mut self, x: i32, y: i32) -> &mut bool { + &mut self.modules[(y * self.size + x) as usize] + } + + + // Based on the given number of border modules to add as padding, this returns a + // string whose contents represents an SVG XML file that depicts this QR Code symbol. + // Note that Unix newlines (\n) are always used, regardless of the platform. + pub fn to_svg_string(&self, border: i32) -> String { + assert!(border >= 0, "Border must be non-negative"); + let mut result: String = String::new(); + result.push_str("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + result.push_str("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + let dimension = self.size.checked_add(border.checked_mul(2).unwrap()).unwrap(); + result.push_str(&format!( + "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 {0} {0}\" stroke=\"none\">\n", dimension)); + result.push_str("\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n"); + result.push_str("\t<path d=\""); + let mut head: bool = true; + for y in -border .. self.size + border { + for x in -border .. self.size + border { + if self.get_module(x, y) { + if head { + head = false; + } else { + result.push_str(" "); + } + result.push_str(&format!("M{},{}h1v1h-1z", x + border, y + border)); + } + } + } + result.push_str("\" fill=\"#000000\"/>\n"); + result.push_str("</svg>\n"); + result + } + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + fn draw_function_patterns(&mut self) { + // Draw horizontal and vertical timing patterns + let size: i32 = self.size; + for i in 0 .. size { + self.set_function_module(6, i, i % 2 == 0); + self.set_function_module(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + self.draw_finder_pattern(3, 3); + self.draw_finder_pattern(size - 4, 3); + self.draw_finder_pattern(3, size - 4); + + // Draw numerous alignment patterns + let alignpatpos: Vec<i32> = QrCode::get_alignment_pattern_positions(self.version); + let numalign: usize = alignpatpos.len(); + for i in 0 .. numalign { + for j in 0 .. numalign { + if i == 0 && j == 0 || i == 0 && j == numalign - 1 || i == numalign - 1 && j == 0 { + continue; // Skip the three finder corners + } else { + self.draw_alignment_pattern(alignpatpos[i], alignpatpos[j]); + } + } + } + + // Draw configuration data + self.draw_format_bits(Mask::new(0)); // Dummy mask value; overwritten later in the constructor + self.draw_version(); + } + + + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. + fn draw_format_bits(&mut self, mask: Mask) { + // Calculate error correction code and pack bits + let size: i32 = self.size; + // errcorrlvl is uint2, mask is uint3 + let mut data: u32 = self.errorcorrectionlevel.format_bits() << 3 | (mask.value() as u32); + let mut rem: u32 = data; + for _ in 0 .. 10 { + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + } + data = data << 10 | rem; + data ^= 0x5412; // uint15 + assert_eq!(data >> 15, 0, "Assertion error"); + + // Draw first copy + for i in 0 .. 6 { + self.set_function_module(8, i, (data >> i) & 1 != 0); + } + self.set_function_module(8, 7, (data >> 6) & 1 != 0); + self.set_function_module(8, 8, (data >> 7) & 1 != 0); + self.set_function_module(7, 8, (data >> 8) & 1 != 0); + for i in 9 .. 15 { + self.set_function_module(14 - i, 8, (data >> i) & 1 != 0); + } + + // Draw second copy + for i in 0 .. 8 { + self.set_function_module(size - 1 - i, 8, (data >> i) & 1 != 0); + } + for i in 8 .. 15 { + self.set_function_module(8, size - 15 + i, (data >> i) & 1 != 0); + } + self.set_function_module(8, size - 8, true); + } + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field (which only has an effect for 7 <= version <= 40). + fn draw_version(&mut self) { + if self.version.value() < 7 { + return; + } + + // Calculate error correction code and pack bits + let mut rem: u32 = self.version.value() as u32; // version is uint6, in the range [7, 40] + for _ in 0 .. 12 { + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + } + let data: u32 = (self.version.value() as u32) << 12 | rem; // uint18 + assert!(data >> 18 == 0, "Assertion error"); + + // Draw two copies + for i in 0 .. 18 { + let bit: bool = (data >> i) & 1 != 0; + let a: i32 = self.size - 11 + i % 3; + let b: i32 = i / 3; + self.set_function_module(a, b, bit); + self.set_function_module(b, a, bit); + } + } + + + // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + fn draw_finder_pattern(&mut self, x: i32, y: i32) { + for i in -4 .. 5 { + for j in -4 .. 5 { + let xx: i32 = x + j; + let yy: i32 = y + i; + if 0 <= xx && xx < self.size && 0 <= yy && yy < self.size { + let dist: i32 = std::cmp::max(i.abs(), j.abs()); // Chebyshev/infinity norm + self.set_function_module(xx, yy, dist != 2 && dist != 4); + } + } + } + } + + + // Draws a 5*5 alignment pattern, with the center module at (x, y). + fn draw_alignment_pattern(&mut self, x: i32, y: i32) { + for i in -2 .. 3 { + for j in -2 .. 3 { + self.set_function_module(x + j, y + i, std::cmp::max(i.abs(), j.abs()) != 1); + } + } + } + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in range. + fn set_function_module(&mut self, x: i32, y: i32, isblack: bool) { + *self.module_mut(x, y) = isblack; + self.isfunction[(y * self.size + x) as usize] = true; + } + + + /*---- Private helper methods for constructor: Codewords and masking ----*/ + + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. + fn append_error_correction(&self, data: &[u8]) -> Vec<u8> { + assert_eq!(data.len(), QrCode::get_num_data_codewords(self.version, self.errorcorrectionlevel), "Illegal argument"); + + // Calculate parameter numbers + let numblocks: usize = QrCode::table_get(&NUM_ERROR_CORRECTION_BLOCKS, self.version, self.errorcorrectionlevel); + let blockecclen: usize = QrCode::table_get(&ECC_CODEWORDS_PER_BLOCK, self.version, self.errorcorrectionlevel); + let rawcodewords: usize = QrCode::get_num_raw_data_modules(self.version) / 8; + let numshortblocks: usize = numblocks - rawcodewords % numblocks; + let shortblocklen: usize = rawcodewords / numblocks; + + // Split data into blocks and append ECC to each block + let mut blocks = Vec::<Vec<u8>>::with_capacity(numblocks); + let rs = ReedSolomonGenerator::new(blockecclen); + let mut k: usize = 0; + for i in 0 .. numblocks { + let mut dat = Vec::<u8>::with_capacity(shortblocklen + 1); + dat.extend_from_slice(&data[k .. k + shortblocklen - blockecclen + ((i >= numshortblocks) as usize)]); + k += dat.len(); + let ecc: Vec<u8> = rs.get_remainder(&dat); + if i < numshortblocks { + dat.push(0); + } + dat.extend_from_slice(&ecc); + blocks.push(dat); + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + let mut result = Vec::<u8>::with_capacity(rawcodewords); + for i in 0 .. shortblocklen + 1 { + for j in 0 .. numblocks { + // Skip the padding byte in short blocks + if i != shortblocklen - blockecclen || j >= numshortblocks { + result.push(blocks[j][i]); + } + } + } + result + } + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code symbol. Function modules need to be marked off before this is called. + fn draw_codewords(&mut self, data: &[u8]) { + assert_eq!(data.len(), QrCode::get_num_raw_data_modules(self.version) / 8, "Illegal argument"); + + let mut i: usize = 0; // Bit index into the data + // Do the funny zigzag scan + let mut right: i32 = self.size - 1; + while right >= 1 { // Index of right column in each column pair + if right == 6 { + right = 5; + } + for vert in 0 .. self.size { // Vertical counter + for j in 0 .. 2 { + let x: i32 = right - j; // Actual x coordinate + let upward: bool = (right + 1) & 2 == 0; + let y: i32 = if upward { self.size - 1 - vert } else { vert }; // Actual y coordinate + if !self.isfunction[(y * self.size + x) as usize] && i < data.len() * 8 { + *self.module_mut(x, y) = (data[i >> 3] >> (7 - (i & 7))) & 1 != 0; + i += 1; + } + // If there are any remainder bits (0 to 7), they are already + // set to 0/false/white when the grid of modules was initialized + } + } + right -= 2; + } + assert_eq!(i, data.len() * 8, "Assertion error"); + } + + + // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical + // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. + // This means it is possible to apply a mask, undo it, and try another mask. Note that a final + // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + fn apply_mask(&mut self, mask: Mask) { + let mask = mask.value(); + for y in 0 .. self.size { + for x in 0 .. self.size { + let invert: bool = match mask { + 0 => (x + y) % 2 == 0, + 1 => y % 2 == 0, + 2 => x % 3 == 0, + 3 => (x + y) % 3 == 0, + 4 => (x / 3 + y / 2) % 2 == 0, + 5 => x * y % 2 + x * y % 3 == 0, + 6 => (x * y % 2 + x * y % 3) % 2 == 0, + 7 => ((x + y) % 2 + x * y % 3) % 2 == 0, + _ => unreachable!(), + }; + *self.module_mut(x, y) ^= invert & !self.isfunction[(y * self.size + x) as usize]; + } + } + } + + + // A messy helper function for the constructors. This QR Code must be in an unmasked state when this + // method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed. + // This method applies and returns the actual mask chosen, from 0 to 7. + fn handle_constructor_masking(&mut self, mut mask: Option<Mask>) { + if mask.is_none() { // Automatically choose best mask + let mut minpenalty: i32 = std::i32::MAX; + for i in 0u8 .. 8 { + let newmask = Mask::new(i); + self.draw_format_bits(newmask); + self.apply_mask(newmask); + let penalty: i32 = self.get_penalty_score(); + if penalty < minpenalty { + mask = Some(newmask); + minpenalty = penalty; + } + self.apply_mask(newmask); // Undoes the mask due to XOR + } + } + let msk: Mask = mask.unwrap(); + self.draw_format_bits(msk); // Overwrite old format bits + self.apply_mask(msk); // Apply the final choice of mask + self.mask = msk; + } + + + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + fn get_penalty_score(&self) -> i32 { + let mut result: i32 = 0; + let size: i32 = self.size; + + // Adjacent modules in row having same color + for y in 0 .. size { + let mut colorx: bool = false; + let mut runx: i32 = 0; + for x in 0 .. size { + if x == 0 || self.module(x, y) != colorx { + colorx = self.module(x, y); + runx = 1; + } else { + runx += 1; + if runx == 5 { + result += PENALTY_N1; + } else if runx > 5 { + result += 1; + } + } + } + } + // Adjacent modules in column having same color + for x in 0 .. size { + let mut colory: bool = false; + let mut runy: i32 = 0; + for y in 0 .. size { + if y == 0 || self.module(x, y) != colory { + colory = self.module(x, y); + runy = 1; + } else { + runy += 1; + if runy == 5 { + result += PENALTY_N1; + } else if runy > 5 { + result += 1; + } + } + } + } + + // 2*2 blocks of modules having same color + for y in 0 .. size - 1 { + for x in 0 .. size - 1 { + let color: bool = self.module(x, y); + if color == self.module(x + 1, y) && + color == self.module(x, y + 1) && + color == self.module(x + 1, y + 1) { + result += PENALTY_N2; + } + } + } + + // Finder-like pattern in rows + for y in 0 .. size { + let mut bits: u32 = 0; + for x in 0 .. size { + bits = ((bits << 1) & 0x7FF) | (self.module(x, y) as u32); + if x >= 10 && (bits == 0x05D || bits == 0x5D0) { // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + } + // Finder-like pattern in columns + for x in 0 .. size { + let mut bits: u32 = 0; + for y in 0 .. size { + bits = ((bits << 1) & 0x7FF) | (self.module(x, y) as u32); + if y >= 10 && (bits == 0x05D || bits == 0x5D0) { // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + } + + // Balance of black and white modules + let mut black: i32 = 0; + for color in &self.modules { + black += *color as i32; + } + let total: i32 = size * size; + // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + let mut k: i32 = 0; + while black*20 < (9-k)*total || black*20 > (11+k)*total { + result += PENALTY_N4; + k += 1; + } + result + } + + + /*---- Private static helper functions ----*/ + + // Returns a set of positions of the alignment patterns in ascending order. These positions are + // used on both the x and y axes. Each value in the resulting list is in the range [0, 177). + // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. + fn get_alignment_pattern_positions(ver: Version) -> Vec<i32> { + let ver = ver.value(); + if ver == 1 { + vec![] + } else { + let numalign: i32 = (ver as i32) / 7 + 2; + let step: i32 = if ver != 32 { + // ceil((size - 13) / (2*numAlign - 2)) * 2 + ((ver as i32) * 4 + numalign * 2 + 1) / (2 * numalign - 2) * 2 + } else { // C-C-C-Combo breaker! + 26 + }; + let mut result = vec![6i32]; + let mut pos: i32 = (ver as i32) * 4 + 10; + for _ in 0 .. numalign - 1 { + result.insert(1, pos); + pos -= step; + } + result + } + } + + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + fn get_num_raw_data_modules(ver: Version) -> usize { + let ver = ver.value(); + let mut result: usize = (16 * (ver as usize) + 128) * (ver as usize) + 64; + if ver >= 2 { + let numalign: usize = (ver as usize) / 7 + 2; + result -= (25 * numalign - 10) * numalign - 55; + if ver >= 7 { + result -= 18 * 2; // Subtract version information + } + } + result + } + + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. + fn get_num_data_codewords(ver: Version, ecl: QrCodeEcc) -> usize { + QrCode::get_num_raw_data_modules(ver) / 8 + - QrCode::table_get(&ECC_CODEWORDS_PER_BLOCK, ver, ecl) + * QrCode::table_get(&NUM_ERROR_CORRECTION_BLOCKS, ver, ecl) + } + + + // Returns an entry from the given table based on the given values. + fn table_get(table: &'static [[i8; 41]; 4], ver: Version, ecl: QrCodeEcc) -> usize { + table[ecl.ordinal()][ver.value() as usize] as usize + } + +} + + +/*---- Public constants ----*/ + +pub const QrCode_MIN_VERSION: Version = Version( 1); +pub const QrCode_MAX_VERSION: Version = Version(40); + + +/*---- Private tables of constants ----*/ + +// For use in get_penalty_score(), when evaluating which mask is best. +const PENALTY_N1: i32 = 3; +const PENALTY_N2: i32 = 3; +const PENALTY_N3: i32 = 40; +const PENALTY_N4: i32 = 10; + + +static ECC_CODEWORDS_PER_BLOCK: [[i8; 41]; 4] = [ + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + [-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Low + [-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28], // Medium + [-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Quartile + [-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // High +]; + +static NUM_ERROR_CORRECTION_BLOCKS: [[i8; 41]; 4] = [ + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + [-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25], // Low + [-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49], // Medium + [-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68], // Quartile + [-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81], // High +]; + + + +/*---- QrCodeEcc functionality ----*/ + +// Represents the error correction level used in a QR Code symbol. Immutable. +#[derive(Clone, Copy)] +pub enum QrCodeEcc { + Low, + Medium, + Quartile, + High, +} + + +impl QrCodeEcc { + + // Returns an unsigned 2-bit integer (in the range 0 to 3). + fn ordinal(&self) -> usize { + match *self { + QrCodeEcc::Low => 0, + QrCodeEcc::Medium => 1, + QrCodeEcc::Quartile => 2, + QrCodeEcc::High => 3, + } + } + + + // Returns an unsigned 2-bit integer (in the range 0 to 3). + fn format_bits(&self) -> u32 { + match *self { + QrCodeEcc::Low => 1, + QrCodeEcc::Medium => 0, + QrCodeEcc::Quartile => 3, + QrCodeEcc::High => 2, + } + } + +} + + + +/*---- ReedSolomonGenerator functionality ----*/ + +// Computes the Reed-Solomon error correction codewords for a sequence of data codewords +// at a given degree. Objects are immutable, and the state only depends on the degree. +// This class exists because each data block in a QR Code shares the same the divisor polynomial. +struct ReedSolomonGenerator { + + // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which + // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + coefficients: Vec<u8>, + +} + + +impl ReedSolomonGenerator { + + // Creates a Reed-Solomon ECC generator for the given degree. This could be implemented + // as a lookup table over all possible parameter values, instead of as an algorithm. + fn new(degree: usize) -> ReedSolomonGenerator { + assert!(1 <= degree && degree <= 255, "Degree out of range"); + // Start with the monomial x^0 + let mut coefs = vec![0u8; degree - 1]; + coefs.push(1); + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + let mut root: u8 = 1; + for _ in 0 .. degree { // Unused variable i + // Multiply the current product by (x - r^i) + for j in 0 .. degree { + coefs[j] = ReedSolomonGenerator::multiply(coefs[j], root); + if j + 1 < coefs.len() { + coefs[j] ^= coefs[j + 1]; + } + } + root = ReedSolomonGenerator::multiply(root, 0x02); + } + ReedSolomonGenerator { + coefficients: coefs + } + } + + + // Computes and returns the Reed-Solomon error correction codewords for the given sequence of data codewords. + fn get_remainder(&self, data: &[u8]) -> Vec<u8> { + // Compute the remainder by performing polynomial division + let mut result = vec![0u8; self.coefficients.len()]; + for b in data { + let factor: u8 = b ^ result.remove(0); + result.push(0); + for (x, y) in result.iter_mut().zip(self.coefficients.iter()) { + *x ^= ReedSolomonGenerator::multiply(*y, factor); + } + } + result + } + + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result + // are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8. + fn multiply(x: u8, y: u8) -> u8 { + // Russian peasant multiplication + let mut z: u8 = 0; + for i in (0 .. 8).rev() { + z = (z << 1) ^ ((z >> 7) * 0x1D); + z ^= ((y >> i) & 1) * x; + } + z + } + +} + + + +/*---- QrSegment functionality ----*/ + +// Represents a character string to be encoded in a QR Code symbol. +// Each segment has a mode, and a sequence of characters that is already +// encoded as a sequence of bits. Instances of this struct are immutable. +pub struct QrSegment { + + // The mode indicator for this segment. + mode: QrSegmentMode, + + // The length of this segment's unencoded data, measured in characters. + numchars: usize, + + // The bits of this segment. + data: Vec<bool>, + +} + + +impl QrSegment { + + /*---- Static factory functions ----*/ + + // Returns a segment representing the given binary data encoded in byte mode. + pub fn make_bytes(data: &[u8]) -> QrSegment { + let mut bb = BitBuffer(Vec::with_capacity(data.len() * 8)); + for b in data { + bb.append_bits(*b as u32, 8); + } + QrSegment::new(QrSegmentMode::Byte, data.len(), bb.0) + } + + + // Returns a segment representing the given string of decimal digits encoded in numeric mode. + // Panics if the string contains non-digit characters. + pub fn make_numeric(text: &[char]) -> QrSegment { + let mut bb = BitBuffer(Vec::with_capacity(text.len() * 3 + (text.len() + 2) / 3)); + let mut accumdata: u32 = 0; + let mut accumcount: u32 = 0; + for c in text { + assert!('0' <= *c && *c <= '9', "String contains non-numeric characters"); + accumdata = accumdata * 10 + ((*c as u32) - ('0' as u32)); + accumcount += 1; + if accumcount == 3 { + bb.append_bits(accumdata, 10); + accumdata = 0; + accumcount = 0; + } + } + if accumcount > 0 { // 1 or 2 digits remaining + bb.append_bits(accumdata, (accumcount as u8) * 3 + 1); + } + QrSegment::new(QrSegmentMode::Numeric, text.len(), bb.0) + } + + + // Returns a segment representing the given text string encoded in alphanumeric mode. + // The characters allowed are: 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, + // plus, hyphen, period, slash, colon. Panics if the string contains non-encodable characters. + pub fn make_alphanumeric(text: &[char]) -> QrSegment { + let mut bb = BitBuffer(Vec::with_capacity(text.len() * 5 + (text.len() + 1) / 2)); + let mut accumdata: u32 = 0; + let mut accumcount: u32 = 0; + for c in text { + let i = match ALPHANUMERIC_CHARSET.iter().position(|x| *x == *c) { + None => panic!("String contains unencodable characters in alphanumeric mode"), + Some(j) => j, + }; + accumdata = accumdata * 45 + (i as u32); + accumcount += 1; + if accumcount == 2 { + bb.append_bits(accumdata, 11); + accumdata = 0; + accumcount = 0; + } + } + if accumcount > 0 { // 1 character remaining + bb.append_bits(accumdata, 6); + } + QrSegment::new(QrSegmentMode::Alphanumeric, text.len(), bb.0) + } + + + // Returns a new mutable list of zero or more segments to represent the given Unicode text string. + // The result may use various segment modes and switch modes to optimize the length of the bit stream. + pub fn make_segments(text: &[char]) -> Vec<QrSegment> { + if text.is_empty() { + vec![] + } else if QrSegment::is_numeric(text) { + vec![QrSegment::make_numeric(text)] + } else if QrSegment::is_alphanumeric(text) { + vec![QrSegment::make_alphanumeric(text)] + } else { + let s: String = text.iter().cloned().collect(); + vec![QrSegment::make_bytes(s.as_bytes())] + } + } + + + // Returns a segment representing an Extended Channel Interpretation + // (ECI) designator with the given assignment value. + pub fn make_eci(assignval: u32) -> QrSegment { + let mut bb = BitBuffer(Vec::with_capacity(24)); + if assignval < (1 << 7) { + bb.append_bits(assignval, 8); + } else if assignval < (1 << 14) { + bb.append_bits(2, 2); + bb.append_bits(assignval, 14); + } else if assignval < 1_000_000 { + bb.append_bits(6, 3); + bb.append_bits(assignval, 21); + } else { + panic!("ECI assignment value out of range"); + } + QrSegment::new(QrSegmentMode::Eci, 0, bb.0) + } + + + // Creates a new QR Code data segment with the given parameters and data. + pub fn new(mode: QrSegmentMode, numchars: usize, data: Vec<bool>) -> QrSegment { + QrSegment { + mode: mode, + numchars: numchars, + data: data, + } + } + + + /*---- Instance field getters ----*/ + + // Returns the mode indicator for this segment. + pub fn mode(&self) -> QrSegmentMode { + self.mode + } + + + // Returns the length of this segment's unencoded data, measured in characters. + pub fn num_chars(&self) -> usize { + self.numchars + } + + + // Returns a view of the bits of this segment. + pub fn data(&self) -> &Vec<bool> { + &self.data + } + + + /*---- Other static functions ----*/ + + // Package-private helper function. + fn get_total_bits(segs: &[QrSegment], version: Version) -> Option<usize> { + let mut result: usize = 0; + for seg in segs { + let ccbits = seg.mode.num_char_count_bits(version); + if seg.numchars >= 1 << ccbits { + return None; + } + match result.checked_add(4 + (ccbits as usize) + seg.data.len()) { + None => return None, + Some(val) => result = val, + } + } + Some(result) + } + + + // Tests whether the given string can be encoded as a segment in alphanumeric mode. + fn is_alphanumeric(text: &[char]) -> bool { + text.iter().all(|c| ALPHANUMERIC_CHARSET.contains(c)) + } + + + // Tests whether the given string can be encoded as a segment in numeric mode. + fn is_numeric(text: &[char]) -> bool { + text.iter().all(|c| '0' <= *c && *c <= '9') + } + +} + + +// The set of all legal characters in alphanumeric mode, +// where each character value maps to the index in the string. +static ALPHANUMERIC_CHARSET: [char; 45] = ['0','1','2','3','4','5','6','7','8','9', + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + ' ','$','%','*','+','-','.','/',':']; + + + +/*---- QrSegmentMode functionality ----*/ + +// The mode field of a segment. Immutable. +#[derive(Clone, Copy)] +pub enum QrSegmentMode { + Numeric, + Alphanumeric, + Byte, + Kanji, + Eci, +} + + +impl QrSegmentMode { + + // Returns an unsigned 4-bit integer value (range 0 to 15) + // representing the mode indicator bits for this mode object. + fn mode_bits(&self) -> u32 { + match *self { + QrSegmentMode::Numeric => 0x1, + QrSegmentMode::Alphanumeric => 0x2, + QrSegmentMode::Byte => 0x4, + QrSegmentMode::Kanji => 0x8, + QrSegmentMode::Eci => 0x7, + } + } + + + // Returns the bit width of the segment character count field + // for this mode object at the given version number. + pub fn num_char_count_bits(&self, ver: Version) -> u8 { + let array: [u8; 3] = match *self { + QrSegmentMode::Numeric => [10, 12, 14], + QrSegmentMode::Alphanumeric => [ 9, 11, 13], + QrSegmentMode::Byte => [ 8, 16, 16], + QrSegmentMode::Kanji => [ 8, 10, 12], + QrSegmentMode::Eci => [ 0, 0, 0], + }; + + let ver = ver.value(); + if 1 <= ver && ver <= 9 { + array[0] + } else if 10 <= ver && ver <= 26 { + array[1] + } else if 27 <= ver && ver <= 40 { + array[2] + } else { + panic!("Version number out of range"); + } + } + +} + + + +/*---- Bit buffer functionality ----*/ + +pub struct BitBuffer(pub Vec<bool>); + + +impl BitBuffer { + // Appends the given number of low bits of the given value + // to this sequence. Requires 0 <= val < 2^len. + pub fn append_bits(&mut self, val: u32, len: u8) { + assert!(len < 32 && (val >> len) == 0 || len == 32, "Value out of range"); + for i in (0 .. len).rev() { // Append bit by bit + self.0.push((val >> i) & 1 != 0); + } + } +} + + + +/*---- Miscellaneous values ----*/ + +#[derive(Copy, Clone)] +pub struct Version(u8); + +impl Version { + pub fn new(ver: u8) -> Self { + assert!(QrCode_MIN_VERSION.value() <= ver && ver <= QrCode_MAX_VERSION.value(), "Version number out of range"); + Version(ver) + } + + pub fn value(&self) -> u8 { + self.0 + } +} + + +#[derive(Copy, Clone)] +pub struct Mask(u8); + +impl Mask { + pub fn new(mask: u8) -> Self { + assert!(mask <= 7, "Mask value out of range"); + Mask(mask) + } + + pub fn value(&self) -> u8 { + self.0 + } +}
diff --git a/src/third_party/brotli/.editorconfig b/src/third_party/brotli/.editorconfig new file mode 100644 index 0000000..17ed3c1 --- /dev/null +++ b/src/third_party/brotli/.editorconfig
@@ -0,0 +1,40 @@ +# http://editorconfig.org +# Consistent coding style across different editors. + +# Top-most file +root = true + +# Global styles: +# - indent 2 spaces +# - add final new line +# - trim trailing whitespace +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +# BUILD: +# - indent 4 spaces +[BUILD] +indent_size = 4 + +# Makefile: +# - indent 1 tab +[Makefile] +indent_size = tab +indent_style = tab + +# Markdown: +# - indent 4 spaces +# - trailing whitespace is significant +[*.md] +indent_size = 4 +trim_trailing_whitespace = false + +# Python +# - indent 4 spaces +[*.py] +indent_size = 4
diff --git a/src/third_party/brotli/.travis.yml b/src/third_party/brotli/.travis.yml new file mode 100644 index 0000000..78495d0 --- /dev/null +++ b/src/third_party/brotli/.travis.yml
@@ -0,0 +1,208 @@ +language: c +sudo: false +branches: + only: + - master +matrix: + include: + ### + ## Linux builds using various versions of GCC. + ### + - os: linux + env: BUILD_SYSTEM=cmake C_COMPILER=gcc-7 CXX_COMPILER=g++-7 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-7 + - g++-7 + - os: linux + env: BUILD_SYSTEM=cmake C_COMPILER=gcc-4.4 CXX_COMPILER=g++-4.4 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.4 + - g++-4.4 + + ### + ## Test that Autotools build works. + ### + - os: linux + env: BUILD_SYSTEM=autotools C_COMPILER=gcc-5 CXX_COMPILER=g++-5 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-5 + - g++-5 + + ### + ## Test that fuzzer is compiling / working. + ### + - os: linux + env: BUILD_SYSTEM=fuzz C_COMPILER=clang-5.0 CXX_COMPILER=clang++-5.0 ASAN_OPTIONS=detect_leaks=0 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + packages: + - clang-5.0 + + ### + ## clang on Linux + ### + - os: linux + env: BUILD_SYSTEM=cmake C_COMPILER=clang-5.0 CXX_COMPILER=clang++-5.0 + addons: + apt: + sources: + - llvm-toolchain-trusty-5.0 + - ubuntu-toolchain-r-test + packages: + - clang-5.0 + - os: linux + env: BUILD_SYSTEM=cmake C_COMPILER=clang-3.5 CXX_COMPILER=clang++-3.5 + addons: + apt: + sources: + - llvm-toolchain-trusty-3.5 + - ubuntu-toolchain-r-test + packages: + - clang-3.5 + + ### + ## PGI Community Edition on Linux + ### + - os: linux + env: BUILD_SYSTEM=cmake C_COMPILER=pgcc CXX_COMPILER=pgc++ + + ### + ## Python 2.7 and 3.6 builds on Linux + ### + - os: linux + language: python + python: 2.7 + env: BUILD_SYSTEM=python C_COMPILER=gcc-5 CXX_COMPILER=g++-5 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-5 + - g++-5 + - os: linux + language: python + python: 3.6 + env: BUILD_SYSTEM=python C_COMPILER=gcc-5 CXX_COMPILER=g++-5 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-5 + - g++-5 + + ### + ## CMake on OS X + ## + ## These all work, but it seems unnecessary to actually build them + ## all since we already test all these versions of GCC on Linux. + ## We'll just test 4.4 and the most recent version. + ### + - os: osx + env: BUILD_SYSTEM=cmake C_COMPILER=gcc-6 CXX_COMPILER=g++-6 + - os: osx + osx_image: beta-xcode6.2 + env: BUILD_SYSTEM=cmake C_COMPILER=gcc-4.4 CXX_COMPILER=g++-4.4 + + ### + ## Python 2.7 OS X build (using the system /usr/bin/python) + ### + - os: osx + env: BUILD_SYSTEM=python C_COMPILER=gcc CXX_COMPILER=g++ + + ### + ## Sanitizers + ### + - os: linux + env: BUILD_SYSTEM=cmake C_COMPILER=clang-5.0 CXX_COMPILER=clang++-5.0 SANITIZER=address ASAN_OPTIONS=detect_leaks=0 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + packages: + - clang-5.0 + - os: linux + env: BUILD_SYSTEM=cmake C_COMPILER=clang-5.0 CXX_COMPILER=clang++-5.0 SANITIZER=thread + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + packages: + - clang-5.0 + - os: linux + env: BUILD_SYSTEM=cmake C_COMPILER=clang-5.0 CXX_COMPILER=clang++-5.0 SANITIZER=undefined CFLAGS="-fno-sanitize-recover=undefined,integer" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + packages: + - clang-5.0 + + - os: linux + env: BUILD_SYSTEM=maven + language: java + + - os: linux + sudo: required + language: java + env: BUILD_SYSTEM=bazel + addons: + apt: + sources: + - sourceline: "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" + key_url: "https://storage.googleapis.com/bazel-apt/doc/apt-key.pub.gpg" + - ubuntu-toolchain-r-test + packages: + - oracle-java8-installer + - bazel + + - os: osx + env: BUILD_SYSTEM=bazel + +before_install: +### +## If we use the matrix to set CC/CXX Travis, overwrites the values, +## so instead we use C/CXX_COMPILER, then copy the values to CC/CXX +## here (after Travis has set CC/CXX). +### +- if [ -n "${C_COMPILER}" ]; then export CC="${C_COMPILER}"; fi +- if [ -n "${CXX_COMPILER}" ]; then export CXX="${CXX_COMPILER}"; fi +- scripts/.travis.sh before_install +install: +- scripts/.travis.sh install +script: +- scripts/.travis.sh script +after_success: +- scripts/.travis.sh after_success + +before_deploy: +- scripts/.travis.sh before_deploy + +deploy: +- provider: bintray + file: "scripts/.bintray.json" + user: "eustas" + key: + secure: "Kbam/lTAdz72fZivbs6riJT+Y4PbuKP7r6t5PAWxJxAAykjwnYTRe3zF472g9HCE14KYMsdB+KSYSgg6TGJnqGC9gL9xhhGU9U/WmA+vbMWS/MSnMWpK9IRpp77pM2i2NKZD4v33JuEwKFCBJP3Vj6QQ5Qd1NKdobuXJyznhgnw=" + on: + condition: "${BUILD_SYSTEM} = bazel" + skip_cleanup: true
diff --git a/src/third_party/brotli/BUILD b/src/third_party/brotli/BUILD new file mode 100644 index 0000000..6265b71 --- /dev/null +++ b/src/third_party/brotli/BUILD
@@ -0,0 +1,224 @@ +# Description: +# Brotli is a generic-purpose lossless compression algorithm. + +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) # MIT + +exports_files(["LICENSE"]) + +# >>> JNI headers + +config_setting( + name = "darwin", + values = {"cpu": "darwin"}, + visibility = ["//visibility:public"], +) + +config_setting( + name = "darwin_x86_64", + values = {"cpu": "darwin_x86_64"}, + visibility = ["//visibility:public"], +) + +config_setting( + name = "windows", + values = {"cpu": "x64_windows"}, + visibility = ["//visibility:public"], +) + +config_setting( + name = "windows_msvc", + values = {"cpu": "x64_windows_msvc"}, + visibility = ["//visibility:public"], +) + +config_setting( + name = "windows_msys", + values = {"cpu": "x64_windows_msys"}, + visibility = ["//visibility:public"], +) + +genrule( + name = "copy_link_jni_header", + srcs = ["@openjdk_linux//:jni_h"], + outs = ["jni/jni.h"], + cmd = "cp -f $< $@", +) + +genrule( + name = "copy_link_jni_md_header", + srcs = select({ + ":darwin": ["@openjdk_macos//:jni_md_h"], + ":darwin_x86_64": ["@openjdk_macos//:jni_md_h"], + ":windows_msys": ["@openjdk_win//:jni_md_h"], + ":windows_msvc": ["@openjdk_win//:jni_md_h"], + ":windows": ["@openjdk_win//:jni_md_h"], + "//conditions:default": ["@openjdk_linux//:jni_md_h"], + }), + outs = ["jni/jni_md.h"], + cmd = "cp -f $< $@", +) + +cc_library( + name = "jni_inc", + hdrs = [ + ":jni/jni.h", + ":jni/jni_md.h", + ], + includes = ["jni"], +) + +# <<< JNI headers + +STRICT_C_OPTIONS = [ + "--pedantic-errors", + "-Wall", + "-Wconversion", + "-Werror", + "-Wextra", + "-Wlong-long", + "-Wmissing-declarations", + "-Wmissing-prototypes", + "-Wno-strict-aliasing", + "-Wshadow", + "-Wsign-compare", +] + +filegroup( + name = "public_headers", + srcs = glob(["c/include/brotli/*.h"]), +) + +filegroup( + name = "common_headers", + srcs = glob(["c/common/*.h"]), +) + +filegroup( + name = "common_sources", + srcs = glob(["c/common/*.c"]), +) + +filegroup( + name = "dec_headers", + srcs = glob(["c/dec/*.h"]), +) + +filegroup( + name = "dec_sources", + srcs = glob(["c/dec/*.c"]), +) + +filegroup( + name = "enc_headers", + srcs = glob(["c/enc/*.h"]), +) + +filegroup( + name = "enc_sources", + srcs = glob(["c/enc/*.c"]), +) + +cc_library( + name = "brotli_inc", + hdrs = [":public_headers"], + copts = STRICT_C_OPTIONS, + includes = ["c/include"], +) + +cc_library( + name = "brotlicommon", + srcs = [":common_sources"], + hdrs = [":common_headers"], + copts = STRICT_C_OPTIONS, + deps = [":brotli_inc"], +) + +cc_library( + name = "brotlidec", + srcs = [":dec_sources"], + hdrs = [":dec_headers"], + copts = STRICT_C_OPTIONS, + deps = [":brotlicommon"], +) + +cc_library( + name = "brotlienc", + srcs = [":enc_sources"], + hdrs = [":enc_headers"], + copts = STRICT_C_OPTIONS, + linkopts = ["-lm"], + deps = [":brotlicommon"], +) + +cc_binary( + name = "brotli", + srcs = ["c/tools/brotli.c"], + copts = STRICT_C_OPTIONS, + linkstatic = 1, + deps = [ + ":brotlidec", + ":brotlienc", + ], +) + +######################################################## +# WARNING: do not (transitively) depend on this target! +######################################################## +cc_binary( + name = "brotli_jni.dll", + srcs = [ + ":common_headers", + ":common_sources", + ":dec_headers", + ":dec_sources", + ":enc_headers", + ":enc_sources", + "//java/org/brotli/wrapper/common:jni_src", + "//java/org/brotli/wrapper/dec:jni_src", + "//java/org/brotli/wrapper/enc:jni_src", + ], + deps = [ + ":brotli_inc", + ":jni_inc", + ], + linkshared = 1, +) + +######################################################## +# WARNING: do not (transitively) depend on this target! +######################################################## +cc_binary( + name = "brotli_jni_no_dictionary_data.dll", + srcs = [ + ":common_headers", + ":common_sources", + ":dec_headers", + ":dec_sources", + ":enc_headers", + ":enc_sources", + "//java/org/brotli/wrapper/common:jni_src", + "//java/org/brotli/wrapper/dec:jni_src", + "//java/org/brotli/wrapper/enc:jni_src", + ], + defines = [ + "BROTLI_EXTERNAL_DICTIONARY_DATA=", + ], + deps = [ + ":brotli_inc", + ":jni_inc", + ], + linkshared = 1, +) + +filegroup( + name = "dictionary", + srcs = ["c/common/dictionary.bin"], +) + +load("@io_bazel_rules_go//go:def.bzl", "go_prefix") + +go_prefix("github.com/google/brotli")
diff --git a/src/third_party/brotli/CMakeLists.txt b/src/third_party/brotli/CMakeLists.txt new file mode 100644 index 0000000..2dc7232 --- /dev/null +++ b/src/third_party/brotli/CMakeLists.txt
@@ -0,0 +1,356 @@ +# Ubuntu 12.04 LTS has CMake 2.8.7, and is an important target since +# several CI services, such as Travis and Drone, use it. Solaris 11 +# has 2.8.6, and it's not difficult to support if you already have to +# support 2.8.7. +cmake_minimum_required(VERSION 2.8.6) + +project(brotli) + +# If Brotli is being bundled in another project, we don't want to +# install anything. However, we want to let people override this, so +# we'll use the BROTLI_BUNDLED_MODE variable to let them do that; just +# set it to OFF in your project before you add_subdirectory(brotli). +get_directory_property(BROTLI_PARENT_DIRECTORY PARENT_DIRECTORY) +if(BROTLI_BUNDLED_MODE STREQUAL "") + # Bundled mode hasn't been set one way or the other, set the default + # depending on whether or not we are the top-level project. + if(BROTLI_PARENT_DIRECTORY) + set(BROTLI_BUNDLED_MODE OFF) + else() + set(BROTLI_BUNDLED_MODE ON) + endif() +endif() +mark_as_advanced(BROTLI_BUNDLED_MODE) + +include(GNUInstallDirs) + +# Parse version information from common/version.h. Normally we would +# define these values here and write them out to configuration file(s) +# (i.e., config.h), but in this case we parse them from +# common/version.h to be less intrusive. +function(hex_to_dec HEXADECIMAL DECIMAL) + string(TOUPPER "${HEXADECIMAL}" _tail) + set(_decimal 0) + string(LENGTH "${_tail}" _tail_length) + while (_tail_length GREATER 0) + math(EXPR _decimal "${_decimal} * 16") + string(SUBSTRING "${_tail}" 0 1 _digit) + string(SUBSTRING "${_tail}" 1 -1 _tail) + if (_digit STREQUAL "A") + math(EXPR _decimal "${_decimal} + 10") + elseif (_digit STREQUAL "B") + math(EXPR _decimal "${_decimal} + 11") + elseif (_digit STREQUAL "C") + math(EXPR _decimal "${_decimal} + 12") + elseif (_digit STREQUAL "D") + math(EXPR _decimal "${_decimal} + 13") + elseif (_digit STREQUAL "E") + math(EXPR _decimal "${_decimal} + 14") + elseif (_digit STREQUAL "F") + math(EXPR _decimal "${_decimal} + 15") + else() + math(EXPR _decimal "${_decimal} + ${_digit}") + endif() + string(LENGTH "${_tail}" _tail_length) + endwhile() + set(${DECIMAL} ${_decimal} PARENT_SCOPE) +endfunction(hex_to_dec) + +# Version information +file(STRINGS "c/common/version.h" _brotli_version_line REGEX "^#define BROTLI_VERSION (0x[0-9a-fA-F]+)$") +string(REGEX REPLACE "^#define BROTLI_VERSION 0x([0-9a-fA-F]+)$" "\\1" _brotli_version_hex "${_brotli_version_line}") +hex_to_dec("${_brotli_version_hex}" _brotli_version) +math(EXPR BROTLI_VERSION_MAJOR "${_brotli_version} >> 24") +math(EXPR BROTLI_VERSION_MINOR "(${_brotli_version} >> 12) & 4095") +math(EXPR BROTLI_VERSION_PATCH "${_brotli_version} & 4095") +set(BROTLI_VERSION "${BROTLI_VERSION_MAJOR}.${BROTLI_VERSION_MINOR}.${BROTLI_VERSION_PATCH}") +mark_as_advanced(BROTLI_VERSION BROTLI_VERSION_MAJOR BROTLI_VERSION_MINOR BROTLI_VERSION_PATCH) + +# ABI Version information +file(STRINGS "c/common/version.h" _brotli_abi_info_line REGEX "^#define BROTLI_ABI_VERSION (0x[0-9a-fA-F]+)$") +string(REGEX REPLACE "^#define BROTLI_ABI_VERSION 0x([0-9a-fA-F]+)$" "\\1" _brotli_abi_info_hex "${_brotli_abi_info_line}") +hex_to_dec("${_brotli_abi_info_hex}" _brotli_abi_info) +math(EXPR BROTLI_ABI_CURRENT "${_brotli_abi_info} >> 24") +math(EXPR BROTLI_ABI_REVISION "(${_brotli_abi_info} >> 12) & 4095") +math(EXPR BROTLI_ABI_AGE "${_brotli_abi_info} & 4095") +math(EXPR BROTLI_ABI_COMPATIBILITY "${BROTLI_ABI_CURRENT} - ${BROTLI_ABI_AGE}") +mark_as_advanced(BROTLI_ABI_CURRENT BROTLI_ABI_REVISION BROTLI_ABI_AGE BROTLI_ABI_COMPATIBILITY) + +if (ENABLE_SANITIZER) + set(CMAKE_C_FLAGS " ${CMAKE_C_FLAGS} -fsanitize=${ENABLE_SANITIZER}") + set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fsanitize=${ENABLE_SANITIZER}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${ENABLE_SANITIZER}") + + # By default, brotli depends on undefined behavior, but setting + # BROTLI_BUILD_PORTABLE should result in a build which does not. + if(ENABLE_SANITIZER STREQUAL "undefined") + add_definitions(-DBROTLI_BUILD_PORTABLE) + endif() +endif () + +include(CheckFunctionExists) +set(LIBM_LIBRARY) +CHECK_FUNCTION_EXISTS(log2 LOG2_RES) +if(NOT LOG2_RES) + set(orig_req_libs "${CMAKE_REQUIRED_LIBRARIES}") + set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};m") + CHECK_FUNCTION_EXISTS(log2 LOG2_LIBM_RES) + if(LOG2_LIBM_RES) + set(LIBM_LIBRARY "m") + else() + message(FATAL_ERROR "log2() not found") + endif() + + set(CMAKE_REQUIRED_LIBRARIES "${orig_req_libs}") + unset(LOG2_LIBM_RES) + unset(orig_req_libs) +endif() +unset(LOG2_RES) + +set(BROTLI_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/c/include") +mark_as_advanced(BROTLI_INCLUDE_DIRS) + +set(BROTLI_LIBRARIES_CORE brotlienc brotlidec brotlicommon) +set(BROTLI_LIBRARIES ${BROTLI_LIBRARIES_CORE} ${LIBM_LIBRARY}) +mark_as_advanced(BROTLI_LIBRARIES) + +set(BROTLI_LIBRARIES_CORE_STATIC brotlienc-static brotlidec-static brotlicommon-static) +set(BROTLI_LIBRARIES_STATIC ${BROTLI_LIBRARIES_CORE_STATIC} ${LIBM_LIBRARY}) +mark_as_advanced(BROTLI_LIBRARIES_STATIC) + +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + add_definitions(-DOS_LINUX) +elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + add_definitions(-DOS_FREEBSD) +elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + add_definitions(-DOS_MACOSX) +endif() + +function(transform_sources_list INPUT_FILE OUTPUT_FILE) + file(READ ${INPUT_FILE} TEXT) + string(REGEX REPLACE "\\\\\n" "~continuation~" TEXT ${TEXT}) + string(REGEX REPLACE "([a-zA-Z_][a-zA-Z0-9_]*)[\t ]*=[\t ]*([^\n]*)" "SET(\\1 \\2)" TEXT ${TEXT}) + string(REPLACE "~continuation~" "\n" TEXT ${TEXT}) + file(WRITE ${OUTPUT_FILE} ${TEXT}) +endfunction() + +transform_sources_list("scripts/sources.lst" "${CMAKE_CURRENT_BINARY_DIR}/sources.lst.cmake") +include("${CMAKE_CURRENT_BINARY_DIR}/sources.lst.cmake") + +add_library(brotlicommon SHARED ${BROTLI_COMMON_C}) +add_library(brotlidec SHARED ${BROTLI_DEC_C}) +add_library(brotlienc SHARED ${BROTLI_ENC_C}) + +add_library(brotlicommon-static STATIC ${BROTLI_COMMON_C}) +add_library(brotlidec-static STATIC ${BROTLI_DEC_C}) +add_library(brotlienc-static STATIC ${BROTLI_ENC_C}) + +# Older CMake versions does not understand INCLUDE_DIRECTORIES property. +include_directories(${BROTLI_INCLUDE_DIRS}) + +foreach(lib brotlicommon brotlidec brotlienc) + target_compile_definitions(${lib} PUBLIC "BROTLI_SHARED_COMPILATION" ) + string(TOUPPER "${lib}" LIB) + set_target_properties (${lib} PROPERTIES DEFINE_SYMBOL "${LIB}_SHARED_COMPILATION" ) +endforeach() + +foreach(lib brotlicommon brotlidec brotlienc brotlicommon-static brotlidec-static brotlienc-static) + target_link_libraries(${lib} ${LIBM_LIBRARY}) + set_property(TARGET ${lib} APPEND PROPERTY INCLUDE_DIRECTORIES ${BROTLI_INCLUDE_DIRS}) + set_target_properties(${lib} PROPERTIES + VERSION "${BROTLI_ABI_COMPATIBILITY}.${BROTLI_ABI_AGE}.${BROTLI_ABI_REVISION}" + SOVERSION "${BROTLI_ABI_COMPATIBILITY}" + POSITION_INDEPENDENT_CODE TRUE) + set_property(TARGET ${lib} APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${BROTLI_INCLUDE_DIRS}") +endforeach() + +target_link_libraries(brotlidec brotlicommon) +target_link_libraries(brotlienc brotlicommon) + +target_link_libraries(brotlidec-static brotlicommon-static) +target_link_libraries(brotlienc-static brotlicommon-static) + +# For projects stuck on older versions of CMake, this will set the +# BROTLI_INCLUDE_DIRS and BROTLI_LIBRARIES variables so they still +# have a relatively easy way to use Brotli: +# +# include_directories(${BROTLI_INCLUDE_DIRS}) +# target_link_libraries(foo ${BROTLI_LIBRARIES}) +if(BROTLI_PARENT_DIRECTORY) + set(BROTLI_INCLUDE_DIRS "${BROTLI_INCLUDE_DIRS}" PARENT_SCOPE) + set(BROTLI_LIBRARIES "${BROTLI_LIBRARIES}" PARENT_SCOPE) +endif() + +# Build the brotli executable +add_executable(brotli ${BROTLI_CLI_C}) +target_link_libraries(brotli ${BROTLI_LIBRARIES_STATIC}) + +# Installation +if(NOT BROTLI_BUNDLED_MODE) + install( + TARGETS brotli + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) + + install( + TARGETS ${BROTLI_LIBRARIES_CORE} + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) + + install( + TARGETS ${BROTLI_LIBRARIES_CORE_STATIC} + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) + + install( + DIRECTORY ${BROTLI_INCLUDE_DIRS}/brotli + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + ) +endif() + +# Tests + +# If we're targeting Windows but not running on Windows, we need Wine +# to run the tests... +if(NOT BROTLI_DISABLE_TESTS) + if(WIN32 AND NOT CMAKE_HOST_WIN32) + find_program(BROTLI_WINE NAMES wine) + + if(NOT BROTLI_WINE) + message(STATUS "wine not found, disabling tests") + set(BROTLI_DISABLE_TESTS TRUE) + endif() + endif() +endif() + +if(NOT BROTLI_DISABLE_TESTS) + include(CTest) + enable_testing() + + set(ROUNDTRIP_INPUTS + tests/testdata/alice29.txt + tests/testdata/asyoulik.txt + tests/testdata/lcet10.txt + tests/testdata/plrabn12.txt + c/enc/encode.c + c/common/dictionary.h + c/dec/decode.c) + + foreach(INPUT ${ROUNDTRIP_INPUTS}) + get_filename_component(OUTPUT_NAME "${INPUT}" NAME) + + set(OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_NAME}") + set(INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}") + + foreach(quality 1 6 9 11) + add_test(NAME "${BROTLI_TEST_PREFIX}roundtrip/${INPUT}/${quality}" + COMMAND "${CMAKE_COMMAND}" + -DBROTLI_WRAPPER=${BROTLI_WINE} + -DBROTLI_CLI=$<TARGET_FILE:brotli> + -DQUALITY=${quality} + -DINPUT=${INPUT_FILE} + -DOUTPUT=${OUTPUT_FILE}.${quality} + -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-roundtrip-test.cmake) + endforeach() + endforeach() + + file(GLOB_RECURSE + COMPATIBILITY_INPUTS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + tests/testdata/*.compressed*) + + foreach(INPUT ${COMPATIBILITY_INPUTS}) + add_test(NAME "${BROTLI_TEST_PREFIX}compatibility/${INPUT}" + COMMAND "${CMAKE_COMMAND}" + -DBROTLI_WRAPPER=${BROTLI_WINE} + -DBROTLI_CLI=$<TARGET_FILE:brotli> + -DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/${INPUT} + -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-compatibility-test.cmake) + endforeach() +endif() + +# Generate a pkg-config files + +function(generate_pkg_config_path outvar path) + string(LENGTH "${path}" path_length) + + set(path_args ${ARGV}) + list(REMOVE_AT path_args 0 1) + list(LENGTH path_args path_args_remaining) + + set("${outvar}" "${path}") + + while(path_args_remaining GREATER 1) + list(GET path_args 0 name) + list(GET path_args 1 value) + + get_filename_component(value_full "${value}" ABSOLUTE) + string(LENGTH "${value}" value_length) + + if(path_length EQUAL value_length AND path STREQUAL value) + set("${outvar}" "\${${name}}") + break() + elseif(path_length GREATER value_length) + # We might be in a subdirectory of the value, but we have to be + # careful about a prefix matching but not being a subdirectory + # (for example, /usr/lib64 is not a subdirectory of /usr/lib). + # We'll do this by making sure the next character is a directory + # separator. + string(SUBSTRING "${path}" ${value_length} 1 sep) + if(sep STREQUAL "/") + string(SUBSTRING "${path}" 0 ${value_length} s) + if(s STREQUAL value) + string(SUBSTRING "${path}" "${value_length}" -1 suffix) + set("${outvar}" "\${${name}}${suffix}") + break() + endif() + endif() + endif() + + list(REMOVE_AT path_args 0 1) + list(LENGTH path_args path_args_remaining) + endwhile() + + set("${outvar}" "${${outvar}}" PARENT_SCOPE) +endfunction(generate_pkg_config_path) + +function(transform_pc_file INPUT_FILE OUTPUT_FILE VERSION) + file(READ ${INPUT_FILE} TEXT) + + set(PREFIX "${CMAKE_INSTALL_PREFIX}") + string(REGEX REPLACE "@prefix@" "${PREFIX}" TEXT ${TEXT}) + string(REGEX REPLACE "@exec_prefix@" "${PREFIX}" TEXT ${TEXT}) + + generate_pkg_config_path(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}" prefix "${PREFIX}") + string(REGEX REPLACE "@libdir@" "${LIBDIR}" TEXT ${TEXT}) + + generate_pkg_config_path(INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}" prefix "${PREFIX}") + string(REGEX REPLACE "@includedir@" "${INCLUDEDIR}" TEXT ${TEXT}) + + string(REGEX REPLACE "@PACKAGE_VERSION@" "${VERSION}" TEXT ${TEXT}) + + file(WRITE ${OUTPUT_FILE} ${TEXT}) +endfunction() + +transform_pc_file("scripts/libbrotlicommon.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libbrotlicommon.pc" "${BROTLI_VERSION}") + +transform_pc_file("scripts/libbrotlidec.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libbrotlidec.pc" "${BROTLI_VERSION}") + +transform_pc_file("scripts/libbrotlienc.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libbrotlienc.pc" "${BROTLI_VERSION}") + +if(NOT BROTLI_BUNDLED_MODE) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlicommon.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlidec.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbrotlienc.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif() + +if (ENABLE_COVERAGE STREQUAL "yes") + SETUP_TARGET_FOR_COVERAGE(coverage test coverage) +endif ()
diff --git a/src/third_party/brotli/CONTRIBUTING.md b/src/third_party/brotli/CONTRIBUTING.md new file mode 100644 index 0000000..a00e37d --- /dev/null +++ b/src/third_party/brotli/CONTRIBUTING.md
@@ -0,0 +1,27 @@ +Want to contribute? Great! First, read this page (including the small print at +the end). + +### Before you contribute +Before we can use your code, you must sign the +[Google Individual Contributor License Agreement] +(https://cla.developers.google.com/about/google-individual) +(CLA), which you can do online. The CLA is necessary mainly because you own the +copyright to your changes, even after your contribution becomes part of our +codebase, so we need your permission to use and distribute your code. We also +need to be sure of various other things—for instance that you'll tell us if you +know that your code infringes on other people's patents. You don't have to sign +the CLA until after you've submitted your code for review and a member has +approved it, but you must do it before we can put your code into our codebase. +Before you start working on a larger contribution, you should get in touch with +us first through the issue tracker with your idea so that we can help out and +possibly guide you. Coordinating up front makes it much easier to avoid +frustration later on. + +### Code reviews +All submissions, including submissions by project members, require review. We +use Github pull requests for this purpose. + +### The small print +Contributions made by corporations are covered by a different agreement than +the one above, the [Software Grant and Corporate Contributor License Agreement] +(https://cla.developers.google.com/about/google-corporate).
diff --git a/src/third_party/brotli/LICENSE b/src/third_party/brotli/LICENSE new file mode 100644 index 0000000..33b7cdd --- /dev/null +++ b/src/third_party/brotli/LICENSE
@@ -0,0 +1,19 @@ +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.
diff --git a/src/third_party/brotli/MANIFEST.in b/src/third_party/brotli/MANIFEST.in new file mode 100644 index 0000000..b7b8e72 --- /dev/null +++ b/src/third_party/brotli/MANIFEST.in
@@ -0,0 +1,17 @@ +include CONTRIBUTING.md +include c/common/*.c +include c/common/*.h +include c/dec/*.c +include c/dec/*.h +include c/enc/*.c +include c/enc/*.h +include c/include/brotli/*.h +include LICENSE +include MANIFEST.in +include python/_brotli.cc +include python/bro.py +include python/brotli.py +include python/README.md +include README.md +include setup.py +include c/tools/brotli.c
diff --git a/src/third_party/brotli/Makefile b/src/third_party/brotli/Makefile new file mode 100644 index 0000000..c48d798 --- /dev/null +++ b/src/third_party/brotli/Makefile
@@ -0,0 +1,43 @@ +OS := $(shell uname) +LIBSOURCES = $(wildcard c/common/*.c) $(wildcard c/dec/*.c) \ + $(wildcard c/enc/*.c) +SOURCES = $(LIBSOURCES) c/tools/brotli.c +BINDIR = bin +OBJDIR = $(BINDIR)/obj +LIBOBJECTS = $(addprefix $(OBJDIR)/, $(LIBSOURCES:.c=.o)) +OBJECTS = $(addprefix $(OBJDIR)/, $(SOURCES:.c=.o)) +LIB_A = libbrotli.a +EXECUTABLE = brotli +DIRS = $(OBJDIR)/c/common $(OBJDIR)/c/dec $(OBJDIR)/c/enc \ + $(OBJDIR)/c/tools $(BINDIR)/tmp +CFLAGS += -O2 +ifeq ($(os), Darwin) + CPPFLAGS += -DOS_MACOSX +endif + +all: test + @: + +.PHONY: all clean test + +$(DIRS): + mkdir -p $@ + +$(EXECUTABLE): $(OBJECTS) + $(CC) $(LDFLAGS) $(OBJECTS) -lm -o $(BINDIR)/$(EXECUTABLE) + +lib: $(LIBOBJECTS) + rm -f $(LIB_A) + ar -crs $(LIB_A) $(LIBOBJECTS) + +test: $(EXECUTABLE) + tests/compatibility_test.sh + tests/roundtrip_test.sh + +clean: + rm -rf $(BINDIR) $(LIB_A) + +.SECONDEXPANSION: +$(OBJECTS): $$(patsubst %.o,%.c,$$(patsubst $$(OBJDIR)/%,%,$$@)) | $(DIRS) + $(CC) $(CFLAGS) $(CPPFLAGS) -Ic/include \ + -c $(patsubst %.o,%.c,$(patsubst $(OBJDIR)/%,%,$@)) -o $@
diff --git a/src/third_party/brotli/Makefile.am b/src/third_party/brotli/Makefile.am new file mode 100644 index 0000000..ace7a85 --- /dev/null +++ b/src/third_party/brotli/Makefile.am
@@ -0,0 +1,38 @@ +AUTOMAKE_OPTIONS = foreign nostdinc subdir-objects + +ACLOCAL_AMFLAGS = -I m4 + +# Actual ABI version is substituted by bootstrap +LIBBROTLI_VERSION_INFO = -version-info 0:0:0 + +bin_PROGRAMS = brotli +lib_LTLIBRARIES = libbrotlicommon.la libbrotlidec.la libbrotlienc.la + +include scripts/sources.lst + +brotliincludedir = $(includedir)/brotli +brotliinclude_HEADERS = $(BROTLI_INCLUDE) + +AM_CFLAGS = -I$(top_srcdir)/c/include + +brotli_SOURCES = $(BROTLI_CLI_C) +brotli_LDADD = libbrotlidec.la libbrotlienc.la libbrotlicommon.la -lm +#brotli_LDFLAGS = -static + +libbrotlicommon_la_SOURCES = $(BROTLI_COMMON_C) $(BROTLI_COMMON_H) +libbrotlicommon_la_LDFLAGS = $(AM_LDFLAGS) $(LIBBROTLI_VERSION_INFO) $(LDFLAGS) +libbrotlidec_la_SOURCES = $(BROTLI_DEC_C) $(BROTLI_DEC_H) +libbrotlidec_la_LDFLAGS = $(AM_LDFLAGS) $(LIBBROTLI_VERSION_INFO) $(LDFLAGS) +libbrotlidec_la_LIBADD = libbrotlicommon.la -lm +libbrotlienc_la_SOURCES = $(BROTLI_ENC_C) $(BROTLI_ENC_H) +libbrotlienc_la_LDFLAGS = $(AM_LDFLAGS) $(LIBBROTLI_VERSION_INFO) $(LDFLAGS) +libbrotlienc_la_LIBADD = libbrotlicommon.la -lm + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = \ + scripts/libbrotlicommon.pc \ + scripts/libbrotlidec.pc \ + scripts/libbrotlienc.pc +pkgincludedir= $(brotliincludedir) + +dist_doc_DATA = README
diff --git a/src/third_party/brotli/README b/src/third_party/brotli/README new file mode 100644 index 0000000..3fb3f22 --- /dev/null +++ b/src/third_party/brotli/README
@@ -0,0 +1,15 @@ +BROTLI DATA COMPRESSIOM LIBRARY + +Brotli is a generic-purpose lossless compression algorithm that compresses data +using a combination of a modern variant of the LZ77 algorithm, Huffman coding +and 2nd order context modeling, with a compression ratio comparable to the best +currently available general-purpose compression methods. It is similar in speed +with deflate but offers more dense compression. + +The specification of the Brotli Compressed Data Format is defined in RFC 7932 +https://tools.ietf.org/html/rfc7932 + +Brotli is open-sourced under the MIT License, see the LICENSE file. + +Brotli mailing list: +https://groups.google.com/forum/#!forum/brotli
diff --git a/src/third_party/brotli/README.md b/src/third_party/brotli/README.md new file mode 100644 index 0000000..6b9b0cf --- /dev/null +++ b/src/third_party/brotli/README.md
@@ -0,0 +1,83 @@ +<p align="center"><img src="https://brotli.org/brotli.svg" alt="Brotli" width="64"></p> + +### Introduction + +Brotli is a generic-purpose lossless compression algorithm that compresses data +using a combination of a modern variant of the LZ77 algorithm, Huffman coding +and 2nd order context modeling, with a compression ratio comparable to the best +currently available general-purpose compression methods. It is similar in speed +with deflate but offers more dense compression. + +The specification of the Brotli Compressed Data Format is defined in [RFC 7932](https://tools.ietf.org/html/rfc7932). + +Brotli is open-sourced under the MIT License, see the LICENSE file. + +Brotli mailing list: +https://groups.google.com/forum/#!forum/brotli + +[](https://travis-ci.org/google/brotli) +[](https://ci.appveyor.com/project/szabadka/brotli) + +### Build instructions + +#### Autotools-style CMake + +[configure-cmake](https://github.com/nemequ/configure-cmake) is an +autotools-style configure script for CMake-based projects (not supported on Windows). + +The basic commands to build, test and install brotli are: + + $ mkdir out && cd out + $ ../configure-cmake + $ make + $ make test + $ make install + +By default, debug binaries are built. To generate "release" `Makefile` specify `--disable-debug` option to `configure-cmake`. + +#### Bazel + +See [Bazel](http://www.bazel.build/) + +#### CMake + +The basic commands to build and install brotli are: + + $ mkdir out && cd out + $ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./installed .. + $ cmake --build . --config Release --target install + +You can use other [CMake](https://cmake.org/) configuration. + +#### Premake5 + +See [Premake5](https://premake.github.io/) + +#### Python + +To install the latest release of the Python module, run the following: + + $ pip install brotli + +To install the tip-of-the-tree version, run: + + $ pip install --upgrade git+https://github.com/google/brotli + +See the [Python readme](python/README.md) for more details on installing +from source, development, and testing. + +### Benchmarks +* [Squash Compression Benchmark](https://quixdb.github.io/squash-benchmark/) / [Unstable Squash Compression Benchmark](https://quixdb.github.io/squash-benchmark/unstable/) +* [Large Text Compression Benchmark](http://mattmahoney.net/dc/text.html) +* [Lzturbo Benchmark](https://sites.google.com/site/powturbo/home/benchmark) + +### Related projects +> **Disclaimer:** Brotli authors take no responsibility for the third party projects mentioned in this section. + +Independent [decoder](https://github.com/madler/brotli) implementation by Mark Adler, based entirely on format specification. + +JavaScript port of brotli [decoder](https://github.com/devongovett/brotli.js). Could be used directly via `npm install brotli` + +Hand ported [decoder / encoder](https://github.com/dominikhlbg/BrotliHaxe) in haxe by Dominik Homberger. Output source code: JavaScript, PHP, Python, Java and C# + +7Zip [plugin](https://github.com/mcmilk/7-Zip-Zstd)
diff --git a/src/third_party/brotli/WORKSPACE b/src/third_party/brotli/WORKSPACE new file mode 100644 index 0000000..b239745 --- /dev/null +++ b/src/third_party/brotli/WORKSPACE
@@ -0,0 +1,90 @@ +# Description: +# Bazel workspace file for Brotli. + +workspace(name = "org_brotli") + +maven_jar( + name = "junit_junit", + artifact = "junit:junit:4.12", +) + +git_repository( + name = "io_bazel_rules_go", + remote = "https://github.com/bazelbuild/rules_go.git", + tag = "0.9.0", +) + +http_archive( + name = "io_bazel_rules_closure", + sha256 = "6691c58a2cd30a86776dd9bb34898b041e37136f2dc7e24cadaeaf599c95c657", + strip_prefix = "rules_closure-08039ba8ca59f64248bb3b6ae016460fe9c9914f", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_closure/archive/08039ba8ca59f64248bb3b6ae016460fe9c9914f.tar.gz", + "https://github.com/bazelbuild/rules_closure/archive/08039ba8ca59f64248bb3b6ae016460fe9c9914f.tar.gz", # 2018-01-16 + ], +) + +new_http_archive( + name = "openjdk_linux", + urls = [ + "https://mirror.bazel.build/openjdk/azul-zulu-8.23.0.3-jdk8.0.144/zulu8.23.0.3-jdk8.0.144-linux_x64.tar.gz", + "https://bazel-mirror.storage.googleapis.com/openjdk/azul-zulu-8.23.0.3-jdk8.0.144/zulu8.23.0.3-jdk8.0.144-linux_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu8.23.0.3-jdk8.0.144-linux_x64.tar.gz", + ], + sha256 = "7e6284739c0e5b7142bc7a9adc61ced70dc5bb26b130b582b18e809013bcb251", + build_file_content = """ +package( + default_visibility = ["//visibility:public"], +) +filegroup( + name = "jni_h", + srcs = ["zulu8.23.0.3-jdk8.0.144-linux_x64/include/jni.h"], +) +filegroup( + name = "jni_md_h", + srcs = ["zulu8.23.0.3-jdk8.0.144-linux_x64/include/linux/jni_md.h"], +)""", +) + +new_http_archive( + name = "openjdk_macos", + urls = [ + "https://mirror.bazel.build/openjdk/azul-zulu-8.23.0.3-jdk8.0.144/zulu8.23.0.3-jdk8.0.144-macosx_x64.zip", + "https://bazel-mirror.storage.googleapis.com/openjdk/azul-zulu-8.23.0.3-jdk8.0.144/zulu8.23.0.3-jdk8.0.144-macosx_x64.zip", + "https://cdn.azul.com/zulu/bin/zulu8.23.0.3-jdk8.0.144-macosx_x64.zip", + ], + sha256 = "ff533364c9cbd3b271ab5328efe28e2dd6d7bae5b630098a5683f742ecf0709d", + build_file_content = """ +package( + default_visibility = ["//visibility:public"], +) +filegroup( + name = "jni_md_h", + srcs = ["zulu8.23.0.3-jdk8.0.144-macosx_x64/include/darwin/jni_md.h"], +)""", +) + +new_http_archive( + name = "openjdk_win", + urls = [ + "https://mirror.bazel.build/openjdk/azul-zulu-8.23.0.3-jdk8.0.144/zulu8.23.0.3-jdk8.0.144-win_x64.zip", + "https://bazel-mirror.storage.googleapis.com/openjdk/azul-zulu-8.23.0.3-jdk8.0.144/zulu8.23.0.3-jdk8.0.144-win_x64.zip", + "https://cdn.azul.com/zulu/bin/zulu8.23.0.3-jdk8.0.144-win_x64.zip", + ], + sha256 = "f1d9d3341ef7c8c9baff3597953e99a6a7c64f8608ee62c03fdd7574b7655c02", + build_file_content = """ +package( + default_visibility = ["//visibility:public"], +) +filegroup( + name = "jni_md_h", + srcs = ["zulu8.23.0.3-jdk8.0.144-win_x64/include/win32/jni_md.h"], +)""", +) + +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories") +closure_repositories() + +load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains") +go_rules_dependencies() +go_register_toolchains()
diff --git a/src/third_party/brotli/bootstrap b/src/third_party/brotli/bootstrap new file mode 100755 index 0000000..dbaea15 --- /dev/null +++ b/src/third_party/brotli/bootstrap
@@ -0,0 +1,29 @@ +# !/bin/sh -e + +REQUIRED='is required, but not installed.' +bc -v >/dev/null 2>&1 || { echo >&2 "'bc' $REQUIRED"; exit 1; } +if [ `uname -s` != "FreeBSD" ]; then +sed --version >/dev/null 2>&1 || { echo >&2 "'sed' $REQUIRED"; exit 1; } +fi +autoreconf --version >/dev/null 2>&1 || { echo >&2 "'autoconf' $REQUIRED"; exit 1; } + +mkdir m4 2>/dev/null + +BROTLI_ABI_HEX=`sed -n 's/#define BROTLI_ABI_VERSION 0x//p' c/common/version.h` +BROTLI_ABI_INT=`echo "ibase=16;$BROTLI_ABI_HEX" | bc` +BROTLI_ABI_CURRENT=`expr $BROTLI_ABI_INT / 16777216` +BROTLI_ABI_REVISION=`expr $BROTLI_ABI_INT / 4096 % 4096` +BROTLI_ABI_AGE=`expr $BROTLI_ABI_INT % 4096` +BROTLI_ABI_INFO="$BROTLI_ABI_CURRENT:$BROTLI_ABI_REVISION:$BROTLI_ABI_AGE" + +BROTLI_VERSION_HEX=`sed -n 's/#define BROTLI_VERSION 0x//p' c/common/version.h` +BROTLI_VERSION_INT=`echo "ibase=16;$BROTLI_VERSION_HEX" | bc` +BROTLI_VERSION_MAJOR=`expr $BROTLI_VERSION_INT / 16777216` +BROTLI_VERSION_MINOR=`expr $BROTLI_VERSION_INT / 4096 % 4096` +BROTLI_VERSION_PATCH=`expr $BROTLI_VERSION_INT % 4096` +BROTLI_VERSION="$BROTLI_VERSION_MAJOR.$BROTLI_VERSION_MINOR.$BROTLI_VERSION_PATCH" + +sed -i.bak -r "s/[0-9]+:[0-9]+:[0-9]+/$BROTLI_ABI_INFO/" Makefile.am +sed -i.bak -r "s/\[[0-9]+\.[0-9]+\.[0-9]+\]/[$BROTLI_VERSION]/" configure.ac + +autoreconf --install --force --symlink || exit $
diff --git a/src/third_party/brotli/bro.gypi b/src/third_party/brotli/bro.gypi new file mode 100644 index 0000000..a416b92 --- /dev/null +++ b/src/third_party/brotli/bro.gypi
@@ -0,0 +1,46 @@ +# Copyright (c) 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Usage: +# { +# 'target_name': 'your_target_name', +# 'type': 'none', +# 'actions': [ +# { +# 'variables': { +# 'input_file': 'file/to/compress', +# 'output_file': 'file/to/put/compressed', +# }, +# 'includes': ['../third_party/brotli/bro.gypi'], +# } +# ], +# 'dependencies': [ +# 'path/to:builds_file_to_compress' +# ], +# }, + +{ + 'action_name': 'genbro', + 'variables': { + 'bro': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)bro<(EXECUTABLE_SUFFIX)', + }, + 'inputs': [ + '<(bro)', + '<(input_file)', + ], + 'outputs': [ + '<(output_file)', + ], + 'action': [ + '<(bro)', + '--force', + '--input', + '<(input_file)', + '--output', + '<(output_file)', + ], + 'dependencies': [ + '<(DEPTH)/third_party/brotli/brotli.gyp:bro#host', + ], +}
diff --git a/src/third_party/brotli/brotli.gyp b/src/third_party/brotli/brotli.gyp new file mode 100644 index 0000000..63f9ba9 --- /dev/null +++ b/src/third_party/brotli/brotli.gyp
@@ -0,0 +1,192 @@ +# Copyright 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'headers', + 'type': 'static_library', + 'sources': [ + 'c/include/brotli/decode.h', + 'c/include/brotli/encode.h', + 'c/include/brotli/port.h', + 'c/include/brotli/types.h', + ], + 'toolsets': ['host', 'target'], + }, + { + 'target_name': 'common', + 'type': 'static_library', + 'include_dirs': [ + 'c/include', + ], + 'sources': [ + 'c/common/constants.h', + 'c/common/dictionary.c', + 'c/common/dictionary.h', + 'c/common/platform.h', + 'c/common/version.h', + ], + 'dependencies': [ + 'headers', + ], + 'toolsets': ['host', 'target'], + }, + { + 'target_name': 'common_no_dictionary_data', + 'type': 'static_library', + 'include_dirs': [ + 'c/include', + ], + 'sources': [ + 'c/common/constants.h', + 'c/common/dictionary.c', + 'c/common/dictionary.h', + 'c/common/platform.h', + 'c/common/version.h', + ], + 'dependencies': [ + 'headers', + ], + 'defines': [ + 'BROTLI_EXTERNAL_DICTIONARY_DATA', + ], + 'toolsets': ['host', 'target'], + }, + { + 'target_name': 'dec', + 'type': 'static_library', + 'include_dirs': [ + 'c/include', + ], + 'sources': [ + 'c/dec/bit_reader.c', + 'c/dec/bit_reader.h', + 'c/dec/context.h', + 'c/dec/decode.c', + 'c/dec/huffman.c', + 'c/dec/huffman.h', + 'c/dec/prefix.h', + 'c/dec/state.c', + 'c/dec/state.h', + 'c/dec/transform.h', + ], + 'dependencies': [ + 'headers', + 'common', + ], + 'conditions': [ + ['os_posix==1 and (target_arch=="arm" or target_arch=="armv7" or target_arch=="arm64")', { + 'cflags!': ['-Os'], + 'cflags': ['-O2'], + }], + ], + 'toolsets': ['host', 'target'], + }, + { + 'target_name': 'dec_no_dictionary_data', + 'type': 'static_library', + 'include_dirs': [ + 'c/include', + ], + 'sources': [ + 'c/dec/bit_reader.c', + 'c/dec/bit_reader.h', + 'c/dec/context.h', + 'c/dec/decode.c', + 'c/dec/huffman.c', + 'c/dec/huffman.h', + 'c/dec/prefix.h', + 'c/dec/state.c', + 'c/dec/state.h', + 'c/dec/transform.h', + ], + 'dependencies': [ + 'headers', + 'common_no_dictionary_data', + ], + 'toolsets': ['host', 'target'], + }, + { + 'target_name': 'bro', + 'type': 'executable', + 'dependencies': [ + 'headers', + 'common', + 'dec', + ], + 'include_dirs': [ + 'c/include', + ], + 'sources': [ + 'c/enc/backward_references.c', + 'c/enc/backward_references.h', + 'c/enc/backward_references_hq.c', + 'c/enc/backward_references_hq.h', + 'c/enc/backward_references_inc.h', + 'c/enc/bit_cost.c', + 'c/enc/bit_cost.h', + 'c/enc/bit_cost_inc.h', + 'c/enc/block_encoder_inc.h', + 'c/enc/block_splitter.c', + 'c/enc/block_splitter.h', + 'c/enc/block_splitter_inc.h', + 'c/enc/brotli_bit_stream.c', + 'c/enc/brotli_bit_stream.h', + 'c/enc/cluster.c', + 'c/enc/cluster.h', + 'c/enc/cluster_inc.h', + 'c/enc/command.h', + 'c/enc/compress_fragment.c', + 'c/enc/compress_fragment.h', + 'c/enc/compress_fragment_two_pass.c', + 'c/enc/compress_fragment_two_pass.h', + 'c/enc/context.h', + 'c/enc/dictionary_hash.c', + 'c/enc/dictionary_hash.h', + 'c/enc/encode.c', + 'c/enc/entropy_encode.c', + 'c/enc/entropy_encode.h', + 'c/enc/entropy_encode_static.h', + 'c/enc/fast_log.h', + 'c/enc/find_match_length.h', + 'c/enc/hash_forgetful_chain_inc.h', + 'c/enc/hash.h', + 'c/enc/hash_longest_match64_inc.h', + 'c/enc/hash_longest_match_inc.h', + 'c/enc/hash_longest_match_quickly_inc.h', + 'c/enc/hash_to_binary_tree_inc.h', + 'c/enc/histogram.c', + 'c/enc/histogram.h', + 'c/enc/histogram_inc.h', + 'c/enc/literal_cost.c', + 'c/enc/literal_cost.h', + 'c/enc/memory.c', + 'c/enc/memory.h', + 'c/enc/metablock.c', + 'c/enc/metablock.h', + 'c/enc/metablock_inc.h', + 'c/enc/params.h', + 'c/enc/prefix.h', + 'c/enc/quality.h', + 'c/enc/ringbuffer.h', + 'c/enc/static_dict.c', + 'c/enc/static_dict.h', + 'c/enc/static_dict_lut.h', + 'c/enc/utf8_util.c', + 'c/enc/utf8_util.h', + 'c/enc/write_bits.h', + 'c/tools/brotli.c', + ], + 'toolsets': ['host'], + 'conditions': [ + ['OS=="win" and MSVS_VERSION == "2015"', { + # Disabling "result of 32-bit shift implicitly converted to 64 bits", + # caused by code like: foo |= (1 << i); // warning 4334 + 'msvs_disabled_warnings': [ 4334, ], + }], + ], + } + ], +}
diff --git a/src/third_party/brotli/c/common/constants.h b/src/third_party/brotli/c/common/constants.h new file mode 100644 index 0000000..416ec55 --- /dev/null +++ b/src/third_party/brotli/c/common/constants.h
@@ -0,0 +1,57 @@ +/* Copyright 2016 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#ifndef BROTLI_COMMON_CONSTANTS_H_ +#define BROTLI_COMMON_CONSTANTS_H_ + +/* Specification: 7.3. Encoding of the context map */ +#define BROTLI_CONTEXT_MAP_MAX_RLE 16 + +/* Specification: 2. Compressed representation overview */ +#define BROTLI_MAX_NUMBER_OF_BLOCK_TYPES 256 + +/* Specification: 3.3. Alphabet sizes: insert-and-copy length */ +#define BROTLI_NUM_LITERAL_SYMBOLS 256 +#define BROTLI_NUM_COMMAND_SYMBOLS 704 +#define BROTLI_NUM_BLOCK_LEN_SYMBOLS 26 +#define BROTLI_MAX_CONTEXT_MAP_SYMBOLS (BROTLI_MAX_NUMBER_OF_BLOCK_TYPES + \ + BROTLI_CONTEXT_MAP_MAX_RLE) +#define BROTLI_MAX_BLOCK_TYPE_SYMBOLS (BROTLI_MAX_NUMBER_OF_BLOCK_TYPES + 2) + +/* Specification: 3.5. Complex prefix codes */ +#define BROTLI_REPEAT_PREVIOUS_CODE_LENGTH 16 +#define BROTLI_REPEAT_ZERO_CODE_LENGTH 17 +#define BROTLI_CODE_LENGTH_CODES (BROTLI_REPEAT_ZERO_CODE_LENGTH + 1) +/* "code length of 8 is repeated" */ +#define BROTLI_INITIAL_REPEATED_CODE_LENGTH 8 + +/* Specification: 4. Encoding of distances */ +#define BROTLI_NUM_DISTANCE_SHORT_CODES 16 +#define BROTLI_MAX_NPOSTFIX 3 +#define BROTLI_MAX_NDIRECT 120 +#define BROTLI_MAX_DISTANCE_BITS 24U +/* BROTLI_NUM_DISTANCE_SYMBOLS == 520 */ +#define BROTLI_NUM_DISTANCE_SYMBOLS (BROTLI_NUM_DISTANCE_SHORT_CODES + \ + BROTLI_MAX_NDIRECT + \ + (BROTLI_MAX_DISTANCE_BITS << \ + (BROTLI_MAX_NPOSTFIX + 1))) +/* Distance that is guaranteed to be representable in any stream. */ +#define BROTLI_MAX_DISTANCE 0x3FFFFFC + +/* 7.1. Context modes and context ID lookup for literals */ +/* "context IDs for literals are in the range of 0..63" */ +#define BROTLI_LITERAL_CONTEXT_BITS 6 + +/* 7.2. Context ID for distances */ +#define BROTLI_DISTANCE_CONTEXT_BITS 2 + +/* 9.1. Format of the Stream Header */ +/* Number of slack bytes for window size. Don't confuse + with BROTLI_NUM_DISTANCE_SHORT_CODES. */ +#define BROTLI_WINDOW_GAP 16 +#define BROTLI_MAX_BACKWARD_LIMIT(W) (((size_t)1 << (W)) - BROTLI_WINDOW_GAP) + +#endif /* BROTLI_COMMON_CONSTANTS_H_ */
diff --git a/src/third_party/brotli/c/common/dictionary.bin b/src/third_party/brotli/c/common/dictionary.bin new file mode 100644 index 0000000..a585c0e --- /dev/null +++ b/src/third_party/brotli/c/common/dictionary.bin Binary files differ
diff --git a/src/third_party/brotli/c/common/dictionary.c b/src/third_party/brotli/c/common/dictionary.c new file mode 100644 index 0000000..d0872bd --- /dev/null +++ b/src/third_party/brotli/c/common/dictionary.c
@@ -0,0 +1,5905 @@ +/* Copyright 2013 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include "./dictionary.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#ifndef BROTLI_EXTERNAL_DICTIONARY_DATA +static const uint8_t kBrotliDictionaryData[] = +{ +116,105,109,101,100,111,119,110,108,105,102,101,108,101,102,116,98,97,99,107,99, +111,100,101,100,97,116,97,115,104,111,119,111,110,108,121,115,105,116,101,99,105 +,116,121,111,112,101,110,106,117,115,116,108,105,107,101,102,114,101,101,119,111 +,114,107,116,101,120,116,121,101,97,114,111,118,101,114,98,111,100,121,108,111, +118,101,102,111,114,109,98,111,111,107,112,108,97,121,108,105,118,101,108,105, +110,101,104,101,108,112,104,111,109,101,115,105,100,101,109,111,114,101,119,111, +114,100,108,111,110,103,116,104,101,109,118,105,101,119,102,105,110,100,112,97, +103,101,100,97,121,115,102,117,108,108,104,101,97,100,116,101,114,109,101,97,99, +104,97,114,101,97,102,114,111,109,116,114,117,101,109,97,114,107,97,98,108,101, +117,112,111,110,104,105,103,104,100,97,116,101,108,97,110,100,110,101,119,115, +101,118,101,110,110,101,120,116,99,97,115,101,98,111,116,104,112,111,115,116,117 +,115,101,100,109,97,100,101,104,97,110,100,104,101,114,101,119,104,97,116,110,97 +,109,101,76,105,110,107,98,108,111,103,115,105,122,101,98,97,115,101,104,101,108 +,100,109,97,107,101,109,97,105,110,117,115,101,114,39,41,32,43,104,111,108,100, +101,110,100,115,119,105,116,104,78,101,119,115,114,101,97,100,119,101,114,101, +115,105,103,110,116,97,107,101,104,97,118,101,103,97,109,101,115,101,101,110,99, +97,108,108,112,97,116,104,119,101,108,108,112,108,117,115,109,101,110,117,102, +105,108,109,112,97,114,116,106,111,105,110,116,104,105,115,108,105,115,116,103, +111,111,100,110,101,101,100,119,97,121,115,119,101,115,116,106,111,98,115,109, +105,110,100,97,108,115,111,108,111,103,111,114,105,99,104,117,115,101,115,108,97 +,115,116,116,101,97,109,97,114,109,121,102,111,111,100,107,105,110,103,119,105, +108,108,101,97,115,116,119,97,114,100,98,101,115,116,102,105,114,101,80,97,103, +101,107,110,111,119,97,119,97,121,46,112,110,103,109,111,118,101,116,104,97,110, +108,111,97,100,103,105,118,101,115,101,108,102,110,111,116,101,109,117,99,104, +102,101,101,100,109,97,110,121,114,111,99,107,105,99,111,110,111,110,99,101,108, +111,111,107,104,105,100,101,100,105,101,100,72,111,109,101,114,117,108,101,104, +111,115,116,97,106,97,120,105,110,102,111,99,108,117,98,108,97,119,115,108,101, +115,115,104,97,108,102,115,111,109,101,115,117,99,104,122,111,110,101,49,48,48, +37,111,110,101,115,99,97,114,101,84,105,109,101,114,97,99,101,98,108,117,101,102 +,111,117,114,119,101,101,107,102,97,99,101,104,111,112,101,103,97,118,101,104,97 +,114,100,108,111,115,116,119,104,101,110,112,97,114,107,107,101,112,116,112,97, +115,115,115,104,105,112,114,111,111,109,72,84,77,76,112,108,97,110,84,121,112, +101,100,111,110,101,115,97,118,101,107,101,101,112,102,108,97,103,108,105,110, +107,115,111,108,100,102,105,118,101,116,111,111,107,114,97,116,101,116,111,119, +110,106,117,109,112,116,104,117,115,100,97,114,107,99,97,114,100,102,105,108,101 +,102,101,97,114,115,116,97,121,107,105,108,108,116,104,97,116,102,97,108,108,97, +117,116,111,101,118,101,114,46,99,111,109,116,97,108,107,115,104,111,112,118,111 +,116,101,100,101,101,112,109,111,100,101,114,101,115,116,116,117,114,110,98,111, +114,110,98,97,110,100,102,101,108,108,114,111,115,101,117,114,108,40,115,107,105 +,110,114,111,108,101,99,111,109,101,97,99,116,115,97,103,101,115,109,101,101,116 +,103,111,108,100,46,106,112,103,105,116,101,109,118,97,114,121,102,101,108,116, +116,104,101,110,115,101,110,100,100,114,111,112,86,105,101,119,99,111,112,121,49 +,46,48,34,60,47,97,62,115,116,111,112,101,108,115,101,108,105,101,115,116,111, +117,114,112,97,99,107,46,103,105,102,112,97,115,116,99,115,115,63,103,114,97,121 +,109,101,97,110,38,103,116,59,114,105,100,101,115,104,111,116,108,97,116,101,115 +,97,105,100,114,111,97,100,118,97,114,32,102,101,101,108,106,111,104,110,114,105 +,99,107,112,111,114,116,102,97,115,116,39,85,65,45,100,101,97,100,60,47,98,62, +112,111,111,114,98,105,108,108,116,121,112,101,85,46,83,46,119,111,111,100,109, +117,115,116,50,112,120,59,73,110,102,111,114,97,110,107,119,105,100,101,119,97, +110,116,119,97,108,108,108,101,97,100,91,48,93,59,112,97,117,108,119,97,118,101, +115,117,114,101,36,40,39,35,119,97,105,116,109,97,115,115,97,114,109,115,103,111 +,101,115,103,97,105,110,108,97,110,103,112,97,105,100,33,45,45,32,108,111,99,107 +,117,110,105,116,114,111,111,116,119,97,108,107,102,105,114,109,119,105,102,101, +120,109,108,34,115,111,110,103,116,101,115,116,50,48,112,120,107,105,110,100,114 +,111,119,115,116,111,111,108,102,111,110,116,109,97,105,108,115,97,102,101,115, +116,97,114,109,97,112,115,99,111,114,101,114,97,105,110,102,108,111,119,98,97,98 +,121,115,112,97,110,115,97,121,115,52,112,120,59,54,112,120,59,97,114,116,115, +102,111,111,116,114,101,97,108,119,105,107,105,104,101,97,116,115,116,101,112, +116,114,105,112,111,114,103,47,108,97,107,101,119,101,97,107,116,111,108,100,70, +111,114,109,99,97,115,116,102,97,110,115,98,97,110,107,118,101,114,121,114,117, +110,115,106,117,108,121,116,97,115,107,49,112,120,59,103,111,97,108,103,114,101, +119,115,108,111,119,101,100,103,101,105,100,61,34,115,101,116,115,53,112,120,59, +46,106,115,63,52,48,112,120,105,102,32,40,115,111,111,110,115,101,97,116,110,111 +,110,101,116,117,98,101,122,101,114,111,115,101,110,116,114,101,101,100,102,97, +99,116,105,110,116,111,103,105,102,116,104,97,114,109,49,56,112,120,99,97,109, +101,104,105,108,108,98,111,108,100,122,111,111,109,118,111,105,100,101,97,115, +121,114,105,110,103,102,105,108,108,112,101,97,107,105,110,105,116,99,111,115, +116,51,112,120,59,106,97,99,107,116,97,103,115,98,105,116,115,114,111,108,108, +101,100,105,116,107,110,101,119,110,101,97,114,60,33,45,45,103,114,111,119,74,83 +,79,78,100,117,116,121,78,97,109,101,115,97,108,101,121,111,117,32,108,111,116, +115,112,97,105,110,106,97,122,122,99,111,108,100,101,121,101,115,102,105,115,104 +,119,119,119,46,114,105,115,107,116,97,98,115,112,114,101,118,49,48,112,120,114, +105,115,101,50,53,112,120,66,108,117,101,100,105,110,103,51,48,48,44,98,97,108, +108,102,111,114,100,101,97,114,110,119,105,108,100,98,111,120,46,102,97,105,114, +108,97,99,107,118,101,114,115,112,97,105,114,106,117,110,101,116,101,99,104,105, +102,40,33,112,105,99,107,101,118,105,108,36,40,34,35,119,97,114,109,108,111,114, +100,100,111,101,115,112,117,108,108,44,48,48,48,105,100,101,97,100,114,97,119, +104,117,103,101,115,112,111,116,102,117,110,100,98,117,114,110,104,114,101,102, +99,101,108,108,107,101,121,115,116,105,99,107,104,111,117,114,108,111,115,115, +102,117,101,108,49,50,112,120,115,117,105,116,100,101,97,108,82,83,83,34,97,103, +101,100,103,114,101,121,71,69,84,34,101,97,115,101,97,105,109,115,103,105,114, +108,97,105,100,115,56,112,120,59,110,97,118,121,103,114,105,100,116,105,112,115, +35,57,57,57,119,97,114,115,108,97,100,121,99,97,114,115,41,59,32,125,112,104,112 +,63,104,101,108,108,116,97,108,108,119,104,111,109,122,104,58,229,42,47,13,10,32 +,49,48,48,104,97,108,108,46,10,10,65,55,112,120,59,112,117,115,104,99,104,97,116 +,48,112,120,59,99,114,101,119,42,47,60,47,104,97,115,104,55,53,112,120,102,108, +97,116,114,97,114,101,32,38,38,32,116,101,108,108,99,97,109,112,111,110,116,111, +108,97,105,100,109,105,115,115,115,107,105,112,116,101,110,116,102,105,110,101, +109,97,108,101,103,101,116,115,112,108,111,116,52,48,48,44,13,10,13,10,99,111, +111,108,102,101,101,116,46,112,104,112,60,98,114,62,101,114,105,99,109,111,115, +116,103,117,105,100,98,101,108,108,100,101,115,99,104,97,105,114,109,97,116,104, +97,116,111,109,47,105,109,103,38,35,56,50,108,117,99,107,99,101,110,116,48,48,48 +,59,116,105,110,121,103,111,110,101,104,116,109,108,115,101,108,108,100,114,117, +103,70,82,69,69,110,111,100,101,110,105,99,107,63,105,100,61,108,111,115,101,110 +,117,108,108,118,97,115,116,119,105,110,100,82,83,83,32,119,101,97,114,114,101, +108,121,98,101,101,110,115,97,109,101,100,117,107,101,110,97,115,97,99,97,112, +101,119,105,115,104,103,117,108,102,84,50,51,58,104,105,116,115,115,108,111,116, +103,97,116,101,107,105,99,107,98,108,117,114,116,104,101,121,49,53,112,120,39,39 +,41,59,41,59,34,62,109,115,105,101,119,105,110,115,98,105,114,100,115,111,114, +116,98,101,116,97,115,101,101,107,84,49,56,58,111,114,100,115,116,114,101,101, +109,97,108,108,54,48,112,120,102,97,114,109,226,128,153,115,98,111,121,115,91,48 +,93,46,39,41,59,34,80,79,83,84,98,101,97,114,107,105,100,115,41,59,125,125,109, +97,114,121,116,101,110,100,40,85,75,41,113,117,97,100,122,104,58,230,45,115,105, +122,45,45,45,45,112,114,111,112,39,41,59,13,108,105,102,116,84,49,57,58,118,105, +99,101,97,110,100,121,100,101,98,116,62,82,83,83,112,111,111,108,110,101,99,107, +98,108,111,119,84,49,54,58,100,111,111,114,101,118,97,108,84,49,55,58,108,101, +116,115,102,97,105,108,111,114,97,108,112,111,108,108,110,111,118,97,99,111,108, +115,103,101,110,101,32,226,128,148,115,111,102,116,114,111,109,101,116,105,108, +108,114,111,115,115,60,104,51,62,112,111,117,114,102,97,100,101,112,105,110,107, +60,116,114,62,109,105,110,105,41,124,33,40,109,105,110,101,122,104,58,232,98,97, +114,115,104,101,97,114,48,48,41,59,109,105,108,107,32,45,45,62,105,114,111,110, +102,114,101,100,100,105,115,107,119,101,110,116,115,111,105,108,112,117,116,115, +47,106,115,47,104,111,108,121,84,50,50,58,73,83,66,78,84,50,48,58,97,100,97,109, +115,101,101,115,60,104,50,62,106,115,111,110,39,44,32,39,99,111,110,116,84,50,49 +,58,32,82,83,83,108,111,111,112,97,115,105,97,109,111,111,110,60,47,112,62,115, +111,117,108,76,73,78,69,102,111,114,116,99,97,114,116,84,49,52,58,60,104,49,62, +56,48,112,120,33,45,45,60,57,112,120,59,84,48,52,58,109,105,107,101,58,52,54,90, +110,105,99,101,105,110,99,104,89,111,114,107,114,105,99,101,122,104,58,228,39,41 +,41,59,112,117,114,101,109,97,103,101,112,97,114,97,116,111,110,101,98,111,110, +100,58,51,55,90,95,111,102,95,39,93,41,59,48,48,48,44,122,104,58,231,116,97,110, +107,121,97,114,100,98,111,119,108,98,117,115,104,58,53,54,90,74,97,118,97,51,48, +112,120,10,124,125,10,37,67,51,37,58,51,52,90,106,101,102,102,69,88,80,73,99,97, +115,104,118,105,115,97,103,111,108,102,115,110,111,119,122,104,58,233,113,117, +101,114,46,99,115,115,115,105,99,107,109,101,97,116,109,105,110,46,98,105,110, +100,100,101,108,108,104,105,114,101,112,105,99,115,114,101,110,116,58,51,54,90, +72,84,84,80,45,50,48,49,102,111,116,111,119,111,108,102,69,78,68,32,120,98,111, +120,58,53,52,90,66,79,68,89,100,105,99,107,59,10,125,10,101,120,105,116,58,51,53 +,90,118,97,114,115,98,101,97,116,39,125,41,59,100,105,101,116,57,57,57,59,97,110 +,110,101,125,125,60,47,91,105,93,46,76,97,110,103,107,109,194,178,119,105,114, +101,116,111,121,115,97,100,100,115,115,101,97,108,97,108,101,120,59,10,9,125,101 +,99,104,111,110,105,110,101,46,111,114,103,48,48,53,41,116,111,110,121,106,101, +119,115,115,97,110,100,108,101,103,115,114,111,111,102,48,48,48,41,32,50,48,48, +119,105,110,101,103,101,97,114,100,111,103,115,98,111,111,116,103,97,114,121,99, +117,116,115,116,121,108,101,116,101,109,112,116,105,111,110,46,120,109,108,99, +111,99,107,103,97,110,103,36,40,39,46,53,48,112,120,80,104,46,68,109,105,115,99, +97,108,97,110,108,111,97,110,100,101,115,107,109,105,108,101,114,121,97,110,117, +110,105,120,100,105,115,99,41,59,125,10,100,117,115,116,99,108,105,112,41,46,10, +10,55,48,112,120,45,50,48,48,68,86,68,115,55,93,62,60,116,97,112,101,100,101,109 +,111,105,43,43,41,119,97,103,101,101,117,114,111,112,104,105,108,111,112,116,115 +,104,111,108,101,70,65,81,115,97,115,105,110,45,50,54,84,108,97,98,115,112,101, +116,115,85,82,76,32,98,117,108,107,99,111,111,107,59,125,13,10,72,69,65,68,91,48 +,93,41,97,98,98,114,106,117,97,110,40,49,57,56,108,101,115,104,116,119,105,110, +60,47,105,62,115,111,110,121,103,117,121,115,102,117,99,107,112,105,112,101,124, +45,10,33,48,48,50,41,110,100,111,119,91,49,93,59,91,93,59,10,76,111,103,32,115, +97,108,116,13,10,9,9,98,97,110,103,116,114,105,109,98,97,116,104,41,123,13,10,48 +,48,112,120,10,125,41,59,107,111,58,236,102,101,101,115,97,100,62,13,115,58,47, +47,32,91,93,59,116,111,108,108,112,108,117,103,40,41,123,10,123,13,10,32,46,106, +115,39,50,48,48,112,100,117,97,108,98,111,97,116,46,74,80,71,41,59,10,125,113, +117,111,116,41,59,10,10,39,41,59,10,13,10,125,13,50,48,49,52,50,48,49,53,50,48, +49,54,50,48,49,55,50,48,49,56,50,48,49,57,50,48,50,48,50,48,50,49,50,48,50,50,50 +,48,50,51,50,48,50,52,50,48,50,53,50,48,50,54,50,48,50,55,50,48,50,56,50,48,50, +57,50,48,51,48,50,48,51,49,50,48,51,50,50,48,51,51,50,48,51,52,50,48,51,53,50,48 +,51,54,50,48,51,55,50,48,49,51,50,48,49,50,50,48,49,49,50,48,49,48,50,48,48,57, +50,48,48,56,50,48,48,55,50,48,48,54,50,48,48,53,50,48,48,52,50,48,48,51,50,48,48 +,50,50,48,48,49,50,48,48,48,49,57,57,57,49,57,57,56,49,57,57,55,49,57,57,54,49, +57,57,53,49,57,57,52,49,57,57,51,49,57,57,50,49,57,57,49,49,57,57,48,49,57,56,57 +,49,57,56,56,49,57,56,55,49,57,56,54,49,57,56,53,49,57,56,52,49,57,56,51,49,57, +56,50,49,57,56,49,49,57,56,48,49,57,55,57,49,57,55,56,49,57,55,55,49,57,55,54,49 +,57,55,53,49,57,55,52,49,57,55,51,49,57,55,50,49,57,55,49,49,57,55,48,49,57,54, +57,49,57,54,56,49,57,54,55,49,57,54,54,49,57,54,53,49,57,54,52,49,57,54,51,49,57 +,54,50,49,57,54,49,49,57,54,48,49,57,53,57,49,57,53,56,49,57,53,55,49,57,53,54, +49,57,53,53,49,57,53,52,49,57,53,51,49,57,53,50,49,57,53,49,49,57,53,48,49,48,48 +,48,49,48,50,52,49,51,57,52,48,48,48,48,57,57,57,57,99,111,109,111,109,195,161, +115,101,115,116,101,101,115,116,97,112,101,114,111,116,111,100,111,104,97,99,101 +,99,97,100,97,97,195,177,111,98,105,101,110,100,195,173,97,97,115,195,173,118, +105,100,97,99,97,115,111,111,116,114,111,102,111,114,111,115,111,108,111,111,116 +,114,97,99,117,97,108,100,105,106,111,115,105,100,111,103,114,97,110,116,105,112 +,111,116,101,109,97,100,101,98,101,97,108,103,111,113,117,195,169,101,115,116, +111,110,97,100,97,116,114,101,115,112,111,99,111,99,97,115,97,98,97,106,111,116, +111,100,97,115,105,110,111,97,103,117,97,112,117,101,115,117,110,111,115,97,110, +116,101,100,105,99,101,108,117,105,115,101,108,108,97,109,97,121,111,122,111,110 +,97,97,109,111,114,112,105,115,111,111,98,114,97,99,108,105,99,101,108,108,111, +100,105,111,115,104,111,114,97,99,97,115,105,208,183,208,176,208,189,208,176,208 +,190,208,188,209,128,208,176,209,128,209,131,209,130,208,176,208,189,208,181,208 +,191,208,190,208,190,209,130,208,184,208,183,208,189,208,190,208,180,208,190,209 +,130,208,190,208,182,208,181,208,190,208,189,208,184,209,133,208,157,208,176,208 +,181,208,181,208,177,209,139,208,188,209,139,208,146,209,139,209,129,208,190,208 +,178,209,139,208,178,208,190,208,157,208,190,208,190,208,177,208,159,208,190,208 +,187,208,184,208,189,208,184,208,160,208,164,208,157,208,181,208,156,209,139,209 +,130,209,139,208,158,208,189,208,184,208,188,208,180,208,176,208,151,208,176,208 +,148,208,176,208,157,209,131,208,158,208,177,209,130,208,181,208,152,208,183,208 +,181,208,185,208,189,209,131,208,188,208,188,208,162,209,139,209,131,208,182,217 +,129,217,138,216,163,217,134,217,133,216,167,217,133,216,185,217,131,217,132,216 +,163,217,136,216,177,216,175,217,138,216,167,217,129,217,137,217,135,217,136,217 +,132,217,133,217,132,217,131,216,167,217,136,217,132,217,135,216,168,216,179,216 +,167,217,132,216,165,217,134,217,135,217,138,216,163,217,138,217,130,216,175,217 +,135,217,132,216,171,217,133,216,168,217,135,217,132,217,136,217,132,217,138,216 +,168,217,132,216,167,217,138,216,168,217,131,216,180,217,138,216,167,217,133,216 +,163,217,133,217,134,216,170,216,168,217,138,217,132,217,134,216,173,216,168,217 +,135,217,133,217,133,216,180,217,136,216,180,102,105,114,115,116,118,105,100,101 +,111,108,105,103,104,116,119,111,114,108,100,109,101,100,105,97,119,104,105,116, +101,99,108,111,115,101,98,108,97,99,107,114,105,103,104,116,115,109,97,108,108, +98,111,111,107,115,112,108,97,99,101,109,117,115,105,99,102,105,101,108,100,111, +114,100,101,114,112,111,105,110,116,118,97,108,117,101,108,101,118,101,108,116, +97,98,108,101,98,111,97,114,100,104,111,117,115,101,103,114,111,117,112,119,111, +114,107,115,121,101,97,114,115,115,116,97,116,101,116,111,100,97,121,119,97,116, +101,114,115,116,97,114,116,115,116,121,108,101,100,101,97,116,104,112,111,119, +101,114,112,104,111,110,101,110,105,103,104,116,101,114,114,111,114,105,110,112, +117,116,97,98,111,117,116,116,101,114,109,115,116,105,116,108,101,116,111,111, +108,115,101,118,101,110,116,108,111,99,97,108,116,105,109,101,115,108,97,114,103 +,101,119,111,114,100,115,103,97,109,101,115,115,104,111,114,116,115,112,97,99, +101,102,111,99,117,115,99,108,101,97,114,109,111,100,101,108,98,108,111,99,107, +103,117,105,100,101,114,97,100,105,111,115,104,97,114,101,119,111,109,101,110,97 +,103,97,105,110,109,111,110,101,121,105,109,97,103,101,110,97,109,101,115,121, +111,117,110,103,108,105,110,101,115,108,97,116,101,114,99,111,108,111,114,103, +114,101,101,110,102,114,111,110,116,38,97,109,112,59,119,97,116,99,104,102,111, +114,99,101,112,114,105,99,101,114,117,108,101,115,98,101,103,105,110,97,102,116, +101,114,118,105,115,105,116,105,115,115,117,101,97,114,101,97,115,98,101,108,111 +,119,105,110,100,101,120,116,111,116,97,108,104,111,117,114,115,108,97,98,101, +108,112,114,105,110,116,112,114,101,115,115,98,117,105,108,116,108,105,110,107, +115,115,112,101,101,100,115,116,117,100,121,116,114,97,100,101,102,111,117,110, +100,115,101,110,115,101,117,110,100,101,114,115,104,111,119,110,102,111,114,109, +115,114,97,110,103,101,97,100,100,101,100,115,116,105,108,108,109,111,118,101, +100,116,97,107,101,110,97,98,111,118,101,102,108,97,115,104,102,105,120,101,100, +111,102,116,101,110,111,116,104,101,114,118,105,101,119,115,99,104,101,99,107, +108,101,103,97,108,114,105,118,101,114,105,116,101,109,115,113,117,105,99,107, +115,104,97,112,101,104,117,109,97,110,101,120,105,115,116,103,111,105,110,103, +109,111,118,105,101,116,104,105,114,100,98,97,115,105,99,112,101,97,99,101,115, +116,97,103,101,119,105,100,116,104,108,111,103,105,110,105,100,101,97,115,119, +114,111,116,101,112,97,103,101,115,117,115,101,114,115,100,114,105,118,101,115, +116,111,114,101,98,114,101,97,107,115,111,117,116,104,118,111,105,99,101,115,105 +,116,101,115,109,111,110,116,104,119,104,101,114,101,98,117,105,108,100,119,104, +105,99,104,101,97,114,116,104,102,111,114,117,109,116,104,114,101,101,115,112, +111,114,116,112,97,114,116,121,67,108,105,99,107,108,111,119,101,114,108,105,118 +,101,115,99,108,97,115,115,108,97,121,101,114,101,110,116,114,121,115,116,111, +114,121,117,115,97,103,101,115,111,117,110,100,99,111,117,114,116,121,111,117, +114,32,98,105,114,116,104,112,111,112,117,112,116,121,112,101,115,97,112,112,108 +,121,73,109,97,103,101,98,101,105,110,103,117,112,112,101,114,110,111,116,101, +115,101,118,101,114,121,115,104,111,119,115,109,101,97,110,115,101,120,116,114, +97,109,97,116,99,104,116,114,97,99,107,107,110,111,119,110,101,97,114,108,121,98 +,101,103,97,110,115,117,112,101,114,112,97,112,101,114,110,111,114,116,104,108, +101,97,114,110,103,105,118,101,110,110,97,109,101,100,101,110,100,101,100,84,101 +,114,109,115,112,97,114,116,115,71,114,111,117,112,98,114,97,110,100,117,115,105 +,110,103,119,111,109,97,110,102,97,108,115,101,114,101,97,100,121,97,117,100,105 +,111,116,97,107,101,115,119,104,105,108,101,46,99,111,109,47,108,105,118,101,100 +,99,97,115,101,115,100,97,105,108,121,99,104,105,108,100,103,114,101,97,116,106, +117,100,103,101,116,104,111,115,101,117,110,105,116,115,110,101,118,101,114,98, +114,111,97,100,99,111,97,115,116,99,111,118,101,114,97,112,112,108,101,102,105, +108,101,115,99,121,99,108,101,115,99,101,110,101,112,108,97,110,115,99,108,105, +99,107,119,114,105,116,101,113,117,101,101,110,112,105,101,99,101,101,109,97,105 +,108,102,114,97,109,101,111,108,100,101,114,112,104,111,116,111,108,105,109,105, +116,99,97,99,104,101,99,105,118,105,108,115,99,97,108,101,101,110,116,101,114, +116,104,101,109,101,116,104,101,114,101,116,111,117,99,104,98,111,117,110,100, +114,111,121,97,108,97,115,107,101,100,119,104,111,108,101,115,105,110,99,101,115 +,116,111,99,107,32,110,97,109,101,102,97,105,116,104,104,101,97,114,116,101,109, +112,116,121,111,102,102,101,114,115,99,111,112,101,111,119,110,101,100,109,105, +103,104,116,97,108,98,117,109,116,104,105,110,107,98,108,111,111,100,97,114,114, +97,121,109,97,106,111,114,116,114,117,115,116,99,97,110,111,110,117,110,105,111, +110,99,111,117,110,116,118,97,108,105,100,115,116,111,110,101,83,116,121,108,101 +,76,111,103,105,110,104,97,112,112,121,111,99,99,117,114,108,101,102,116,58,102, +114,101,115,104,113,117,105,116,101,102,105,108,109,115,103,114,97,100,101,110, +101,101,100,115,117,114,98,97,110,102,105,103,104,116,98,97,115,105,115,104,111, +118,101,114,97,117,116,111,59,114,111,117,116,101,46,104,116,109,108,109,105,120 +,101,100,102,105,110,97,108,89,111,117,114,32,115,108,105,100,101,116,111,112, +105,99,98,114,111,119,110,97,108,111,110,101,100,114,97,119,110,115,112,108,105, +116,114,101,97,99,104,82,105,103,104,116,100,97,116,101,115,109,97,114,99,104, +113,117,111,116,101,103,111,111,100,115,76,105,110,107,115,100,111,117,98,116,97 +,115,121,110,99,116,104,117,109,98,97,108,108,111,119,99,104,105,101,102,121,111 +,117,116,104,110,111,118,101,108,49,48,112,120,59,115,101,114,118,101,117,110, +116,105,108,104,97,110,100,115,67,104,101,99,107,83,112,97,99,101,113,117,101, +114,121,106,97,109,101,115,101,113,117,97,108,116,119,105,99,101,48,44,48,48,48, +83,116,97,114,116,112,97,110,101,108,115,111,110,103,115,114,111,117,110,100,101 +,105,103,104,116,115,104,105,102,116,119,111,114,116,104,112,111,115,116,115,108 +,101,97,100,115,119,101,101,107,115,97,118,111,105,100,116,104,101,115,101,109, +105,108,101,115,112,108,97,110,101,115,109,97,114,116,97,108,112,104,97,112,108, +97,110,116,109,97,114,107,115,114,97,116,101,115,112,108,97,121,115,99,108,97, +105,109,115,97,108,101,115,116,101,120,116,115,115,116,97,114,115,119,114,111, +110,103,60,47,104,51,62,116,104,105,110,103,46,111,114,103,47,109,117,108,116, +105,104,101,97,114,100,80,111,119,101,114,115,116,97,110,100,116,111,107,101,110 +,115,111,108,105,100,40,116,104,105,115,98,114,105,110,103,115,104,105,112,115, +115,116,97,102,102,116,114,105,101,100,99,97,108,108,115,102,117,108,108,121,102 +,97,99,116,115,97,103,101,110,116,84,104,105,115,32,47,47,45,45,62,97,100,109, +105,110,101,103,121,112,116,69,118,101,110,116,49,53,112,120,59,69,109,97,105, +108,116,114,117,101,34,99,114,111,115,115,115,112,101,110,116,98,108,111,103,115 +,98,111,120,34,62,110,111,116,101,100,108,101,97,118,101,99,104,105,110,97,115, +105,122,101,115,103,117,101,115,116,60,47,104,52,62,114,111,98,111,116,104,101, +97,118,121,116,114,117,101,44,115,101,118,101,110,103,114,97,110,100,99,114,105, +109,101,115,105,103,110,115,97,119,97,114,101,100,97,110,99,101,112,104,97,115, +101,62,60,33,45,45,101,110,95,85,83,38,35,51,57,59,50,48,48,112,120,95,110,97, +109,101,108,97,116,105,110,101,110,106,111,121,97,106,97,120,46,97,116,105,111, +110,115,109,105,116,104,85,46,83,46,32,104,111,108,100,115,112,101,116,101,114, +105,110,100,105,97,110,97,118,34,62,99,104,97,105,110,115,99,111,114,101,99,111, +109,101,115,100,111,105,110,103,112,114,105,111,114,83,104,97,114,101,49,57,57, +48,115,114,111,109,97,110,108,105,115,116,115,106,97,112,97,110,102,97,108,108, +115,116,114,105,97,108,111,119,110,101,114,97,103,114,101,101,60,47,104,50,62,97 +,98,117,115,101,97,108,101,114,116,111,112,101,114,97,34,45,47,47,87,99,97,114, +100,115,104,105,108,108,115,116,101,97,109,115,80,104,111,116,111,116,114,117, +116,104,99,108,101,97,110,46,112,104,112,63,115,97,105,110,116,109,101,116,97, +108,108,111,117,105,115,109,101,97,110,116,112,114,111,111,102,98,114,105,101, +102,114,111,119,34,62,103,101,110,114,101,116,114,117,99,107,108,111,111,107,115 +,86,97,108,117,101,70,114,97,109,101,46,110,101,116,47,45,45,62,10,60,116,114, +121,32,123,10,118,97,114,32,109,97,107,101,115,99,111,115,116,115,112,108,97,105 +,110,97,100,117,108,116,113,117,101,115,116,116,114,97,105,110,108,97,98,111,114 +,104,101,108,112,115,99,97,117,115,101,109,97,103,105,99,109,111,116,111,114,116 +,104,101,105,114,50,53,48,112,120,108,101,97,115,116,115,116,101,112,115,67,111, +117,110,116,99,111,117,108,100,103,108,97,115,115,115,105,100,101,115,102,117, +110,100,115,104,111,116,101,108,97,119,97,114,100,109,111,117,116,104,109,111, +118,101,115,112,97,114,105,115,103,105,118,101,115,100,117,116,99,104,116,101, +120,97,115,102,114,117,105,116,110,117,108,108,44,124,124,91,93,59,116,111,112, +34,62,10,60,33,45,45,80,79,83,84,34,111,99,101,97,110,60,98,114,47,62,102,108, +111,111,114,115,112,101,97,107,100,101,112,116,104,32,115,105,122,101,98,97,110, +107,115,99,97,116,99,104,99,104,97,114,116,50,48,112,120,59,97,108,105,103,110, +100,101,97,108,115,119,111,117,108,100,53,48,112,120,59,117,114,108,61,34,112,97 +,114,107,115,109,111,117,115,101,77,111,115,116,32,46,46,46,60,47,97,109,111,110 +,103,98,114,97,105,110,98,111,100,121,32,110,111,110,101,59,98,97,115,101,100,99 +,97,114,114,121,100,114,97,102,116,114,101,102,101,114,112,97,103,101,95,104,111 +,109,101,46,109,101,116,101,114,100,101,108,97,121,100,114,101,97,109,112,114, +111,118,101,106,111,105,110,116,60,47,116,114,62,100,114,117,103,115,60,33,45,45 +,32,97,112,114,105,108,105,100,101,97,108,97,108,108,101,110,101,120,97,99,116, +102,111,114,116,104,99,111,100,101,115,108,111,103,105,99,86,105,101,119,32,115, +101,101,109,115,98,108,97,110,107,112,111,114,116,115,32,40,50,48,48,115,97,118, +101,100,95,108,105,110,107,103,111,97,108,115,103,114,97,110,116,103,114,101,101 +,107,104,111,109,101,115,114,105,110,103,115,114,97,116,101,100,51,48,112,120,59 +,119,104,111,115,101,112,97,114,115,101,40,41,59,34,32,66,108,111,99,107,108,105 +,110,117,120,106,111,110,101,115,112,105,120,101,108,39,41,59,34,62,41,59,105, +102,40,45,108,101,102,116,100,97,118,105,100,104,111,114,115,101,70,111,99,117, +115,114,97,105,115,101,98,111,120,101,115,84,114,97,99,107,101,109,101,110,116, +60,47,101,109,62,98,97,114,34,62,46,115,114,99,61,116,111,119,101,114,97,108,116 +,61,34,99,97,98,108,101,104,101,110,114,121,50,52,112,120,59,115,101,116,117,112 +,105,116,97,108,121,115,104,97,114,112,109,105,110,111,114,116,97,115,116,101, +119,97,110,116,115,116,104,105,115,46,114,101,115,101,116,119,104,101,101,108, +103,105,114,108,115,47,99,115,115,47,49,48,48,37,59,99,108,117,98,115,115,116, +117,102,102,98,105,98,108,101,118,111,116,101,115,32,49,48,48,48,107,111,114,101 +,97,125,41,59,13,10,98,97,110,100,115,113,117,101,117,101,61,32,123,125,59,56,48 +,112,120,59,99,107,105,110,103,123,13,10,9,9,97,104,101,97,100,99,108,111,99,107 +,105,114,105,115,104,108,105,107,101,32,114,97,116,105,111,115,116,97,116,115,70 +,111,114,109,34,121,97,104,111,111,41,91,48,93,59,65,98,111,117,116,102,105,110, +100,115,60,47,104,49,62,100,101,98,117,103,116,97,115,107,115,85,82,76,32,61,99, +101,108,108,115,125,41,40,41,59,49,50,112,120,59,112,114,105,109,101,116,101,108 +,108,115,116,117,114,110,115,48,120,54,48,48,46,106,112,103,34,115,112,97,105, +110,98,101,97,99,104,116,97,120,101,115,109,105,99,114,111,97,110,103,101,108,45 +,45,62,60,47,103,105,102,116,115,115,116,101,118,101,45,108,105,110,107,98,111, +100,121,46,125,41,59,10,9,109,111,117,110,116,32,40,49,57,57,70,65,81,60,47,114, +111,103,101,114,102,114,97,110,107,67,108,97,115,115,50,56,112,120,59,102,101, +101,100,115,60,104,49,62,60,115,99,111,116,116,116,101,115,116,115,50,50,112,120 +,59,100,114,105,110,107,41,32,124,124,32,108,101,119,105,115,115,104,97,108,108, +35,48,51,57,59,32,102,111,114,32,108,111,118,101,100,119,97,115,116,101,48,48, +112,120,59,106,97,58,227,130,115,105,109,111,110,60,102,111,110,116,114,101,112, +108,121,109,101,101,116,115,117,110,116,101,114,99,104,101,97,112,116,105,103, +104,116,66,114,97,110,100,41,32,33,61,32,100,114,101,115,115,99,108,105,112,115, +114,111,111,109,115,111,110,107,101,121,109,111,98,105,108,109,97,105,110,46,78, +97,109,101,32,112,108,97,116,101,102,117,110,110,121,116,114,101,101,115,99,111, +109,47,34,49,46,106,112,103,119,109,111,100,101,112,97,114,97,109,83,84,65,82,84 +,108,101,102,116,32,105,100,100,101,110,44,32,50,48,49,41,59,10,125,10,102,111, +114,109,46,118,105,114,117,115,99,104,97,105,114,116,114,97,110,115,119,111,114, +115,116,80,97,103,101,115,105,116,105,111,110,112,97,116,99,104,60,33,45,45,10, +111,45,99,97,99,102,105,114,109,115,116,111,117,114,115,44,48,48,48,32,97,115, +105,97,110,105,43,43,41,123,97,100,111,98,101,39,41,91,48,93,105,100,61,49,48,98 +,111,116,104,59,109,101,110,117,32,46,50,46,109,105,46,112,110,103,34,107,101, +118,105,110,99,111,97,99,104,67,104,105,108,100,98,114,117,99,101,50,46,106,112, +103,85,82,76,41,43,46,106,112,103,124,115,117,105,116,101,115,108,105,99,101,104 +,97,114,114,121,49,50,48,34,32,115,119,101,101,116,116,114,62,13,10,110,97,109, +101,61,100,105,101,103,111,112,97,103,101,32,115,119,105,115,115,45,45,62,10,10, +35,102,102,102,59,34,62,76,111,103,46,99,111,109,34,116,114,101,97,116,115,104, +101,101,116,41,32,38,38,32,49,52,112,120,59,115,108,101,101,112,110,116,101,110, +116,102,105,108,101,100,106,97,58,227,131,105,100,61,34,99,78,97,109,101,34,119, +111,114,115,101,115,104,111,116,115,45,98,111,120,45,100,101,108,116,97,10,38, +108,116,59,98,101,97,114,115,58,52,56,90,60,100,97,116,97,45,114,117,114,97,108, +60,47,97,62,32,115,112,101,110,100,98,97,107,101,114,115,104,111,112,115,61,32, +34,34,59,112,104,112,34,62,99,116,105,111,110,49,51,112,120,59,98,114,105,97,110 +,104,101,108,108,111,115,105,122,101,61,111,61,37,50,70,32,106,111,105,110,109, +97,121,98,101,60,105,109,103,32,105,109,103,34,62,44,32,102,106,115,105,109,103, +34,32,34,41,91,48,93,77,84,111,112,66,84,121,112,101,34,110,101,119,108,121,68, +97,110,115,107,99,122,101,99,104,116,114,97,105,108,107,110,111,119,115,60,47, +104,53,62,102,97,113,34,62,122,104,45,99,110,49,48,41,59,10,45,49,34,41,59,116, +121,112,101,61,98,108,117,101,115,116,114,117,108,121,100,97,118,105,115,46,106, +115,39,59,62,13,10,60,33,115,116,101,101,108,32,121,111,117,32,104,50,62,13,10, +102,111,114,109,32,106,101,115,117,115,49,48,48,37,32,109,101,110,117,46,13,10,9 +,13,10,119,97,108,101,115,114,105,115,107,115,117,109,101,110,116,100,100,105, +110,103,98,45,108,105,107,116,101,97,99,104,103,105,102,34,32,118,101,103,97,115 +,100,97,110,115,107,101,101,115,116,105,115,104,113,105,112,115,117,111,109,105, +115,111,98,114,101,100,101,115,100,101,101,110,116,114,101,116,111,100,111,115, +112,117,101,100,101,97,195,177,111,115,101,115,116,195,161,116,105,101,110,101, +104,97,115,116,97,111,116,114,111,115,112,97,114,116,101,100,111,110,100,101,110 +,117,101,118,111,104,97,99,101,114,102,111,114,109,97,109,105,115,109,111,109, +101,106,111,114,109,117,110,100,111,97,113,117,195,173,100,195,173,97,115,115, +195,179,108,111,97,121,117,100,97,102,101,99,104,97,116,111,100,97,115,116,97, +110,116,111,109,101,110,111,115,100,97,116,111,115,111,116,114,97,115,115,105, +116,105,111,109,117,99,104,111,97,104,111,114,97,108,117,103,97,114,109,97,121, +111,114,101,115,116,111,115,104,111,114,97,115,116,101,110,101,114,97,110,116, +101,115,102,111,116,111,115,101,115,116,97,115,112,97,195,173,115,110,117,101, +118,97,115,97,108,117,100,102,111,114,111,115,109,101,100,105,111,113,117,105, +101,110,109,101,115,101,115,112,111,100,101,114,99,104,105,108,101,115,101,114, +195,161,118,101,99,101,115,100,101,99,105,114,106,111,115,195,169,101,115,116,97 +,114,118,101,110,116,97,103,114,117,112,111,104,101,99,104,111,101,108,108,111, +115,116,101,110,103,111,97,109,105,103,111,99,111,115,97,115,110,105,118,101,108 +,103,101,110,116,101,109,105,115,109,97,97,105,114,101,115,106,117,108,105,111, +116,101,109,97,115,104,97,99,105,97,102,97,118,111,114,106,117,110,105,111,108, +105,98,114,101,112,117,110,116,111,98,117,101,110,111,97,117,116,111,114,97,98, +114,105,108,98,117,101,110,97,116,101,120,116,111,109,97,114,122,111,115,97,98, +101,114,108,105,115,116,97,108,117,101,103,111,99,195,179,109,111,101,110,101, +114,111,106,117,101,103,111,112,101,114,195,186,104,97,98,101,114,101,115,116, +111,121,110,117,110,99,97,109,117,106,101,114,118,97,108,111,114,102,117,101,114 +,97,108,105,98,114,111,103,117,115,116,97,105,103,117,97,108,118,111,116,111,115 +,99,97,115,111,115,103,117,195,173,97,112,117,101,100,111,115,111,109,111,115,97 +,118,105,115,111,117,115,116,101,100,100,101,98,101,110,110,111,99,104,101,98, +117,115,99,97,102,97,108,116,97,101,117,114,111,115,115,101,114,105,101,100,105, +99,104,111,99,117,114,115,111,99,108,97,118,101,99,97,115,97,115,108,101,195,179 +,110,112,108,97,122,111,108,97,114,103,111,111,98,114,97,115,118,105,115,116,97, +97,112,111,121,111,106,117,110,116,111,116,114,97,116,97,118,105,115,116,111,99, +114,101,97,114,99,97,109,112,111,104,101,109,111,115,99,105,110,99,111,99,97,114 +,103,111,112,105,115,111,115,111,114,100,101,110,104,97,99,101,110,195,161,114, +101,97,100,105,115,99,111,112,101,100,114,111,99,101,114,99,97,112,117,101,100, +97,112,97,112,101,108,109,101,110,111,114,195,186,116,105,108,99,108,97,114,111, +106,111,114,103,101,99,97,108,108,101,112,111,110,101,114,116,97,114,100,101,110 +,97,100,105,101,109,97,114,99,97,115,105,103,117,101,101,108,108,97,115,115,105, +103,108,111,99,111,99,104,101,109,111,116,111,115,109,97,100,114,101,99,108,97, +115,101,114,101,115,116,111,110,105,195,177,111,113,117,101,100,97,112,97,115,97 +,114,98,97,110,99,111,104,105,106,111,115,118,105,97,106,101,112,97,98,108,111, +195,169,115,116,101,118,105,101,110,101,114,101,105,110,111,100,101,106,97,114, +102,111,110,100,111,99,97,110,97,108,110,111,114,116,101,108,101,116,114,97,99, +97,117,115,97,116,111,109,97,114,109,97,110,111,115,108,117,110,101,115,97,117, +116,111,115,118,105,108,108,97,118,101,110,100,111,112,101,115,97,114,116,105, +112,111,115,116,101,110,103,97,109,97,114,99,111,108,108,101,118,97,112,97,100, +114,101,117,110,105,100,111,118,97,109,111,115,122,111,110,97,115,97,109,98,111, +115,98,97,110,100,97,109,97,114,105,97,97,98,117,115,111,109,117,99,104,97,115, +117,98,105,114,114,105,111,106,97,118,105,118,105,114,103,114,97,100,111,99,104, +105,99,97,97,108,108,195,173,106,111,118,101,110,100,105,99,104,97,101,115,116, +97,110,116,97,108,101,115,115,97,108,105,114,115,117,101,108,111,112,101,115,111 +,115,102,105,110,101,115,108,108,97,109,97,98,117,115,99,111,195,169,115,116,97, +108,108,101,103,97,110,101,103,114,111,112,108,97,122,97,104,117,109,111,114,112 +,97,103,97,114,106,117,110,116,97,100,111,98,108,101,105,115,108,97,115,98,111, +108,115,97,98,97,195,177,111,104,97,98,108,97,108,117,99,104,97,195,129,114,101, +97,100,105,99,101,110,106,117,103,97,114,110,111,116,97,115,118,97,108,108,101, +97,108,108,195,161,99,97,114,103,97,100,111,108,111,114,97,98,97,106,111,101,115 +,116,195,169,103,117,115,116,111,109,101,110,116,101,109,97,114,105,111,102,105, +114,109,97,99,111,115,116,111,102,105,99,104,97,112,108,97,116,97,104,111,103,97 +,114,97,114,116,101,115,108,101,121,101,115,97,113,117,101,108,109,117,115,101, +111,98,97,115,101,115,112,111,99,111,115,109,105,116,97,100,99,105,101,108,111, +99,104,105,99,111,109,105,101,100,111,103,97,110,97,114,115,97,110,116,111,101, +116,97,112,97,100,101,98,101,115,112,108,97,121,97,114,101,100,101,115,115,105, +101,116,101,99,111,114,116,101,99,111,114,101,97,100,117,100,97,115,100,101,115, +101,111,118,105,101,106,111,100,101,115,101,97,97,103,117,97,115,38,113,117,111, +116,59,100,111,109,97,105,110,99,111,109,109,111,110,115,116,97,116,117,115,101, +118,101,110,116,115,109,97,115,116,101,114,115,121,115,116,101,109,97,99,116,105 +,111,110,98,97,110,110,101,114,114,101,109,111,118,101,115,99,114,111,108,108, +117,112,100,97,116,101,103,108,111,98,97,108,109,101,100,105,117,109,102,105,108 +,116,101,114,110,117,109,98,101,114,99,104,97,110,103,101,114,101,115,117,108, +116,112,117,98,108,105,99,115,99,114,101,101,110,99,104,111,111,115,101,110,111, +114,109,97,108,116,114,97,118,101,108,105,115,115,117,101,115,115,111,117,114,99 +,101,116,97,114,103,101,116,115,112,114,105,110,103,109,111,100,117,108,101,109, +111,98,105,108,101,115,119,105,116,99,104,112,104,111,116,111,115,98,111,114,100 +,101,114,114,101,103,105,111,110,105,116,115,101,108,102,115,111,99,105,97,108, +97,99,116,105,118,101,99,111,108,117,109,110,114,101,99,111,114,100,102,111,108, +108,111,119,116,105,116,108,101,62,101,105,116,104,101,114,108,101,110,103,116, +104,102,97,109,105,108,121,102,114,105,101,110,100,108,97,121,111,117,116,97,117 +,116,104,111,114,99,114,101,97,116,101,114,101,118,105,101,119,115,117,109,109, +101,114,115,101,114,118,101,114,112,108,97,121,101,100,112,108,97,121,101,114, +101,120,112,97,110,100,112,111,108,105,99,121,102,111,114,109,97,116,100,111,117 +,98,108,101,112,111,105,110,116,115,115,101,114,105,101,115,112,101,114,115,111, +110,108,105,118,105,110,103,100,101,115,105,103,110,109,111,110,116,104,115,102, +111,114,99,101,115,117,110,105,113,117,101,119,101,105,103,104,116,112,101,111, +112,108,101,101,110,101,114,103,121,110,97,116,117,114,101,115,101,97,114,99,104 +,102,105,103,117,114,101,104,97,118,105,110,103,99,117,115,116,111,109,111,102, +102,115,101,116,108,101,116,116,101,114,119,105,110,100,111,119,115,117,98,109, +105,116,114,101,110,100,101,114,103,114,111,117,112,115,117,112,108,111,97,100, +104,101,97,108,116,104,109,101,116,104,111,100,118,105,100,101,111,115,115,99, +104,111,111,108,102,117,116,117,114,101,115,104,97,100,111,119,100,101,98,97,116 +,101,118,97,108,117,101,115,79,98,106,101,99,116,111,116,104,101,114,115,114,105 +,103,104,116,115,108,101,97,103,117,101,99,104,114,111,109,101,115,105,109,112, +108,101,110,111,116,105,99,101,115,104,97,114,101,100,101,110,100,105,110,103, +115,101,97,115,111,110,114,101,112,111,114,116,111,110,108,105,110,101,115,113, +117,97,114,101,98,117,116,116,111,110,105,109,97,103,101,115,101,110,97,98,108, +101,109,111,118,105,110,103,108,97,116,101,115,116,119,105,110,116,101,114,70, +114,97,110,99,101,112,101,114,105,111,100,115,116,114,111,110,103,114,101,112, +101,97,116,76,111,110,100,111,110,100,101,116,97,105,108,102,111,114,109,101,100 +,100,101,109,97,110,100,115,101,99,117,114,101,112,97,115,115,101,100,116,111, +103,103,108,101,112,108,97,99,101,115,100,101,118,105,99,101,115,116,97,116,105, +99,99,105,116,105,101,115,115,116,114,101,97,109,121,101,108,108,111,119,97,116, +116,97,99,107,115,116,114,101,101,116,102,108,105,103,104,116,104,105,100,100, +101,110,105,110,102,111,34,62,111,112,101,110,101,100,117,115,101,102,117,108, +118,97,108,108,101,121,99,97,117,115,101,115,108,101,97,100,101,114,115,101,99, +114,101,116,115,101,99,111,110,100,100,97,109,97,103,101,115,112,111,114,116,115 +,101,120,99,101,112,116,114,97,116,105,110,103,115,105,103,110,101,100,116,104, +105,110,103,115,101,102,102,101,99,116,102,105,101,108,100,115,115,116,97,116, +101,115,111,102,102,105,99,101,118,105,115,117,97,108,101,100,105,116,111,114, +118,111,108,117,109,101,82,101,112,111,114,116,109,117,115,101,117,109,109,111, +118,105,101,115,112,97,114,101,110,116,97,99,99,101,115,115,109,111,115,116,108, +121,109,111,116,104,101,114,34,32,105,100,61,34,109,97,114,107,101,116,103,114, +111,117,110,100,99,104,97,110,99,101,115,117,114,118,101,121,98,101,102,111,114, +101,115,121,109,98,111,108,109,111,109,101,110,116,115,112,101,101,99,104,109, +111,116,105,111,110,105,110,115,105,100,101,109,97,116,116,101,114,67,101,110, +116,101,114,111,98,106,101,99,116,101,120,105,115,116,115,109,105,100,100,108, +101,69,117,114,111,112,101,103,114,111,119,116,104,108,101,103,97,99,121,109,97, +110,110,101,114,101,110,111,117,103,104,99,97,114,101,101,114,97,110,115,119,101 +,114,111,114,105,103,105,110,112,111,114,116,97,108,99,108,105,101,110,116,115, +101,108,101,99,116,114,97,110,100,111,109,99,108,111,115,101,100,116,111,112,105 +,99,115,99,111,109,105,110,103,102,97,116,104,101,114,111,112,116,105,111,110, +115,105,109,112,108,121,114,97,105,115,101,100,101,115,99,97,112,101,99,104,111, +115,101,110,99,104,117,114,99,104,100,101,102,105,110,101,114,101,97,115,111,110 +,99,111,114,110,101,114,111,117,116,112,117,116,109,101,109,111,114,121,105,102, +114,97,109,101,112,111,108,105,99,101,109,111,100,101,108,115,78,117,109,98,101, +114,100,117,114,105,110,103,111,102,102,101,114,115,115,116,121,108,101,115,107, +105,108,108,101,100,108,105,115,116,101,100,99,97,108,108,101,100,115,105,108, +118,101,114,109,97,114,103,105,110,100,101,108,101,116,101,98,101,116,116,101, +114,98,114,111,119,115,101,108,105,109,105,116,115,71,108,111,98,97,108,115,105, +110,103,108,101,119,105,100,103,101,116,99,101,110,116,101,114,98,117,100,103, +101,116,110,111,119,114,97,112,99,114,101,100,105,116,99,108,97,105,109,115,101, +110,103,105,110,101,115,97,102,101,116,121,99,104,111,105,99,101,115,112,105,114 +,105,116,45,115,116,121,108,101,115,112,114,101,97,100,109,97,107,105,110,103, +110,101,101,100,101,100,114,117,115,115,105,97,112,108,101,97,115,101,101,120, +116,101,110,116,83,99,114,105,112,116,98,114,111,107,101,110,97,108,108,111,119, +115,99,104,97,114,103,101,100,105,118,105,100,101,102,97,99,116,111,114,109,101, +109,98,101,114,45,98,97,115,101,100,116,104,101,111,114,121,99,111,110,102,105, +103,97,114,111,117,110,100,119,111,114,107,101,100,104,101,108,112,101,100,67, +104,117,114,99,104,105,109,112,97,99,116,115,104,111,117,108,100,97,108,119,97, +121,115,108,111,103,111,34,32,98,111,116,116,111,109,108,105,115,116,34,62,41, +123,118,97,114,32,112,114,101,102,105,120,111,114,97,110,103,101,72,101,97,100, +101,114,46,112,117,115,104,40,99,111,117,112,108,101,103,97,114,100,101,110,98, +114,105,100,103,101,108,97,117,110,99,104,82,101,118,105,101,119,116,97,107,105, +110,103,118,105,115,105,111,110,108,105,116,116,108,101,100,97,116,105,110,103, +66,117,116,116,111,110,98,101,97,117,116,121,116,104,101,109,101,115,102,111,114 +,103,111,116,83,101,97,114,99,104,97,110,99,104,111,114,97,108,109,111,115,116, +108,111,97,100,101,100,67,104,97,110,103,101,114,101,116,117,114,110,115,116,114 +,105,110,103,114,101,108,111,97,100,77,111,98,105,108,101,105,110,99,111,109,101 +,115,117,112,112,108,121,83,111,117,114,99,101,111,114,100,101,114,115,118,105, +101,119,101,100,38,110,98,115,112,59,99,111,117,114,115,101,65,98,111,117,116,32 +,105,115,108,97,110,100,60,104,116,109,108,32,99,111,111,107,105,101,110,97,109, +101,61,34,97,109,97,122,111,110,109,111,100,101,114,110,97,100,118,105,99,101, +105,110,60,47,97,62,58,32,84,104,101,32,100,105,97,108,111,103,104,111,117,115, +101,115,66,69,71,73,78,32,77,101,120,105,99,111,115,116,97,114,116,115,99,101, +110,116,114,101,104,101,105,103,104,116,97,100,100,105,110,103,73,115,108,97,110 +,100,97,115,115,101,116,115,69,109,112,105,114,101,83,99,104,111,111,108,101,102 +,102,111,114,116,100,105,114,101,99,116,110,101,97,114,108,121,109,97,110,117,97 +,108,83,101,108,101,99,116,46,10,10,79,110,101,106,111,105,110,101,100,109,101, +110,117,34,62,80,104,105,108,105,112,97,119,97,114,100,115,104,97,110,100,108, +101,105,109,112,111,114,116,79,102,102,105,99,101,114,101,103,97,114,100,115,107 +,105,108,108,115,110,97,116,105,111,110,83,112,111,114,116,115,100,101,103,114, +101,101,119,101,101,107,108,121,32,40,101,46,103,46,98,101,104,105,110,100,100, +111,99,116,111,114,108,111,103,103,101,100,117,110,105,116,101,100,60,47,98,62, +60,47,98,101,103,105,110,115,112,108,97,110,116,115,97,115,115,105,115,116,97, +114,116,105,115,116,105,115,115,117,101,100,51,48,48,112,120,124,99,97,110,97, +100,97,97,103,101,110,99,121,115,99,104,101,109,101,114,101,109,97,105,110,66, +114,97,122,105,108,115,97,109,112,108,101,108,111,103,111,34,62,98,101,121,111, +110,100,45,115,99,97,108,101,97,99,99,101,112,116,115,101,114,118,101,100,109,97 +,114,105,110,101,70,111,111,116,101,114,99,97,109,101,114,97,60,47,104,49,62,10, +95,102,111,114,109,34,108,101,97,118,101,115,115,116,114,101,115,115,34,32,47,62 +,13,10,46,103,105,102,34,32,111,110,108,111,97,100,108,111,97,100,101,114,79,120 +,102,111,114,100,115,105,115,116,101,114,115,117,114,118,105,118,108,105,115,116 +,101,110,102,101,109,97,108,101,68,101,115,105,103,110,115,105,122,101,61,34,97, +112,112,101,97,108,116,101,120,116,34,62,108,101,118,101,108,115,116,104,97,110, +107,115,104,105,103,104,101,114,102,111,114,99,101,100,97,110,105,109,97,108,97, +110,121,111,110,101,65,102,114,105,99,97,97,103,114,101,101,100,114,101,99,101, +110,116,80,101,111,112,108,101,60,98,114,32,47,62,119,111,110,100,101,114,112, +114,105,99,101,115,116,117,114,110,101,100,124,124,32,123,125,59,109,97,105,110, +34,62,105,110,108,105,110,101,115,117,110,100,97,121,119,114,97,112,34,62,102,97 +,105,108,101,100,99,101,110,115,117,115,109,105,110,117,116,101,98,101,97,99,111 +,110,113,117,111,116,101,115,49,53,48,112,120,124,101,115,116,97,116,101,114,101 +,109,111,116,101,101,109,97,105,108,34,108,105,110,107,101,100,114,105,103,104, +116,59,115,105,103,110,97,108,102,111,114,109,97,108,49,46,104,116,109,108,115, +105,103,110,117,112,112,114,105,110,99,101,102,108,111,97,116,58,46,112,110,103, +34,32,102,111,114,117,109,46,65,99,99,101,115,115,112,97,112,101,114,115,115,111 +,117,110,100,115,101,120,116,101,110,100,72,101,105,103,104,116,115,108,105,100, +101,114,85,84,70,45,56,34,38,97,109,112,59,32,66,101,102,111,114,101,46,32,87, +105,116,104,115,116,117,100,105,111,111,119,110,101,114,115,109,97,110,97,103, +101,112,114,111,102,105,116,106,81,117,101,114,121,97,110,110,117,97,108,112,97, +114,97,109,115,98,111,117,103,104,116,102,97,109,111,117,115,103,111,111,103,108 +,101,108,111,110,103,101,114,105,43,43,41,32,123,105,115,114,97,101,108,115,97, +121,105,110,103,100,101,99,105,100,101,104,111,109,101,34,62,104,101,97,100,101, +114,101,110,115,117,114,101,98,114,97,110,99,104,112,105,101,99,101,115,98,108, +111,99,107,59,115,116,97,116,101,100,116,111,112,34,62,60,114,97,99,105,110,103, +114,101,115,105,122,101,45,45,38,103,116,59,112,97,99,105,116,121,115,101,120, +117,97,108,98,117,114,101,97,117,46,106,112,103,34,32,49,48,44,48,48,48,111,98, +116,97,105,110,116,105,116,108,101,115,97,109,111,117,110,116,44,32,73,110,99,46 +,99,111,109,101,100,121,109,101,110,117,34,32,108,121,114,105,99,115,116,111,100 +,97,121,46,105,110,100,101,101,100,99,111,117,110,116,121,95,108,111,103,111,46, +70,97,109,105,108,121,108,111,111,107,101,100,77,97,114,107,101,116,108,115,101, +32,105,102,80,108,97,121,101,114,116,117,114,107,101,121,41,59,118,97,114,32,102 +,111,114,101,115,116,103,105,118,105,110,103,101,114,114,111,114,115,68,111,109, +97,105,110,125,101,108,115,101,123,105,110,115,101,114,116,66,108,111,103,60,47, +102,111,111,116,101,114,108,111,103,105,110,46,102,97,115,116,101,114,97,103,101 +,110,116,115,60,98,111,100,121,32,49,48,112,120,32,48,112,114,97,103,109,97,102, +114,105,100,97,121,106,117,110,105,111,114,100,111,108,108,97,114,112,108,97,99, +101,100,99,111,118,101,114,115,112,108,117,103,105,110,53,44,48,48,48,32,112,97, +103,101,34,62,98,111,115,116,111,110,46,116,101,115,116,40,97,118,97,116,97,114, +116,101,115,116,101,100,95,99,111,117,110,116,102,111,114,117,109,115,115,99,104 +,101,109,97,105,110,100,101,120,44,102,105,108,108,101,100,115,104,97,114,101, +115,114,101,97,100,101,114,97,108,101,114,116,40,97,112,112,101,97,114,83,117,98 +,109,105,116,108,105,110,101,34,62,98,111,100,121,34,62,10,42,32,84,104,101,84, +104,111,117,103,104,115,101,101,105,110,103,106,101,114,115,101,121,78,101,119, +115,60,47,118,101,114,105,102,121,101,120,112,101,114,116,105,110,106,117,114, +121,119,105,100,116,104,61,67,111,111,107,105,101,83,84,65,82,84,32,97,99,114, +111,115,115,95,105,109,97,103,101,116,104,114,101,97,100,110,97,116,105,118,101, +112,111,99,107,101,116,98,111,120,34,62,10,83,121,115,116,101,109,32,68,97,118, +105,100,99,97,110,99,101,114,116,97,98,108,101,115,112,114,111,118,101,100,65, +112,114,105,108,32,114,101,97,108,108,121,100,114,105,118,101,114,105,116,101, +109,34,62,109,111,114,101,34,62,98,111,97,114,100,115,99,111,108,111,114,115,99, +97,109,112,117,115,102,105,114,115,116,32,124,124,32,91,93,59,109,101,100,105,97 +,46,103,117,105,116,97,114,102,105,110,105,115,104,119,105,100,116,104,58,115, +104,111,119,101,100,79,116,104,101,114,32,46,112,104,112,34,32,97,115,115,117, +109,101,108,97,121,101,114,115,119,105,108,115,111,110,115,116,111,114,101,115, +114,101,108,105,101,102,115,119,101,100,101,110,67,117,115,116,111,109,101,97, +115,105,108,121,32,121,111,117,114,32,83,116,114,105,110,103,10,10,87,104,105, +108,116,97,121,108,111,114,99,108,101,97,114,58,114,101,115,111,114,116,102,114, +101,110,99,104,116,104,111,117,103,104,34,41,32,43,32,34,60,98,111,100,121,62,98 +,117,121,105,110,103,98,114,97,110,100,115,77,101,109,98,101,114,110,97,109,101, +34,62,111,112,112,105,110,103,115,101,99,116,111,114,53,112,120,59,34,62,118,115 +,112,97,99,101,112,111,115,116,101,114,109,97,106,111,114,32,99,111,102,102,101, +101,109,97,114,116,105,110,109,97,116,117,114,101,104,97,112,112,101,110,60,47, +110,97,118,62,107,97,110,115,97,115,108,105,110,107,34,62,73,109,97,103,101,115, +61,102,97,108,115,101,119,104,105,108,101,32,104,115,112,97,99,101,48,38,97,109, +112,59,32,10,10,73,110,32,32,112,111,119,101,114,80,111,108,115,107,105,45,99, +111,108,111,114,106,111,114,100,97,110,66,111,116,116,111,109,83,116,97,114,116, +32,45,99,111,117,110,116,50,46,104,116,109,108,110,101,119,115,34,62,48,49,46, +106,112,103,79,110,108,105,110,101,45,114,105,103,104,116,109,105,108,108,101, +114,115,101,110,105,111,114,73,83,66,78,32,48,48,44,48,48,48,32,103,117,105,100, +101,115,118,97,108,117,101,41,101,99,116,105,111,110,114,101,112,97,105,114,46, +120,109,108,34,32,32,114,105,103,104,116,115,46,104,116,109,108,45,98,108,111,99 +,107,114,101,103,69,120,112,58,104,111,118,101,114,119,105,116,104,105,110,118, +105,114,103,105,110,112,104,111,110,101,115,60,47,116,114,62,13,117,115,105,110, +103,32,10,9,118,97,114,32,62,39,41,59,10,9,60,47,116,100,62,10,60,47,116,114,62, +10,98,97,104,97,115,97,98,114,97,115,105,108,103,97,108,101,103,111,109,97,103, +121,97,114,112,111,108,115,107,105,115,114,112,115,107,105,216,177,216,175,217, +136,228,184,173,230,150,135,231,174,128,228,189,147,231,185,129,233,171,148,228, +191,161,230,129,175,228,184,173,229,155,189,230,136,145,228,187,172,228,184,128, +228,184,170,229,133,172,229,143,184,231,174,161,231,144,134,232,174,186,229,157, +155,229,143,175,228,187,165,230,156,141,229,138,161,230,151,182,233,151,180,228, +184,170,228,186,186,228,186,167,229,147,129,232,135,170,229,183,177,228,188,129, +228,184,154,230,159,165,231,156,139,229,183,165,228,189,156,232,129,148,231,179, +187,230,178,161,230,156,137,231,189,145,231,171,153,230,137,128,230,156,137,232, +175,132,232,174,186,228,184,173,229,191,131,230,150,135,231,171,160,231,148,168, +230,136,183,233,166,150,233,161,181,228,189,156,232,128,133,230,138,128,230,156, +175,233,151,174,233,162,152,231,155,184,229,133,179,228,184,139,232,189,189,230, +144,156,231,180,162,228,189,191,231,148,168,232,189,175,228,187,182,229,156,168, +231,186,191,228,184,187,233,162,152,232,181,132,230,150,153,232,167,134,233,162, +145,229,155,158,229,164,141,230,179,168,229,134,140,231,189,145,231,187,156,230, +148,182,232,151,143,229,134,133,229,174,185,230,142,168,232,141,144,229,184,130, +229,156,186,230,182,136,230,129,175,231,169,186,233,151,180,229,143,145,229,184, +131,228,187,128,228,185,136,229,165,189,229,143,139,231,148,159,230,180,187,229, +155,190,231,137,135,229,143,145,229,177,149,229,166,130,230,158,156,230,137,139, +230,156,186,230,150,176,233,151,187,230,156,128,230,150,176,230,150,185,229,188, +143,229,140,151,228,186,172,230,143,144,228,190,155,229,133,179,228,186,142,230, +155,180,229,164,154,232,191,153,228,184,170,231,179,187,231,187,159,231,159,165, +233,129,147,230,184,184,230,136,143,229,185,191,229,145,138,229,133,182,228,187, +150,229,143,145,232,161,168,229,174,137,229,133,168,231,172,172,228,184,128,228, +188,154,229,145,152,232,191,155,232,161,140,231,130,185,229,135,187,231,137,136, +230,157,131,231,148,181,229,173,144,228,184,150,231,149,140,232,174,190,232,174, +161,229,133,141,232,180,185,230,149,153,232,130,178,229,138,160,229,133,165,230, +180,187,229,138,168,228,187,150,228,187,172,229,149,134,229,147,129,229,141,154, +229,174,162,231,142,176,229,156,168,228,184,138,230,181,183,229,166,130,228,189, +149,229,183,178,231,187,143,231,149,153,232,168,128,232,175,166,231,187,134,231, +164,190,229,140,186,231,153,187,229,189,149,230,156,172,231,171,153,233,156,128, +232,166,129,228,187,183,230,160,188,230,148,175,230,140,129,229,155,189,233,153, +133,233,147,190,230,142,165,229,155,189,229,174,182,229,187,186,232,174,190,230, +156,139,229,143,139,233,152,133,232,175,187,230,179,149,229,190,139,228,189,141, +231,189,174,231,187,143,230,181,142,233,128,137,230,139,169,232,191,153,230,160, +183,229,189,147,229,137,141,229,136,134,231,177,187,230,142,146,232,161,140,229, +155,160,228,184,186,228,186,164,230,152,147,230,156,128,229,144,142,233,159,179, +228,185,144,228,184,141,232,131,189,233,128,154,232,191,135,232,161,140,228,184, +154,231,167,145,230,138,128,229,143,175,232,131,189,232,174,190,229,164,135,229, +144,136,228,189,156,229,164,167,229,174,182,231,164,190,228,188,154,231,160,148, +231,169,182,228,184,147,228,184,154,229,133,168,233,131,168,233,161,185,231,155, +174,232,191,153,233,135,140,232,191,152,230,152,175,229,188,128,229,167,139,230, +131,133,229,134,181,231,148,181,232,132,145,230,150,135,228,187,182,229,147,129, +231,137,140,229,184,174,229,138,169,230,150,135,229,140,150,232,181,132,230,186, +144,229,164,167,229,173,166,229,173,166,228,185,160,229,156,176,229,157,128,230, +181,143,232,167,136,230,138,149,232,181,132,229,183,165,231,168,139,232,166,129, +230,177,130,230,128,142,228,185,136,230,151,182,229,128,153,229,138,159,232,131, +189,228,184,187,232,166,129,231,155,174,229,137,141,232,181,132,232,174,175,229, +159,142,229,184,130,230,150,185,230,179,149,231,148,181,229,189,177,230,139,155, +232,129,152,229,163,176,230,152,142,228,187,187,228,189,149,229,129,165,229,186, +183,230,149,176,230,141,174,231,190,142,229,155,189,230,177,189,232,189,166,228, +187,139,231,187,141,228,189,134,230,152,175,228,186,164,230,181,129,231,148,159, +228,186,167,230,137,128,228,187,165,231,148,181,232,175,157,230,152,190,231,164, +186,228,184,128,228,186,155,229,141,149,228,189,141,228,186,186,229,145,152,229, +136,134,230,158,144,229,156,176,229,155,190,230,151,133,230,184,184,229,183,165, +229,133,183,229,173,166,231,148,159,231,179,187,229,136,151,231,189,145,229,143, +139,229,184,150,229,173,144,229,175,134,231,160,129,233,162,145,233,129,147,230, +142,167,229,136,182,229,156,176,229,140,186,229,159,186,230,156,172,229,133,168, +229,155,189,231,189,145,228,184,138,233,135,141,232,166,129,231,172,172,228,186, +140,229,150,156,230,172,162,232,191,155,229,133,165,229,143,139,230,131,133,232, +191,153,228,186,155,232,128,131,232,175,149,229,143,145,231,142,176,229,159,185, +232,174,173,228,187,165,228,184,138,230,148,191,229,186,156,230,136,144,228,184, +186,231,142,175,229,162,131,233,166,153,230,184,175,229,144,140,230,151,182,229, +168,177,228,185,144,229,143,145,233,128,129,228,184,128,229,174,154,229,188,128, +229,143,145,228,189,156,229,147,129,230,160,135,229,135,134,230,172,162,232,191, +142,232,167,163,229,134,179,229,156,176,230,150,185,228,184,128,228,184,139,228, +187,165,229,143,138,232,180,163,228,187,187,230,136,150,232,128,133,229,174,162, +230,136,183,228,187,163,232,161,168,231,167,175,229,136,134,229,165,179,228,186, +186,230,149,176,231,160,129,233,148,128,229,148,174,229,135,186,231,142,176,231, +166,187,231,186,191,229,186,148,231,148,168,229,136,151,232,161,168,228,184,141, +229,144,140,231,188,150,232,190,145,231,187,159,232,174,161,230,159,165,232,175, +162,228,184,141,232,166,129,230,156,137,229,133,179,230,156,186,230,158,132,229, +190,136,229,164,154,230,146,173,230,148,190,231,187,132,231,187,135,230,148,191, +231,173,150,231,155,180,230,142,165,232,131,189,229,138,155,230,157,165,230,186, +144,230,153,130,233,150,147,231,156,139,229,136,176,231,131,173,233,151,168,229, +133,179,233,148,174,228,184,147,229,140,186,233,157,158,229,184,184,232,139,177, +232,175,173,231,153,190,229,186,166,229,184,140,230,156,155,231,190,142,229,165, +179,230,175,148,232,190,131,231,159,165,232,175,134,232,167,132,229,174,154,229, +187,186,232,174,174,233,131,168,233,151,168,230,132,143,232,167,129,231,178,190, +229,189,169,230,151,165,230,156,172,230,143,144,233,171,152,229,143,145,232,168, +128,230,150,185,233,157,162,229,159,186,233,135,145,229,164,132,231,144,134,230, +157,131,233,153,144,229,189,177,231,137,135,233,147,182,232,161,140,232,191,152, +230,156,137,229,136,134,228,186,171,231,137,169,229,147,129,231,187,143,232,144, +165,230,183,187,229,138,160,228,184,147,229,174,182,232,191,153,231,167,141,232, +175,157,233,162,152,232,181,183,230,157,165,228,184,154,229,138,161,229,133,172, +229,145,138,232,174,176,229,189,149,231,174,128,228,187,139,232,180,168,233,135, +143,231,148,183,228,186,186,229,189,177,229,147,141,229,188,149,231,148,168,230, +138,165,229,145,138,233,131,168,229,136,134,229,191,171,233,128,159,229,146,168, +232,175,162,230,151,182,229,176,154,230,179,168,230,132,143,231,148,179,232,175, +183,229,173,166,230,160,161,229,186,148,232,175,165,229,142,134,229,143,178,229, +143,170,230,152,175,232,191,148,229,155,158,232,180,173,228,185,176,229,144,141, +231,167,176,228,184,186,228,186,134,230,136,144,229,138,159,232,175,180,230,152, +142,228,190,155,229,186,148,229,173,169,229,173,144,228,184,147,233,162,152,231, +168,139,229,186,143,228,184,128,232,136,172,230,156,131,229,147,161,229,143,170, +230,156,137,229,133,182,229,174,131,228,191,157,230,138,164,232,128,140,228,184, +148,228,187,138,229,164,169,231,170,151,229,143,163,229,138,168,230,128,129,231, +138,182,230,128,129,231,137,185,229,136,171,232,174,164,228,184,186,229,191,133, +233,161,187,230,155,180,230,150,176,229,176,143,232,175,180,230,136,145,229,128, +145,228,189,156,228,184,186,229,170,146,228,189,147,229,140,133,230,139,172,233, +130,163,228,185,136,228,184,128,230,160,183,229,155,189,229,134,133,230,152,175, +229,144,166,230,160,185,230,141,174,231,148,181,232,167,134,229,173,166,233,153, +162,229,133,183,230,156,137,232,191,135,231,168,139,231,148,177,228,186,142,228, +186,186,230,137,141,229,135,186,230,157,165,228,184,141,232,191,135,230,173,163, +229,156,168,230,152,142,230,152,159,230,149,133,228,186,139,229,133,179,231,179, +187,230,160,135,233,162,152,229,149,134,229,138,161,232,190,147,229,133,165,228, +184,128,231,155,180,229,159,186,231,161,128,230,149,153,229,173,166,228,186,134, +232,167,163,229,187,186,231,173,145,231,187,147,230,158,156,229,133,168,231,144, +131,233,128,154,231,159,165,232,174,161,229,136,146,229,175,185,228,186,142,232, +137,186,230,156,175,231,155,184,229,134,140,229,143,145,231,148,159,231,156,159, +231,154,132,229,187,186,231,171,139,231,173,137,231,186,167,231,177,187,229,158, +139,231,187,143,233,170,140,229,174,158,231,142,176,229,136,182,228,189,156,230, +157,165,232,135,170,230,160,135,231,173,190,228,187,165,228,184,139,229,142,159, +229,136,155,230,151,160,230,179,149,229,133,182,228,184,173,229,128,139,228,186, +186,228,184,128,229,136,135,230,140,135,229,141,151,229,133,179,233,151,173,233, +155,134,229,155,162,231,172,172,228,184,137,229,133,179,230,179,168,229,155,160, +230,173,164,231,133,167,231,137,135,230,183,177,229,156,179,229,149,134,228,184, +154,229,185,191,229,183,158,230,151,165,230,156,159,233,171,152,231,186,167,230, +156,128,232,191,145,231,187,188,229,144,136,232,161,168,231,164,186,228,184,147, +232,190,145,232,161,140,228,184,186,228,186,164,233,128,154,232,175,132,228,187, +183,232,167,137,229,190,151,231,178,190,229,141,142,229,174,182,229,186,173,229, +174,140,230,136,144,230,132,159,232,167,137,229,174,137,232,163,133,229,190,151, +229,136,176,233,130,174,228,187,182,229,136,182,229,186,166,233,163,159,229,147, +129,232,153,189,231,132,182,232,189,172,232,189,189,230,138,165,228,187,183,232, +174,176,232,128,133,230,150,185,230,161,136,232,161,140,230,148,191,228,186,186, +230,176,145,231,148,168,229,147,129,228,184,156,232,165,191,230,143,144,229,135, +186,233,133,146,229,186,151,231,132,182,229,144,142,228,187,152,230,172,190,231, +131,173,231,130,185,228,187,165,229,137,141,229,174,140,229,133,168,229,143,145, +229,184,150,232,174,190,231,189,174,233,162,134,229,175,188,229,183,165,228,184, +154,229,140,187,233,153,162,231,156,139,231,156,139,231,187,143,229,133,184,229, +142,159,229,155,160,229,185,179,229,143,176,229,144,132,231,167,141,229,162,158, +229,138,160,230,157,144,230,150,153,230,150,176,229,162,158,228,185,139,229,144, +142,232,129,140,228,184,154,230,149,136,230,158,156,228,187,138,229,185,180,232, +174,186,230,150,135,230,136,145,229,155,189,229,145,138,232,175,137,231,137,136, +228,184,187,228,191,174,230,148,185,229,143,130,228,184,142,230,137,147,229,141, +176,229,191,171,228,185,144,230,156,186,230,162,176,232,167,130,231,130,185,229, +173,152,229,156,168,231,178,190,231,165,158,232,142,183,229,190,151,229,136,169, +231,148,168,231,187,167,231,187,173,228,189,160,228,187,172,232,191,153,228,185, +136,230,168,161,229,188,143,232,175,173,232,168,128,232,131,189,229,164,159,233, +155,133,232,153,142,230,147,141,228,189,156,233,163,142,230,160,188,228,184,128, +232,181,183,231,167,145,229,173,166,228,189,147,232,130,178,231,159,173,228,191, +161,230,157,161,228,187,182,230,178,187,231,150,151,232,191,144,229,138,168,228, +186,167,228,184,154,228,188,154,232,174,174,229,175,188,232,136,170,229,133,136, +231,148,159,232,129,148,231,155,159,229,143,175,230,152,175,229,149,143,233,161, +140,231,187,147,230,158,132,228,189,156,231,148,168,232,176,131,230,159,165,232, +179,135,230,150,153,232,135,170,229,138,168,232,180,159,232,180,163,229,134,156, +228,184,154,232,174,191,233,151,174,229,174,158,230,150,189,230,142,165,229,143, +151,232,174,168,232,174,186,233,130,163,228,184,170,229,143,141,233,166,136,229, +138,160,229,188,186,229,165,179,230,128,167,232,140,131,229,155,180,230,156,141, +229,139,153,228,188,145,233,151,178,228,187,138,230,151,165,229,174,162,230,156, +141,232,167,128,231,156,139,229,143,130,229,138,160,231,154,132,232,175,157,228, +184,128,231,130,185,228,191,157,232,175,129,229,155,190,228,185,166,230,156,137, +230,149,136,230,181,139,232,175,149,231,167,187,229,138,168,230,137,141,232,131, +189,229,134,179,229,174,154,232,130,161,231,165,168,228,184,141,230,150,173,233, +156,128,230,177,130,228,184,141,229,190,151,229,138,158,230,179,149,228,185,139, +233,151,180,233,135,135,231,148,168,232,144,165,233,148,128,230,138,149,232,175, +137,231,155,174,230,160,135,231,136,177,230,131,133,230,145,132,229,189,177,230, +156,137,228,186,155,232,164,135,232,163,189,230,150,135,229,173,166,230,156,186, +228,188,154,230,149,176,229,173,151,232,163,133,228,191,174,232,180,173,231,137, +169,229,134,156,230,157,145,229,133,168,233,157,162,231,178,190,229,147,129,229, +133,182,229,174,158,228,186,139,230,131,133,230,176,180,229,185,179,230,143,144, +231,164,186,228,184,138,229,184,130,232,176,162,232,176,162,230,153,174,233,128, +154,230,149,153,229,184,136,228,184,138,228,188,160,231,177,187,229,136,171,230, +173,140,230,155,178,230,139,165,230,156,137,229,136,155,230,150,176,233,133,141, +228,187,182,229,143,170,232,166,129,230,151,182,228,187,163,232,179,135,232,168, +138,232,190,190,229,136,176,228,186,186,231,148,159,232,174,162,233,152,133,232, +128,129,229,184,136,229,177,149,231,164,186,229,191,131,231,144,134,232,180,180, +229,173,144,231,182,178,231,171,153,228,184,187,233,161,140,232,135,170,231,132, +182,231,186,167,229,136,171,231,174,128,229,141,149,230,148,185,233,157,169,233, +130,163,228,186,155,230,157,165,232,175,180,230,137,147,229,188,128,228,187,163, +231,160,129,229,136,160,233,153,164,232,175,129,229,136,184,232,138,130,231,155, +174,233,135,141,231,130,185,230,172,161,230,149,184,229,164,154,229,176,145,232, +167,132,229,136,146,232,181,132,233,135,145,230,137,190,229,136,176,228,187,165, +229,144,142,229,164,167,229,133,168,228,184,187,233,161,181,230,156,128,228,189, +179,229,155,158,231,173,148,229,164,169,228,184,139,228,191,157,233,154,156,231, +142,176,228,187,163,230,163,128,230,159,165,230,138,149,231,165,168,229,176,143, +230,151,182,230,178,146,230,156,137,230,173,163,229,184,184,231,148,154,232,135, +179,228,187,163,231,144,134,231,155,174,229,189,149,229,133,172,229,188,128,229, +164,141,229,136,182,233,135,145,232,158,141,229,185,184,231,166,143,231,137,136, +230,156,172,229,189,162,230,136,144,229,135,134,229,164,135,232,161,140,230,131, +133,229,155,158,229,136,176,230,128,157,230,131,179,230,128,142,230,160,183,229, +141,143,232,174,174,232,174,164,232,175,129,230,156,128,229,165,189,228,186,167, +231,148,159,230,140,137,231,133,167,230,156,141,232,163,133,229,185,191,228,184, +156,229,138,168,230,188,171,233,135,135,232,180,173,230,150,176,230,137,139,231, +187,132,229,155,190,233,157,162,230,157,191,229,143,130,232,128,131,230,148,191, +230,178,187,229,174,185,230,152,147,229,164,169,229,156,176,229,138,170,229,138, +155,228,186,186,228,187,172,229,141,135,231,186,167,233,128,159,229,186,166,228, +186,186,231,137,169,232,176,131,230,149,180,230,181,129,232,161,140,233,128,160, +230,136,144,230,150,135,229,173,151,233,159,169,229,155,189,232,180,184,230,152, +147,229,188,128,229,177,149,231,155,184,233,151,156,232,161,168,231,142,176,229, +189,177,232,167,134,229,166,130,230,173,164,231,190,142,229,174,185,229,164,167, +229,176,143,230,138,165,233,129,147,230,157,161,230,172,190,229,191,131,230,131, +133,232,174,184,229,164,154,230,179,149,232,167,132,229,174,182,229,177,133,228, +185,166,229,186,151,232,191,158,230,142,165,231,171,139,229,141,179,228,184,190, +230,138,165,230,138,128,229,183,167,229,165,165,232,191,144,231,153,187,229,133, +165,228,187,165,230,157,165,231,144,134,232,174,186,228,186,139,228,187,182,232, +135,170,231,148,177,228,184,173,229,141,142,229,138,158,229,133,172,229,166,136, +229,166,136,231,156,159,230,173,163,228,184,141,233,148,153,229,133,168,230,150, +135,229,144,136,229,144,140,228,187,183,229,128,188,229,136,171,228,186,186,231, +155,145,231,157,163,229,133,183,228,189,147,228,184,150,231,186,170,229,155,162, +233,152,159,229,136,155,228,184,154,230,137,191,230,139,133,229,162,158,233,149, +191,230,156,137,228,186,186,228,191,157,230,140,129,229,149,134,229,174,182,231, +187,180,228,191,174,229,143,176,230,185,190,229,183,166,229,143,179,232,130,161, +228,187,189,231,173,148,230,161,136,229,174,158,233,153,133,231,148,181,228,191, +161,231,187,143,231,144,134,231,148,159,229,145,189,229,174,163,228,188,160,228, +187,187,229,138,161,230,173,163,229,188,143,231,137,185,232,137,178,228,184,139, +230,157,165,229,141,143,228,188,154,229,143,170,232,131,189,229,189,147,231,132, +182,233,135,141,230,150,176,229,133,167,229,174,185,230,140,135,229,175,188,232, +191,144,232,161,140,230,151,165,229,191,151,232,179,163,229,174,182,232,182,133, +232,191,135,229,156,159,229,156,176,230,181,153,230,177,159,230,148,175,228,187, +152,230,142,168,229,135,186,231,171,153,233,149,191,230,157,173,229,183,158,230, +137,167,232,161,140,229,136,182,233,128,160,228,185,139,228,184,128,230,142,168, +229,185,191,231,142,176,229,156,186,230,143,143,232,191,176,229,143,152,229,140, +150,228,188,160,231,187,159,230,173,140,230,137,139,228,191,157,233,153,169,232, +175,190,231,168,139,229,140,187,231,150,151,231,187,143,232,191,135,232,191,135, +229,142,187,228,185,139,229,137,141,230,148,182,229,133,165,229,185,180,229,186, +166,230,157,130,229,191,151,231,190,142,228,184,189,230,156,128,233,171,152,231, +153,187,233,153,134,230,156,170,230,157,165,229,138,160,229,183,165,229,133,141, +232,180,163,230,149,153,231,168,139,231,137,136,229,157,151,232,186,171,228,189, +147,233,135,141,229,186,134,229,135,186,229,148,174,230,136,144,230,156,172,229, +189,162,229,188,143,229,156,159,232,177,134,229,135,186,229,131,185,228,184,156, +230,150,185,233,130,174,231,174,177,229,141,151,228,186,172,230,177,130,232,129, +140,229,143,150,229,190,151,232,129,140,228,189,141,231,155,184,228,191,161,233, +161,181,233,157,162,229,136,134,233,146,159,231,189,145,233,161,181,231,161,174, +229,174,154,229,155,190,228,190,139,231,189,145,229,157,128,231,167,175,230,158, +129,233,148,153,232,175,175,231,155,174,231,154,132,229,174,157,232,180,157,230, +156,186,229,133,179,233,163,142,233,153,169,230,142,136,230,157,131,231,151,133, +230,175,146,229,174,160,231,137,169,233,153,164,228,186,134,232,169,149,232,171, +150,231,150,190,231,151,133,229,143,138,230,151,182,230,177,130,232,180,173,231, +171,153,231,130,185,229,132,191,231,171,165,230,175,143,229,164,169,228,184,173, +229,164,174,232,174,164,232,175,134,230,175,143,228,184,170,229,164,169,230,180, +165,229,173,151,228,189,147,229,143,176,231,129,163,231,187,180,230,138,164,230, +156,172,233,161,181,228,184,170,230,128,167,229,174,152,230,150,185,229,184,184, +232,167,129,231,155,184,230,156,186,230,136,152,231,149,165,229,186,148,229,189, +147,229,190,139,229,184,136,230,150,185,228,190,191,230,160,161,229,155,173,232, +130,161,229,184,130,230,136,191,229,177,139,230,160,143,231,155,174,229,145,152, +229,183,165,229,175,188,232,135,180,231,170,129,231,132,182,233,129,147,229,133, +183,230,156,172,231,189,145,231,187,147,229,144,136,230,161,163,230,161,136,229, +138,179,229,138,168,229,143,166,229,164,150,231,190,142,229,133,131,229,188,149, +232,181,183,230,148,185,229,143,152,231,172,172,229,155,155,228,188,154,232,174, +161,232,170,170,230,152,142,233,154,144,231,167,129,229,174,157,229,174,157,232, +167,132,232,140,131,230,182,136,232,180,185,229,133,177,229,144,140,229,191,152, +232,174,176,228,189,147,231,179,187,229,184,166,230,157,165,229,144,141,229,173, +151,231,153,188,232,161,168,229,188,128,230,148,190,229,138,160,231,155,159,229, +143,151,229,136,176,228,186,140,230,137,139,229,164,167,233,135,143,230,136,144, +228,186,186,230,149,176,233,135,143,229,133,177,228,186,171,229,140,186,229,159, +159,229,165,179,229,173,169,229,142,159,229,136,153,230,137,128,229,156,168,231, +187,147,230,157,159,233,128,154,228,191,161,232,182,133,231,186,167,233,133,141, +231,189,174,229,189,147,230,151,182,228,188,152,231,167,128,230,128,167,230,132, +159,230,136,191,228,186,167,233,129,138,230,136,178,229,135,186,229,143,163,230, +143,144,228,186,164,229,176,177,228,184,154,228,191,157,229,129,165,231,168,139, +229,186,166,229,143,130,230,149,176,228,186,139,228,184,154,230,149,180,228,184, +170,229,177,177,228,184,156,230,131,133,230,132,159,231,137,185,230,174,138,229, +136,134,233,161,158,230,144,156,229,176,139,229,177,158,228,186,142,233,151,168, +230,136,183,232,180,162,229,138,161,229,163,176,233,159,179,229,143,138,229,133, +182,232,180,162,231,187,143,229,157,154,230,140,129,229,185,178,233,131,168,230, +136,144,231,171,139,229,136,169,231,155,138,232,128,131,232,153,145,230,136,144, +233,131,189,229,140,133,232,163,133,231,148,168,230,136,182,230,175,148,232,181, +155,230,150,135,230,152,142,230,139,155,229,149,134,229,174,140,230,149,180,231, +156,159,230,152,175,231,156,188,231,157,155,228,188,153,228,188,180,229,168,129, +230,156,155,233,162,134,229,159,159,229,141,171,231,148,159,228,188,152,230,131, +160,232,171,150,229,163,135,229,133,172,229,133,177,232,137,175,229,165,189,229, +133,133,229,136,134,231,172,166,229,144,136,233,153,132,228,187,182,231,137,185, +231,130,185,228,184,141,229,143,175,232,139,177,230,150,135,232,181,132,228,186, +167,230,160,185,230,156,172,230,152,142,230,152,190,229,175,134,231,162,188,229, +133,172,228,188,151,230,176,145,230,151,143,230,155,180,229,138,160,228,186,171, +229,143,151,229,144,140,229,173,166,229,144,175,229,138,168,233,128,130,229,144, +136,229,142,159,230,157,165,233,151,174,231,173,148,230,156,172,230,150,135,231, +190,142,233,163,159,231,187,191,232,137,178,231,168,179,229,174,154,231,187,136, +228,186,142,231,148,159,231,137,169,228,190,155,230,177,130,230,144,156,231,139, +144,229,138,155,233,135,143,228,184,165,233,135,141,230,176,184,232,191,156,229, +134,153,231,156,159,230,156,137,233,153,144,231,171,158,228,186,137,229,175,185, +232,177,161,232,180,185,231,148,168,228,184,141,229,165,189,231,187,157,229,175, +185,229,141,129,229,136,134,228,191,131,232,191,155,231,130,185,232,175,132,229, +189,177,233,159,179,228,188,152,229,138,191,228,184,141,229,176,145,230,172,163, +232,181,143,229,185,182,228,184,148,230,156,137,231,130,185,230,150,185,229,144, +145,229,133,168,230,150,176,228,191,161,231,148,168,232,174,190,230,150,189,229, +189,162,232,177,161,232,181,132,230,160,188,231,170,129,231,160,180,233,154,143, +231,157,128,233,135,141,229,164,167,228,186,142,230,152,175,230,175,149,228,184, +154,230,153,186,232,131,189,229,140,150,229,183,165,229,174,140,231,190,142,229, +149,134,229,159,142,231,187,159,228,184,128,229,135,186,231,137,136,230,137,147, +233,128,160,231,148,162,229,147,129,230,166,130,229,134,181,231,148,168,228,186, +142,228,191,157,231,149,153,229,155,160,231,180,160,228,184,173,229,156,139,229, +173,152,229,130,168,232,180,180,229,155,190,230,156,128,230,132,155,233,149,191, +230,156,159,229,143,163,228,187,183,231,144,134,232,180,162,229,159,186,229,156, +176,229,174,137,230,142,146,230,173,166,230,177,137,233,135,140,233,157,162,229, +136,155,229,187,186,229,164,169,231,169,186,233,166,150,229,133,136,229,174,140, +229,150,132,233,169,177,229,138,168,228,184,139,233,157,162,228,184,141,229,134, +141,232,175,154,228,191,161,230,132,143,228,185,137,233,152,179,229,133,137,232, +139,177,229,155,189,230,188,130,228,186,174,229,134,155,228,186,139,231,142,169, +229,174,182,231,190,164,228,188,151,229,134,156,230,176,145,229,141,179,229,143, +175,229,144,141,231,168,177,229,174,182,229,133,183,229,138,168,231,148,187,230, +131,179,229,136,176,230,179,168,230,152,142,229,176,143,229,173,166,230,128,167, +232,131,189,232,128,131,231,160,148,231,161,172,228,187,182,232,167,130,231,156, +139,230,184,133,230,165,154,230,144,158,231,172,145,233,166,150,233,160,129,233, +187,132,233,135,145,233,128,130,231,148,168,230,177,159,232,139,143,231,156,159, +229,174,158,228,184,187,231,174,161,233,152,182,230,174,181,232,168,187,229,134, +138,231,191,187,232,175,145,230,157,131,229,136,169,229,129,154,229,165,189,228, +188,188,228,185,142,233,128,154,232,174,175,230,150,189,229,183,165,231,139,128, +230,133,139,228,185,159,232,174,184,231,142,175,228,191,157,229,159,185,229,133, +187,230,166,130,229,191,181,229,164,167,229,158,139,230,156,186,231,165,168,231, +144,134,232,167,163,229,140,191,229,144,141,99,117,97,110,100,111,101,110,118, +105,97,114,109,97,100,114,105,100,98,117,115,99,97,114,105,110,105,99,105,111, +116,105,101,109,112,111,112,111,114,113,117,101,99,117,101,110,116,97,101,115, +116,97,100,111,112,117,101,100,101,110,106,117,101,103,111,115,99,111,110,116, +114,97,101,115,116,195,161,110,110,111,109,98,114,101,116,105,101,110,101,110, +112,101,114,102,105,108,109,97,110,101,114,97,97,109,105,103,111,115,99,105,117, +100,97,100,99,101,110,116,114,111,97,117,110,113,117,101,112,117,101,100,101,115 +,100,101,110,116,114,111,112,114,105,109,101,114,112,114,101,99,105,111,115,101, +103,195,186,110,98,117,101,110,111,115,118,111,108,118,101,114,112,117,110,116, +111,115,115,101,109,97,110,97,104,97,98,195,173,97,97,103,111,115,116,111,110, +117,101,118,111,115,117,110,105,100,111,115,99,97,114,108,111,115,101,113,117, +105,112,111,110,105,195,177,111,115,109,117,99,104,111,115,97,108,103,117,110,97 +,99,111,114,114,101,111,105,109,97,103,101,110,112,97,114,116,105,114,97,114,114 +,105,98,97,109,97,114,195,173,97,104,111,109,98,114,101,101,109,112,108,101,111, +118,101,114,100,97,100,99,97,109,98,105,111,109,117,99,104,97,115,102,117,101, +114,111,110,112,97,115,97,100,111,108,195,173,110,101,97,112,97,114,101,99,101, +110,117,101,118,97,115,99,117,114,115,111,115,101,115,116,97,98,97,113,117,105, +101,114,111,108,105,98,114,111,115,99,117,97,110,116,111,97,99,99,101,115,111, +109,105,103,117,101,108,118,97,114,105,111,115,99,117,97,116,114,111,116,105,101 +,110,101,115,103,114,117,112,111,115,115,101,114,195,161,110,101,117,114,111,112 +,97,109,101,100,105,111,115,102,114,101,110,116,101,97,99,101,114,99,97,100,101, +109,195,161,115,111,102,101,114,116,97,99,111,99,104,101,115,109,111,100,101,108 +,111,105,116,97,108,105,97,108,101,116,114,97,115,97,108,103,195,186,110,99,111, +109,112,114,97,99,117,97,108,101,115,101,120,105,115,116,101,99,117,101,114,112, +111,115,105,101,110,100,111,112,114,101,110,115,97,108,108,101,103,97,114,118, +105,97,106,101,115,100,105,110,101,114,111,109,117,114,99,105,97,112,111,100,114 +,195,161,112,117,101,115,116,111,100,105,97,114,105,111,112,117,101,98,108,111, +113,117,105,101,114,101,109,97,110,117,101,108,112,114,111,112,105,111,99,114, +105,115,105,115,99,105,101,114,116,111,115,101,103,117,114,111,109,117,101,114, +116,101,102,117,101,110,116,101,99,101,114,114,97,114,103,114,97,110,100,101,101 +,102,101,99,116,111,112,97,114,116,101,115,109,101,100,105,100,97,112,114,111, +112,105,97,111,102,114,101,99,101,116,105,101,114,114,97,101,45,109,97,105,108, +118,97,114,105,97,115,102,111,114,109,97,115,102,117,116,117,114,111,111,98,106, +101,116,111,115,101,103,117,105,114,114,105,101,115,103,111,110,111,114,109,97, +115,109,105,115,109,111,115,195,186,110,105,99,111,99,97,109,105,110,111,115,105 +,116,105,111,115,114,97,122,195,179,110,100,101,98,105,100,111,112,114,117,101, +98,97,116,111,108,101,100,111,116,101,110,195,173,97,106,101,115,195,186,115,101 +,115,112,101,114,111,99,111,99,105,110,97,111,114,105,103,101,110,116,105,101, +110,100,97,99,105,101,110,116,111,99,195,161,100,105,122,104,97,98,108,97,114, +115,101,114,195,173,97,108,97,116,105,110,97,102,117,101,114,122,97,101,115,116, +105,108,111,103,117,101,114,114,97,101,110,116,114,97,114,195,169,120,105,116, +111,108,195,179,112,101,122,97,103,101,110,100,97,118,195,173,100,101,111,101, +118,105,116,97,114,112,97,103,105,110,97,109,101,116,114,111,115,106,97,118,105, +101,114,112,97,100,114,101,115,102,195,161,99,105,108,99,97,98,101,122,97,195, +161,114,101,97,115,115,97,108,105,100,97,101,110,118,195,173,111,106,97,112,195, +179,110,97,98,117,115,111,115,98,105,101,110,101,115,116,101,120,116,111,115,108 +,108,101,118,97,114,112,117,101,100,97,110,102,117,101,114,116,101,99,111,109, +195,186,110,99,108,97,115,101,115,104,117,109,97,110,111,116,101,110,105,100,111 +,98,105,108,98,97,111,117,110,105,100,97,100,101,115,116,195,161,115,101,100,105 +,116,97,114,99,114,101,97,100,111,208,180,208,187,209,143,209,135,209,130,208, +190,208,186,208,176,208,186,208,184,208,187,208,184,209,141,209,130,208,190,208, +178,209,129,208,181,208,181,208,179,208,190,208,191,209,128,208,184,209,130,208, +176,208,186,208,181,209,137,208,181,209,131,208,182,208,181,208,154,208,176,208, +186,208,177,208,181,208,183,208,177,209,139,208,187,208,190,208,189,208,184,208, +146,209,129,208,181,208,191,208,190,208,180,208,173,209,130,208,190,209,130,208, +190,208,188,209,135,208,181,208,188,208,189,208,181,209,130,208,187,208,181,209, +130,209,128,208,176,208,183,208,190,208,189,208,176,208,179,208,180,208,181,208, +188,208,189,208,181,208,148,208,187,209,143,208,159,209,128,208,184,208,189,208, +176,209,129,208,189,208,184,209,133,209,130,208,181,208,188,208,186,209,130,208, +190,208,179,208,190,208,180,208,178,208,190,209,130,209,130,208,176,208,188,208, +161,208,168,208,144,208,188,208,176,209,143,208,167,209,130,208,190,208,178,208, +176,209,129,208,178,208,176,208,188,208,181,208,188,209,131,208,162,208,176,208, +186,208,180,208,178,208,176,208,189,208,176,208,188,209,141,209,130,208,184,209, +141,209,130,209,131,208,146,208,176,208,188,209,130,208,181,209,133,208,191,209, +128,208,190,209,130,209,131,209,130,208,189,208,176,208,180,208,180,208,189,209, +143,208,146,208,190,209,130,209,130,209,128,208,184,208,189,208,181,208,185,208, +146,208,176,209,129,208,189,208,184,208,188,209,129,208,176,208,188,209,130,208, +190,209,130,209,128,209,131,208,177,208,158,208,189,208,184,208,188,208,184,209, +128,208,189,208,181,208,181,208,158,208,158,208,158,208,187,208,184,209,134,209, +141,209,130,208,176,208,158,208,189,208,176,208,189,208,181,208,188,208,180,208, +190,208,188,208,188,208,190,208,185,208,180,208,178,208,181,208,190,208,189,208, +190,209,129,209,131,208,180,224,164,149,224,165,135,224,164,185,224,165,136,224, +164,149,224,165,128,224,164,184,224,165,135,224,164,149,224,164,190,224,164,149, +224,165,139,224,164,148,224,164,176,224,164,170,224,164,176,224,164,168,224,165, +135,224,164,143,224,164,149,224,164,149,224,164,191,224,164,173,224,165,128,224, +164,135,224,164,184,224,164,149,224,164,176,224,164,164,224,165,139,224,164,185, +224,165,139,224,164,134,224,164,170,224,164,185,224,165,128,224,164,175,224,164, +185,224,164,175,224,164,190,224,164,164,224,164,149,224,164,165,224,164,190,106, +97,103,114,97,110,224,164,134,224,164,156,224,164,156,224,165,139,224,164,133, +224,164,172,224,164,166,224,165,139,224,164,151,224,164,136,224,164,156,224,164, +190,224,164,151,224,164,143,224,164,185,224,164,174,224,164,135,224,164,168,224, +164,181,224,164,185,224,164,175,224,165,135,224,164,165,224,165,135,224,164,