Revved to chromium 290d2cfa5187ac1f2c87fde4c6dd6f27dad0c93a refs/remotes/origin/HEAD
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 9949150..6751f06 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -1285,6 +1285,8 @@ results.extend(_CheckNoAbbreviationInPngFileName(input_api, output_api)) results.extend(_CheckForInvalidOSMacros(input_api, output_api)) results.extend(_CheckForInvalidIfDefinedMacros(input_api, output_api)) + # TODO(danakj): Remove this when base/move.h is removed. + results.extend(_CheckForUsingSideEffectsOfPass(input_api, output_api)) results.extend(_CheckAddedDepsHaveTargetApprovals(input_api, output_api)) results.extend( input_api.canned_checks.CheckChangeHasNoTabs( @@ -1444,6 +1446,20 @@ bad_macros)] +def _CheckForUsingSideEffectsOfPass(input_api, output_api): + """Check all affected files for using side effects of Pass.""" + errors = [] + for f in input_api.AffectedFiles(): + if f.LocalPath().endswith(('.h', '.c', '.cc', '.m', '.mm')): + for lnum, line in f.ChangedContents(): + # Disallow Foo(*my_scoped_thing.Pass()); See crbug.com/418297. + if re.search(r'\*[a-zA-Z0-9_]+\.Pass\(\)', line): + errors.append(output_api.PresubmitError( + ('%s:%d uses *foo.Pass() to delete the contents of scoped_ptr. ' + + 'See crbug.com/418297.') % (f.LocalPath(), lnum))) + return errors + + def _CheckForIPCRules(input_api, output_api): """Check for same IPC rules described in http://www.chromium.org/Home/chromium-security/education/security-tips-for-ipc
diff --git a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java index 2d7c1a1..a4d8e0b 100644 --- a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java +++ b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -5,6 +5,7 @@ package org.chromium.base; import android.animation.ValueAnimator; +import android.annotation.TargetApi; import android.app.ActivityOptions; import android.app.Notification; import android.app.PendingIntent; @@ -14,6 +15,7 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.provider.Settings; import android.view.View; import android.view.ViewGroup.MarginLayoutParams; import android.view.ViewTreeObserver; @@ -366,4 +368,16 @@ return builder.getNotification(); } } + + /** + * @see android.provider.Settings.Global#DEVICE_PROVISIONED + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public static boolean isDeviceProvisioned(Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return true; + if (context == null) return true; + if (context.getContentResolver() == null) return true; + return Settings.Global.getInt( + context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; + } }
diff --git a/base/i18n/break_iterator.cc b/base/i18n/break_iterator.cc index 89b1d0f..e3aaa2b 100644 --- a/base/i18n/break_iterator.cc +++ b/base/i18n/break_iterator.cc
@@ -14,7 +14,7 @@ const size_t npos = static_cast<size_t>(-1); -BreakIterator::BreakIterator(const string16& str, BreakType break_type) +BreakIterator::BreakIterator(const StringPiece16& str, BreakType break_type) : iter_(NULL), string_(str), break_type_(break_type), @@ -22,7 +22,7 @@ pos_(0) { } -BreakIterator::BreakIterator(const string16& str, const string16& rules) +BreakIterator::BreakIterator(const StringPiece16& str, const string16& rules) : iter_(NULL), string_(str), rules_(rules), @@ -132,6 +132,7 @@ NOTREACHED() << "ubrk_setText failed"; return false; } + string_ = StringPiece16(text, length); return true; } @@ -172,6 +173,10 @@ } string16 BreakIterator::GetString() const { + return GetStringPiece().as_string(); +} + +StringPiece16 BreakIterator::GetStringPiece() const { DCHECK(prev_ != npos && pos_ != npos); return string_.substr(prev_, pos_ - prev_); }
diff --git a/base/i18n/break_iterator.h b/base/i18n/break_iterator.h index 8120d47..19fdbe0 100644 --- a/base/i18n/break_iterator.h +++ b/base/i18n/break_iterator.h
@@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/i18n/base_i18n_export.h" #include "base/strings/string16.h" +#include "base/strings/string_piece.h" // The BreakIterator class iterates through the words, word breaks, and // line breaks in a UTF-16 string. @@ -71,12 +72,12 @@ }; // Requires |str| to live as long as the BreakIterator does. - BreakIterator(const string16& str, BreakType break_type); + BreakIterator(const StringPiece16& str, BreakType break_type); // Make a rule-based iterator. BreakType == RULE_BASED is implied. // TODO(andrewhayden): This signature could easily be misinterpreted as // "(const string16& str, const string16& locale)". We should do something // better. - BreakIterator(const string16& str, const string16& rules); + BreakIterator(const StringPiece16& str, const string16& rules); ~BreakIterator(); // Init() must be called before any of the iterators are valid. @@ -115,6 +116,8 @@ // have advanced to somewhere useful. string16 GetString() const; + StringPiece16 GetStringPiece() const; + // Returns the value of pos() returned before Advance() was last called. size_t prev() const { return prev_; } @@ -130,7 +133,7 @@ void* iter_; // The string we're iterating over. Can be changed with SetText(...) - const string16& string_; + StringPiece16 string_; // Rules for our iterator. Mutually exclusive with break_type_. const string16 rules_;
diff --git a/base/i18n/break_iterator_unittest.cc b/base/i18n/break_iterator_unittest.cc index 6dcae18..8569135 100644 --- a/base/i18n/break_iterator_unittest.cc +++ b/base/i18n/break_iterator_unittest.cc
@@ -334,5 +334,40 @@ } } +// Test for https://code.google.com/p/chromium/issues/detail?id=411213 +// We should be able to get valid substrings with GetString() function +// after setting new content by calling SetText(). +TEST(BreakIteratorTest, GetStringAfterSetText) { + const string16 initial_string(ASCIIToUTF16("str")); + BreakIterator iter(initial_string, BreakIterator::BREAK_WORD); + ASSERT_TRUE(iter.Init()); + + const string16 long_string(ASCIIToUTF16("another,string")); + EXPECT_TRUE(iter.SetText(long_string.c_str(), long_string.size())); + EXPECT_TRUE(iter.Advance()); + EXPECT_TRUE(iter.Advance()); // Advance to ',' in |long_string| + + // Check that the current position is out of bounds of the |initial_string|. + EXPECT_LT(initial_string.size(), iter.pos()); + + // Check that we can get a valid substring of |long_string|. + EXPECT_EQ(ASCIIToUTF16(","), iter.GetString()); +} + +TEST(BreakIteratorTest, GetStringPiece) { + const string16 initial_string(ASCIIToUTF16("some string")); + BreakIterator iter(initial_string, BreakIterator::BREAK_WORD); + ASSERT_TRUE(iter.Init()); + + EXPECT_TRUE(iter.Advance()); + EXPECT_EQ(iter.GetString(), iter.GetStringPiece().as_string()); + EXPECT_EQ(StringPiece16(ASCIIToUTF16("some")), iter.GetStringPiece()); + + EXPECT_TRUE(iter.Advance()); + EXPECT_TRUE(iter.Advance()); + EXPECT_EQ(iter.GetString(), iter.GetStringPiece().as_string()); + EXPECT_EQ(StringPiece16(ASCIIToUTF16("string")), iter.GetStringPiece()); +} + } // namespace i18n } // namespace base
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc index 65eeacb..43f15a3 100644 --- a/base/strings/string_util.cc +++ b/base/strings/string_util.cc
@@ -91,6 +91,14 @@ template<> struct NonASCIIMask<8, char> { static inline uint64_t value() { return 0x8080808080808080ULL; } }; +#if defined(WCHAR_T_IS_UTF32) +template<> struct NonASCIIMask<4, wchar_t> { + static inline uint32_t value() { return 0xFFFFFF80U; } +}; +template<> struct NonASCIIMask<8, wchar_t> { + static inline uint64_t value() { return 0xFFFFFF80FFFFFF80ULL; } +}; +#endif // WCHAR_T_IS_UTF32 } // namespace @@ -392,6 +400,12 @@ return DoIsStringASCII(str.data(), str.length()); } +#if defined(WCHAR_T_IS_UTF32) +bool IsStringASCII(const std::wstring& str) { + return DoIsStringASCII(str.data(), str.length()); +} +#endif + bool IsStringUTF8(const std::string& str) { const char *src = str.data(); int32 src_len = static_cast<int32>(str.length());
diff --git a/base/strings/string_util.h b/base/strings/string_util.h index c3464e6..1e2ac70 100644 --- a/base/strings/string_util.h +++ b/base/strings/string_util.h
@@ -254,6 +254,9 @@ // A convenience adaptor for WebStrings, as they don't convert into // StringPieces directly. BASE_EXPORT bool IsStringASCII(const string16& str); +#if defined(WCHAR_T_IS_UTF32) +BASE_EXPORT bool IsStringASCII(const std::wstring& str); +#endif // Converts the elements of the given string. This version uses a pointer to // clearly differentiate it from the non-pointer variant.
diff --git a/base/strings/string_util_unittest.cc b/base/strings/string_util_unittest.cc index 1644846..d46bc9b 100644 --- a/base/strings/string_util_unittest.cc +++ b/base/strings/string_util_unittest.cc
@@ -393,6 +393,8 @@ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F', 0 }; + static std::wstring wchar_ascii( + L"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"); // Test a variety of the fragment start positions and lengths in order to make // sure that bit masking in IsStringASCII works correctly. @@ -433,6 +435,29 @@ } } } + + { + const size_t string_length = wchar_ascii.length(); + for (size_t len = 0; len < string_length; ++len) { + EXPECT_TRUE(IsStringASCII(wchar_ascii.substr(0, len))); + for (size_t char_pos = 0; char_pos < len; ++char_pos) { + wchar_ascii[char_pos] |= 0x80; + EXPECT_FALSE( + IsStringASCII(wchar_ascii.substr(0, len))); + wchar_ascii[char_pos] &= ~0x80; + wchar_ascii[char_pos] |= 0x100; + EXPECT_FALSE( + IsStringASCII(wchar_ascii.substr(0, len))); + wchar_ascii[char_pos] &= ~0x100; +#if defined(WCHAR_T_IS_UTF32) + wchar_ascii[char_pos] |= 0x10000; + EXPECT_FALSE( + IsStringASCII(wchar_ascii.substr(0, len))); + wchar_ascii[char_pos] &= ~0x10000; +#endif // WCHAR_T_IS_UTF32 + } + } + } } TEST(StringUtilTest, ConvertASCII) {
diff --git a/base/strings/utf_string_conversions.cc b/base/strings/utf_string_conversions.cc index 9d520fa..9796eec 100644 --- a/base/strings/utf_string_conversions.cc +++ b/base/strings/utf_string_conversions.cc
@@ -43,15 +43,23 @@ // UTF-8 <-> Wide -------------------------------------------------------------- bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output) { - PrepareForUTF8Output(src, src_len, output); - return ConvertUnicode(src, src_len, output); + if (IsStringASCII(std::wstring(src, src_len))) { + output->assign(src, src + src_len); + return true; + } else { + PrepareForUTF8Output(src, src_len, output); + return ConvertUnicode(src, src_len, output); + } } std::string WideToUTF8(const std::wstring& wide) { + if (IsStringASCII(wide)) { + return std::string(wide.data(), wide.data() + wide.length()); + } + std::string ret; - // Ignore the success flag of this call, it will do the best it can for - // invalid input, which is what we want here. - WideToUTF8(wide.data(), wide.length(), &ret); + PrepareForUTF8Output(wide.data(), wide.length(), &ret); + ConvertUnicode(wide.data(), wide.length(), &ret); return ret; } @@ -159,11 +167,20 @@ } bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) { - PrepareForUTF8Output(src, src_len, output); - return ConvertUnicode(src, src_len, output); + if (IsStringASCII(StringPiece16(src, src_len))) { + output->assign(src, src + src_len); + return true; + } else { + PrepareForUTF8Output(src, src_len, output); + return ConvertUnicode(src, src_len, output); + } } std::string UTF16ToUTF8(const string16& utf16) { + if (IsStringASCII(utf16)) { + return std::string(utf16.begin(), utf16.end()); + } + std::string ret; // Ignore the success flag of this call, it will do the best it can for // invalid input, which is what we want here.
diff --git a/build/all.gyp b/build/all.gyp index e9eaa5b..2dbd4fb 100644 --- a/build/all.gyp +++ b/build/all.gyp
@@ -836,13 +836,6 @@ # Unit test bundles packaged as an apk. '../content/content_shell_and_tests.gyp:content_browsertests_apk', ], - 'conditions': [ - ['"<(libpeer_target_type)"=="static_library"', { - 'dependencies': [ - '../third_party/libjingle/libjingle.gyp:libjingle_peerconnection_javalib', - ], - }], - ], }, # target_name: android_builder_chromium_webrtc ], # targets }], # OS="android"
diff --git a/build/android/gyp/insert_chromium_version.py b/build/android/gyp/insert_chromium_version.py index f858225..171f9d4 100755 --- a/build/android/gyp/insert_chromium_version.py +++ b/build/android/gyp/insert_chromium_version.py
@@ -38,7 +38,7 @@ parser.add_option('--android-objcopy', help='Path to the toolchain\'s objcopy binary') - parser.add_option('--libraries-source-dir', + parser.add_option('--stripped-libraries-dir', help='Directory of native libraries') parser.add_option('--libraries', help='List of libraries') @@ -50,7 +50,7 @@ libraries = build_utils.ParseGypList(options.libraries) for library in libraries: - library_path = os.path.join(options.libraries_source_dir, library) + library_path = os.path.join(options.stripped_libraries_dir, library) InsertChromiumVersion(options.android_objcopy, library_path,
diff --git a/build/android/insert_chromium_version.gypi b/build/android/insert_chromium_version.gypi index 158a227..a6ff908 100644 --- a/build/android/insert_chromium_version.gypi +++ b/build/android/insert_chromium_version.gypi
@@ -11,7 +11,7 @@ # 'actions': [ # 'variables': { # 'ordered_libraries_file': 'file generated by write_ordered_libraries' -# 'libraries_source_dir': 'the directory contains native libraries' +# 'stripped_libraries_dir': 'the directory contains native libraries' # 'input_paths': 'files to be added to the list of inputs' # 'stamp': 'file to touch when the action is complete' # 'version_string': 'chromium version string to be inserted' @@ -37,7 +37,7 @@ 'action': [ 'python', '<(DEPTH)/build/android/gyp/insert_chromium_version.py', '--android-objcopy=<(android_objcopy)', - '--libraries-source-dir=<(libraries_source_dir)', + '--stripped-libraries-dir=<(stripped_libraries_dir)', '--libraries=@FileArg(<(ordered_libraries_file):libraries)', '--version-string=<(version_string)', '--stamp=<(stamp)',
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 42d3b3a..ad97902 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -475,6 +475,9 @@ } _java_srcjars = [] + if (defined(invoker.srcjars)) { + _java_srcjars = invoker.srcjars + } foreach(dep, _srcjar_deps) { _dep_gen_dir = get_label_info(dep, "target_gen_dir") _dep_name = get_label_info(dep, "name") @@ -542,7 +545,8 @@ template("android_java_library") { if (defined(invoker.testonly)) { testonly = invoker.testonly } - assert(defined(invoker.java_files) || defined(invoker.DEPRECATED_java_in_dir)) + assert(defined(invoker.java_files) || defined(invoker.DEPRECATED_java_in_dir) + || defined(invoker.srcjars)) assert(defined(invoker.build_config)) assert(defined(invoker.jar_path)) assert(defined(invoker.dex_path)) @@ -552,10 +556,15 @@ _srcjar_deps = invoker.srcjar_deps } + _srcjars = [] + if (defined(invoker.srcjars)) { + _srcjars = invoker.srcjars + } + _java_files = [] if (defined(invoker.java_files)) { _java_files = invoker.java_files - } else { + } else if (defined(invoker.DEPRECATED_java_in_dir)) { _java_files_build_rel = exec_script( "//build/android/gyp/find.py", [ @@ -567,7 +576,7 @@ ) _java_files = rebase_path(_java_files_build_rel, ".", root_build_dir) } - assert(_java_files != [] || _srcjar_deps != []) + assert(_java_files != [] || _srcjar_deps != [] || _srcjars != []) _jar_path = invoker.jar_path _dex_path = invoker.dex_path @@ -589,6 +598,7 @@ build_config = invoker.build_config java_files = _java_files srcjar_deps = _srcjar_deps + srcjars = _srcjars if (defined(invoker.proguard_preprocess) && invoker.proguard_preprocess) { proguard_preprocess = invoker.proguard_preprocess
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 4b5d62b..bef77b2 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -482,6 +482,8 @@ # java_files: List of .java files included in this library. # srcjar_deps: List of srcjar dependencies. The .java files in the srcjars # will be added to java_files and be included in this library. +# srcjars: List of srcjars to be included in this library, together with the +# ones obtained from srcjar_deps. # chromium_code: If true, extra analysis warning/errors will be enabled. # jar_excluded_patterns: List of patterns of .class files to exclude from the # final jar. @@ -513,7 +515,8 @@ template("android_library") { if (defined(invoker.testonly)) { testonly = invoker.testonly } - assert(defined(invoker.java_files) || defined(invoker.DEPRECATED_java_in_dir)) + assert(defined(invoker.java_files) || defined(invoker.DEPRECATED_java_in_dir) + || defined(invoker.srcjars)) _base_path = "$target_gen_dir/$target_name" _build_config = _base_path + ".build_config" _jar_path = _base_path + ".jar" @@ -541,7 +544,7 @@ chromium_code = _chromium_code if (defined(invoker.java_files)) { java_files = invoker.java_files - } else { + } else if (defined(invoker.DEPRECATED_java_in_dir)) { DEPRECATED_java_in_dir = invoker.DEPRECATED_java_in_dir } build_config = _build_config @@ -564,6 +567,9 @@ if (defined(invoker.srcjar_deps)) { srcjar_deps = invoker.srcjar_deps } + if (defined(invoker.srcjars)) { + srcjars = invoker.srcjars + } } }
diff --git a/build/java_apk.gypi b/build/java_apk.gypi index fbc5a3a..1b90a1b 100644 --- a/build/java_apk.gypi +++ b/build/java_apk.gypi
@@ -342,6 +342,19 @@ 'includes': ['../build/android/strip_native_libraries.gypi'], }, { + 'action_name': 'insert_chromium_version', + 'variables': { + 'ordered_libraries_file%': '<(ordered_libraries_file)', + 'stripped_libraries_dir%': '<(stripped_libraries_dir)', + 'version_string': '<(native_lib_version_name)', + 'input_paths': [ + '<(strip_stamp)', + ], + 'stamp': '<(version_stamp)' + }, + 'includes': ['../build/android/insert_chromium_version.gypi'], + }, + { 'action_name': 'pack_arm_relocations', 'variables': { 'conditions': [ @@ -358,26 +371,13 @@ 'stripped_libraries_dir%': '<(stripped_libraries_dir)', 'packed_libraries_dir': '<(libraries_source_dir)', 'input_paths': [ - '<(strip_stamp)', + '<(version_stamp)' ], 'stamp': '<(pack_arm_relocations_stamp)', }, 'includes': ['../build/android/pack_arm_relocations.gypi'], }, { - 'action_name': 'insert_chromium_version', - 'variables': { - 'ordered_libraries_file%': '<(ordered_libraries_file)', - 'libraries_source_dir%': '<(libraries_source_dir)', - 'version_string': '<(native_lib_version_name)', - 'input_paths': [ - '<(pack_arm_relocations_stamp)', - ], - 'stamp': '<(version_stamp)' - }, - 'includes': ['../build/android/insert_chromium_version.gypi'], - }, - { 'variables': { 'input_libraries': [ '<@(additional_bundled_libs)', @@ -476,7 +476,7 @@ 'inputs': [ '<(ordered_libraries_file)', '<(strip_additional_stamp)', - '<(version_stamp)', + '<(pack_arm_relocations_stamp)', ], 'input_apk_path': '<(unsigned_apk_path)', 'output_apk_path': '<(unsigned_standalone_apk_path)', @@ -493,7 +493,7 @@ 'libraries_source_dir': '<(apk_package_native_libs_dir)/<(android_app_abi)', 'package_input_paths': [ '<(strip_additional_stamp)', - '<(version_stamp)', + '<(pack_arm_relocations_stamp)', ], }, }],
diff --git a/build/landmines.py b/build/landmines.py index 96bc485..a034864 100755 --- a/build/landmines.py +++ b/build/landmines.py
@@ -15,6 +15,7 @@ import difflib import errno +import gyp_environment import logging import optparse import os @@ -120,6 +121,7 @@ if landmine_utils.builder() in ('dump_dependency_json', 'eclipse'): return 0 + gyp_environment.SetEnvironment() landmines = [] for s in landmine_scripts:
diff --git a/build/whitespace_file.txt b/build/whitespace_file.txt index f11fa16..2d17f0e 100644 --- a/build/whitespace_file.txt +++ b/build/whitespace_file.txt
@@ -143,3 +143,4 @@ Cool whitespace change for git-cl land Oh god the bots are red! I'm blind! Mmmm, donuts. +*
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index 5009d58..0515a6f 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -208,6 +208,13 @@ "layers/ui_resource_layer.h", "layers/ui_resource_layer_impl.cc", "layers/ui_resource_layer_impl.h", + "layers/video_frame_provider.h", + "layers/video_frame_provider_client_impl.cc", + "layers/video_frame_provider_client_impl.h", + "layers/video_layer.cc", + "layers/video_layer.h", + "layers/video_layer_impl.cc", + "layers/video_layer_impl.h", "output/begin_frame_args.cc", "output/begin_frame_args.h", "output/bsp_tree.cc", @@ -420,6 +427,8 @@ "resources/ui_resource_client.h", "resources/ui_resource_request.cc", "resources/ui_resource_request.h", + "resources/video_resource_updater.cc", + "resources/video_resource_updater.h", "resources/zero_copy_raster_worker_pool.cc", "resources/zero_copy_raster_worker_pool.h", "scheduler/begin_frame_source.cc", @@ -480,6 +489,7 @@ "//base/third_party/dynamic_annotations", "//gpu", "//gpu/command_buffer/client:gles2_interface", + "//media", "//ui/events:events_base", "//ui/gfx", "//ui/gfx/geometry", @@ -548,6 +558,8 @@ "test/fake_tile_manager_client.h", "test/fake_ui_resource_layer_tree_host_impl.cc", "test/fake_ui_resource_layer_tree_host_impl.h", + "test/fake_video_frame_provider.cc", + "test/fake_video_frame_provider.h", "test/geometry_test_utils.cc", "test/geometry_test_utils.h", "test/test_in_process_context_provider.cc", @@ -692,6 +704,7 @@ "layers/tiled_layer_unittest.cc", "layers/ui_resource_layer_impl_unittest.cc", "layers/ui_resource_layer_unittest.cc", + "layers/video_layer_impl_unittest.cc", "output/begin_frame_args_unittest.cc", "output/delegating_renderer_unittest.cc", "output/filter_operations_unittest.cc", @@ -722,6 +735,7 @@ "resources/texture_uploader_unittest.cc", "resources/tile_manager_unittest.cc", "resources/tile_priority_unittest.cc", + "resources/video_resource_updater_unittest.cc", "scheduler/begin_frame_source_unittest.cc", "scheduler/delay_based_time_source_unittest.cc", "scheduler/scheduler_state_machine_unittest.cc", @@ -749,6 +763,7 @@ "trees/layer_tree_host_unittest_picture.cc", "trees/layer_tree_host_unittest_proxy.cc", "trees/layer_tree_host_unittest_scroll.cc", + "trees/layer_tree_host_unittest_video.cc", "trees/layer_tree_impl_unittest.cc", "trees/occlusion_tracker_unittest.cc", "trees/tree_synchronizer_unittest.cc", @@ -774,6 +789,7 @@ "//gpu:test_support", "//gpu/command_buffer/client:gles2_interface", "//gpu/command_buffer/common:gles2_utils", + "//media", "//testing/gmock", "//testing/gtest", "//ui/events:events_base", @@ -805,6 +821,7 @@ "//gpu", "//gpu:test_support", "//gpu/command_buffer/common:gles2_utils", + "//media", "//skia", "//testing/gmock", "//testing/gtest",
diff --git a/cc/animation/animation_registrar.cc b/cc/animation/animation_registrar.cc index 7af5fea..5327926 100644 --- a/cc/animation/animation_registrar.cc +++ b/cc/animation/animation_registrar.cc
@@ -16,7 +16,7 @@ for (AnimationControllerMap::iterator iter = copy.begin(); iter != copy.end(); ++iter) - (*iter).second->SetAnimationRegistrar(NULL); + (*iter).second->SetAnimationRegistrar(nullptr); } scoped_refptr<LayerAnimationController>
diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc index 04fff18..911c5c4 100644 --- a/cc/animation/layer_animation_controller.cc +++ b/cc/animation/layer_animation_controller.cc
@@ -24,8 +24,8 @@ : registrar_(0), id_(id), is_active_(false), - value_provider_(NULL), - layer_animation_delegate_(NULL), + value_provider_(nullptr), + layer_animation_delegate_(nullptr), needs_to_start_animations_(false) { } @@ -933,7 +933,7 @@ ObserverListBase<LayerAnimationValueObserver>::Iterator it( value_observers_); LayerAnimationValueObserver* obs; - while ((obs = it.GetNext()) != NULL) { + while ((obs = it.GetNext()) != nullptr) { if ((notify_active_observers && notify_pending_observers) || (notify_active_observers && obs->IsActive()) || (notify_pending_observers && !obs->IsActive())) @@ -950,7 +950,7 @@ ObserverListBase<LayerAnimationValueObserver>::Iterator it( value_observers_); LayerAnimationValueObserver* obs; - while ((obs = it.GetNext()) != NULL) { + while ((obs = it.GetNext()) != nullptr) { if ((notify_active_observers && notify_pending_observers) || (notify_active_observers && obs->IsActive()) || (notify_pending_observers && !obs->IsActive())) @@ -967,7 +967,7 @@ ObserverListBase<LayerAnimationValueObserver>::Iterator it( value_observers_); LayerAnimationValueObserver* obs; - while ((obs = it.GetNext()) != NULL) { + while ((obs = it.GetNext()) != nullptr) { if ((notify_active_observers && notify_pending_observers) || (notify_active_observers && obs->IsActive()) || (notify_pending_observers && !obs->IsActive())) @@ -984,7 +984,7 @@ ObserverListBase<LayerAnimationValueObserver>::Iterator it( value_observers_); LayerAnimationValueObserver* obs; - while ((obs = it.GetNext()) != NULL) { + while ((obs = it.GetNext()) != nullptr) { if ((notify_active_observers && notify_pending_observers) || (notify_active_observers && obs->IsActive()) || (notify_pending_observers && !obs->IsActive())) @@ -1003,7 +1003,7 @@ if (value_observers_.might_have_observers()) { ObserverListBase<LayerAnimationValueObserver>::Iterator it( value_observers_); - return it.GetNext() != NULL; + return it.GetNext() != nullptr; } return false; } @@ -1013,7 +1013,7 @@ ObserverListBase<LayerAnimationValueObserver>::Iterator it( value_observers_); LayerAnimationValueObserver* obs; - while ((obs = it.GetNext()) != NULL) + while ((obs = it.GetNext()) != nullptr) if (obs->IsActive()) return true; }
diff --git a/cc/animation/layer_animation_controller.h b/cc/animation/layer_animation_controller.h index 4f56772..e57491a 100644 --- a/cc/animation/layer_animation_controller.h +++ b/cc/animation/layer_animation_controller.h
@@ -106,7 +106,7 @@ void remove_value_provider(LayerAnimationValueProvider* provider) { if (value_provider_ == provider) - value_provider_ = NULL; + value_provider_ = nullptr; } void set_layer_animation_delegate(AnimationDelegate* delegate) { @@ -115,7 +115,7 @@ void remove_layer_animation_delegate(AnimationDelegate* delegate) { if (layer_animation_delegate_ == delegate) - layer_animation_delegate_ = NULL; + layer_animation_delegate_ = nullptr; } bool HasFilterAnimationThatInflatesBounds() const;
diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc index 34bdad0..d32751b 100644 --- a/cc/animation/layer_animation_controller_unittest.cc +++ b/cc/animation/layer_animation_controller_unittest.cc
@@ -107,7 +107,7 @@ // Start the animation on the main thread. Should not affect the start time. controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500)); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_EQ(controller->GetAnimation(group_id, Animation::Opacity)->start_time(), controller_impl->GetAnimation(group_id, @@ -156,7 +156,7 @@ // Start the animation on the main thread. Should not affect the start time. controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500)); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_EQ(start_time, controller->GetAnimation(group_id, Animation::Opacity)->start_time()); @@ -210,11 +210,11 @@ EXPECT_EQ(1u, registrar_impl->active_animation_controllers().size()); controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500)); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_EQ(1u, registrar->active_animation_controllers().size()); controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000)); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_EQ(Animation::Finished, controller->GetAnimation(Animation::Opacity)->run_state()); EXPECT_EQ(1u, registrar->active_animation_controllers().size()); @@ -232,7 +232,7 @@ EXPECT_EQ(1u, events->size()); controller->NotifyAnimationFinished((*events)[0]); controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1500)); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_EQ(Animation::WaitingForDeletion, controller->GetAnimation(Animation::Opacity)->run_state()); @@ -246,8 +246,8 @@ EXPECT_EQ(0u, registrar->active_animation_controllers().size()); EXPECT_EQ(0u, registrar_impl->active_animation_controllers().size()); - controller->SetAnimationRegistrar(NULL); - controller_impl->SetAnimationRegistrar(NULL); + controller->SetAnimationRegistrar(nullptr); + controller_impl->SetAnimationRegistrar(nullptr); } TEST(LayerAnimationControllerTest, SyncPause) { @@ -279,7 +279,7 @@ controller_impl->Animate(kInitialTickTime); controller_impl->UpdateState(true, &events); controller->Animate(kInitialTickTime); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_EQ(Animation::Running, controller_impl->GetAnimation(group_id, Animation::Opacity)->run_state()); @@ -364,7 +364,7 @@ AddOpacityTransitionToController(controller.get(), 1.0, 0.0f, 1.0f, false); controller->Animate(kInitialTickTime); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); controller->PushAnimationUpdatesTo(controller_impl.get()); controller_impl->ActivateAnimations(); @@ -377,7 +377,7 @@ controller->NotifyAnimationStarted((*events)[0]); controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000)); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_FALSE(dummy.animation_waiting_for_deletion()); EXPECT_FALSE(dummy_impl.animation_waiting_for_deletion()); @@ -400,7 +400,7 @@ controller->NotifyAnimationFinished((*events)[0]); controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(3000)); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_TRUE(dummy.animation_waiting_for_deletion()); controller->PushAnimationUpdatesTo(controller_impl.get()); @@ -690,7 +690,7 @@ controller->GetAnimation(Animation::ScrollOffset)->curve()->Duration()); controller->Animate(kInitialTickTime); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_TRUE(controller->HasActiveAnimation()); EXPECT_EQ(initial_value, dummy.scroll_offset()); @@ -704,7 +704,7 @@ controller->NotifyAnimationStarted((*events)[0]); controller->Animate(kInitialTickTime + duration / 2); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_TRUE(controller->HasActiveAnimation()); EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(200.f, 250.f), dummy.scroll_offset()); @@ -723,7 +723,7 @@ EXPECT_FALSE(event); controller->Animate(kInitialTickTime + duration); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_VECTOR2DF_EQ(target_value, dummy.scroll_offset()); EXPECT_FALSE(controller->HasActiveAnimation()); } @@ -770,7 +770,7 @@ controller->GetAnimation(Animation::ScrollOffset)->curve()->Duration()); controller->Animate(kInitialTickTime); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_TRUE(controller->HasActiveAnimation()); EXPECT_EQ(initial_value, dummy.scroll_offset()); @@ -787,7 +787,7 @@ controller->NotifyAnimationStarted((*events)[0]); controller->Animate(kInitialTickTime + duration / 2); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_TRUE(controller->HasActiveAnimation()); EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(400.f, 150.f), dummy.scroll_offset()); @@ -806,7 +806,7 @@ EXPECT_FALSE(event); controller->Animate(kInitialTickTime + duration); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_VECTOR2DF_EQ(target_value, dummy.scroll_offset()); EXPECT_FALSE(controller->HasActiveAnimation()); } @@ -1569,9 +1569,9 @@ Animation::Opacity)); controller->Animate(kInitialTickTime); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000)); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_EQ(Animation::Finished, controller->GetAnimation(1, Animation::Transform)->run_state()); @@ -1624,7 +1624,7 @@ EXPECT_FALSE(dummy_impl.animation_waiting_for_deletion()); controller->Animate(kInitialTickTime); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_TRUE(dummy.animation_waiting_for_deletion()); EXPECT_EQ(Animation::WaitingForDeletion, controller->GetAnimation(Animation::Opacity)->run_state()); @@ -1673,7 +1673,7 @@ controller->GetAnimation(Animation::Opacity)->run_state()); controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500)); - controller->UpdateState(true, NULL); + controller->UpdateState(true, nullptr); EXPECT_TRUE(dummy.animation_waiting_for_deletion()); EXPECT_EQ(Animation::WaitingForDeletion, controller->GetAnimation(Animation::Opacity)->run_state());
diff --git a/cc/animation/transform_operations.cc b/cc/animation/transform_operations.cc index 34c526b..6d8ad0f 100644 --- a/cc/animation/transform_operations.cc +++ b/cc/animation/transform_operations.cc
@@ -68,8 +68,8 @@ for (int i = num_operations - 1; i >= 0; --i) { gfx::BoxF bounds_for_operation; const TransformOperation* from_op = - from_identity ? NULL : &from.operations_[i]; - const TransformOperation* to_op = to_identity ? NULL : &operations_[i]; + from_identity ? nullptr : &from.operations_[i]; + const TransformOperation* to_op = to_identity ? nullptr : &operations_[i]; if (!TransformOperation::BlendedBoundsForBox(*bounds, from_op, to_op,
diff --git a/cc/base/latency_info_swap_promise_monitor.cc b/cc/base/latency_info_swap_promise_monitor.cc index f724d6c..dc28739 100644 --- a/cc/base/latency_info_swap_promise_monitor.cc +++ b/cc/base/latency_info_swap_promise_monitor.cc
@@ -14,7 +14,7 @@ bool AddRenderingScheduledComponent(ui::LatencyInfo* latency_info) { if (latency_info->FindLatency( - ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL)) + ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, nullptr)) return false; latency_info->AddLatencyNumber( ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, 0); @@ -25,7 +25,7 @@ if (latency_info->FindLatency( ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT, 0, - NULL)) + nullptr)) return false; latency_info->AddLatencyNumber( ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT,
diff --git a/cc/base/math_util.cc b/cc/base/math_util.cc index 5df1e56..8cd0692 100644 --- a/cc/base/math_util.cc +++ b/cc/base/math_util.cc
@@ -706,7 +706,7 @@ } bool MathUtil::FromValue(const base::Value* raw_value, gfx::Rect* out_rect) { - const base::ListValue* value = NULL; + const base::ListValue* value = nullptr; if (!raw_value->GetAsList(&value)) return false;
diff --git a/cc/base/scoped_ptr_vector.h b/cc/base/scoped_ptr_vector.h index 0b83e35..164b27c 100644 --- a/cc/base/scoped_ptr_vector.h +++ b/cc/base/scoped_ptr_vector.h
@@ -77,7 +77,7 @@ typename std::vector<T*>::iterator writable_position = position; scoped_ptr<T> ret(*writable_position); - *writable_position = NULL; + *writable_position = nullptr; return ret.Pass(); }
diff --git a/cc/base/scoped_ptr_vector_unittest.cc b/cc/base/scoped_ptr_vector_unittest.cc index 73a7f81..391ab48 100644 --- a/cc/base/scoped_ptr_vector_unittest.cc +++ b/cc/base/scoped_ptr_vector_unittest.cc
@@ -70,9 +70,9 @@ EXPECT_EQ(6, v[5]->data()); EXPECT_EQ(3u, v2.size()); - EXPECT_EQ(NULL, v2[0]); - EXPECT_EQ(NULL, v2[1]); - EXPECT_EQ(NULL, v2[2]); + EXPECT_EQ(nullptr, v2[0]); + EXPECT_EQ(nullptr, v2[1]); + EXPECT_EQ(nullptr, v2[2]); } TEST(ScopedPtrVectorTest, Partition) {
diff --git a/cc/base/swap_promise_monitor.h b/cc/base/swap_promise_monitor.h index b8c8cd0..cf06bee 100644 --- a/cc/base/swap_promise_monitor.h +++ b/cc/base/swap_promise_monitor.h
@@ -24,9 +24,9 @@ class CC_EXPORT SwapPromiseMonitor { public: // If the monitor lives on the main thread, pass in layer_tree_host - // and set layer_tree_host_impl to NULL. + // and set layer_tree_host_impl to nullptr. // If the monitor lives on the impl thread, pass in layer_tree_host_impl - // and set layer_tree_host to NULL. + // and set layer_tree_host to nullptr. SwapPromiseMonitor(LayerTreeHost* layer_tree_host, LayerTreeHostImpl* layer_tree_host_impl); virtual ~SwapPromiseMonitor();
diff --git a/cc/base/tiling_data.cc b/cc/base/tiling_data.cc index 879d421..a42fb3c 100644 --- a/cc/base/tiling_data.cc +++ b/cc/base/tiling_data.cc
@@ -299,7 +299,7 @@ index_y_(-1) { } -TilingData::Iterator::Iterator() : BaseIterator(NULL) { done(); } +TilingData::Iterator::Iterator() : BaseIterator(nullptr) { done(); } TilingData::Iterator::Iterator(const TilingData* tiling_data, const gfx::Rect& consider_rect, @@ -441,7 +441,7 @@ } TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator() - : BaseIterator(NULL) { + : BaseIterator(nullptr) { done(); }
diff --git a/cc/layers/nine_patch_layer_impl_unittest.cc b/cc/layers/nine_patch_layer_impl_unittest.cc index bc7abcf..9d1d870 100644 --- a/cc/layers/nine_patch_layer_impl_unittest.cc +++ b/cc/layers/nine_patch_layer_impl_unittest.cc
@@ -70,15 +70,13 @@ EXPECT_EQ(expected_quad_size, quads.size()); Region remaining(visible_content_rect); - size_t i = 0; for (QuadList::ConstIterator iter = quads.begin(); iter != quads.end(); ++iter) { gfx::Rect quad_rect = iter->rect; - EXPECT_TRUE(visible_content_rect.Contains(quad_rect)) << i; - EXPECT_TRUE(remaining.Contains(quad_rect)) << i; + EXPECT_TRUE(visible_content_rect.Contains(quad_rect)) << iter.index(); + EXPECT_TRUE(remaining.Contains(quad_rect)) << iter.index(); remaining.Subtract(Region(quad_rect)); - ++i; } // Check if the left-over quad is the same size as the mapped aperture quad in
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index 0bd4718..20e61dc 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -3501,17 +3501,18 @@ // The content_to_target_transform should be scaled by the // MaximumTilingContentsScale on the layer. EXPECT_EQ(scaled_draw_transform.ToString(), - render_pass->shared_quad_state_list[0] + render_pass->shared_quad_state_list.front() ->content_to_target_transform.ToString()); // The content_bounds should be scaled by the // MaximumTilingContentsScale on the layer. - EXPECT_EQ(gfx::Size(2500u, 5000u).ToString(), - render_pass->shared_quad_state_list[0]->content_bounds.ToString()); + EXPECT_EQ( + gfx::Size(2500u, 5000u).ToString(), + render_pass->shared_quad_state_list.front()->content_bounds.ToString()); // The visible_content_rect should be scaled by the // MaximumTilingContentsScale on the layer. - EXPECT_EQ( - gfx::Rect(0u, 0u, 2500u, 5000u).ToString(), - render_pass->shared_quad_state_list[0]->visible_content_rect.ToString()); + EXPECT_EQ(gfx::Rect(0u, 0u, 2500u, 5000u).ToString(), + render_pass->shared_quad_state_list.front() + ->visible_content_rect.ToString()); } TEST_F(PictureLayerImplTest, UpdateTilesForMasksWithNoVisibleContent) {
diff --git a/cc/layers/render_surface_unittest.cc b/cc/layers/render_surface_unittest.cc index e2aca7d..f9ac05b 100644 --- a/cc/layers/render_surface_unittest.cc +++ b/cc/layers/render_surface_unittest.cc
@@ -119,7 +119,8 @@ RenderPassId(2, 0)); ASSERT_EQ(1u, render_pass->shared_quad_state_list.size()); - SharedQuadState* shared_quad_state = render_pass->shared_quad_state_list[0]; + SharedQuadState* shared_quad_state = + render_pass->shared_quad_state_list.front(); EXPECT_EQ( 30.0,
diff --git a/cc/layers/tiled_layer_impl_unittest.cc b/cc/layers/tiled_layer_impl_unittest.cc index 400e6cf..7609ade 100644 --- a/cc/layers/tiled_layer_impl_unittest.cc +++ b/cc/layers/tiled_layer_impl_unittest.cc
@@ -266,17 +266,17 @@ LayerTilingData::NO_BORDER_TEXELS, gfx::Rect(layer_size)); - size_t i = 0; for (QuadList::Iterator iter = render_pass->quad_list.begin(); iter != render_pass->quad_list.end(); ++iter) { const TileDrawQuad* quad = TileDrawQuad::MaterialCast(&*iter); - EXPECT_NE(0u, quad->resource_id) << LayerTestCommon::quad_string << i; + EXPECT_NE(0u, quad->resource_id) << LayerTestCommon::quad_string + << iter.index(); EXPECT_EQ(gfx::RectF(gfx::PointF(), tile_size), quad->tex_coord_rect) - << LayerTestCommon::quad_string << i; + << LayerTestCommon::quad_string << iter.index(); EXPECT_EQ(tile_size, quad->texture_size) << LayerTestCommon::quad_string - << i; + << iter.index(); } }
diff --git a/cc/layers/video_frame_provider.h b/cc/layers/video_frame_provider.h new file mode 100644 index 0000000..784d951 --- /dev/null +++ b/cc/layers/video_frame_provider.h
@@ -0,0 +1,63 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_VIDEO_FRAME_PROVIDER_H_ +#define CC_LAYERS_VIDEO_FRAME_PROVIDER_H_ + +#include "base/memory/ref_counted.h" + +namespace media { +class VideoFrame; +} + +namespace cc { + +// Threading notes: This class may be used in a multi threaded manner. +// Specifically, the implementation may call GetCurrentFrame() or +// PutCurrentFrame() from the compositor thread. If so, the caller is +// responsible for making sure Client::DidReceiveFrame() and +// Client::DidUpdateMatrix() are only called from this same thread. +class VideoFrameProvider { + public: + virtual ~VideoFrameProvider() {} + + class Client { + public: + // Provider will call this method to tell the client to stop using it. + // StopUsingProvider() may be called from any thread. The client should + // block until it has PutCurrentFrame() any outstanding frames. + virtual void StopUsingProvider() = 0; + + // Notifies the provider's client that a call to GetCurrentFrame() will + // return new data. + virtual void DidReceiveFrame() = 0; + + // Notifies the provider's client of a new UV transform matrix to be used. + virtual void DidUpdateMatrix(const float* matrix) = 0; + + protected: + virtual ~Client() {} + }; + + // May be called from any thread, but there must be some external guarantee + // that the provider is not destroyed before this call returns. + virtual void SetVideoFrameProviderClient(Client* client) = 0; + + // This function places a lock on the current frame and returns a pointer to + // it. Calls to this method should always be followed with a call to + // PutCurrentFrame(). + // Only the current provider client should call this function. + virtual scoped_refptr<media::VideoFrame> GetCurrentFrame() = 0; + + // This function releases the lock on the video frame. It should always be + // called after GetCurrentFrame(). Frames passed into this method + // should no longer be referenced after the call is made. Only the current + // provider client should call this function. + virtual void PutCurrentFrame( + const scoped_refptr<media::VideoFrame>& frame) = 0; +}; + +} // namespace cc + +#endif // CC_LAYERS_VIDEO_FRAME_PROVIDER_H_
diff --git a/cc/layers/video_frame_provider_client_impl.cc b/cc/layers/video_frame_provider_client_impl.cc new file mode 100644 index 0000000..cf78413 --- /dev/null +++ b/cc/layers/video_frame_provider_client_impl.cc
@@ -0,0 +1,95 @@ +// 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. + +#include "cc/layers/video_frame_provider_client_impl.h" + +#include "base/debug/trace_event.h" +#include "cc/base/math_util.h" +#include "cc/layers/video_layer_impl.h" +#include "media/base/video_frame.h" + +namespace cc { + +// static +scoped_refptr<VideoFrameProviderClientImpl> + VideoFrameProviderClientImpl::Create( + VideoFrameProvider* provider) { + return make_scoped_refptr( + new VideoFrameProviderClientImpl(provider)); +} + +VideoFrameProviderClientImpl::~VideoFrameProviderClientImpl() {} + +VideoFrameProviderClientImpl::VideoFrameProviderClientImpl( + VideoFrameProvider* provider) + : active_video_layer_(NULL), provider_(provider) { + // This only happens during a commit on the compositor thread while the main + // thread is blocked. That makes this a thread-safe call to set the video + // frame provider client that does not require a lock. The same is true of + // the call to Stop(). + provider_->SetVideoFrameProviderClient(this); + + // This matrix is the default transformation for stream textures, and flips + // on the Y axis. + stream_texture_matrix_ = gfx::Transform( + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 1.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0); +} + +void VideoFrameProviderClientImpl::Stop() { + if (!provider_) + return; + provider_->SetVideoFrameProviderClient(NULL); + provider_ = NULL; +} + +scoped_refptr<media::VideoFrame> +VideoFrameProviderClientImpl::AcquireLockAndCurrentFrame() { + provider_lock_.Acquire(); // Balanced by call to ReleaseLock(). + if (!provider_) + return NULL; + + return provider_->GetCurrentFrame(); +} + +void VideoFrameProviderClientImpl::PutCurrentFrame( + const scoped_refptr<media::VideoFrame>& frame) { + provider_lock_.AssertAcquired(); + provider_->PutCurrentFrame(frame); +} + +void VideoFrameProviderClientImpl::ReleaseLock() { + provider_lock_.AssertAcquired(); + provider_lock_.Release(); +} + +void VideoFrameProviderClientImpl::StopUsingProvider() { + // Block the provider from shutting down until this client is done + // using the frame. + base::AutoLock locker(provider_lock_); + provider_ = NULL; +} + +void VideoFrameProviderClientImpl::DidReceiveFrame() { + TRACE_EVENT1("cc", + "VideoFrameProviderClientImpl::DidReceiveFrame", + "active_video_layer", + !!active_video_layer_); + if (active_video_layer_) + active_video_layer_->SetNeedsRedraw(); +} + +void VideoFrameProviderClientImpl::DidUpdateMatrix(const float* matrix) { + stream_texture_matrix_ = gfx::Transform( + matrix[0], matrix[4], matrix[8], matrix[12], + matrix[1], matrix[5], matrix[9], matrix[13], + matrix[2], matrix[6], matrix[10], matrix[14], + matrix[3], matrix[7], matrix[11], matrix[15]); + if (active_video_layer_) + active_video_layer_->SetNeedsRedraw(); +} + +} // namespace cc
diff --git a/cc/layers/video_frame_provider_client_impl.h b/cc/layers/video_frame_provider_client_impl.h new file mode 100644 index 0000000..e676d0b --- /dev/null +++ b/cc/layers/video_frame_provider_client_impl.h
@@ -0,0 +1,64 @@ +// 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. + +#ifndef CC_LAYERS_VIDEO_FRAME_PROVIDER_CLIENT_IMPL_H_ +#define CC_LAYERS_VIDEO_FRAME_PROVIDER_CLIENT_IMPL_H_ + +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "cc/layers/video_frame_provider.h" +#include "ui/gfx/transform.h" + +namespace media { class VideoFrame; } + +namespace cc { +class VideoLayerImpl; + +class VideoFrameProviderClientImpl + : public VideoFrameProvider::Client, + public base::RefCounted<VideoFrameProviderClientImpl> { + public: + static scoped_refptr<VideoFrameProviderClientImpl> Create( + VideoFrameProvider* provider); + + VideoLayerImpl* active_video_layer() { return active_video_layer_; } + void set_active_video_layer(VideoLayerImpl* video_layer) { + active_video_layer_ = video_layer; + } + + void Stop(); + bool Stopped() const { return !provider_; } + + scoped_refptr<media::VideoFrame> AcquireLockAndCurrentFrame(); + void PutCurrentFrame(const scoped_refptr<media::VideoFrame>& frame); + void ReleaseLock(); + const gfx::Transform& stream_texture_matrix() const { + return stream_texture_matrix_; + } + + // VideoFrameProvider::Client implementation. These methods are all callable + // on any thread. + virtual void StopUsingProvider() OVERRIDE; + virtual void DidReceiveFrame() OVERRIDE; + virtual void DidUpdateMatrix(const float* matrix) OVERRIDE; + + private: + explicit VideoFrameProviderClientImpl(VideoFrameProvider* provider); + friend class base::RefCounted<VideoFrameProviderClientImpl>; + virtual ~VideoFrameProviderClientImpl(); + + VideoLayerImpl* active_video_layer_; + + // Guards the destruction of provider_ and the frame that it provides + base::Lock provider_lock_; + VideoFrameProvider* provider_; + + gfx::Transform stream_texture_matrix_; + + DISALLOW_COPY_AND_ASSIGN(VideoFrameProviderClientImpl); +}; + +} // namespace cc + +#endif // CC_LAYERS_VIDEO_FRAME_PROVIDER_CLIENT_IMPL_H_
diff --git a/cc/layers/video_layer.h b/cc/layers/video_layer.h new file mode 100644 index 0000000..361007d --- /dev/null +++ b/cc/layers/video_layer.h
@@ -0,0 +1,47 @@ +// Copyright 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_VIDEO_LAYER_H_ +#define CC_LAYERS_VIDEO_LAYER_H_ + +#include "base/callback.h" +#include "cc/base/cc_export.h" +#include "cc/layers/layer.h" +#include "media/base/video_rotation.h" + +namespace media { class VideoFrame; } + +namespace cc { + +class VideoFrameProvider; +class VideoLayerImpl; + +// A Layer that contains a Video element. +class CC_EXPORT VideoLayer : public Layer { + public: + static scoped_refptr<VideoLayer> Create(VideoFrameProvider* provider, + media::VideoRotation video_rotation); + + virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) + OVERRIDE; + + virtual bool Update(ResourceUpdateQueue* queue, + const OcclusionTracker<Layer>* occlusion) OVERRIDE; + + private: + VideoLayer(VideoFrameProvider* provider, media::VideoRotation video_rotation); + virtual ~VideoLayer(); + + // This pointer is only for passing to VideoLayerImpl's constructor. It should + // never be dereferenced by this class. + VideoFrameProvider* provider_; + + media::VideoRotation video_rotation_; + + DISALLOW_COPY_AND_ASSIGN(VideoLayer); +}; + +} // namespace cc + +#endif // CC_LAYERS_VIDEO_LAYER_H_
diff --git a/cc/layers/video_layer_impl.h b/cc/layers/video_layer_impl.h new file mode 100644 index 0000000..34f013a --- /dev/null +++ b/cc/layers/video_layer_impl.h
@@ -0,0 +1,80 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_VIDEO_LAYER_IMPL_H_ +#define CC_LAYERS_VIDEO_LAYER_IMPL_H_ + +#include <vector> + +#include "cc/base/cc_export.h" +#include "cc/layers/layer_impl.h" +#include "cc/resources/release_callback_impl.h" +#include "cc/resources/video_resource_updater.h" +#include "media/base/video_rotation.h" + +namespace media { +class VideoFrame; +} + +namespace cc { +class VideoFrameProvider; +class VideoFrameProviderClientImpl; + +class CC_EXPORT VideoLayerImpl : public LayerImpl { + public: + static scoped_ptr<VideoLayerImpl> Create(LayerTreeImpl* tree_impl, + int id, + VideoFrameProvider* provider, + media::VideoRotation video_rotation); + virtual ~VideoLayerImpl(); + + // LayerImpl implementation. + virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) + OVERRIDE; + virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; + virtual bool WillDraw(DrawMode draw_mode, + ResourceProvider* resource_provider) OVERRIDE; + virtual void AppendQuads(RenderPass* render_pass, + const OcclusionTracker<LayerImpl>& occlusion_tracker, + AppendQuadsData* append_quads_data) OVERRIDE; + virtual void DidDraw(ResourceProvider* resource_provider) OVERRIDE; + virtual void DidBecomeActive() OVERRIDE; + virtual void ReleaseResources() OVERRIDE; + + void SetNeedsRedraw(); + + void SetProviderClientImpl( + scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl); + + media::VideoRotation video_rotation() const { return video_rotation_; } + + private: + VideoLayerImpl(LayerTreeImpl* tree_impl, + int id, + media::VideoRotation video_rotation); + + virtual const char* LayerTypeAsString() const OVERRIDE; + + scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl_; + + scoped_refptr<media::VideoFrame> frame_; + + media::VideoRotation video_rotation_; + + scoped_ptr<VideoResourceUpdater> updater_; + VideoFrameExternalResources::ResourceType frame_resource_type_; + std::vector<ResourceProvider::ResourceId> frame_resources_; + + // TODO(danakj): Remove these, hide software path inside ResourceProvider and + // ExternalResource (aka TextureMailbox) classes. + std::vector<unsigned> software_resources_; + // Called once for each software resource. + ReleaseCallbackImpl software_release_callback_; + + DISALLOW_COPY_AND_ASSIGN(VideoLayerImpl); +}; + +} // namespace cc + +#endif // CC_LAYERS_VIDEO_LAYER_IMPL_H_
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc index 0c43e02..0a897f3 100644 --- a/cc/output/gl_renderer.cc +++ b/cc/output/gl_renderer.cc
@@ -13,6 +13,7 @@ #include "base/debug/trace_event.h" #include "base/logging.h" #include "cc/base/math_util.h" +#include "cc/layers/video_layer_impl.h" #include "cc/output/compositor_frame.h" #include "cc/output/compositor_frame_metadata.h" #include "cc/output/context_provider.h"
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc index 6d5af36..892adbd 100644 --- a/cc/output/renderer_pixeltest.cc +++ b/cc/output/renderer_pixeltest.cc
@@ -12,6 +12,7 @@ #include "cc/test/fake_picture_pile_impl.h" #include "cc/test/pixel_test.h" #include "gpu/command_buffer/client/gles2_interface.h" +#include "media/base/video_frame.h" #include "third_party/skia/include/core/SkColorPriv.h" #include "third_party/skia/include/core/SkImageFilter.h" #include "third_party/skia/include/core/SkMatrix.h" @@ -383,6 +384,346 @@ FuzzyPixelOffByOneComparator(true))); } +class VideoGLRendererPixelTest : public GLRendererPixelTest { + protected: + void CreateTestYUVVideoDrawQuad_Striped(const SharedQuadState* shared_state, + media::VideoFrame::Format format, + bool is_transparent, + const gfx::RectF& tex_coord_rect, + RenderPass* render_pass) { + const gfx::Rect rect(this->device_viewport_size_); + + scoped_refptr<media::VideoFrame> video_frame = + media::VideoFrame::CreateFrame( + format, rect.size(), rect, rect.size(), base::TimeDelta()); + + // YUV values representing a striped pattern, for validating texture + // coordinates for sampling. + uint8_t y_value = 0; + uint8_t u_value = 0; + uint8_t v_value = 0; + for (int i = 0; i < video_frame->rows(media::VideoFrame::kYPlane); ++i) { + uint8_t* y_row = video_frame->data(media::VideoFrame::kYPlane) + + video_frame->stride(media::VideoFrame::kYPlane) * i; + for (int j = 0; j < video_frame->row_bytes(media::VideoFrame::kYPlane); + ++j) { + y_row[j] = (y_value += 1); + } + } + for (int i = 0; i < video_frame->rows(media::VideoFrame::kUPlane); ++i) { + uint8_t* u_row = video_frame->data(media::VideoFrame::kUPlane) + + video_frame->stride(media::VideoFrame::kUPlane) * i; + uint8_t* v_row = video_frame->data(media::VideoFrame::kVPlane) + + video_frame->stride(media::VideoFrame::kVPlane) * i; + for (int j = 0; j < video_frame->row_bytes(media::VideoFrame::kUPlane); + ++j) { + u_row[j] = (u_value += 3); + v_row[j] = (v_value += 5); + } + } + CreateTestYUVVideoDrawQuad_FromVideoFrame( + shared_state, video_frame, is_transparent, tex_coord_rect, render_pass); + } + + void CreateTestYUVVideoDrawQuad_Solid(const SharedQuadState* shared_state, + media::VideoFrame::Format format, + bool is_transparent, + const gfx::RectF& tex_coord_rect, + uint8 y, + uint8 u, + uint8 v, + RenderPass* render_pass) { + const gfx::Rect rect(this->device_viewport_size_); + + scoped_refptr<media::VideoFrame> video_frame = + media::VideoFrame::CreateFrame( + format, rect.size(), rect, rect.size(), base::TimeDelta()); + + // YUV values of a solid, constant, color. Useful for testing that color + // space/color range are being handled properly. + memset(video_frame->data(media::VideoFrame::kYPlane), + y, + video_frame->stride(media::VideoFrame::kYPlane) * + video_frame->rows(media::VideoFrame::kYPlane)); + memset(video_frame->data(media::VideoFrame::kUPlane), + u, + video_frame->stride(media::VideoFrame::kUPlane) * + video_frame->rows(media::VideoFrame::kUPlane)); + memset(video_frame->data(media::VideoFrame::kVPlane), + v, + video_frame->stride(media::VideoFrame::kVPlane) * + video_frame->rows(media::VideoFrame::kVPlane)); + + CreateTestYUVVideoDrawQuad_FromVideoFrame( + shared_state, video_frame, is_transparent, tex_coord_rect, render_pass); + } + + void CreateTestYUVVideoDrawQuad_FromVideoFrame( + const SharedQuadState* shared_state, + scoped_refptr<media::VideoFrame> video_frame, + bool is_transparent, + const gfx::RectF& tex_coord_rect, + RenderPass* render_pass) { + const bool with_alpha = (video_frame->format() == media::VideoFrame::YV12A); + const YUVVideoDrawQuad::ColorSpace color_space = + (video_frame->format() == media::VideoFrame::YV12J + ? YUVVideoDrawQuad::REC_601_JPEG + : YUVVideoDrawQuad::REC_601); + const gfx::Rect rect(this->device_viewport_size_); + const gfx::Rect opaque_rect(0, 0, 0, 0); + + if (with_alpha) + memset(video_frame->data(media::VideoFrame::kAPlane), + is_transparent ? 0 : 128, + video_frame->stride(media::VideoFrame::kAPlane) * + video_frame->rows(media::VideoFrame::kAPlane)); + + VideoFrameExternalResources resources = + video_resource_updater_->CreateExternalResourcesFromVideoFrame( + video_frame); + + EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type); + EXPECT_EQ(media::VideoFrame::NumPlanes(video_frame->format()), + resources.mailboxes.size()); + EXPECT_EQ(media::VideoFrame::NumPlanes(video_frame->format()), + resources.release_callbacks.size()); + + ResourceProvider::ResourceId y_resource = + resource_provider_->CreateResourceFromTextureMailbox( + resources.mailboxes[media::VideoFrame::kYPlane], + SingleReleaseCallbackImpl::Create( + resources.release_callbacks[media::VideoFrame::kYPlane])); + ResourceProvider::ResourceId u_resource = + resource_provider_->CreateResourceFromTextureMailbox( + resources.mailboxes[media::VideoFrame::kUPlane], + SingleReleaseCallbackImpl::Create( + resources.release_callbacks[media::VideoFrame::kUPlane])); + ResourceProvider::ResourceId v_resource = + resource_provider_->CreateResourceFromTextureMailbox( + resources.mailboxes[media::VideoFrame::kVPlane], + SingleReleaseCallbackImpl::Create( + resources.release_callbacks[media::VideoFrame::kVPlane])); + ResourceProvider::ResourceId a_resource = 0; + if (with_alpha) { + a_resource = resource_provider_->CreateResourceFromTextureMailbox( + resources.mailboxes[media::VideoFrame::kAPlane], + SingleReleaseCallbackImpl::Create( + resources.release_callbacks[media::VideoFrame::kAPlane])); + } + + YUVVideoDrawQuad* yuv_quad = + render_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>(); + yuv_quad->SetNew(shared_state, + rect, + opaque_rect, + rect, + tex_coord_rect, + y_resource, + u_resource, + v_resource, + a_resource, + color_space); + } + + virtual void SetUp() OVERRIDE { + GLRendererPixelTest::SetUp(); + video_resource_updater_.reset(new VideoResourceUpdater( + output_surface_->context_provider(), resource_provider_.get())); + } + + private: + scoped_ptr<VideoResourceUpdater> video_resource_updater_; +}; + +TEST_F(VideoGLRendererPixelTest, SimpleYUVRect) { + gfx::Rect rect(this->device_viewport_size_); + + RenderPassId id(1, 1); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); + + SharedQuadState* shared_state = + CreateTestSharedQuadState(gfx::Transform(), rect, pass.get()); + + CreateTestYUVVideoDrawQuad_Striped(shared_state, + media::VideoFrame::YV12, + false, + gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), + pass.get()); + + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE( + this->RunPixelTest(&pass_list, + base::FilePath(FILE_PATH_LITERAL("yuv_stripes.png")), + FuzzyPixelOffByOneComparator(true))); +} + +TEST_F(VideoGLRendererPixelTest, OffsetYUVRect) { + gfx::Rect rect(this->device_viewport_size_); + + RenderPassId id(1, 1); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); + + SharedQuadState* shared_state = + CreateTestSharedQuadState(gfx::Transform(), rect, pass.get()); + + // Intentionally sets frame format to I420 for testing coverage. + CreateTestYUVVideoDrawQuad_Striped(shared_state, + media::VideoFrame::I420, + false, + gfx::RectF(0.125f, 0.25f, 0.75f, 0.5f), + pass.get()); + + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + base::FilePath(FILE_PATH_LITERAL("yuv_stripes_offset.png")), + FuzzyPixelOffByOneComparator(true))); +} + +TEST_F(VideoGLRendererPixelTest, SimpleYUVRectBlack) { + gfx::Rect rect(this->device_viewport_size_); + + RenderPassId id(1, 1); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); + + SharedQuadState* shared_state = + CreateTestSharedQuadState(gfx::Transform(), rect, pass.get()); + + // In MPEG color range YUV values of (15,128,128) should produce black. + CreateTestYUVVideoDrawQuad_Solid(shared_state, + media::VideoFrame::YV12, + false, + gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), + 15, + 128, + 128, + pass.get()); + + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + // If we didn't get black out of the YUV values above, then we probably have a + // color range issue. + EXPECT_TRUE(this->RunPixelTest(&pass_list, + base::FilePath(FILE_PATH_LITERAL("black.png")), + FuzzyPixelOffByOneComparator(true))); +} + +TEST_F(VideoGLRendererPixelTest, SimpleYUVJRect) { + gfx::Rect rect(this->device_viewport_size_); + + RenderPassId id(1, 1); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); + + SharedQuadState* shared_state = + CreateTestSharedQuadState(gfx::Transform(), rect, pass.get()); + + // YUV of (149,43,21) should be green (0,255,0) in RGB. + CreateTestYUVVideoDrawQuad_Solid(shared_state, + media::VideoFrame::YV12J, + false, + gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), + 149, + 43, + 21, + pass.get()); + + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE(this->RunPixelTest(&pass_list, + base::FilePath(FILE_PATH_LITERAL("green.png")), + FuzzyPixelOffByOneComparator(true))); +} + +TEST_F(VideoGLRendererPixelTest, SimpleYUVJRectGrey) { + gfx::Rect rect(this->device_viewport_size_); + + RenderPassId id(1, 1); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); + + SharedQuadState* shared_state = + CreateTestSharedQuadState(gfx::Transform(), rect, pass.get()); + + // Dark grey in JPEG color range (in MPEG, this is black). + CreateTestYUVVideoDrawQuad_Solid(shared_state, + media::VideoFrame::YV12J, + false, + gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), + 15, + 128, + 128, + pass.get()); + + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE( + this->RunPixelTest(&pass_list, + base::FilePath(FILE_PATH_LITERAL("dark_grey.png")), + FuzzyPixelOffByOneComparator(true))); +} + +TEST_F(VideoGLRendererPixelTest, SimpleYUVARect) { + gfx::Rect rect(this->device_viewport_size_); + + RenderPassId id(1, 1); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); + + SharedQuadState* shared_state = + CreateTestSharedQuadState(gfx::Transform(), rect, pass.get()); + + CreateTestYUVVideoDrawQuad_Striped(shared_state, + media::VideoFrame::YV12A, + false, + gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), + pass.get()); + + SolidColorDrawQuad* color_quad = + pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); + color_quad->SetNew(shared_state, rect, rect, SK_ColorWHITE, false); + + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + base::FilePath(FILE_PATH_LITERAL("yuv_stripes_alpha.png")), + FuzzyPixelOffByOneComparator(true))); +} + +TEST_F(VideoGLRendererPixelTest, FullyTransparentYUVARect) { + gfx::Rect rect(this->device_viewport_size_); + + RenderPassId id(1, 1); + scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect); + + SharedQuadState* shared_state = + CreateTestSharedQuadState(gfx::Transform(), rect, pass.get()); + + CreateTestYUVVideoDrawQuad_Striped(shared_state, + media::VideoFrame::YV12A, + true, + gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), + pass.get()); + + SolidColorDrawQuad* color_quad = + pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>(); + color_quad->SetNew(shared_state, rect, rect, SK_ColorBLACK, false); + + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE(this->RunPixelTest( + &pass_list, + base::FilePath(FILE_PATH_LITERAL("black.png")), + ExactPixelComparator(true))); +} + TYPED_TEST(RendererPixelTest, FastPassColorFilterAlpha) { gfx::Rect viewport_rect(this->device_viewport_size_);
diff --git a/cc/quads/list_container.cc b/cc/quads/list_container.cc index b1501c0..fd4e2f4 100644 --- a/cc/quads/list_container.cc +++ b/cc/quads/list_container.cc
@@ -87,8 +87,8 @@ size_(0), list_count_(0), last_list_(NULL) { - DCHECK_NE(0u, element_count); - AllocateNewList(element_count); + AllocateNewList(element_count > 0 ? element_count + : kDefaultNumElementTypesToReserve); } ~ListContainerCharAllocator() {} @@ -281,72 +281,72 @@ typename ListContainer<BaseElementType>::ConstReverseIterator ListContainer<BaseElementType>::rbegin() const { if (data_->IsEmpty()) - return ConstReverseIterator(data_.get(), 0, NULL); + return ConstReverseIterator(data_.get(), 0, NULL, 0); size_t last_id = data_->list_count() - 1; return ConstReverseIterator( - data_.get(), last_id, data_->InnerListById(last_id)->LastElement()); + data_.get(), last_id, data_->InnerListById(last_id)->LastElement(), 0); } template <typename BaseElementType> typename ListContainer<BaseElementType>::ConstReverseIterator ListContainer<BaseElementType>::rend() const { - return ConstReverseIterator(data_.get(), 0, NULL); + return ConstReverseIterator(data_.get(), 0, NULL, size()); } template <typename BaseElementType> typename ListContainer<BaseElementType>::ReverseIterator ListContainer<BaseElementType>::rbegin() { if (data_->IsEmpty()) - return ReverseIterator(data_.get(), 0, NULL); + return ReverseIterator(data_.get(), 0, NULL, 0); size_t last_id = data_->list_count() - 1; return ReverseIterator( - data_.get(), last_id, data_->InnerListById(last_id)->LastElement()); + data_.get(), last_id, data_->InnerListById(last_id)->LastElement(), 0); } template <typename BaseElementType> typename ListContainer<BaseElementType>::ReverseIterator ListContainer<BaseElementType>::rend() { - return ReverseIterator(data_.get(), 0, NULL); + return ReverseIterator(data_.get(), 0, NULL, size()); } template <typename BaseElementType> typename ListContainer<BaseElementType>::ConstIterator ListContainer<BaseElementType>::begin() const { if (data_->IsEmpty()) - return ConstIterator(data_.get(), 0, NULL); + return ConstIterator(data_.get(), 0, NULL, 0); - return ConstIterator(data_.get(), 0, data_->InnerListById(0)->Begin()); + return ConstIterator(data_.get(), 0, data_->InnerListById(0)->Begin(), 0); } template <typename BaseElementType> typename ListContainer<BaseElementType>::ConstIterator ListContainer<BaseElementType>::end() const { if (data_->IsEmpty()) - return ConstIterator(data_.get(), 0, NULL); + return ConstIterator(data_.get(), 0, NULL, size()); size_t last_id = data_->list_count() - 1; - return ConstIterator(data_.get(), last_id, NULL); + return ConstIterator(data_.get(), last_id, NULL, size()); } template <typename BaseElementType> typename ListContainer<BaseElementType>::Iterator ListContainer<BaseElementType>::begin() { if (data_->IsEmpty()) - return Iterator(data_.get(), 0, NULL); + return Iterator(data_.get(), 0, NULL, 0); - return Iterator(data_.get(), 0, data_->InnerListById(0)->Begin()); + return Iterator(data_.get(), 0, data_->InnerListById(0)->Begin(), 0); } template <typename BaseElementType> typename ListContainer<BaseElementType>::Iterator ListContainer<BaseElementType>::end() { if (data_->IsEmpty()) - return Iterator(data_.get(), 0, NULL); + return Iterator(data_.get(), 0, NULL, size()); size_t last_id = data_->list_count() - 1; - return Iterator(data_.get(), last_id, NULL); + return Iterator(data_.get(), last_id, NULL, size()); } template <typename BaseElementType> @@ -377,6 +377,7 @@ const BaseElementType* ListContainer<BaseElementType>::ElementAt( size_t index) const { DCHECK_LT(index, size()); + size_t original_index = index; size_t list_index; for (list_index = 0; list_index < data_->list_count(); ++list_index) { size_t current_size = data_->InnerListById(list_index)->size; @@ -386,12 +387,14 @@ } return &*ConstIterator(data_.get(), list_index, - data_->InnerListById(list_index)->ElementAt(index)); + data_->InnerListById(list_index)->ElementAt(index), + original_index); } template <typename BaseElementType> BaseElementType* ListContainer<BaseElementType>::ElementAt(size_t index) { DCHECK_LT(index, size()); + size_t original_index = index; size_t list_index; for (list_index = 0; list_index < data_->list_count(); ++list_index) { size_t current_size = data_->InnerListById(list_index)->size; @@ -401,7 +404,8 @@ } return &*Iterator(data_.get(), list_index, - data_->InnerListById(list_index)->ElementAt(index)); + data_->InnerListById(list_index)->ElementAt(index), + original_index); } template <typename BaseElementType> @@ -442,8 +446,10 @@ ListContainer<BaseElementType>::Iterator::Iterator( ListContainerCharAllocator* container, size_t vector_ind, - char* item_iter) - : PositionInListContainerCharAllocator(container, vector_ind, item_iter) { + char* item_iter, + size_t index) + : PositionInListContainerCharAllocator(container, vector_ind, item_iter), + index_(index) { } template <typename BaseElementType> @@ -474,23 +480,31 @@ ListContainer<BaseElementType>::Iterator:: operator++() { this->Increment(); + ++index_; return *this; } +template <typename BaseElementType> +size_t ListContainer<BaseElementType>::Iterator::index() const { + return index_; +} + // ListContainer::ConstIterator ///////////////////////////////////////////////// template <typename BaseElementType> ListContainer<BaseElementType>::ConstIterator::ConstIterator( const typename ListContainer<BaseElementType>::Iterator& other) - : PositionInListContainerCharAllocator(other) { + : PositionInListContainerCharAllocator(other), index_(other.index()) { } template <typename BaseElementType> ListContainer<BaseElementType>::ConstIterator::ConstIterator( ListContainerCharAllocator* container, size_t vector_ind, - char* item_iter) - : PositionInListContainerCharAllocator(container, vector_ind, item_iter) { + char* item_iter, + size_t index) + : PositionInListContainerCharAllocator(container, vector_ind, item_iter), + index_(index) { } template <typename BaseElementType> @@ -523,17 +537,25 @@ ListContainer<BaseElementType>::ConstIterator:: operator++() { this->Increment(); + ++index_; return *this; } +template <typename BaseElementType> +size_t ListContainer<BaseElementType>::ConstIterator::index() const { + return index_; +} + // ListContainer::ReverseIterator ///////////////////////////////////////////////// template <typename BaseElementType> ListContainer<BaseElementType>::ReverseIterator::ReverseIterator( ListContainerCharAllocator* container, size_t vector_ind, - char* item_iter) - : PositionInListContainerCharAllocator(container, vector_ind, item_iter) { + char* item_iter, + size_t index) + : PositionInListContainerCharAllocator(container, vector_ind, item_iter), + index_(index) { } template <typename BaseElementType> @@ -566,23 +588,31 @@ ListContainer<BaseElementType>::ReverseIterator:: operator++() { this->ReverseIncrement(); + ++index_; return *this; } +template <typename BaseElementType> +size_t ListContainer<BaseElementType>::ReverseIterator::index() const { + return index_; +} + // ListContainer::ConstReverseIterator ///////////////////////////////////////////////// template <typename BaseElementType> ListContainer<BaseElementType>::ConstReverseIterator::ConstReverseIterator( const typename ListContainer<BaseElementType>::ReverseIterator& other) - : PositionInListContainerCharAllocator(other) { + : PositionInListContainerCharAllocator(other), index_(other.index()) { } template <typename BaseElementType> ListContainer<BaseElementType>::ConstReverseIterator::ConstReverseIterator( ListContainerCharAllocator* container, size_t vector_ind, - char* item_iter) - : PositionInListContainerCharAllocator(container, vector_ind, item_iter) { + char* item_iter, + size_t index) + : PositionInListContainerCharAllocator(container, vector_ind, item_iter), + index_(index) { } template <typename BaseElementType> @@ -615,9 +645,15 @@ ListContainer<BaseElementType>::ConstReverseIterator:: operator++() { this->ReverseIncrement(); + ++index_; return *this; } +template <typename BaseElementType> +size_t ListContainer<BaseElementType>::ConstReverseIterator::index() const { + return index_; +} + template class ListContainer<SharedQuadState>; template class ListContainer<DrawQuad>;
diff --git a/cc/quads/list_container.h b/cc/quads/list_container.h index 6c559f6..c3c19b0 100644 --- a/cc/quads/list_container.h +++ b/cc/quads/list_container.h
@@ -33,8 +33,9 @@ // is used when there is no derived classes from BaseElementType we need to // worry about, and allocation size is just sizeof(BaseElementType). ListContainer(); - // This constructor reserves the requested memory up front so only a single - // allocation is needed. + // This constructor reserves the requested memory up front so only single + // allocation is needed. When num_of_elements_to_reserve_for is zero, use the + // default size. ListContainer(size_t max_size_for_derived_class, size_t num_of_elements_to_reserve_for); @@ -73,12 +74,22 @@ public: Iterator(ListContainerCharAllocator* container, size_t vector_ind, - char* item_iter); + char* item_iter, + size_t index); ~Iterator(); BaseElementType* operator->() const; BaseElementType& operator*() const; Iterator operator++(int unused_post_increment); Iterator operator++(); + + size_t index() const; + + private: + // This is used to track how many increment has happened since begin(). It + // is used to avoid double increment at places an index reference is + // needed. For iterator this means begin() corresponds to index 0 and end() + // corresponds to index |size|. + size_t index_; }; class CC_EXPORT ConstIterator : public PositionInListContainerCharAllocator { @@ -87,13 +98,23 @@ public: ConstIterator(ListContainerCharAllocator* container, size_t vector_ind, - char* item_iter); + char* item_iter, + size_t index); ConstIterator(const Iterator& other); // NOLINT ~ConstIterator(); const BaseElementType* operator->() const; const BaseElementType& operator*() const; ConstIterator operator++(int unused_post_increment); ConstIterator operator++(); + + size_t index() const; + + private: + // This is used to track how many increment has happened since begin(). It + // is used to avoid double increment at places an index reference is + // needed. For iterator this means begin() corresponds to index 0 and end() + // corresponds to index |size|. + size_t index_; }; class CC_EXPORT ReverseIterator @@ -103,12 +124,22 @@ public: ReverseIterator(ListContainerCharAllocator* container, size_t vector_ind, - char* item_iter); + char* item_iter, + size_t index); ~ReverseIterator(); BaseElementType* operator->() const; BaseElementType& operator*() const; ReverseIterator operator++(int unused_post_increment); ReverseIterator operator++(); + + size_t index() const; + + private: + // This is used to track how many increment has happened since rbegin(). It + // is used to avoid double increment at places an index reference is + // needed. For reverse iterator this means rbegin() corresponds to index 0 + // and rend() corresponds to index |size|. + size_t index_; }; class CC_EXPORT ConstReverseIterator @@ -118,13 +149,23 @@ public: ConstReverseIterator(ListContainerCharAllocator* container, size_t vector_ind, - char* item_iter); + char* item_iter, + size_t index); ConstReverseIterator(const ReverseIterator& other); // NOLINT ~ConstReverseIterator(); const BaseElementType* operator->() const; const BaseElementType& operator*() const; ConstReverseIterator operator++(int unused_post_increment); ConstReverseIterator operator++(); + + size_t index() const; + + private: + // This is used to track how many increment has happened since rbegin(). It + // is used to avoid double increment at places an index reference is + // needed. For reverse iterator this means rbegin() corresponds to index 0 + // and rend() corresponds to index |size|. + size_t index_; }; // When called, all raw pointers that have been handed out are no longer
diff --git a/cc/quads/list_container_unittest.cc b/cc/quads/list_container_unittest.cc index dd06877..cb79f7a 100644 --- a/cc/quads/list_container_unittest.cc +++ b/cc/quads/list_container_unittest.cc
@@ -525,5 +525,32 @@ } } +TEST(ListContainerTest, + SimpleIterationAndReverseIterationWithIndexSharedQuadState) { + ListContainer<SharedQuadState> list; + std::vector<SharedQuadState*> sqs_list; + size_t size = 10; + for (size_t i = 0; i < size; ++i) { + sqs_list.push_back(list.AllocateAndConstruct<SharedQuadState>()); + } + EXPECT_EQ(size, list.size()); + + size_t i = 0; + for (ListContainer<SharedQuadState>::Iterator iter = list.begin(); + iter != list.end(); + ++iter) { + EXPECT_EQ(i, iter.index()); + ++i; + } + + i = 0; + for (ListContainer<SharedQuadState>::ReverseIterator iter = list.rbegin(); + iter != list.rend(); + ++iter) { + EXPECT_EQ(i, iter.index()); + ++i; + } +} + } // namespace } // namespace cc
diff --git a/cc/quads/render_pass.cc b/cc/quads/render_pass.cc index e487c09..2cb72de 100644 --- a/cc/quads/render_pass.cc +++ b/cc/quads/render_pass.cc
@@ -4,6 +4,8 @@ #include "cc/quads/render_pass.h" +#include <algorithm> + #include "base/debug/trace_event_argument.h" #include "base/values.h" #include "cc/base/math_util.h" @@ -44,20 +46,36 @@ return make_scoped_ptr(new RenderPass(num_layers)); } +scoped_ptr<RenderPass> RenderPass::Create(size_t shared_quad_state_list_size, + size_t quad_list_size) { + return make_scoped_ptr( + new RenderPass(shared_quad_state_list_size, quad_list_size)); +} + RenderPass::RenderPass() : id(RenderPassId(-1, -1)), has_transparent_background(true), - quad_list(kDefaultNumQuadsToReserve) { - shared_quad_state_list.reserve(kDefaultNumSharedQuadStatesToReserve); + quad_list(kDefaultNumQuadsToReserve), + shared_quad_state_list(sizeof(SharedQuadState), + kDefaultNumSharedQuadStatesToReserve) { } +// Each layer usually produces one shared quad state, so the number of layers +// is a good hint for what to reserve here. RenderPass::RenderPass(size_t num_layers) : id(RenderPassId(-1, -1)), has_transparent_background(true), - quad_list(kDefaultNumQuadsToReserve) { - // Each layer usually produces one shared quad state, so the number of layers - // is a good hint for what to reserve here. - shared_quad_state_list.reserve(num_layers); + quad_list(kDefaultNumQuadsToReserve), + shared_quad_state_list(sizeof(SharedQuadState), num_layers) { +} + +RenderPass::RenderPass(size_t shared_quad_state_list_size, + size_t quad_list_size) + : id(RenderPassId(-1, -1)), + has_transparent_background(true), + quad_list(quad_list_size), + shared_quad_state_list(sizeof(SharedQuadState), + shared_quad_state_list_size) { } RenderPass::~RenderPass() { @@ -67,7 +85,8 @@ } scoped_ptr<RenderPass> RenderPass::Copy(RenderPassId new_id) const { - scoped_ptr<RenderPass> copy_pass(Create()); + scoped_ptr<RenderPass> copy_pass( + Create(shared_quad_state_list.size(), quad_list.size())); copy_pass->SetAll(new_id, output_rect, damage_rect, @@ -86,39 +105,39 @@ // you may have copy_requests present. DCHECK_EQ(source->copy_requests.size(), 0u); - scoped_ptr<RenderPass> copy_pass(Create()); + scoped_ptr<RenderPass> copy_pass(Create( + source->shared_quad_state_list.size(), source->quad_list.size())); copy_pass->SetAll(source->id, source->output_rect, source->damage_rect, source->transform_to_root_target, source->has_transparent_background); - for (size_t i = 0; i < source->shared_quad_state_list.size(); ++i) { + for (const auto& shared_quad_state : source->shared_quad_state_list) { SharedQuadState* copy_shared_quad_state = copy_pass->CreateAndAppendSharedQuadState(); - copy_shared_quad_state->CopyFrom(source->shared_quad_state_list[i]); + copy_shared_quad_state->CopyFrom(&shared_quad_state); } - size_t sqs_i = 0; - for (QuadList::Iterator iter = source->quad_list.begin(); - iter != source->quad_list.end(); - ++iter) { - while (iter->shared_quad_state != source->shared_quad_state_list[sqs_i]) { - ++sqs_i; - DCHECK_LT(sqs_i, source->shared_quad_state_list.size()); + SharedQuadStateList::Iterator sqs_iter = + source->shared_quad_state_list.begin(); + SharedQuadStateList::Iterator copy_sqs_iter = + copy_pass->shared_quad_state_list.begin(); + for (const auto& quad : source->quad_list) { + while (quad.shared_quad_state != &*sqs_iter) { + ++sqs_iter; + ++copy_sqs_iter; + DCHECK(sqs_iter != source->shared_quad_state_list.end()); } - DCHECK(iter->shared_quad_state == source->shared_quad_state_list[sqs_i]); + DCHECK(quad.shared_quad_state == &*sqs_iter); - DrawQuad* quad = &*iter; + SharedQuadState* copy_shared_quad_state = &*copy_sqs_iter; - if (quad->material == DrawQuad::RENDER_PASS) { + if (quad.material == DrawQuad::RENDER_PASS) { const RenderPassDrawQuad* pass_quad = - RenderPassDrawQuad::MaterialCast(quad); + RenderPassDrawQuad::MaterialCast(&quad); copy_pass->CopyFromAndAppendRenderPassDrawQuad( - pass_quad, - copy_pass->shared_quad_state_list[sqs_i], - pass_quad->render_pass_id); + pass_quad, copy_shared_quad_state, pass_quad->render_pass_id); } else { - copy_pass->CopyFromAndAppendDrawQuad( - quad, copy_pass->shared_quad_state_list[sqs_i]); + copy_pass->CopyFromAndAppendDrawQuad(&quad, copy_shared_quad_state); } } out->push_back(copy_pass.Pass()); @@ -175,19 +194,17 @@ value->SetInteger("copy_requests", copy_requests.size()); value->BeginArray("shared_quad_state_list"); - for (size_t i = 0; i < shared_quad_state_list.size(); ++i) { + for (const auto& shared_quad_state : shared_quad_state_list) { value->BeginDictionary(); - shared_quad_state_list[i]->AsValueInto(value); + shared_quad_state.AsValueInto(value); value->EndDictionary(); } value->EndArray(); value->BeginArray("quad_list"); - for (QuadList::ConstIterator iter = quad_list.begin(); - iter != quad_list.end(); - ++iter) { + for (const auto& quad : quad_list) { value->BeginDictionary(); - iter->AsValueInto(value); + quad.AsValueInto(value); value->EndDictionary(); } value->EndArray(); @@ -200,8 +217,7 @@ } SharedQuadState* RenderPass::CreateAndAppendSharedQuadState() { - shared_quad_state_list.push_back(make_scoped_ptr(new SharedQuadState)); - return shared_quad_state_list.back(); + return shared_quad_state_list.AllocateAndConstruct<SharedQuadState>(); } RenderPassDrawQuad* RenderPass::CopyFromAndAppendRenderPassDrawQuad(
diff --git a/cc/quads/render_pass.h b/cc/quads/render_pass.h index db42faf..65c712d 100644 --- a/cc/quads/render_pass.h +++ b/cc/quads/render_pass.h
@@ -47,7 +47,7 @@ inline ConstBackToFrontIterator BackToFrontEnd() const { return rend(); } }; -typedef ScopedPtrVector<SharedQuadState> SharedQuadStateList; +typedef ListContainer<SharedQuadState> SharedQuadStateList; class CC_EXPORT RenderPass { public: @@ -55,6 +55,8 @@ static scoped_ptr<RenderPass> Create(); static scoped_ptr<RenderPass> Create(size_t num_layers); + static scoped_ptr<RenderPass> Create(size_t shared_quad_state_list_size, + size_t quad_list_size); // A shallow copy of the render pass, which does not include its quads or copy // requests. @@ -117,6 +119,7 @@ protected: explicit RenderPass(size_t num_layers); RenderPass(); + RenderPass(size_t shared_quad_state_list_size, size_t quad_list_size); private: template <typename DrawQuadType>
diff --git a/cc/quads/yuv_video_draw_quad.h b/cc/quads/yuv_video_draw_quad.h index fa77562..930c821 100644 --- a/cc/quads/yuv_video_draw_quad.h +++ b/cc/quads/yuv_video_draw_quad.h
@@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" +#include "cc/layers/video_layer_impl.h" #include "cc/quads/draw_quad.h" namespace cc {
diff --git a/cc/surfaces/surface_aggregator.cc b/cc/surfaces/surface_aggregator.cc index 35107e8..ea547a0 100644 --- a/cc/surfaces/surface_aggregator.cc +++ b/cc/surfaces/surface_aggregator.cc
@@ -188,7 +188,9 @@ for (size_t j = 0; j < passes_to_copy; ++j) { const RenderPass& source = *referenced_passes[j]; - scoped_ptr<RenderPass> copy_pass(RenderPass::Create()); + size_t sqs_size = source.shared_quad_state_list.size(); + size_t dq_size = source.quad_list.size(); + scoped_ptr<RenderPass> copy_pass(RenderPass::Create(sqs_size, dq_size)); RenderPassId remapped_pass_id = RemapPassId(source.id, surface_id); @@ -284,29 +286,28 @@ SurfaceId surface_id) { const SharedQuadState* last_copied_source_shared_quad_state = NULL; - size_t sqs_i = 0; - for (QuadList::ConstIterator iter = source_quad_list.begin(); - iter != source_quad_list.end(); - ++iter) { - const DrawQuad* quad = &*iter; - while (quad->shared_quad_state != source_shared_quad_state_list[sqs_i]) { - ++sqs_i; - DCHECK_LT(sqs_i, source_shared_quad_state_list.size()); + SharedQuadStateList::ConstIterator sqs_iter = + source_shared_quad_state_list.begin(); + for (const auto& quad : source_quad_list) { + while (quad.shared_quad_state != &*sqs_iter) { + ++sqs_iter; + DCHECK(sqs_iter != source_shared_quad_state_list.end()); } - DCHECK_EQ(quad->shared_quad_state, source_shared_quad_state_list[sqs_i]); + DCHECK_EQ(quad.shared_quad_state, &*sqs_iter); - if (quad->material == DrawQuad::SURFACE_CONTENT) { - const SurfaceDrawQuad* surface_quad = SurfaceDrawQuad::MaterialCast(quad); + if (quad.material == DrawQuad::SURFACE_CONTENT) { + const SurfaceDrawQuad* surface_quad = + SurfaceDrawQuad::MaterialCast(&quad); HandleSurfaceQuad(surface_quad, dest_pass); } else { - if (quad->shared_quad_state != last_copied_source_shared_quad_state) { + if (quad.shared_quad_state != last_copied_source_shared_quad_state) { CopySharedQuadState( - quad->shared_quad_state, content_to_target_transform, dest_pass); - last_copied_source_shared_quad_state = quad->shared_quad_state; + quad.shared_quad_state, content_to_target_transform, dest_pass); + last_copied_source_shared_quad_state = quad.shared_quad_state; } - if (quad->material == DrawQuad::RENDER_PASS) { + if (quad.material == DrawQuad::RENDER_PASS) { const RenderPassDrawQuad* pass_quad = - RenderPassDrawQuad::MaterialCast(quad); + RenderPassDrawQuad::MaterialCast(&quad); RenderPassId original_pass_id = pass_quad->render_pass_id; RenderPassId remapped_pass_id = RemapPassId(original_pass_id, surface_id); @@ -317,7 +318,7 @@ remapped_pass_id); } else { dest_pass->CopyFromAndAppendDrawQuad( - quad, dest_pass->shared_quad_state_list.back()); + &quad, dest_pass->shared_quad_state_list.back()); } } } @@ -328,7 +329,9 @@ for (size_t i = 0; i < source_pass_list.size(); ++i) { const RenderPass& source = *source_pass_list[i]; - scoped_ptr<RenderPass> copy_pass(RenderPass::Create()); + size_t sqs_size = source.shared_quad_state_list.size(); + size_t dq_size = source.quad_list.size(); + scoped_ptr<RenderPass> copy_pass(RenderPass::Create(sqs_size, dq_size)); RenderPassId remapped_pass_id = RemapPassId(source.id, surface->surface_id());
diff --git a/cc/surfaces/surface_aggregator_test_helpers.cc b/cc/surfaces/surface_aggregator_test_helpers.cc index 8ecf89e..8afead1 100644 --- a/cc/surfaces/surface_aggregator_test_helpers.cc +++ b/cc/surfaces/surface_aggregator_test_helpers.cc
@@ -134,13 +134,11 @@ void TestPassMatchesExpectations(Pass expected_pass, const RenderPass* pass) { ASSERT_EQ(expected_pass.quad_count, pass->quad_list.size()); - size_t i = 0; for (QuadList::ConstIterator iter = pass->quad_list.begin(); iter != pass->quad_list.end(); ++iter) { - SCOPED_TRACE(base::StringPrintf("Quad number %" PRIuS, i)); - TestQuadMatchesExpectations(expected_pass.quads[i], &*iter); - ++i; + SCOPED_TRACE(base::StringPrintf("Quad number %" PRIuS, iter.index())); + TestQuadMatchesExpectations(expected_pass.quads[iter.index()], &*iter); } }
diff --git a/cc/surfaces/surface_aggregator_unittest.cc b/cc/surfaces/surface_aggregator_unittest.cc index 2811152..cde8b8f 100644 --- a/cc/surfaces/surface_aggregator_unittest.cc +++ b/cc/surfaces/surface_aggregator_unittest.cc
@@ -698,12 +698,11 @@ ASSERT_EQ(7u, aggregated_quad_list.size()); - size_t i = 0; for (QuadList::ConstIterator iter = aggregated_quad_list.begin(); iter != aggregated_quad_list.end(); ++iter) { - EXPECT_EQ(blend_modes[i], iter->shared_quad_state->blend_mode) << i; - ++i; + EXPECT_EQ(blend_modes[iter.index()], iter->shared_quad_state->blend_mode) + << iter.index(); } factory_.Destroy(child_one_surface_id); factory_.Destroy(child_two_surface_id); @@ -745,12 +744,12 @@ RenderPass* child_nonroot_pass = child_pass_list.at(0u); child_nonroot_pass->transform_to_root_target.Translate(8, 0); SharedQuadState* child_nonroot_pass_sqs = - child_nonroot_pass->shared_quad_state_list[0]; + child_nonroot_pass->shared_quad_state_list.front(); child_nonroot_pass_sqs->content_to_target_transform.Translate(5, 0); RenderPass* child_root_pass = child_pass_list.at(1u); SharedQuadState* child_root_pass_sqs = - child_root_pass->shared_quad_state_list[0]; + child_root_pass->shared_quad_state_list.front(); child_root_pass_sqs->content_to_target_transform.Translate(8, 0); child_root_pass_sqs->is_clipped = true; child_root_pass_sqs->clip_rect = gfx::Rect(0, 0, 5, 5); @@ -774,10 +773,10 @@ arraysize(root_passes)); root_pass_list.at(0) - ->shared_quad_state_list[0] + ->shared_quad_state_list.front() ->content_to_target_transform.Translate(0, 7); root_pass_list.at(0) - ->shared_quad_state_list[1] + ->shared_quad_state_list.ElementAt(1) ->content_to_target_transform.Translate(0, 10); scoped_ptr<DelegatedFrameData> root_frame_data(new DelegatedFrameData); @@ -838,24 +837,23 @@ // and the child surface draw quad's translation (8, 0). expected_root_pass_quad_transforms[1].Translate(8, 10); - size_t i = 0; for (QuadList::Iterator iter = aggregated_pass_list[1]->quad_list.begin(); iter != aggregated_pass_list[1]->quad_list.end(); ++iter) { - EXPECT_EQ(expected_root_pass_quad_transforms[i].ToString(), + EXPECT_EQ(expected_root_pass_quad_transforms[iter.index()].ToString(), iter->quadTransform().ToString()) - << i; - i++; + << iter.index(); } - EXPECT_EQ(true, - aggregated_pass_list[1]->shared_quad_state_list[1]->is_clipped); + EXPECT_TRUE( + aggregated_pass_list[1]->shared_quad_state_list.ElementAt(1)->is_clipped); // The second quad in the root pass is aggregated from the child, so its // clip rect must be transformed by the child's translation. - EXPECT_EQ( - gfx::Rect(0, 10, 5, 5).ToString(), - aggregated_pass_list[1]->shared_quad_state_list[1]->clip_rect.ToString()); + EXPECT_EQ(gfx::Rect(0, 10, 5, 5).ToString(), + aggregated_pass_list[1] + ->shared_quad_state_list.ElementAt(1) + ->clip_rect.ToString()); factory_.Destroy(child_surface_id); } @@ -877,7 +875,7 @@ RenderPass* child_root_pass = child_pass_list.at(0u); SharedQuadState* child_root_pass_sqs = - child_root_pass->shared_quad_state_list[0]; + child_root_pass->shared_quad_state_list.front(); child_root_pass_sqs->content_to_target_transform.Translate(8, 0); scoped_ptr<DelegatedFrameData> child_frame_data(new DelegatedFrameData); @@ -898,7 +896,7 @@ arraysize(root_passes)); root_pass_list.at(0) - ->shared_quad_state_list[0] + ->shared_quad_state_list.front() ->content_to_target_transform.Translate(0, 10); root_pass_list.at(0)->damage_rect = gfx::Rect(5, 5, 10, 10); @@ -934,7 +932,7 @@ RenderPass* child_root_pass = child_pass_list.at(0u); SharedQuadState* child_root_pass_sqs = - child_root_pass->shared_quad_state_list[0]; + child_root_pass->shared_quad_state_list.front(); child_root_pass_sqs->content_to_target_transform.Translate(8, 0); child_root_pass->damage_rect = gfx::Rect(10, 10, 10, 10); @@ -973,7 +971,7 @@ arraysize(root_passes)); root_pass_list.at(0) - ->shared_quad_state_list[0] + ->shared_quad_state_list.front() ->content_to_target_transform.Translate(0, 10); root_pass_list.at(0)->damage_rect = gfx::Rect(0, 0, 1, 1); @@ -994,7 +992,7 @@ arraysize(root_passes)); root_pass_list.at(0) - ->shared_quad_state_list[0] + ->shared_quad_state_list.front() ->content_to_target_transform.Translate(0, 10); root_pass_list.at(0)->damage_rect = gfx::Rect(1, 1, 1, 1);
diff --git a/cc/test/layer_test_common.cc b/cc/test/layer_test_common.cc index 2870887..9ca2aa2 100644 --- a/cc/test/layer_test_common.cc +++ b/cc/test/layer_test_common.cc
@@ -40,7 +40,6 @@ const gfx::Rect& rect) { Region remaining = rect; - size_t i = 0; for (QuadList::ConstIterator iter = quads.begin(); iter != quads.end(); ++iter) { const DrawQuad* quad = &*iter; @@ -53,15 +52,13 @@ gfx::Rect quad_rect = gfx::ToEnclosingRect(quad_rectf); - EXPECT_TRUE(rect.Contains(quad_rect)) << quad_string << i + EXPECT_TRUE(rect.Contains(quad_rect)) << quad_string << iter.index() << " rect: " << rect.ToString() << " quad: " << quad_rect.ToString(); EXPECT_TRUE(remaining.Contains(quad_rect)) - << quad_string << i << " remaining: " << remaining.ToString() + << quad_string << iter.index() << " remaining: " << remaining.ToString() << " quad: " << quad_rect.ToString(); remaining.Subtract(quad_rect); - - ++i; } EXPECT_TRUE(remaining.IsEmpty());
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 981e670..cb52be7 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -26,6 +26,7 @@ #include "cc/layers/solid_color_scrollbar_layer_impl.h" #include "cc/layers/texture_layer_impl.h" #include "cc/layers/tiled_layer_impl.h" +#include "cc/layers/video_layer_impl.h" #include "cc/output/begin_frame_args.h" #include "cc/output/compositor_frame_ack.h" #include "cc/output/compositor_frame_metadata.h" @@ -46,6 +47,7 @@ #include "cc/test/fake_picture_pile_impl.h" #include "cc/test/fake_proxy.h" #include "cc/test/fake_rendering_stats_instrumentation.h" +#include "cc/test/fake_video_frame_provider.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_test_common.h" #include "cc/test/render_pass_test_common.h" @@ -53,6 +55,7 @@ #include "cc/test/test_web_graphics_context_3d.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" +#include "media/base/media.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkMallocPixelRef.h" @@ -66,6 +69,7 @@ using ::testing::AnyNumber; using ::testing::AtLeast; using ::testing::_; +using media::VideoFrame; namespace cc { namespace { @@ -89,6 +93,7 @@ reduce_memory_result_(true), current_limit_bytes_(0), current_priority_cutoff_value_(0) { + media::InitializeMediaLibraryForTesting(); } LayerTreeSettings DefaultSettings() { @@ -4821,6 +4826,18 @@ LayerImpl::Create(host_impl_->active_tree(), 1); root_layer->SetBounds(gfx::Size(10, 10)); + scoped_refptr<VideoFrame> softwareFrame = + media::VideoFrame::CreateColorFrame( + gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta()); + FakeVideoFrameProvider provider; + provider.set_frame(softwareFrame); + scoped_ptr<VideoLayerImpl> video_layer = VideoLayerImpl::Create( + host_impl_->active_tree(), 4, &provider, media::VIDEO_ROTATION_0); + video_layer->SetBounds(gfx::Size(10, 10)); + video_layer->SetContentBounds(gfx::Size(10, 10)); + video_layer->SetDrawsContent(true); + root_layer->AddChild(video_layer.Pass()); + scoped_ptr<IOSurfaceLayerImpl> io_surface_layer = IOSurfaceLayerImpl::Create(host_impl_->active_tree(), 5); io_surface_layer->SetBounds(gfx::Size(10, 10)); @@ -5856,6 +5873,16 @@ scoped_ptr<SolidColorLayerImpl> root_layer = SolidColorLayerImpl::Create(host_impl_->active_tree(), 1); + // VideoLayerImpl will not be drawn. + FakeVideoFrameProvider provider; + scoped_ptr<VideoLayerImpl> video_layer = VideoLayerImpl::Create( + host_impl_->active_tree(), 2, &provider, media::VIDEO_ROTATION_0); + video_layer->SetBounds(gfx::Size(10, 10)); + video_layer->SetContentBounds(gfx::Size(10, 10)); + video_layer->SetDrawsContent(true); + root_layer->AddChild(video_layer.Pass()); + SetupRootLayerImpl(root_layer.Pass()); + LayerTreeHostImpl::FrameData frame; EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); host_impl_->DrawLayers(&frame, gfx::FrameTime::Now());
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index 054449f..4a3fa7e 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc
@@ -18,6 +18,7 @@ #include "cc/layers/painted_scrollbar_layer.h" #include "cc/layers/picture_layer.h" #include "cc/layers/solid_color_layer.h" +#include "cc/layers/video_layer.h" #include "cc/output/begin_frame_args.h" #include "cc/output/compositor_frame_ack.h" #include "cc/output/copy_output_request.h" @@ -38,6 +39,7 @@ #include "cc/test/fake_picture_layer_impl.h" #include "cc/test/fake_proxy.h" #include "cc/test/fake_scoped_ui_resource.h" +#include "cc/test/fake_video_frame_provider.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_tree_test.h" #include "cc/test/test_shared_bitmap_manager.h" @@ -3971,6 +3973,28 @@ int num_draws_; }; +// VideoLayer must support being invalidated and then passing that along +// to the compositor thread, even though no resources are updated in +// response to that invalidation. +class LayerTreeHostTestVideoLayerInvalidate : public LayerInvalidateCausesDraw { + public: + virtual void SetupTree() OVERRIDE { + LayerTreeHostTest::SetupTree(); + scoped_refptr<VideoLayer> video_layer = + VideoLayer::Create(&provider_, media::VIDEO_ROTATION_0); + video_layer->SetBounds(gfx::Size(10, 10)); + video_layer->SetIsDrawable(true); + layer_tree_host()->root_layer()->AddChild(video_layer); + + invalidate_layer_ = video_layer; + } + + private: + FakeVideoFrameProvider provider_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestVideoLayerInvalidate); + // IOSurfaceLayer must support being invalidated and then passing that along // to the compositor thread, even though no resources are updated in // response to that invalidation.
diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc index 8bfb08a..07e12a2 100644 --- a/cc/trees/layer_tree_host_unittest_context.cc +++ b/cc/trees/layer_tree_host_unittest_context.cc
@@ -15,6 +15,8 @@ #include "cc/layers/picture_layer.h" #include "cc/layers/texture_layer.h" #include "cc/layers/texture_layer_impl.h" +#include "cc/layers/video_layer.h" +#include "cc/layers/video_layer_impl.h" #include "cc/output/filter_operations.h" #include "cc/resources/single_release_callback.h" #include "cc/test/fake_content_layer.h" @@ -30,6 +32,7 @@ #include "cc/test/fake_picture_layer_impl.h" #include "cc/test/fake_scoped_ui_resource.h" #include "cc/test/fake_scrollbar.h" +#include "cc/test/fake_video_frame_provider.h" #include "cc/test/layer_tree_test.h" #include "cc/test/render_pass_test_common.h" #include "cc/test/test_context_provider.h" @@ -39,6 +42,9 @@ #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" #include "gpu/GLES2/gl2extchromium.h" +#include "media/base/media.h" + +using media::VideoFrame; namespace cc { namespace { @@ -59,6 +65,7 @@ context_should_support_io_surface_(false), fallback_context_works_(false), async_output_surface_creation_(false) { + media::InitializeMediaLibraryForTesting(); } void LoseContext() { @@ -937,6 +944,49 @@ layer_with_mask->SetMaskLayer(mask.get()); root->AddChild(layer_with_mask); + scoped_refptr<VideoLayer> video_color = + VideoLayer::Create(&color_frame_provider_, media::VIDEO_ROTATION_0); + video_color->SetBounds(gfx::Size(10, 10)); + video_color->SetIsDrawable(true); + root->AddChild(video_color); + + scoped_refptr<VideoLayer> video_hw = + VideoLayer::Create(&hw_frame_provider_, media::VIDEO_ROTATION_0); + video_hw->SetBounds(gfx::Size(10, 10)); + video_hw->SetIsDrawable(true); + root->AddChild(video_hw); + + scoped_refptr<VideoLayer> video_scaled_hw = + VideoLayer::Create(&scaled_hw_frame_provider_, media::VIDEO_ROTATION_0); + video_scaled_hw->SetBounds(gfx::Size(10, 10)); + video_scaled_hw->SetIsDrawable(true); + root->AddChild(video_scaled_hw); + + color_video_frame_ = VideoFrame::CreateColorFrame( + gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta()); + hw_video_frame_ = + VideoFrame::WrapNativeTexture(make_scoped_ptr(new gpu::MailboxHolder( + mailbox, GL_TEXTURE_2D, sync_point)), + media::VideoFrame::ReleaseMailboxCB(), + gfx::Size(4, 4), + gfx::Rect(0, 0, 4, 4), + gfx::Size(4, 4), + base::TimeDelta(), + VideoFrame::ReadPixelsCB()); + scaled_hw_video_frame_ = + VideoFrame::WrapNativeTexture(make_scoped_ptr(new gpu::MailboxHolder( + mailbox, GL_TEXTURE_2D, sync_point)), + media::VideoFrame::ReleaseMailboxCB(), + gfx::Size(4, 4), + gfx::Rect(0, 0, 3, 2), + gfx::Size(4, 4), + base::TimeDelta(), + VideoFrame::ReadPixelsCB()); + + color_frame_provider_.set_frame(color_video_frame_); + hw_frame_provider_.set_frame(hw_video_frame_); + scaled_hw_frame_provider_.set_frame(scaled_hw_video_frame_); + if (!delegating_renderer()) { // TODO(danakj): IOSurface layer can not be transported. crbug.com/239335 scoped_refptr<IOSurfaceLayer> io_surface = IOSurfaceLayer::Create(); @@ -966,6 +1016,14 @@ virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { LayerTreeHostContextTest::CommitCompleteOnThread(host_impl); + + if (host_impl->active_tree()->source_frame_number() == 3) { + // On the third commit we're recovering from context loss. Hardware + // video frames should not be reused by the VideoFrameProvider, but + // software frames can be. + hw_frame_provider_.set_frame(NULL); + scaled_hw_frame_provider_.set_frame(NULL); + } } virtual DrawResult PrepareToDrawOnThread( @@ -1016,6 +1074,14 @@ scoped_refptr<DelegatedFrameResourceCollection> delegated_resource_collection_; scoped_refptr<DelegatedFrameProvider> delegated_frame_provider_; + + scoped_refptr<VideoFrame> color_video_frame_; + scoped_refptr<VideoFrame> hw_video_frame_; + scoped_refptr<VideoFrame> scaled_hw_video_frame_; + + FakeVideoFrameProvider color_frame_provider_; + FakeVideoFrameProvider hw_frame_provider_; + FakeVideoFrameProvider scaled_hw_frame_provider_; }; SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostContextTestDontUseLostResources);
diff --git a/crypto/OWNERS b/crypto/OWNERS index 6f5cc98..bde32a0 100644 --- a/crypto/OWNERS +++ b/crypto/OWNERS
@@ -1,4 +1,5 @@ agl@chromium.org +davidben@chromium.org rsleevi@chromium.org rvargas@chromium.org wtc@chromium.org
diff --git a/gin/BUILD.gn b/gin/BUILD.gn new file mode 100644 index 0000000..b352c3a --- /dev/null +++ b/gin/BUILD.gn
@@ -0,0 +1,129 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +component("gin") { + sources = [ + "arguments.cc", + "arguments.h", + "array_buffer.cc", + "array_buffer.h", + "context_holder.cc", + "converter.cc", + "converter.h", + "debug_impl.cc", + "debug_impl.h", + "dictionary.cc", + "dictionary.h", + "function_template.cc", + "function_template.h", + "gin_export.h", + "handle.h", + "interceptor.cc", + "interceptor.h", + "isolate_holder.cc", + "modules/console.cc", + "modules/console.h", + "modules/file_module_provider.cc", + "modules/file_module_provider.h", + "modules/module_registry.cc", + "modules/module_registry.h", + "modules/module_registry_observer.h", + "modules/module_runner_delegate.cc", + "modules/module_runner_delegate.h", + "modules/timer.cc", + "modules/timer.h", + "object_template_builder.cc", + "object_template_builder.h", + "per_context_data.cc", + "per_context_data.h", + "per_isolate_data.cc", + "per_isolate_data.h", + "public/context_holder.h", + "public/debug.h", + "public/gin_embedders.h", + "public/isolate_holder.h", + "public/v8_platform.h", + "public/wrapper_info.h", + "runner.cc", + "runner.h", + "run_microtasks_observer.cc", + "run_microtasks_observer.h", + "shell_runner.cc", + "shell_runner.h", + "try_catch.cc", + "try_catch.h", + "v8_platform.cc", + "wrappable.cc", + "wrappable.h", + "wrapper_info.cc", + ] + + defines = [ "GIN_IMPLEMENTATION" ] + + public_deps = [ + "//base", + "//v8", + ] + deps = [ + "//base/third_party/dynamic_annotations", + ] +} + +executable("gin_shell") { + sources = [ + "shell/gin_main.cc", + ] + + deps = [ + ":gin", + "//base", + "//base:i18n", + "//v8", + ] +} + +source_set("gin_test") { + testonly = true + sources = [ + "test/file.cc", + "test/file.h", + "test/file_runner.cc", + "test/file_runner.h", + "test/gc.cc", + "test/gc.h", + "test/gtest.cc", + "test/gtest.h", + "test/v8_test.cc", + "test/v8_test.h", + ] + + public_deps = [ + ":gin", + "//testing/gtest", + ] + deps = [ + "//v8", + ] +} + +test("gin_unittests") { + sources = [ + "converter_unittest.cc", + "interceptor_unittest.cc", + "modules/module_registry_unittest.cc", + "modules/timer_unittest.cc", + "per_context_data_unittest.cc", + "shell_runner_unittest.cc", + "shell/gin_shell_unittest.cc", + "test/run_all_unittests.cc", + "test/run_js_tests.cc", + "wrappable_unittest.cc", + ] + + deps = [ + ":gin_test", + "//base/test:test_support", + "//v8", + ] +}
diff --git a/gin/DEPS b/gin/DEPS new file mode 100644 index 0000000..4e3f30a --- /dev/null +++ b/gin/DEPS
@@ -0,0 +1,4 @@ +include_rules = [ + "+base", + "+v8", +]
diff --git a/gin/OWNERS b/gin/OWNERS new file mode 100644 index 0000000..35dde41 --- /dev/null +++ b/gin/OWNERS
@@ -0,0 +1,3 @@ +aa@chromium.org +abarth@chromium.org +jochen@chromium.org
diff --git a/gin/README b/gin/README new file mode 100644 index 0000000..fc2d92e --- /dev/null +++ b/gin/README
@@ -0,0 +1,24 @@ +Gin - Lightweight bindings for V8 +================================= + +This directory contains Gin, a set of utilities to make working with V8 easier. + +Here are some of the key bits: + +* converter.h: Templatized JS<->C++ conversion routines for many common C++ + types. You can define your own by specializing Converter. + +* function_template.h: Create JavaScript functions that dispatch to any C++ + function, member function pointer, or base::Callback. + +* object_template_builder.h: A handy utility for creation of v8::ObjectTemplate. + +* wrappable.h: Base class for C++ classes that want to be owned by the V8 GC. + Wrappable objects are automatically deleted when GC discovers that nothing in + the V8 heap refers to them. This is also an easy way to expose C++ objects to + JavaScript. + +* runner.h: Create script contexts and run code in them. + +* module_runner_delegate.h: A delegate for runner that implements a subset of + the AMD module specification. Also see modules/ with some example modules.
diff --git a/gin/arguments.cc b/gin/arguments.cc new file mode 100644 index 0000000..6703fc2 --- /dev/null +++ b/gin/arguments.cc
@@ -0,0 +1,52 @@ +// 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. + +#include "gin/arguments.h" + +#include "base/strings/stringprintf.h" +#include "gin/converter.h" + +namespace gin { + +Arguments::Arguments() + : isolate_(NULL), + info_(NULL), + next_(0), + insufficient_arguments_(false) { +} + +Arguments::Arguments(const v8::FunctionCallbackInfo<v8::Value>& info) + : isolate_(info.GetIsolate()), + info_(&info), + next_(0), + insufficient_arguments_(false) { +} + +Arguments::~Arguments() { +} + +v8::Handle<v8::Value> Arguments::PeekNext() const { + if (next_ >= info_->Length()) + return v8::Handle<v8::Value>(); + return (*info_)[next_]; +} + +void Arguments::ThrowError() const { + if (insufficient_arguments_) + return ThrowTypeError("Insufficient number of arguments."); + + ThrowTypeError(base::StringPrintf( + "Error processing argument %d.", next_ - 1)); +} + +void Arguments::ThrowTypeError(const std::string& message) const { + isolate_->ThrowException(v8::Exception::TypeError( + StringToV8(isolate_, message))); +} + +bool Arguments::IsConstructCall() const { + return info_->IsConstructCall(); +} + +} // namespace gin
diff --git a/gin/arguments.h b/gin/arguments.h new file mode 100644 index 0000000..96a3701 --- /dev/null +++ b/gin/arguments.h
@@ -0,0 +1,95 @@ +// 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. + +#ifndef GIN_ARGUMENTS_H_ +#define GIN_ARGUMENTS_H_ + +#include "base/basictypes.h" +#include "gin/converter.h" +#include "gin/gin_export.h" + +namespace gin { + +// Arguments is a wrapper around v8::FunctionCallbackInfo that integrates +// with Converter to make it easier to marshall arguments and return values +// between V8 and C++. +class GIN_EXPORT Arguments { + public: + Arguments(); + explicit Arguments(const v8::FunctionCallbackInfo<v8::Value>& info); + ~Arguments(); + + template<typename T> + bool GetHolder(T* out) { + return ConvertFromV8(isolate_, info_->Holder(), out); + } + + template<typename T> + bool GetData(T* out) { + return ConvertFromV8(isolate_, info_->Data(), out); + } + + template<typename T> + bool GetNext(T* out) { + if (next_ >= info_->Length()) { + insufficient_arguments_ = true; + return false; + } + v8::Handle<v8::Value> val = (*info_)[next_++]; + return ConvertFromV8(isolate_, val, out); + } + + template<typename T> + bool GetRemaining(std::vector<T>* out) { + if (next_ >= info_->Length()) { + insufficient_arguments_ = true; + return false; + } + int remaining = info_->Length() - next_; + out->resize(remaining); + for (int i = 0; i < remaining; ++i) { + v8::Handle<v8::Value> val = (*info_)[next_++]; + if (!ConvertFromV8(isolate_, val, &out->at(i))) + return false; + } + return true; + } + + bool Skip() { + if (next_ >= info_->Length()) + return false; + next_++; + return true; + } + + int Length() const { + return info_->Length(); + } + + template<typename T> + void Return(T val) { + info_->GetReturnValue().Set(ConvertToV8(isolate_, val)); + } + + v8::Handle<v8::Value> PeekNext() const; + + void ThrowError() const; + void ThrowTypeError(const std::string& message) const; + + v8::Isolate* isolate() const { return isolate_; } + + // Allows the function handler to distinguish between normal invocation + // and object construction. + bool IsConstructCall() const; + + private: + v8::Isolate* isolate_; + const v8::FunctionCallbackInfo<v8::Value>* info_; + int next_; + bool insufficient_arguments_; +}; + +} // namespace gin + +#endif // GIN_ARGUMENTS_H_
diff --git a/gin/array_buffer.cc b/gin/array_buffer.cc new file mode 100644 index 0000000..b777402 --- /dev/null +++ b/gin/array_buffer.cc
@@ -0,0 +1,197 @@ +// 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. + +#include <stdlib.h> + +#include "base/logging.h" +#include "gin/array_buffer.h" +#include "gin/per_isolate_data.h" + +namespace gin { + +namespace { + +gin::WrapperInfo g_array_buffer_wrapper_info = {gin::kEmbedderNativeGin}; + +} // namespace + +COMPILE_ASSERT(V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT == 2, + array_buffers_must_have_two_internal_fields); + +// ArrayBufferAllocator ------------------------------------------------------- + +void* ArrayBufferAllocator::Allocate(size_t length) { + return calloc(1, length); +} + +void* ArrayBufferAllocator::AllocateUninitialized(size_t length) { + return malloc(length); +} + +void ArrayBufferAllocator::Free(void* data, size_t length) { + free(data); +} + +ArrayBufferAllocator* ArrayBufferAllocator::SharedInstance() { + static ArrayBufferAllocator* instance = new ArrayBufferAllocator(); + return instance; +} + +// ArrayBuffer::Private ------------------------------------------------------- + +// This class exists to solve a tricky lifetime problem. The V8 API doesn't +// want to expose a direct view into the memory behind an array buffer because +// V8 might deallocate that memory during garbage collection. Instead, the V8 +// API forces us to externalize the buffer and take ownership of the memory. +// In order to know when to free the memory, we need to figure out both when +// we're done with it and when V8 is done with it. +// +// To determine whether we're done with the memory, every view we have into +// the array buffer takes a reference to the ArrayBuffer::Private object that +// actually owns the memory. To determine when V8 is done with the memory, we +// open a weak handle to the ArrayBuffer object. When we receive the weak +// callback, we know the object is about to be garbage collected and we can +// drop V8's implied reference to the memory. +// +// The final subtlety is that we need every ArrayBuffer into the same array +// buffer to AddRef the same ArrayBuffer::Private. To make that work, we store +// a pointer to the ArrayBuffer::Private object in an internal field of the +// ArrayBuffer object. +// +class ArrayBuffer::Private : public base::RefCounted<ArrayBuffer::Private> { + public: + static scoped_refptr<Private> From(v8::Isolate* isolate, + v8::Handle<v8::ArrayBuffer> array); + + void* buffer() const { return buffer_; } + size_t length() const { return length_; } + + private: + friend class base::RefCounted<Private>; + + Private(v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array); + ~Private(); + + static void WeakCallback( + const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data); + + v8::Persistent<v8::ArrayBuffer> array_buffer_; + scoped_refptr<Private> self_reference_; + v8::Isolate* isolate_; + void* buffer_; + size_t length_; +}; + +scoped_refptr<ArrayBuffer::Private> ArrayBuffer::Private::From( + v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> array) { + if (array->IsExternal()) { + CHECK_EQ(WrapperInfo::From(v8::Handle<v8::Object>::Cast(array)), + &g_array_buffer_wrapper_info) + << "Cannot mix blink and gin ArrayBuffers"; + return make_scoped_refptr(static_cast<Private*>( + array->GetAlignedPointerFromInternalField(kEncodedValueIndex))); + } + return make_scoped_refptr(new Private(isolate, array)); +} + +ArrayBuffer::Private::Private(v8::Isolate* isolate, + v8::Handle<v8::ArrayBuffer> array) + : array_buffer_(isolate, array), isolate_(isolate) { + // Take ownership of the array buffer. + CHECK(!array->IsExternal()); + v8::ArrayBuffer::Contents contents = array->Externalize(); + buffer_ = contents.Data(); + length_ = contents.ByteLength(); + + array->SetAlignedPointerInInternalField(kWrapperInfoIndex, + &g_array_buffer_wrapper_info); + array->SetAlignedPointerInInternalField(kEncodedValueIndex, this); + + self_reference_ = this; // Cleared in WeakCallback. + array_buffer_.SetWeak(this, WeakCallback); +} + +ArrayBuffer::Private::~Private() { + PerIsolateData::From(isolate_)->allocator()->Free(buffer_, length_); +} + +void ArrayBuffer::Private::WeakCallback( + const v8::WeakCallbackData<v8::ArrayBuffer, Private>& data) { + Private* parameter = data.GetParameter(); + parameter->array_buffer_.Reset(); + parameter->self_reference_ = NULL; +} + +// ArrayBuffer ---------------------------------------------------------------- + +ArrayBuffer::ArrayBuffer() + : bytes_(0), + num_bytes_(0) { +} + +ArrayBuffer::ArrayBuffer(v8::Isolate* isolate, + v8::Handle<v8::ArrayBuffer> array) { + private_ = ArrayBuffer::Private::From(isolate, array); + bytes_ = private_->buffer(); + num_bytes_ = private_->length(); +} + +ArrayBuffer::~ArrayBuffer() { +} + +ArrayBuffer& ArrayBuffer::operator=(const ArrayBuffer& other) { + private_ = other.private_; + bytes_ = other.bytes_; + num_bytes_ = other.num_bytes_; + return *this; +} + +// Converter<ArrayBuffer> ----------------------------------------------------- + +bool Converter<ArrayBuffer>::FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + ArrayBuffer* out) { + if (!val->IsArrayBuffer()) + return false; + *out = ArrayBuffer(isolate, v8::Handle<v8::ArrayBuffer>::Cast(val)); + return true; +} + +// ArrayBufferView ------------------------------------------------------------ + +ArrayBufferView::ArrayBufferView() + : offset_(0), + num_bytes_(0) { +} + +ArrayBufferView::ArrayBufferView(v8::Isolate* isolate, + v8::Handle<v8::ArrayBufferView> view) + : array_buffer_(isolate, view->Buffer()), + offset_(view->ByteOffset()), + num_bytes_(view->ByteLength()) { +} + +ArrayBufferView::~ArrayBufferView() { +} + +ArrayBufferView& ArrayBufferView::operator=(const ArrayBufferView& other) { + array_buffer_ = other.array_buffer_; + offset_ = other.offset_; + num_bytes_ = other.num_bytes_; + return *this; +} + + +// Converter<ArrayBufferView> ------------------------------------------------- + +bool Converter<ArrayBufferView>::FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + ArrayBufferView* out) { + if (!val->IsArrayBufferView()) + return false; + *out = ArrayBufferView(isolate, v8::Handle<v8::ArrayBufferView>::Cast(val)); + return true; +} + +} // namespace gin
diff --git a/gin/array_buffer.h b/gin/array_buffer.h new file mode 100644 index 0000000..048a35f --- /dev/null +++ b/gin/array_buffer.h
@@ -0,0 +1,80 @@ +// 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. + +#ifndef GIN_ARRAY_BUFFER_H_ +#define GIN_ARRAY_BUFFER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "gin/converter.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { + public: + virtual void* Allocate(size_t length) override; + virtual void* AllocateUninitialized(size_t length) override; + virtual void Free(void* data, size_t length) override; + + GIN_EXPORT static ArrayBufferAllocator* SharedInstance(); +}; + +class GIN_EXPORT ArrayBuffer { + public: + ArrayBuffer(); + ArrayBuffer(v8::Isolate* isolate, v8::Handle<v8::ArrayBuffer> buffer); + ~ArrayBuffer(); + ArrayBuffer& operator=(const ArrayBuffer& other); + + void* bytes() const { return bytes_; } + size_t num_bytes() const { return num_bytes_; } + + private: + class Private; + + scoped_refptr<Private> private_; + void* bytes_; + size_t num_bytes_; + + DISALLOW_COPY(ArrayBuffer); +}; + +template<> +struct GIN_EXPORT Converter<ArrayBuffer> { + static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, + ArrayBuffer* out); +}; + +class GIN_EXPORT ArrayBufferView { + public: + ArrayBufferView(); + ArrayBufferView(v8::Isolate* isolate, v8::Handle<v8::ArrayBufferView> view); + ~ArrayBufferView(); + ArrayBufferView& operator=(const ArrayBufferView& other); + + void* bytes() const { + return static_cast<uint8_t*>(array_buffer_.bytes()) + offset_; + } + size_t num_bytes() const { return num_bytes_; } + + private: + ArrayBuffer array_buffer_; + size_t offset_; + size_t num_bytes_; + + DISALLOW_COPY(ArrayBufferView); +}; + +template<> +struct GIN_EXPORT Converter<ArrayBufferView> { + static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, + ArrayBufferView* out); +}; + +} // namespace gin + +#endif // GIN_ARRAY_BUFFER_H_
diff --git a/gin/context_holder.cc b/gin/context_holder.cc new file mode 100644 index 0000000..241b256 --- /dev/null +++ b/gin/context_holder.cc
@@ -0,0 +1,27 @@ +// 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. + +#include "gin/public/context_holder.h" + +#include "base/logging.h" +#include "gin/per_context_data.h" + +namespace gin { + +ContextHolder::ContextHolder(v8::Isolate* isolate) + : isolate_(isolate) { +} + +ContextHolder::~ContextHolder() { + // PerContextData needs to be destroyed before the context. + data_.reset(); +} + +void ContextHolder::SetContext(v8::Handle<v8::Context> context) { + DCHECK(context_.IsEmpty()); + context_.Reset(isolate_, context); + data_.reset(new PerContextData(this, context)); +} + +} // namespace gin
diff --git a/gin/converter.cc b/gin/converter.cc new file mode 100644 index 0000000..07437b7 --- /dev/null +++ b/gin/converter.cc
@@ -0,0 +1,205 @@ +// 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. + +#include "gin/converter.h" + +#include "v8/include/v8.h" + +using v8::ArrayBuffer; +using v8::Boolean; +using v8::External; +using v8::Function; +using v8::Handle; +using v8::Integer; +using v8::Isolate; +using v8::Number; +using v8::Object; +using v8::String; +using v8::Value; + +namespace gin { + +Handle<Value> Converter<bool>::ToV8(Isolate* isolate, bool val) { + return Boolean::New(isolate, val).As<Value>(); +} + +bool Converter<bool>::FromV8(Isolate* isolate, Handle<Value> val, bool* out) { + *out = val->BooleanValue(); + return true; +} + +Handle<Value> Converter<int32_t>::ToV8(Isolate* isolate, int32_t val) { + return Integer::New(isolate, val).As<Value>(); +} + +bool Converter<int32_t>::FromV8(Isolate* isolate, Handle<Value> val, + int32_t* out) { + if (!val->IsInt32()) + return false; + *out = val->Int32Value(); + return true; +} + +Handle<Value> Converter<uint32_t>::ToV8(Isolate* isolate, uint32_t val) { + return Integer::NewFromUnsigned(isolate, val).As<Value>(); +} + +bool Converter<uint32_t>::FromV8(Isolate* isolate, Handle<Value> val, + uint32_t* out) { + if (!val->IsUint32()) + return false; + *out = val->Uint32Value(); + return true; +} + +Handle<Value> Converter<int64_t>::ToV8(Isolate* isolate, int64_t val) { + return Number::New(isolate, static_cast<double>(val)).As<Value>(); +} + +bool Converter<int64_t>::FromV8(Isolate* isolate, Handle<Value> val, + int64_t* out) { + if (!val->IsNumber()) + return false; + // Even though IntegerValue returns int64_t, JavaScript cannot represent + // the full precision of int64_t, which means some rounding might occur. + *out = val->IntegerValue(); + return true; +} + +Handle<Value> Converter<uint64_t>::ToV8(Isolate* isolate, uint64_t val) { + return Number::New(isolate, static_cast<double>(val)).As<Value>(); +} + +bool Converter<uint64_t>::FromV8(Isolate* isolate, Handle<Value> val, + uint64_t* out) { + if (!val->IsNumber()) + return false; + *out = static_cast<uint64_t>(val->IntegerValue()); + return true; +} + +Handle<Value> Converter<float>::ToV8(Isolate* isolate, float val) { + return Number::New(isolate, val).As<Value>(); +} + +bool Converter<float>::FromV8(Isolate* isolate, Handle<Value> val, + float* out) { + if (!val->IsNumber()) + return false; + *out = static_cast<float>(val->NumberValue()); + return true; +} + +Handle<Value> Converter<double>::ToV8(Isolate* isolate, double val) { + return Number::New(isolate, val).As<Value>(); +} + +bool Converter<double>::FromV8(Isolate* isolate, Handle<Value> val, + double* out) { + if (!val->IsNumber()) + return false; + *out = val->NumberValue(); + return true; +} + +Handle<Value> Converter<base::StringPiece>::ToV8( + Isolate* isolate, const base::StringPiece& val) { + return String::NewFromUtf8(isolate, val.data(), String::kNormalString, + static_cast<uint32_t>(val.length())); +} + +Handle<Value> Converter<std::string>::ToV8(Isolate* isolate, + const std::string& val) { + return Converter<base::StringPiece>::ToV8(isolate, val); +} + +bool Converter<std::string>::FromV8(Isolate* isolate, Handle<Value> val, + std::string* out) { + if (!val->IsString()) + return false; + Handle<String> str = Handle<String>::Cast(val); + int length = str->Utf8Length(); + out->resize(length); + str->WriteUtf8(&(*out)[0], length, NULL, String::NO_NULL_TERMINATION); + return true; +} + +bool Converter<Handle<Function> >::FromV8(Isolate* isolate, Handle<Value> val, + Handle<Function>* out) { + if (!val->IsFunction()) + return false; + *out = Handle<Function>::Cast(val); + return true; +} + +Handle<Value> Converter<Handle<Object> >::ToV8(Isolate* isolate, + Handle<Object> val) { + return val.As<Value>(); +} + +bool Converter<Handle<Object> >::FromV8(Isolate* isolate, Handle<Value> val, + Handle<Object>* out) { + if (!val->IsObject()) + return false; + *out = Handle<Object>::Cast(val); + return true; +} + +Handle<Value> Converter<Handle<ArrayBuffer> >::ToV8(Isolate* isolate, + Handle<ArrayBuffer> val) { + return val.As<Value>(); +} + +bool Converter<Handle<ArrayBuffer> >::FromV8(Isolate* isolate, + Handle<Value> val, + Handle<ArrayBuffer>* out) { + if (!val->IsArrayBuffer()) + return false; + *out = Handle<ArrayBuffer>::Cast(val); + return true; +} + +Handle<Value> Converter<Handle<External> >::ToV8(Isolate* isolate, + Handle<External> val) { + return val.As<Value>(); +} + +bool Converter<Handle<External> >::FromV8(Isolate* isolate, + v8::Handle<Value> val, + Handle<External>* out) { + if (!val->IsExternal()) + return false; + *out = Handle<External>::Cast(val); + return true; +} + +Handle<Value> Converter<Handle<Value> >::ToV8(Isolate* isolate, + Handle<Value> val) { + return val; +} + +bool Converter<Handle<Value> >::FromV8(Isolate* isolate, Handle<Value> val, + Handle<Value>* out) { + *out = val; + return true; +} + +v8::Handle<v8::String> StringToSymbol(v8::Isolate* isolate, + const base::StringPiece& val) { + return String::NewFromUtf8(isolate, + val.data(), + String::kInternalizedString, + static_cast<uint32_t>(val.length())); +} + +std::string V8ToString(v8::Handle<v8::Value> value) { + if (value.IsEmpty()) + return std::string(); + std::string result; + if (!ConvertFromV8(NULL, value, &result)) + return std::string(); + return result; +} + +} // namespace gin
diff --git a/gin/converter.h b/gin/converter.h new file mode 100644 index 0000000..e5c95fc --- /dev/null +++ b/gin/converter.h
@@ -0,0 +1,202 @@ +// 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. + +#ifndef GIN_CONVERTER_H_ +#define GIN_CONVERTER_H_ + +#include <string> +#include <vector> + +#include "base/strings/string_piece.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +template<typename T, typename Enable = void> +struct Converter {}; + +template<> +struct GIN_EXPORT Converter<bool> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + bool val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + bool* out); +}; + +template<> +struct GIN_EXPORT Converter<int32_t> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + int32_t val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + int32_t* out); +}; + +template<> +struct GIN_EXPORT Converter<uint32_t> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + uint32_t val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + uint32_t* out); +}; + +template<> +struct GIN_EXPORT Converter<int64_t> { + // Warning: JavaScript cannot represent 64 integers precisely. + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + int64_t val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + int64_t* out); +}; + +template<> +struct GIN_EXPORT Converter<uint64_t> { + // Warning: JavaScript cannot represent 64 integers precisely. + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + uint64_t val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + uint64_t* out); +}; + +template<> +struct GIN_EXPORT Converter<float> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + float val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + float* out); +}; + +template<> +struct GIN_EXPORT Converter<double> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + double val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + double* out); +}; + +template<> +struct GIN_EXPORT Converter<base::StringPiece> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + const base::StringPiece& val); + // No conversion out is possible because StringPiece does not contain storage. +}; + +template<> +struct GIN_EXPORT Converter<std::string> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + const std::string& val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + std::string* out); +}; + +template<> +struct GIN_EXPORT Converter<v8::Handle<v8::Function> > { + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + v8::Handle<v8::Function>* out); +}; + +template<> +struct GIN_EXPORT Converter<v8::Handle<v8::Object> > { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + v8::Handle<v8::Object> val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + v8::Handle<v8::Object>* out); +}; + +template<> +struct GIN_EXPORT Converter<v8::Handle<v8::ArrayBuffer> > { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + v8::Handle<v8::ArrayBuffer> val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + v8::Handle<v8::ArrayBuffer>* out); +}; + +template<> +struct GIN_EXPORT Converter<v8::Handle<v8::External> > { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + v8::Handle<v8::External> val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + v8::Handle<v8::External>* out); +}; + +template<> +struct GIN_EXPORT Converter<v8::Handle<v8::Value> > { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + v8::Handle<v8::Value>* out); +}; + +template<typename T> +struct Converter<std::vector<T> > { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + const std::vector<T>& val) { + v8::Handle<v8::Array> result( + v8::Array::New(isolate, static_cast<int>(val.size()))); + for (size_t i = 0; i < val.size(); ++i) { + result->Set(static_cast<int>(i), Converter<T>::ToV8(isolate, val[i])); + } + return result; + } + + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + std::vector<T>* out) { + if (!val->IsArray()) + return false; + + std::vector<T> result; + v8::Handle<v8::Array> array(v8::Handle<v8::Array>::Cast(val)); + uint32_t length = array->Length(); + for (uint32_t i = 0; i < length; ++i) { + T item; + if (!Converter<T>::FromV8(isolate, array->Get(i), &item)) + return false; + result.push_back(item); + } + + out->swap(result); + return true; + } +}; + +// Convenience functions that deduce T. +template<typename T> +v8::Handle<v8::Value> ConvertToV8(v8::Isolate* isolate, T input) { + return Converter<T>::ToV8(isolate, input); +} + +GIN_EXPORT inline v8::Handle<v8::String> StringToV8( + v8::Isolate* isolate, + const base::StringPiece& input) { + return ConvertToV8(isolate, input).As<v8::String>(); +} + +GIN_EXPORT v8::Handle<v8::String> StringToSymbol(v8::Isolate* isolate, + const base::StringPiece& val); + +template<typename T> +bool ConvertFromV8(v8::Isolate* isolate, v8::Handle<v8::Value> input, + T* result) { + return Converter<T>::FromV8(isolate, input, result); +} + +GIN_EXPORT std::string V8ToString(v8::Handle<v8::Value> value); + +} // namespace gin + +#endif // GIN_CONVERTER_H_
diff --git a/gin/converter_unittest.cc b/gin/converter_unittest.cc new file mode 100644 index 0000000..791d7e6 --- /dev/null +++ b/gin/converter_unittest.cc
@@ -0,0 +1,134 @@ +// 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. + +#include "gin/converter.h" + +#include <limits.h> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "gin/public/isolate_holder.h" +#include "gin/test/v8_test.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "v8/include/v8.h" + +using v8::Array; +using v8::Boolean; +using v8::Handle; +using v8::HandleScope; +using v8::Integer; +using v8::Null; +using v8::Number; +using v8::Object; +using v8::String; +using v8::Undefined; +using v8::Value; + +namespace gin { + +typedef V8Test ConverterTest; + +TEST_F(ConverterTest, Bool) { + HandleScope handle_scope(instance_->isolate()); + + EXPECT_TRUE(Converter<bool>::ToV8(instance_->isolate(), true)->StrictEquals( + Boolean::New(instance_->isolate(), true))); + EXPECT_TRUE(Converter<bool>::ToV8(instance_->isolate(), false)->StrictEquals( + Boolean::New(instance_->isolate(), false))); + + struct { + Handle<Value> input; + bool expected; + } test_data[] = { + { Boolean::New(instance_->isolate(), false).As<Value>(), false }, + { Boolean::New(instance_->isolate(), true).As<Value>(), true }, + { Number::New(instance_->isolate(), 0).As<Value>(), false }, + { Number::New(instance_->isolate(), 1).As<Value>(), true }, + { Number::New(instance_->isolate(), -1).As<Value>(), true }, + { Number::New(instance_->isolate(), 0.1).As<Value>(), true }, + { String::NewFromUtf8(instance_->isolate(), "").As<Value>(), false }, + { String::NewFromUtf8(instance_->isolate(), "foo").As<Value>(), true }, + { Object::New(instance_->isolate()).As<Value>(), true }, + { Null(instance_->isolate()).As<Value>(), false }, + { Undefined(instance_->isolate()).As<Value>(), false }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) { + bool result = false; + EXPECT_TRUE(Converter<bool>::FromV8(instance_->isolate(), + test_data[i].input, &result)); + EXPECT_EQ(test_data[i].expected, result); + + result = true; + EXPECT_TRUE(Converter<bool>::FromV8(instance_->isolate(), + test_data[i].input, &result)); + EXPECT_EQ(test_data[i].expected, result); + } +} + +TEST_F(ConverterTest, Int32) { + HandleScope handle_scope(instance_->isolate()); + + int test_data_to[] = {-1, 0, 1}; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data_to); ++i) { + EXPECT_TRUE(Converter<int32_t>::ToV8(instance_->isolate(), test_data_to[i]) + ->StrictEquals( + Integer::New(instance_->isolate(), test_data_to[i]))); + } + + struct { + v8::Handle<v8::Value> input; + bool expect_sucess; + int expected_result; + } test_data_from[] = { + { Boolean::New(instance_->isolate(), false).As<Value>(), false, 0 }, + { Boolean::New(instance_->isolate(), true).As<Value>(), false, 0 }, + { Integer::New(instance_->isolate(), -1).As<Value>(), true, -1 }, + { Integer::New(instance_->isolate(), 0).As<Value>(), true, 0 }, + { Integer::New(instance_->isolate(), 1).As<Value>(), true, 1 }, + { Number::New(instance_->isolate(), -1).As<Value>(), true, -1 }, + { Number::New(instance_->isolate(), 1.1).As<Value>(), false, 0 }, + { String::NewFromUtf8(instance_->isolate(), "42").As<Value>(), false, 0 }, + { String::NewFromUtf8(instance_->isolate(), "foo").As<Value>(), false, 0 }, + { Object::New(instance_->isolate()).As<Value>(), false, 0 }, + { Array::New(instance_->isolate()).As<Value>(), false, 0 }, + { v8::Null(instance_->isolate()).As<Value>(), false, 0 }, + { v8::Undefined(instance_->isolate()).As<Value>(), false, 0 }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data_from); ++i) { + int32_t result = std::numeric_limits<int32_t>::min(); + bool success = Converter<int32_t>::FromV8(instance_->isolate(), + test_data_from[i].input, &result); + EXPECT_EQ(test_data_from[i].expect_sucess, success) << i; + if (success) + EXPECT_EQ(test_data_from[i].expected_result, result) << i; + } +} + +TEST_F(ConverterTest, Vector) { + HandleScope handle_scope(instance_->isolate()); + + std::vector<int> expected; + expected.push_back(-1); + expected.push_back(0); + expected.push_back(1); + + Handle<Array> js_array = Handle<Array>::Cast( + Converter<std::vector<int> >::ToV8(instance_->isolate(), expected)); + ASSERT_FALSE(js_array.IsEmpty()); + EXPECT_EQ(3u, js_array->Length()); + for (size_t i = 0; i < expected.size(); ++i) { + EXPECT_TRUE(Integer::New(instance_->isolate(), expected[i]) + ->StrictEquals(js_array->Get(static_cast<int>(i)))); + } + + std::vector<int> actual; + EXPECT_TRUE(Converter<std::vector<int> >::FromV8(instance_->isolate(), + js_array, &actual)); + EXPECT_EQ(expected, actual); +} + +} // namespace gin
diff --git a/gin/debug_impl.cc b/gin/debug_impl.cc new file mode 100644 index 0000000..5657ec3 --- /dev/null +++ b/gin/debug_impl.cc
@@ -0,0 +1,62 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/debug_impl.h" + +namespace gin { + +namespace { +v8::FunctionEntryHook g_entry_hook = NULL; +v8::JitCodeEventHandler g_jit_code_event_handler = NULL; +#if defined(OS_WIN) +Debug::CodeRangeCreatedCallback g_code_range_created_callback = NULL; +Debug::CodeRangeDeletedCallback g_code_range_deleted_callback = NULL; +#endif +} // namespace + +// static +void Debug::SetFunctionEntryHook(v8::FunctionEntryHook entry_hook) { + g_entry_hook = entry_hook; +} + +// static +void Debug::SetJitCodeEventHandler(v8::JitCodeEventHandler event_handler) { + g_jit_code_event_handler = event_handler; +} + +#if defined(OS_WIN) +// static +void Debug::SetCodeRangeCreatedCallback(CodeRangeCreatedCallback callback) { + g_code_range_created_callback = callback; +} + +// static +void Debug::SetCodeRangeDeletedCallback(CodeRangeDeletedCallback callback) { + g_code_range_deleted_callback = callback; +} +#endif + +// static +v8::FunctionEntryHook DebugImpl::GetFunctionEntryHook() { + return g_entry_hook; +} + +// static +v8::JitCodeEventHandler DebugImpl::GetJitCodeEventHandler() { + return g_jit_code_event_handler; +} + +#if defined(OS_WIN) +// static +Debug::CodeRangeCreatedCallback DebugImpl::GetCodeRangeCreatedCallback() { + return g_code_range_created_callback; +} + +// static +Debug::CodeRangeDeletedCallback DebugImpl::GetCodeRangeDeletedCallback() { + return g_code_range_deleted_callback; +} +#endif + +} // namespace gin
diff --git a/gin/debug_impl.h b/gin/debug_impl.h new file mode 100644 index 0000000..96d48ad --- /dev/null +++ b/gin/debug_impl.h
@@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GIN_PUBLIC_DEBUG_IMPL_H_ +#define GIN_PUBLIC_DEBUG_IMPL_H_ + +#include "gin/public/debug.h" +#include "v8/include/v8.h" + +namespace gin { + +class DebugImpl { + public: + static v8::FunctionEntryHook GetFunctionEntryHook(); + static v8::JitCodeEventHandler GetJitCodeEventHandler(); +#if defined(OS_WIN) + static Debug::CodeRangeCreatedCallback GetCodeRangeCreatedCallback(); + static Debug::CodeRangeDeletedCallback GetCodeRangeDeletedCallback(); +#endif +}; + +} // namespace gin + +#endif // GIN_PUBLIC_DEBUG_IMPL_H_
diff --git a/gin/dictionary.cc b/gin/dictionary.cc new file mode 100644 index 0000000..d336122 --- /dev/null +++ b/gin/dictionary.cc
@@ -0,0 +1,42 @@ +// 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. + +#include "gin/dictionary.h" + +namespace gin { + +Dictionary::Dictionary(v8::Isolate* isolate) + : isolate_(isolate) { +} + +Dictionary::Dictionary(v8::Isolate* isolate, + v8::Handle<v8::Object> object) + : isolate_(isolate), + object_(object) { +} + +Dictionary::~Dictionary() { +} + +Dictionary Dictionary::CreateEmpty(v8::Isolate* isolate) { + Dictionary dictionary(isolate); + dictionary.object_ = v8::Object::New(isolate); + return dictionary; +} + +v8::Handle<v8::Value> Converter<Dictionary>::ToV8(v8::Isolate* isolate, + Dictionary val) { + return val.object_; +} + +bool Converter<Dictionary>::FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + Dictionary* out) { + if (!val->IsObject()) + return false; + *out = Dictionary(isolate, v8::Handle<v8::Object>::Cast(val)); + return true; +} + +} // namespace gin
diff --git a/gin/dictionary.h b/gin/dictionary.h new file mode 100644 index 0000000..972108f --- /dev/null +++ b/gin/dictionary.h
@@ -0,0 +1,65 @@ +// 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. + +#ifndef GIN_DICTIONARY_H_ +#define GIN_DICTIONARY_H_ + +#include "gin/converter.h" +#include "gin/gin_export.h" + +namespace gin { + +// Dictionary is useful when writing bindings for a function that either +// receives an arbitrary JavaScript object as an argument or returns an +// arbitrary JavaScript object as a result. For example, Dictionary is useful +// when you might use the |dictionary| type in WebIDL: +// +// http://heycam.github.io/webidl/#idl-dictionaries +// +// WARNING: You cannot retain a Dictionary object in the heap. The underlying +// storage for Dictionary is tied to the closest enclosing +// v8::HandleScope. Generally speaking, you should store a Dictionary +// on the stack. +// +class GIN_EXPORT Dictionary { + public: + explicit Dictionary(v8::Isolate* isolate); + Dictionary(v8::Isolate* isolate, v8::Handle<v8::Object> object); + ~Dictionary(); + + static Dictionary CreateEmpty(v8::Isolate* isolate); + + template<typename T> + bool Get(const std::string& key, T* out) { + v8::Handle<v8::Value> val = object_->Get(StringToV8(isolate_, key)); + return ConvertFromV8(isolate_, val, out); + } + + template<typename T> + bool Set(const std::string& key, T val) { + return object_->Set(StringToV8(isolate_, key), ConvertToV8(isolate_, val)); + } + + v8::Isolate* isolate() const { return isolate_; } + + private: + friend struct Converter<Dictionary>; + + // TODO(aa): Remove this. Instead, get via FromV8(), Set(), and Get(). + v8::Isolate* isolate_; + v8::Handle<v8::Object> object_; +}; + +template<> +struct GIN_EXPORT Converter<Dictionary> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + Dictionary val); + static bool FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + Dictionary* out); +}; + +} // namespace gin + +#endif // GIN_DICTIONARY_H_
diff --git a/gin/function_template.cc b/gin/function_template.cc new file mode 100644 index 0000000..f76a05b --- /dev/null +++ b/gin/function_template.cc
@@ -0,0 +1,33 @@ +// 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. + +#include "gin/function_template.h" + +namespace gin { + +namespace internal { + +CallbackHolderBase::CallbackHolderBase(v8::Isolate* isolate) + : v8_ref_(isolate, v8::External::New(isolate, this)) { + v8_ref_.SetWeak(this, &CallbackHolderBase::WeakCallback); +} + +CallbackHolderBase::~CallbackHolderBase() { + DCHECK(v8_ref_.IsEmpty()); +} + +v8::Handle<v8::External> CallbackHolderBase::GetHandle(v8::Isolate* isolate) { + return v8::Local<v8::External>::New(isolate, v8_ref_); +} + +// static +void CallbackHolderBase::WeakCallback( + const v8::WeakCallbackData<v8::External, CallbackHolderBase>& data) { + data.GetParameter()->v8_ref_.Reset(); + delete data.GetParameter(); +} + +} // namespace internal + +} // namespace gin
diff --git a/gin/function_template.h b/gin/function_template.h new file mode 100644 index 0000000..7ba54b5 --- /dev/null +++ b/gin/function_template.h
@@ -0,0 +1,520 @@ +// This file was GENERATED by command: +// pump.py function_template.h.pump +// DO NOT EDIT BY HAND!!! + + + +#ifndef GIN_FUNCTION_TEMPLATE_H_ +#define GIN_FUNCTION_TEMPLATE_H_ + +// 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. + +#include "base/callback.h" +#include "base/logging.h" +#include "gin/arguments.h" +#include "gin/converter.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +class PerIsolateData; + +enum CreateFunctionTemplateFlags { + HolderIsFirstArgument = 1 << 0, +}; + +namespace internal { + +template<typename T> +struct CallbackParamTraits { + typedef T LocalType; +}; +template<typename T> +struct CallbackParamTraits<const T&> { + typedef T LocalType; +}; +template<typename T> +struct CallbackParamTraits<const T*> { + typedef T* LocalType; +}; + + +// CallbackHolder and CallbackHolderBase are used to pass a base::Callback from +// CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to +// DispatchToCallback, where it is invoked. + +// This simple base class is used so that we can share a single object template +// among every CallbackHolder instance. +class GIN_EXPORT CallbackHolderBase { + public: + v8::Handle<v8::External> GetHandle(v8::Isolate* isolate); + + protected: + explicit CallbackHolderBase(v8::Isolate* isolate); + virtual ~CallbackHolderBase(); + + private: + static void WeakCallback( + const v8::WeakCallbackData<v8::External, CallbackHolderBase>& data); + + v8::Persistent<v8::External> v8_ref_; + + DISALLOW_COPY_AND_ASSIGN(CallbackHolderBase); +}; + +template<typename Sig> +class CallbackHolder : public CallbackHolderBase { + public: + CallbackHolder(v8::Isolate* isolate, + const base::Callback<Sig>& callback, + int flags) + : CallbackHolderBase(isolate), callback(callback), flags(flags) {} + base::Callback<Sig> callback; + int flags; + private: + virtual ~CallbackHolder() {} + + DISALLOW_COPY_AND_ASSIGN(CallbackHolder); +}; + + +// This set of templates invokes a base::Callback, converts the return type to a +// JavaScript value, and returns that value to script via the provided +// gin::Arguments object. +// +// In C++, you can declare the function foo(void), but you can't pass a void +// expression to foo. As a result, we must specialize the case of Callbacks that +// have the void return type. +template<typename R, typename P1 = void, typename P2 = void, + typename P3 = void, typename P4 = void, typename P5 = void, + typename P6 = void> +struct Invoker { + inline static void Go( + Arguments* args, + const base::Callback<R(P1, P2, P3, P4, P5, P6)>& callback, + const P1& a1, + const P2& a2, + const P3& a3, + const P4& a4, + const P5& a5, + const P6& a6) { + args->Return(callback.Run(a1, a2, a3, a4, a5, a6)); + } +}; +template<typename P1, typename P2, typename P3, typename P4, typename P5, + typename P6> +struct Invoker<void, P1, P2, P3, P4, P5, P6> { + inline static void Go( + Arguments* args, + const base::Callback<void(P1, P2, P3, P4, P5, P6)>& callback, + const P1& a1, + const P2& a2, + const P3& a3, + const P4& a4, + const P5& a5, + const P6& a6) { + callback.Run(a1, a2, a3, a4, a5, a6); + } +}; + +template<typename R, typename P1, typename P2, typename P3, typename P4, + typename P5> +struct Invoker<R, P1, P2, P3, P4, P5, void> { + inline static void Go( + Arguments* args, + const base::Callback<R(P1, P2, P3, P4, P5)>& callback, + const P1& a1, + const P2& a2, + const P3& a3, + const P4& a4, + const P5& a5) { + args->Return(callback.Run(a1, a2, a3, a4, a5)); + } +}; +template<typename P1, typename P2, typename P3, typename P4, typename P5> +struct Invoker<void, P1, P2, P3, P4, P5, void> { + inline static void Go( + Arguments* args, + const base::Callback<void(P1, P2, P3, P4, P5)>& callback, + const P1& a1, + const P2& a2, + const P3& a3, + const P4& a4, + const P5& a5) { + callback.Run(a1, a2, a3, a4, a5); + } +}; + +template<typename R, typename P1, typename P2, typename P3, typename P4> +struct Invoker<R, P1, P2, P3, P4, void, void> { + inline static void Go( + Arguments* args, + const base::Callback<R(P1, P2, P3, P4)>& callback, + const P1& a1, + const P2& a2, + const P3& a3, + const P4& a4) { + args->Return(callback.Run(a1, a2, a3, a4)); + } +}; +template<typename P1, typename P2, typename P3, typename P4> +struct Invoker<void, P1, P2, P3, P4, void, void> { + inline static void Go( + Arguments* args, + const base::Callback<void(P1, P2, P3, P4)>& callback, + const P1& a1, + const P2& a2, + const P3& a3, + const P4& a4) { + callback.Run(a1, a2, a3, a4); + } +}; + +template<typename R, typename P1, typename P2, typename P3> +struct Invoker<R, P1, P2, P3, void, void, void> { + inline static void Go( + Arguments* args, + const base::Callback<R(P1, P2, P3)>& callback, + const P1& a1, + const P2& a2, + const P3& a3) { + args->Return(callback.Run(a1, a2, a3)); + } +}; +template<typename P1, typename P2, typename P3> +struct Invoker<void, P1, P2, P3, void, void, void> { + inline static void Go( + Arguments* args, + const base::Callback<void(P1, P2, P3)>& callback, + const P1& a1, + const P2& a2, + const P3& a3) { + callback.Run(a1, a2, a3); + } +}; + +template<typename R, typename P1, typename P2> +struct Invoker<R, P1, P2, void, void, void, void> { + inline static void Go( + Arguments* args, + const base::Callback<R(P1, P2)>& callback, + const P1& a1, + const P2& a2) { + args->Return(callback.Run(a1, a2)); + } +}; +template<typename P1, typename P2> +struct Invoker<void, P1, P2, void, void, void, void> { + inline static void Go( + Arguments* args, + const base::Callback<void(P1, P2)>& callback, + const P1& a1, + const P2& a2) { + callback.Run(a1, a2); + } +}; + +template<typename R, typename P1> +struct Invoker<R, P1, void, void, void, void, void> { + inline static void Go( + Arguments* args, + const base::Callback<R(P1)>& callback, + const P1& a1) { + args->Return(callback.Run(a1)); + } +}; +template<typename P1> +struct Invoker<void, P1, void, void, void, void, void> { + inline static void Go( + Arguments* args, + const base::Callback<void(P1)>& callback, + const P1& a1) { + callback.Run(a1); + } +}; + +template<typename R> +struct Invoker<R, void, void, void, void, void, void> { + inline static void Go( + Arguments* args, + const base::Callback<R()>& callback) { + args->Return(callback.Run()); + } +}; +template<> +struct Invoker<void, void, void, void, void, void, void> { + inline static void Go( + Arguments* args, + const base::Callback<void()>& callback) { + callback.Run(); + } +}; + + +template<typename T> +bool GetNextArgument(Arguments* args, int create_flags, bool is_first, + T* result) { + if (is_first && (create_flags & HolderIsFirstArgument) != 0) { + return args->GetHolder(result); + } else { + return args->GetNext(result); + } +} + +// For advanced use cases, we allow callers to request the unparsed Arguments +// object and poke around in it directly. +inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first, + Arguments* result) { + *result = *args; + return true; +} +inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first, + Arguments** result) { + *result = args; + return true; +} + +// It's common for clients to just need the isolate, so we make that easy. +inline bool GetNextArgument(Arguments* args, int create_flags, + bool is_first, v8::Isolate** result) { + *result = args->isolate(); + return true; +} + + +// DispatchToCallback converts all the JavaScript arguments to C++ types and +// invokes the base::Callback. +template<typename Sig> +struct Dispatcher { +}; + +template<typename R> +struct Dispatcher<R()> { + static void DispatchToCallback( + const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); + + typedef CallbackHolder<R()> HolderT; + HolderT* holder = static_cast<HolderT*>(holder_base); + + Invoker<R>::Go(&args, holder->callback); + } +}; + +template<typename R, typename P1> +struct Dispatcher<R(P1)> { + static void DispatchToCallback( + const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); + + typedef CallbackHolder<R(P1)> HolderT; + HolderT* holder = static_cast<HolderT*>(holder_base); + + typename CallbackParamTraits<P1>::LocalType a1; + if (!GetNextArgument(&args, holder->flags, true, &a1)) { + args.ThrowError(); + return; + } + + Invoker<R, P1>::Go(&args, holder->callback, a1); + } +}; + +template<typename R, typename P1, typename P2> +struct Dispatcher<R(P1, P2)> { + static void DispatchToCallback( + const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); + + typedef CallbackHolder<R(P1, P2)> HolderT; + HolderT* holder = static_cast<HolderT*>(holder_base); + + typename CallbackParamTraits<P1>::LocalType a1; + typename CallbackParamTraits<P2>::LocalType a2; + if (!GetNextArgument(&args, holder->flags, true, &a1) || + !GetNextArgument(&args, holder->flags, false, &a2)) { + args.ThrowError(); + return; + } + + Invoker<R, P1, P2>::Go(&args, holder->callback, a1, a2); + } +}; + +template<typename R, typename P1, typename P2, typename P3> +struct Dispatcher<R(P1, P2, P3)> { + static void DispatchToCallback( + const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); + + typedef CallbackHolder<R(P1, P2, P3)> HolderT; + HolderT* holder = static_cast<HolderT*>(holder_base); + + typename CallbackParamTraits<P1>::LocalType a1; + typename CallbackParamTraits<P2>::LocalType a2; + typename CallbackParamTraits<P3>::LocalType a3; + if (!GetNextArgument(&args, holder->flags, true, &a1) || + !GetNextArgument(&args, holder->flags, false, &a2) || + !GetNextArgument(&args, holder->flags, false, &a3)) { + args.ThrowError(); + return; + } + + Invoker<R, P1, P2, P3>::Go(&args, holder->callback, a1, a2, a3); + } +}; + +template<typename R, typename P1, typename P2, typename P3, typename P4> +struct Dispatcher<R(P1, P2, P3, P4)> { + static void DispatchToCallback( + const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); + + typedef CallbackHolder<R(P1, P2, P3, P4)> HolderT; + HolderT* holder = static_cast<HolderT*>(holder_base); + + typename CallbackParamTraits<P1>::LocalType a1; + typename CallbackParamTraits<P2>::LocalType a2; + typename CallbackParamTraits<P3>::LocalType a3; + typename CallbackParamTraits<P4>::LocalType a4; + if (!GetNextArgument(&args, holder->flags, true, &a1) || + !GetNextArgument(&args, holder->flags, false, &a2) || + !GetNextArgument(&args, holder->flags, false, &a3) || + !GetNextArgument(&args, holder->flags, false, &a4)) { + args.ThrowError(); + return; + } + + Invoker<R, P1, P2, P3, P4>::Go(&args, holder->callback, a1, a2, a3, a4); + } +}; + +template<typename R, typename P1, typename P2, typename P3, typename P4, + typename P5> +struct Dispatcher<R(P1, P2, P3, P4, P5)> { + static void DispatchToCallback( + const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); + + typedef CallbackHolder<R(P1, P2, P3, P4, P5)> HolderT; + HolderT* holder = static_cast<HolderT*>(holder_base); + + typename CallbackParamTraits<P1>::LocalType a1; + typename CallbackParamTraits<P2>::LocalType a2; + typename CallbackParamTraits<P3>::LocalType a3; + typename CallbackParamTraits<P4>::LocalType a4; + typename CallbackParamTraits<P5>::LocalType a5; + if (!GetNextArgument(&args, holder->flags, true, &a1) || + !GetNextArgument(&args, holder->flags, false, &a2) || + !GetNextArgument(&args, holder->flags, false, &a3) || + !GetNextArgument(&args, holder->flags, false, &a4) || + !GetNextArgument(&args, holder->flags, false, &a5)) { + args.ThrowError(); + return; + } + + Invoker<R, P1, P2, P3, P4, P5>::Go(&args, holder->callback, a1, a2, a3, a4, + a5); + } +}; + +template<typename R, typename P1, typename P2, typename P3, typename P4, + typename P5, typename P6> +struct Dispatcher<R(P1, P2, P3, P4, P5, P6)> { + static void DispatchToCallback( + const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); + + typedef CallbackHolder<R(P1, P2, P3, P4, P5, P6)> HolderT; + HolderT* holder = static_cast<HolderT*>(holder_base); + + typename CallbackParamTraits<P1>::LocalType a1; + typename CallbackParamTraits<P2>::LocalType a2; + typename CallbackParamTraits<P3>::LocalType a3; + typename CallbackParamTraits<P4>::LocalType a4; + typename CallbackParamTraits<P5>::LocalType a5; + typename CallbackParamTraits<P6>::LocalType a6; + if (!GetNextArgument(&args, holder->flags, true, &a1) || + !GetNextArgument(&args, holder->flags, false, &a2) || + !GetNextArgument(&args, holder->flags, false, &a3) || + !GetNextArgument(&args, holder->flags, false, &a4) || + !GetNextArgument(&args, holder->flags, false, &a5) || + !GetNextArgument(&args, holder->flags, false, &a6)) { + args.ThrowError(); + return; + } + + Invoker<R, P1, P2, P3, P4, P5, P6>::Go(&args, holder->callback, a1, a2, a3, + a4, a5, a6); + } +}; + +} // namespace internal + + +// CreateFunctionTemplate creates a v8::FunctionTemplate that will create +// JavaScript functions that execute a provided C++ function or base::Callback. +// JavaScript arguments are automatically converted via gin::Converter, as is +// the return value of the C++ function, if any. +template<typename Sig> +v8::Local<v8::FunctionTemplate> CreateFunctionTemplate( + v8::Isolate* isolate, const base::Callback<Sig> callback, + int callback_flags = 0) { + typedef internal::CallbackHolder<Sig> HolderT; + HolderT* holder = new HolderT(isolate, callback, callback_flags); + + return v8::FunctionTemplate::New( + isolate, + &internal::Dispatcher<Sig>::DispatchToCallback, + ConvertToV8<v8::Handle<v8::External> >(isolate, + holder->GetHandle(isolate))); +} + +// CreateFunctionHandler installs a CallAsFunction handler on the given +// object template that forwards to a provided C++ function or base::Callback. +template<typename Sig> +void CreateFunctionHandler(v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> tmpl, + const base::Callback<Sig> callback, + int callback_flags = 0) { + typedef internal::CallbackHolder<Sig> HolderT; + HolderT* holder = new HolderT(isolate, callback, callback_flags); + tmpl->SetCallAsFunctionHandler(&internal::Dispatcher<Sig>::DispatchToCallback, + ConvertToV8<v8::Handle<v8::External> >( + isolate, holder->GetHandle(isolate))); +} + +} // namespace gin + +#endif // GIN_FUNCTION_TEMPLATE_H_
diff --git a/gin/function_template.h.pump b/gin/function_template.h.pump new file mode 100644 index 0000000..20b2821 --- /dev/null +++ b/gin/function_template.h.pump
@@ -0,0 +1,240 @@ +$$ This is a pump file for generating file templates. Pump is a python +$$ script that is part of the Google Test suite of utilities. Description +$$ can be found here: +$$ +$$ http://code.google.com/p/googletest/wiki/PumpManual +$$ + +#ifndef GIN_FUNCTION_TEMPLATE_H_ +#define GIN_FUNCTION_TEMPLATE_H_ + +$var MAX_ARITY = 6 + +// 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. + +#include "base/callback.h" +#include "base/logging.h" +#include "gin/arguments.h" +#include "gin/converter.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +class PerIsolateData; + +enum CreateFunctionTemplateFlags { + HolderIsFirstArgument = 1 << 0, +}; + +namespace internal { + +template<typename T> +struct CallbackParamTraits { + typedef T LocalType; +}; +template<typename T> +struct CallbackParamTraits<const T&> { + typedef T LocalType; +}; +template<typename T> +struct CallbackParamTraits<const T*> { + typedef T* LocalType; +}; + + +// CallbackHolder and CallbackHolderBase are used to pass a base::Callback from +// CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to +// DispatchToCallback, where it is invoked. + +// This simple base class is used so that we can share a single object template +// among every CallbackHolder instance. +class GIN_EXPORT CallbackHolderBase { + public: + v8::Handle<v8::External> GetHandle(v8::Isolate* isolate); + + protected: + explicit CallbackHolderBase(v8::Isolate* isolate); + virtual ~CallbackHolderBase(); + + private: + static void WeakCallback( + const v8::WeakCallbackData<v8::External, CallbackHolderBase>& data); + + v8::Persistent<v8::External> v8_ref_; + + DISALLOW_COPY_AND_ASSIGN(CallbackHolderBase); +}; + +template<typename Sig> +class CallbackHolder : public CallbackHolderBase { + public: + CallbackHolder(v8::Isolate* isolate, + const base::Callback<Sig>& callback, + int flags) + : CallbackHolderBase(isolate), callback(callback), flags(flags) {} + base::Callback<Sig> callback; + int flags; + private: + virtual ~CallbackHolder() {} + + DISALLOW_COPY_AND_ASSIGN(CallbackHolder); +}; + + +// This set of templates invokes a base::Callback, converts the return type to a +// JavaScript value, and returns that value to script via the provided +// gin::Arguments object. +// +// In C++, you can declare the function foo(void), but you can't pass a void +// expression to foo. As a result, we must specialize the case of Callbacks that +// have the void return type. + +$range ARITY 0..MAX_ARITY +$for ARITY [[ +$var INV_ARITY = MAX_ARITY - ARITY +$range ARG 1..INV_ARITY +$range VOID INV_ARITY+1..MAX_ARITY + +$if ARITY == 0 [[ +template<typename R$for ARG [[, typename P$(ARG) = void]]> +struct Invoker { +]] $else [[ +template<typename R$for ARG [[, typename P$(ARG)]]> +struct Invoker<R$for ARG [[, P$(ARG)]]$for VOID [[, void]]> { +]] + + inline static void Go( + Arguments* args, + const base::Callback<R($for ARG , [[P$(ARG)]])>& callback$for ARG [[, + const P$(ARG)& a$(ARG)]]) { + args->Return(callback.Run($for ARG, [[a$(ARG)]])); + } +}; +template<$for ARG , [[typename P$(ARG)]]> +struct Invoker<void$for ARG [[, P$(ARG)]]$for VOID [[, void]]> { + inline static void Go( + Arguments* args, + const base::Callback<void($for ARG , [[P$(ARG)]])>& callback$for ARG [[, + const P$(ARG)& a$(ARG)]]) { + callback.Run($for ARG, [[a$(ARG)]]); + } +}; + + +]] + +template<typename T> +bool GetNextArgument(Arguments* args, int create_flags, bool is_first, + T* result) { + if (is_first && (create_flags & HolderIsFirstArgument) != 0) { + return args->GetHolder(result); + } else { + return args->GetNext(result); + } +} + +// For advanced use cases, we allow callers to request the unparsed Arguments +// object and poke around in it directly. +inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first, + Arguments* result) { + *result = *args; + return true; +} +inline bool GetNextArgument(Arguments* args, int create_flags, bool is_first, + Arguments** result) { + *result = args; + return true; +} + +// It's common for clients to just need the isolate, so we make that easy. +inline bool GetNextArgument(Arguments* args, int create_flags, + bool is_first, v8::Isolate** result) { + *result = args->isolate(); + return true; +} + + +// DispatchToCallback converts all the JavaScript arguments to C++ types and +// invokes the base::Callback. +template<typename Sig> +struct Dispatcher { +}; + +$range ARITY 0..MAX_ARITY +$for ARITY [[ +$range ARG 1..ARITY + +template<typename R$for ARG [[, typename P$(ARG)]]> +struct Dispatcher<R($for ARG , [[P$(ARG)]])> { + static void DispatchToCallback( + const v8::FunctionCallbackInfo<v8::Value>& info) { + Arguments args(info); + v8::Handle<v8::External> v8_holder; + CHECK(args.GetData(&v8_holder)); + CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>( + v8_holder->Value()); + + typedef CallbackHolder<R($for ARG , [[P$(ARG)]])> HolderT; + HolderT* holder = static_cast<HolderT*>(holder_base); + +$if ARITY != 0 [[ + + +$for ARG [[ typename CallbackParamTraits<P$(ARG)>::LocalType a$(ARG); + +]] + if ($for ARG || + [[!GetNextArgument(&args, holder->flags, $if ARG == 1 [[true]] $else [[false]], &a$(ARG))]]) { + args.ThrowError(); + return; + } + +]] + + Invoker<R$for ARG [[, P$(ARG)]]>::Go(&args, holder->callback$for ARG [[, a$(ARG)]]); + } +}; + +]] + +} // namespace internal + + +// CreateFunctionTemplate creates a v8::FunctionTemplate that will create +// JavaScript functions that execute a provided C++ function or base::Callback. +// JavaScript arguments are automatically converted via gin::Converter, as is +// the return value of the C++ function, if any. +template<typename Sig> +v8::Local<v8::FunctionTemplate> CreateFunctionTemplate( + v8::Isolate* isolate, const base::Callback<Sig> callback, + int callback_flags = 0) { + typedef internal::CallbackHolder<Sig> HolderT; + HolderT* holder = new HolderT(isolate, callback, callback_flags); + + return v8::FunctionTemplate::New( + isolate, + &internal::Dispatcher<Sig>::DispatchToCallback, + ConvertToV8<v8::Handle<v8::External> >(isolate, + holder->GetHandle(isolate))); +} + +// CreateFunctionHandler installs a CallAsFunction handler on the given +// object template that forwards to a provided C++ function or base::Callback. +template<typename Sig> +void CreateFunctionHandler(v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> tmpl, + const base::Callback<Sig> callback, + int callback_flags = 0) { + typedef internal::CallbackHolder<Sig> HolderT; + HolderT* holder = new HolderT(isolate, callback, callback_flags); + tmpl->SetCallAsFunctionHandler(&internal::Dispatcher<Sig>::DispatchToCallback, + ConvertToV8<v8::Handle<v8::External> >( + isolate, holder->GetHandle(isolate))); +} + +} // namespace gin + +#endif // GIN_FUNCTION_TEMPLATE_H_
diff --git a/gin/gin.gyp b/gin/gin.gyp new file mode 100644 index 0000000..b38dc85 --- /dev/null +++ b/gin/gin.gyp
@@ -0,0 +1,147 @@ +# 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'targets': [ + { + 'target_name': 'gin', + 'type': '<(component)', + 'dependencies': [ + '../base/base.gyp:base', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../v8/tools/gyp/v8.gyp:v8', + + ], + 'export_dependent_settings': [ + '../base/base.gyp:base', + '../v8/tools/gyp/v8.gyp:v8', + ], + 'defines': [ + 'GIN_IMPLEMENTATION', + ], + 'sources': [ + 'arguments.cc', + 'arguments.h', + 'array_buffer.cc', + 'array_buffer.h', + 'context_holder.cc', + 'converter.cc', + 'converter.h', + 'debug_impl.cc', + 'debug_impl.h', + 'dictionary.cc', + 'dictionary.h', + 'function_template.cc', + 'function_template.h', + 'gin_export.h', + 'handle.h', + 'interceptor.cc', + 'interceptor.h', + 'isolate_holder.cc', + 'modules/console.cc', + 'modules/console.h', + 'modules/file_module_provider.cc', + 'modules/file_module_provider.h', + 'modules/module_registry.cc', + 'modules/module_registry.h', + 'modules/module_registry_observer.h', + 'modules/module_runner_delegate.cc', + 'modules/module_runner_delegate.h', + 'modules/timer.cc', + 'modules/timer.h', + 'object_template_builder.cc', + 'object_template_builder.h', + 'per_context_data.cc', + 'per_context_data.h', + 'per_isolate_data.cc', + 'per_isolate_data.h', + 'public/context_holder.h', + 'public/debug.h', + 'public/gin_embedders.h', + 'public/isolate_holder.h', + 'public/v8_platform.h', + 'public/wrapper_info.h', + 'runner.cc', + 'runner.h', + 'run_microtasks_observer.cc', + 'run_microtasks_observer.h', + 'shell_runner.cc', + 'shell_runner.h', + 'try_catch.cc', + 'try_catch.h', + 'v8_platform.cc', + 'wrappable.cc', + 'wrappable.h', + 'wrapper_info.cc', + ], + }, + { + 'target_name': 'gin_shell', + 'type': 'executable', + 'dependencies': [ + '../base/base.gyp:base', + '../base/base.gyp:base_i18n', + '../v8/tools/gyp/v8.gyp:v8', + 'gin', + ], + 'sources': [ + 'shell/gin_main.cc', + ], + 'msvs_settings': { + 'VCLinkerTool': { + 'SubSystem': '1', # /SUBSYSTEM:CONSOLE + }, + }, + }, + { + 'target_name': 'gin_test', + 'type': 'static_library', + 'dependencies': [ + '../testing/gtest.gyp:gtest', + '../v8/tools/gyp/v8.gyp:v8', + 'gin', + ], + 'export_dependent_settings': [ + '../testing/gtest.gyp:gtest', + 'gin', + ], + 'sources': [ + 'test/file.cc', + 'test/file.h', + 'test/file_runner.cc', + 'test/file_runner.h', + 'test/gc.cc', + 'test/gc.h', + 'test/gtest.cc', + 'test/gtest.h', + 'test/v8_test.cc', + 'test/v8_test.h', + ], + }, + { + 'target_name': 'gin_unittests', + 'type': 'executable', + 'dependencies': [ + '../base/base.gyp:test_support_base', + '../v8/tools/gyp/v8.gyp:v8', + 'gin_test', + ], + 'sources': [ + 'converter_unittest.cc', + 'interceptor_unittest.cc', + 'modules/module_registry_unittest.cc', + 'modules/timer_unittest.cc', + 'per_context_data_unittest.cc', + 'shell_runner_unittest.cc', + 'shell/gin_shell_unittest.cc', + 'test/run_all_unittests.cc', + 'test/run_js_tests.cc', + 'wrappable_unittest.cc', + ], + }, + ], +}
diff --git a/gin/gin_export.h b/gin/gin_export.h new file mode 100644 index 0000000..fc4bf9b --- /dev/null +++ b/gin/gin_export.h
@@ -0,0 +1,29 @@ +// 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. + +#ifndef GIN_GIN_EXPORT_H_ +#define GIN_GIN_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(GIN_IMPLEMENTATION) +#define GIN_EXPORT __declspec(dllexport) +#else +#define GIN_EXPORT __declspec(dllimport) +#endif // defined(GIN_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(GIN_IMPLEMENTATION) +#define GIN_EXPORT __attribute__((visibility("default"))) +#else +#define GIN_EXPORT +#endif // defined(GIN_IMPLEMENTATION) +#endif + +#else // defined(COMPONENT_BUILD) +#define GIN_EXPORT +#endif + +#endif // GIN_GIN_EXPORT_H_
diff --git a/gin/handle.h b/gin/handle.h new file mode 100644 index 0000000..01db660 --- /dev/null +++ b/gin/handle.h
@@ -0,0 +1,71 @@ +// 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. + +#ifndef GIN_HANDLE_H_ +#define GIN_HANDLE_H_ + +#include "gin/converter.h" + +namespace gin { + +// You can use gin::Handle on the stack to retain a gin::Wrappable object. +// Currently we don't have a mechanism for retaining a gin::Wrappable object +// in the C++ heap because strong references from C++ to V8 can cause memory +// leaks. +template<typename T> +class Handle { + public: + Handle() : object_(NULL) {} + + Handle(v8::Handle<v8::Value> wrapper, T* object) + : wrapper_(wrapper), + object_(object) { + } + + bool IsEmpty() const { return !object_; } + + void Clear() { + wrapper_.Clear(); + object_ = NULL; + } + + T* operator->() const { return object_; } + v8::Handle<v8::Value> ToV8() const { return wrapper_; } + T* get() const { return object_; } + + private: + v8::Handle<v8::Value> wrapper_; + T* object_; +}; + +template<typename T> +struct Converter<gin::Handle<T> > { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + const gin::Handle<T>& val) { + return val.ToV8(); + } + static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, + gin::Handle<T>* out) { + T* object = NULL; + if (!Converter<T*>::FromV8(isolate, val, &object)) { + return false; + } + *out = gin::Handle<T>(val, object); + return true; + } +}; + +// This function is a convenient way to create a handle from a raw pointer +// without having to write out the type of the object explicitly. +template<typename T> +gin::Handle<T> CreateHandle(v8::Isolate* isolate, T* object) { + v8::Handle<v8::Object> wrapper = object->GetWrapper(isolate); + if (wrapper.IsEmpty()) + return gin::Handle<T>(); + return gin::Handle<T>(wrapper, object); +} + +} // namespace gin + +#endif // GIN_HANDLE_H_
diff --git a/gin/interceptor.cc b/gin/interceptor.cc new file mode 100644 index 0000000..617fd08 --- /dev/null +++ b/gin/interceptor.cc
@@ -0,0 +1,68 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/interceptor.h" + +#include <map> + +#include "gin/per_isolate_data.h" + +namespace gin { + +NamedPropertyInterceptor::NamedPropertyInterceptor(v8::Isolate* isolate, + WrappableBase* base) + : isolate_(isolate), base_(base) { + PerIsolateData::From(isolate_)->SetNamedPropertyInterceptor(base_, this); +} + +NamedPropertyInterceptor::~NamedPropertyInterceptor() { + PerIsolateData::From(isolate_)->ClearNamedPropertyInterceptor(base_, this); +} + +v8::Local<v8::Value> NamedPropertyInterceptor::GetNamedProperty( + v8::Isolate* isolate, + const std::string& property) { + return v8::Local<v8::Value>(); +} + +bool NamedPropertyInterceptor::SetNamedProperty(v8::Isolate* isolate, + const std::string& property, + v8::Local<v8::Value> value) { + return false; +} + +std::vector<std::string> NamedPropertyInterceptor::EnumerateNamedProperties( + v8::Isolate* isolate) { + return std::vector<std::string>(); +} + +IndexedPropertyInterceptor::IndexedPropertyInterceptor(v8::Isolate* isolate, + WrappableBase* base) + : isolate_(isolate), base_(base) { + PerIsolateData::From(isolate_)->SetIndexedPropertyInterceptor(base_, this); +} + +IndexedPropertyInterceptor::~IndexedPropertyInterceptor() { + PerIsolateData::From(isolate_)->ClearIndexedPropertyInterceptor(base_, this); +} + +v8::Local<v8::Value> IndexedPropertyInterceptor::GetIndexedProperty( + v8::Isolate* isolate, + uint32_t index) { + return v8::Local<v8::Value>(); +} + +bool IndexedPropertyInterceptor::SetIndexedProperty( + v8::Isolate* isolate, + uint32_t index, + v8::Local<v8::Value> value) { + return false; +} + +std::vector<uint32_t> IndexedPropertyInterceptor::EnumerateIndexedProperties( + v8::Isolate* isolate) { + return std::vector<uint32_t>(); +} + +} // namespace gin
diff --git a/gin/interceptor.h b/gin/interceptor.h new file mode 100644 index 0000000..43cb346 --- /dev/null +++ b/gin/interceptor.h
@@ -0,0 +1,65 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GIN_INTERCEPTOR_H_ +#define GIN_INTERCEPTOR_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +class WrappableBase; + +// Base class for gin::Wrappable-derived classes that want to implement a +// property interceptor. +class GIN_EXPORT NamedPropertyInterceptor { + public: + NamedPropertyInterceptor(v8::Isolate* isolate, WrappableBase* base); + virtual ~NamedPropertyInterceptor(); + + virtual v8::Local<v8::Value> GetNamedProperty(v8::Isolate* isolate, + const std::string& property); + // Return true if the set was interecepted. + virtual bool SetNamedProperty(v8::Isolate* isolate, + const std::string& property, + v8::Local<v8::Value> value); + virtual std::vector<std::string> EnumerateNamedProperties( + v8::Isolate* isolate); + + private: + v8::Isolate* isolate_; + WrappableBase* base_; + + DISALLOW_COPY_AND_ASSIGN(NamedPropertyInterceptor); +}; + +class GIN_EXPORT IndexedPropertyInterceptor { + public: + IndexedPropertyInterceptor(v8::Isolate* isolate, WrappableBase* base); + virtual ~IndexedPropertyInterceptor(); + + virtual v8::Local<v8::Value> GetIndexedProperty(v8::Isolate* isolate, + uint32_t index); + // Return true if the set was interecepted. + virtual bool SetIndexedProperty(v8::Isolate* isolate, + uint32_t index, + v8::Local<v8::Value> value); + virtual std::vector<uint32_t> EnumerateIndexedProperties( + v8::Isolate* isolate); + + private: + v8::Isolate* isolate_; + WrappableBase* base_; + + DISALLOW_COPY_AND_ASSIGN(IndexedPropertyInterceptor); +}; + +} // namespace gin + +#endif // GIN_INTERCEPTOR_H_
diff --git a/gin/interceptor_unittest.cc b/gin/interceptor_unittest.cc new file mode 100644 index 0000000..d267100 --- /dev/null +++ b/gin/interceptor_unittest.cc
@@ -0,0 +1,196 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "gin/arguments.h" +#include "gin/handle.h" +#include "gin/interceptor.h" +#include "gin/object_template_builder.h" +#include "gin/per_isolate_data.h" +#include "gin/public/isolate_holder.h" +#include "gin/test/v8_test.h" +#include "gin/try_catch.h" +#include "gin/wrappable.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "v8/include/v8-util.h" + +namespace gin { + +class MyInterceptor : public Wrappable<MyInterceptor>, + public NamedPropertyInterceptor, + public IndexedPropertyInterceptor { + public: + static WrapperInfo kWrapperInfo; + + static gin::Handle<MyInterceptor> Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new MyInterceptor(isolate)); + } + + int value() const { return value_; } + void set_value(int value) { value_ = value; } + + // gin::NamedPropertyInterceptor + virtual v8::Local<v8::Value> GetNamedProperty(v8::Isolate* isolate, + const std::string& property) + override { + if (property == "value") { + return ConvertToV8(isolate, value_); + } else if (property == "func") { + return GetFunctionTemplate(isolate, "func")->GetFunction(); + } else { + return v8::Local<v8::Value>(); + } + } + virtual bool SetNamedProperty(v8::Isolate* isolate, + const std::string& property, + v8::Local<v8::Value> value) override { + if (property == "value") { + ConvertFromV8(isolate, value, &value_); + return true; + } + return false; + } + virtual std::vector<std::string> EnumerateNamedProperties( + v8::Isolate* isolate) override { + std::vector<std::string> result; + result.push_back("func"); + result.push_back("value"); + return result; + } + + // gin::IndexedPropertyInterceptor + virtual v8::Local<v8::Value> GetIndexedProperty(v8::Isolate* isolate, + uint32_t index) override { + if (index == 0) + return ConvertToV8(isolate, value_); + return v8::Local<v8::Value>(); + } + virtual bool SetIndexedProperty(v8::Isolate* isolate, + uint32_t index, + v8::Local<v8::Value> value) override { + if (index == 0) { + ConvertFromV8(isolate, value, &value_); + return true; + } + // Don't allow bypassing the interceptor. + return true; + } + virtual std::vector<uint32_t> EnumerateIndexedProperties(v8::Isolate* isolate) + override { + std::vector<uint32_t> result; + result.push_back(0); + return result; + } + + private: + explicit MyInterceptor(v8::Isolate* isolate) + : NamedPropertyInterceptor(isolate, this), + IndexedPropertyInterceptor(isolate, this), + value_(0), + template_cache_(isolate) {} + virtual ~MyInterceptor() {} + + // gin::Wrappable + virtual ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) + override { + return Wrappable<MyInterceptor>::GetObjectTemplateBuilder(isolate) + .AddNamedPropertyInterceptor() + .AddIndexedPropertyInterceptor(); + } + + int Call(int value) { + int tmp = value_; + value_ = value; + return tmp; + } + + v8::Local<v8::FunctionTemplate> GetFunctionTemplate(v8::Isolate* isolate, + const std::string& name) { + v8::Local<v8::FunctionTemplate> function_template = + template_cache_.Get(name); + if (!function_template.IsEmpty()) + return function_template; + function_template = CreateFunctionTemplate( + isolate, base::Bind(&MyInterceptor::Call), HolderIsFirstArgument); + template_cache_.Set(name, function_template); + return function_template; + } + + int value_; + + v8::StdPersistentValueMap<std::string, v8::FunctionTemplate> template_cache_; + + DISALLOW_COPY_AND_ASSIGN(MyInterceptor); +}; + +WrapperInfo MyInterceptor::kWrapperInfo = {kEmbedderNativeGin}; + +class InterceptorTest : public V8Test { + public: + void RunInterceptorTest(const std::string& script_source) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + gin::Handle<MyInterceptor> obj = MyInterceptor::Create(isolate); + + obj->set_value(42); + EXPECT_EQ(42, obj->value()); + + v8::Handle<v8::String> source = StringToV8(isolate, script_source); + EXPECT_FALSE(source.IsEmpty()); + + gin::TryCatch try_catch; + v8::Handle<v8::Script> script = v8::Script::Compile(source); + EXPECT_FALSE(script.IsEmpty()); + v8::Handle<v8::Value> val = script->Run(); + EXPECT_FALSE(val.IsEmpty()); + v8::Handle<v8::Function> func; + EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); + v8::Handle<v8::Value> argv[] = {ConvertToV8(isolate, obj.get()), }; + func->Call(v8::Undefined(isolate), 1, argv); + EXPECT_FALSE(try_catch.HasCaught()); + EXPECT_EQ("", try_catch.GetStackTrace()); + + EXPECT_EQ(191, obj->value()); + } +}; + +TEST_F(InterceptorTest, NamedInterceptor) { + RunInterceptorTest( + "(function (obj) {" + " if (obj.value !== 42) throw 'FAIL';" + " else obj.value = 191; })"); +} + +TEST_F(InterceptorTest, NamedInterceptorCall) { + RunInterceptorTest( + "(function (obj) {" + " if (obj.func(191) !== 42) throw 'FAIL';" + " })"); +} + +TEST_F(InterceptorTest, IndexedInterceptor) { + RunInterceptorTest( + "(function (obj) {" + " if (obj[0] !== 42) throw 'FAIL';" + " else obj[0] = 191; })"); +} + +TEST_F(InterceptorTest, BypassInterceptorAllowed) { + RunInterceptorTest( + "(function (obj) {" + " obj.value = 191 /* make test happy */;" + " obj.foo = 23;" + " if (obj.foo !== 23) throw 'FAIL'; })"); +} + +TEST_F(InterceptorTest, BypassInterceptorForbidden) { + RunInterceptorTest( + "(function (obj) {" + " obj.value = 191 /* make test happy */;" + " obj[1] = 23;" + " if (obj[1] === 23) throw 'FAIL'; })"); +} + +} // namespace gin
diff --git a/gin/isolate_holder.cc b/gin/isolate_holder.cc new file mode 100644 index 0000000..740e136 --- /dev/null +++ b/gin/isolate_holder.cc
@@ -0,0 +1,107 @@ +// 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. + +#include "gin/public/isolate_holder.h" + +#include <stdlib.h> +#include <string.h> + +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/rand_util.h" +#include "base/sys_info.h" +#include "gin/array_buffer.h" +#include "gin/debug_impl.h" +#include "gin/function_template.h" +#include "gin/per_isolate_data.h" +#include "gin/public/v8_platform.h" +#include "gin/run_microtasks_observer.h" + +namespace gin { + +namespace { + +v8::ArrayBuffer::Allocator* g_array_buffer_allocator = NULL; + +bool GenerateEntropy(unsigned char* buffer, size_t amount) { + base::RandBytes(buffer, amount); + return true; +} + +} // namespace + +IsolateHolder::IsolateHolder() { + CHECK(g_array_buffer_allocator) + << "You need to invoke gin::IsolateHolder::Initialize first"; + v8::Isolate::CreateParams params; + params.entry_hook = DebugImpl::GetFunctionEntryHook(); + params.code_event_handler = DebugImpl::GetJitCodeEventHandler(); + params.constraints.ConfigureDefaults(base::SysInfo::AmountOfPhysicalMemory(), + base::SysInfo::AmountOfVirtualMemory(), + base::SysInfo::NumberOfProcessors()); + isolate_ = v8::Isolate::New(params); + isolate_data_.reset(new PerIsolateData(isolate_, g_array_buffer_allocator)); +#if defined(OS_WIN) + { + void* code_range; + size_t size; + isolate_->GetCodeRange(&code_range, &size); + Debug::CodeRangeCreatedCallback callback = + DebugImpl::GetCodeRangeCreatedCallback(); + if (code_range && size && callback) + callback(code_range, size); + } +#endif +} + +IsolateHolder::~IsolateHolder() { + if (task_observer_.get()) + base::MessageLoop::current()->RemoveTaskObserver(task_observer_.get()); +#if defined(OS_WIN) + { + void* code_range; + size_t size; + isolate_->GetCodeRange(&code_range, &size); + Debug::CodeRangeDeletedCallback callback = + DebugImpl::GetCodeRangeDeletedCallback(); + if (code_range && callback) + callback(code_range); + } +#endif + isolate_data_.reset(); + isolate_->Dispose(); +} + +// static +void IsolateHolder::Initialize(ScriptMode mode, + v8::ArrayBuffer::Allocator* allocator) { + CHECK(allocator); + static bool v8_is_initialized = false; + if (v8_is_initialized) + return; + v8::V8::InitializePlatform(V8Platform::Get()); + v8::V8::SetArrayBufferAllocator(allocator); + g_array_buffer_allocator = allocator; + if (mode == gin::IsolateHolder::kStrictMode) { + static const char v8_flags[] = "--use_strict"; + v8::V8::SetFlagsFromString(v8_flags, sizeof(v8_flags) - 1); + } + v8::V8::SetEntropySource(&GenerateEntropy); + v8::V8::Initialize(); + v8_is_initialized = true; +} + +void IsolateHolder::AddRunMicrotasksObserver() { + DCHECK(!task_observer_.get()); + task_observer_.reset(new RunMicrotasksObserver(isolate_));; + base::MessageLoop::current()->AddTaskObserver(task_observer_.get()); +} + +void IsolateHolder::RemoveRunMicrotasksObserver() { + DCHECK(task_observer_.get()); + base::MessageLoop::current()->RemoveTaskObserver(task_observer_.get()); + task_observer_.reset(); +} + +} // namespace gin
diff --git a/gin/modules/console.cc b/gin/modules/console.cc new file mode 100644 index 0000000..d172373 --- /dev/null +++ b/gin/modules/console.cc
@@ -0,0 +1,49 @@ +// 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. + +#include "gin/modules/console.h" + +#include <iostream> + +#include "base/strings/string_util.h" +#include "gin/arguments.h" +#include "gin/converter.h" +#include "gin/object_template_builder.h" +#include "gin/per_isolate_data.h" +#include "gin/public/wrapper_info.h" + +using v8::ObjectTemplate; + +namespace gin { + +namespace { + +void Log(Arguments* args) { + std::vector<std::string> messages; + if (!args->GetRemaining(&messages)) { + args->ThrowError(); + return; + } + std::cout << JoinString(messages, ' ') << std::endl; +} + +WrapperInfo g_wrapper_info = { kEmbedderNativeGin }; + +} // namespace + +const char Console::kModuleName[] = "console"; + +v8::Local<v8::Value> Console::GetModule(v8::Isolate* isolate) { + PerIsolateData* data = PerIsolateData::From(isolate); + v8::Local<ObjectTemplate> templ = data->GetObjectTemplate(&g_wrapper_info); + if (templ.IsEmpty()) { + templ = ObjectTemplateBuilder(isolate) + .SetMethod("log", Log) + .Build(); + data->SetObjectTemplate(&g_wrapper_info, templ); + } + return templ->NewInstance(); +} + +} // namespace gin
diff --git a/gin/modules/console.h b/gin/modules/console.h new file mode 100644 index 0000000..ff8061b --- /dev/null +++ b/gin/modules/console.h
@@ -0,0 +1,23 @@ +// 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. + +#ifndef GIN_MODULES_CONSOLE_H_ +#define GIN_MODULES_CONSOLE_H_ + +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +// The Console module provides a basic API for printing to stdout. Over time, +// we'd like to evolve the API to match window.console in browsers. +class GIN_EXPORT Console { + public: + static const char kModuleName[]; + static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); +}; + +} // namespace gin + +#endif // GIN_MODULES_CONSOLE_H_
diff --git a/gin/modules/file_module_provider.cc b/gin/modules/file_module_provider.cc new file mode 100644 index 0000000..b8465ae --- /dev/null +++ b/gin/modules/file_module_provider.cc
@@ -0,0 +1,70 @@ +// 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. + +#include "gin/modules/file_module_provider.h" + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/string_split.h" +#include "gin/converter.h" + +namespace gin { + +namespace { + +void AttempToLoadModule(const base::WeakPtr<Runner>& runner, + const std::vector<base::FilePath>& search_paths, + const std::string& id) { + if (!runner) + return; + + std::vector<std::string> components; + base::SplitString(id, '/', &components); + + base::FilePath path; + for (size_t i = 0; i < components.size(); ++i) { + // TODO(abarth): Technically the path components can be UTF-8. We don't + // handle that case correctly yet. + path = path.AppendASCII(components[i]); + } + path = path.AddExtension(FILE_PATH_LITERAL("js")); + + for (size_t i = 0; i < search_paths.size(); ++i) { + std::string source; + if (!ReadFileToString(search_paths[i].Append(path), &source)) + continue; + + Runner::Scope scope(runner.get()); + runner->Run(source, id); + return; + } + LOG(ERROR) << "Failed to load module from disk: " << id; +} + +} // namespace + +FileModuleProvider::FileModuleProvider( + const std::vector<base::FilePath>& search_paths) + : search_paths_(search_paths) { +} + +FileModuleProvider::~FileModuleProvider() { +} + +void FileModuleProvider::AttempToLoadModules( + Runner* runner, const std::set<std::string>& ids) { + std::set<std::string> modules = ids; + for (std::set<std::string>::const_iterator it = modules.begin(); + it != modules.end(); ++it) { + const std::string& id = *it; + if (attempted_ids_.count(id)) + continue; + attempted_ids_.insert(id); + base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + AttempToLoadModule, runner->GetWeakPtr(), search_paths_, id)); + } +} + +} // namespace gin
diff --git a/gin/modules/file_module_provider.h b/gin/modules/file_module_provider.h new file mode 100644 index 0000000..dd75a0f --- /dev/null +++ b/gin/modules/file_module_provider.h
@@ -0,0 +1,44 @@ +// 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. + +#ifndef GIN_MODULES_FILE_MODULE_PROVIDER_H_ +#define GIN_MODULES_FILE_MODULE_PROVIDER_H_ + +#include <set> +#include <string> +#include <vector> + +#include "base/files/file_path.h" +#include "gin/gin_export.h" +#include "gin/runner.h" + +namespace gin { + +// FileModuleProvider knows how to load AMD modules off disk. It searches for +// modules in the directories indiciated by |search_paths|. Although we still +// read from the file system on the main thread, we'll eventually want to move +// the reads to a background thread. +class GIN_EXPORT FileModuleProvider { + public: + explicit FileModuleProvider( + const std::vector<base::FilePath>& search_paths); + ~FileModuleProvider(); + + // Searches for modules with |ids| in the file system. If found, the modules + // will be executed asynchronously by |runner|. + void AttempToLoadModules(Runner* runner, const std::set<std::string>& ids); + + private: + std::vector<base::FilePath> search_paths_; + + // We'll only search for a given module once. We remember the set of modules + // we've already looked for in |attempted_ids_|. + std::set<std::string> attempted_ids_; + + DISALLOW_COPY_AND_ASSIGN(FileModuleProvider); +}; + +} // namespace gin + +#endif // GIN_MODULES_FILE_MODULE_PROVIDER_H_
diff --git a/gin/modules/module_registry.cc b/gin/modules/module_registry.cc new file mode 100644 index 0000000..a92a546 --- /dev/null +++ b/gin/modules/module_registry.cc
@@ -0,0 +1,273 @@ +// 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. + +#include "gin/modules/module_registry.h" + +#include <string> +#include <vector> + +#include "base/logging.h" +#include "gin/arguments.h" +#include "gin/converter.h" +#include "gin/modules/module_registry_observer.h" +#include "gin/per_context_data.h" +#include "gin/per_isolate_data.h" +#include "gin/public/wrapper_info.h" +#include "gin/runner.h" + +using v8::Context; +using v8::External; +using v8::Function; +using v8::FunctionTemplate; +using v8::Isolate; +using v8::Local; +using v8::Object; +using v8::ObjectTemplate; +using v8::Persistent; +using v8::StackTrace; +using v8::String; +using v8::Value; + +namespace gin { + +struct PendingModule { + PendingModule(); + ~PendingModule(); + + std::string id; + std::vector<std::string> dependencies; + Persistent<Value> factory; +}; + +PendingModule::PendingModule() { +} + +PendingModule::~PendingModule() { + factory.Reset(); +} + +namespace { + +// Key for base::SupportsUserData::Data. +const char kModuleRegistryKey[] = "ModuleRegistry"; + +struct ModuleRegistryData : public base::SupportsUserData::Data { + scoped_ptr<ModuleRegistry> registry; +}; + +void Define(const v8::FunctionCallbackInfo<Value>& info) { + Arguments args(info); + + if (!info.Length()) + return args.ThrowTypeError("At least one argument is required."); + + std::string id; + std::vector<std::string> dependencies; + v8::Handle<Value> factory; + + if (args.PeekNext()->IsString()) + args.GetNext(&id); + if (args.PeekNext()->IsArray()) + args.GetNext(&dependencies); + if (!args.GetNext(&factory)) + return args.ThrowError(); + + scoped_ptr<PendingModule> pending(new PendingModule); + pending->id = id; + pending->dependencies = dependencies; + pending->factory.Reset(args.isolate(), factory); + + ModuleRegistry* registry = + ModuleRegistry::From(args.isolate()->GetCurrentContext()); + registry->AddPendingModule(args.isolate(), pending.Pass()); +} + +WrapperInfo g_wrapper_info = { kEmbedderNativeGin }; + +Local<FunctionTemplate> GetDefineTemplate(Isolate* isolate) { + PerIsolateData* data = PerIsolateData::From(isolate); + Local<FunctionTemplate> templ = data->GetFunctionTemplate( + &g_wrapper_info); + if (templ.IsEmpty()) { + templ = FunctionTemplate::New(isolate, Define); + data->SetFunctionTemplate(&g_wrapper_info, templ); + } + return templ; +} + +} // namespace + +ModuleRegistry::ModuleRegistry(Isolate* isolate) + : modules_(isolate, Object::New(isolate)) { +} + +ModuleRegistry::~ModuleRegistry() { + modules_.Reset(); +} + +// static +void ModuleRegistry::RegisterGlobals(Isolate* isolate, + v8::Handle<ObjectTemplate> templ) { + templ->Set(StringToSymbol(isolate, "define"), GetDefineTemplate(isolate)); +} + +// static +void ModuleRegistry::InstallGlobals(v8::Isolate* isolate, + v8::Handle<v8::Object> obj) { + obj->Set(StringToSymbol(isolate, "define"), + GetDefineTemplate(isolate)->GetFunction()); +} + +// static +ModuleRegistry* ModuleRegistry::From(v8::Handle<Context> context) { + PerContextData* data = PerContextData::From(context); + if (!data) + return NULL; + + ModuleRegistryData* registry_data = static_cast<ModuleRegistryData*>( + data->GetUserData(kModuleRegistryKey)); + if (!registry_data) { + // PerContextData takes ownership of ModuleRegistryData. + registry_data = new ModuleRegistryData; + registry_data->registry.reset(new ModuleRegistry(context->GetIsolate())); + data->SetUserData(kModuleRegistryKey, registry_data); + } + return registry_data->registry.get(); +} + +void ModuleRegistry::AddObserver(ModuleRegistryObserver* observer) { + observer_list_.AddObserver(observer); +} + +void ModuleRegistry::RemoveObserver(ModuleRegistryObserver* observer) { + observer_list_.RemoveObserver(observer); +} + +void ModuleRegistry::AddBuiltinModule(Isolate* isolate, const std::string& id, + v8::Handle<Value> module) { + DCHECK(!id.empty()); + RegisterModule(isolate, id, module); +} + +void ModuleRegistry::AddPendingModule(Isolate* isolate, + scoped_ptr<PendingModule> pending) { + const std::string pending_id = pending->id; + const std::vector<std::string> pending_dependencies = pending->dependencies; + AttemptToLoad(isolate, pending.Pass()); + FOR_EACH_OBSERVER(ModuleRegistryObserver, observer_list_, + OnDidAddPendingModule(pending_id, pending_dependencies)); +} + +void ModuleRegistry::LoadModule(Isolate* isolate, + const std::string& id, + LoadModuleCallback callback) { + if (available_modules_.find(id) != available_modules_.end()) { + // Should we call the callback asynchronously? + callback.Run(GetModule(isolate, id)); + return; + } + waiting_callbacks_.insert(std::make_pair(id, callback)); + unsatisfied_dependencies_.insert(id); +} + +void ModuleRegistry::RegisterModule(Isolate* isolate, + const std::string& id, + v8::Handle<Value> module) { + if (id.empty() || module.IsEmpty()) + return; + + unsatisfied_dependencies_.erase(id); + available_modules_.insert(id); + v8::Handle<Object> modules = Local<Object>::New(isolate, modules_); + modules->Set(StringToSymbol(isolate, id), module); + + std::pair<LoadModuleCallbackMap::iterator, LoadModuleCallbackMap::iterator> + range = waiting_callbacks_.equal_range(id); + std::vector<LoadModuleCallback> callbacks; + callbacks.reserve(waiting_callbacks_.count(id)); + for (LoadModuleCallbackMap::iterator it = range.first; it != range.second; + ++it) { + callbacks.push_back(it->second); + } + waiting_callbacks_.erase(range.first, range.second); + for (std::vector<LoadModuleCallback>::iterator it = callbacks.begin(); + it != callbacks.end(); + ++it) { + // Should we call the callback asynchronously? + it->Run(module); + } +} + +bool ModuleRegistry::CheckDependencies(PendingModule* pending) { + size_t num_missing_dependencies = 0; + size_t len = pending->dependencies.size(); + for (size_t i = 0; i < len; ++i) { + const std::string& dependency = pending->dependencies[i]; + if (available_modules_.count(dependency)) + continue; + unsatisfied_dependencies_.insert(dependency); + num_missing_dependencies++; + } + return num_missing_dependencies == 0; +} + +void ModuleRegistry::Load(Isolate* isolate, scoped_ptr<PendingModule> pending) { + if (!pending->id.empty() && available_modules_.count(pending->id)) + return; // We've already loaded this module. + + uint32_t argc = static_cast<uint32_t>(pending->dependencies.size()); + std::vector<v8::Handle<Value> > argv(argc); + for (uint32_t i = 0; i < argc; ++i) + argv[i] = GetModule(isolate, pending->dependencies[i]); + + v8::Handle<Value> module = Local<Value>::New(isolate, pending->factory); + + v8::Handle<Function> factory; + if (ConvertFromV8(isolate, module, &factory)) { + PerContextData* data = PerContextData::From(isolate->GetCurrentContext()); + Runner* runner = data->runner(); + module = runner->Call(factory, runner->global(), argc, + argv.empty() ? NULL : &argv.front()); + if (pending->id.empty()) + ConvertFromV8(isolate, factory->GetScriptOrigin().ResourceName(), + &pending->id); + } + + RegisterModule(isolate, pending->id, module); +} + +bool ModuleRegistry::AttemptToLoad(Isolate* isolate, + scoped_ptr<PendingModule> pending) { + if (!CheckDependencies(pending.get())) { + pending_modules_.push_back(pending.release()); + return false; + } + Load(isolate, pending.Pass()); + return true; +} + +v8::Handle<v8::Value> ModuleRegistry::GetModule(v8::Isolate* isolate, + const std::string& id) { + v8::Handle<Object> modules = Local<Object>::New(isolate, modules_); + v8::Handle<String> key = StringToSymbol(isolate, id); + DCHECK(modules->HasOwnProperty(key)); + return modules->Get(key); +} + +void ModuleRegistry::AttemptToLoadMoreModules(Isolate* isolate) { + bool keep_trying = true; + while (keep_trying) { + keep_trying = false; + PendingModuleVector pending_modules; + pending_modules.swap(pending_modules_); + for (size_t i = 0; i < pending_modules.size(); ++i) { + scoped_ptr<PendingModule> pending(pending_modules[i]); + pending_modules[i] = NULL; + if (AttemptToLoad(isolate, pending.Pass())) + keep_trying = true; + } + } +} + +} // namespace gin
diff --git a/gin/modules/module_registry.h b/gin/modules/module_registry.h new file mode 100644 index 0000000..439207e --- /dev/null +++ b/gin/modules/module_registry.h
@@ -0,0 +1,109 @@ +// 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. + +#ifndef GIN_MODULES_MODULE_REGISTRY_H_ +#define GIN_MODULES_MODULE_REGISTRY_H_ + +#include <list> +#include <map> +#include <set> +#include <string> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/observer_list.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +class ModuleRegistryObserver; +struct PendingModule; + +// This class implements the Asynchronous Module Definition (AMD) API. +// https://github.com/amdjs/amdjs-api/wiki/AMD +// +// Our implementation isn't complete yet. Missing features: +// 1) Built-in support for require, exports, and module. +// 2) Path resoltuion in module names. +// +// For these reasons, we don't have an "amd" property on the "define" +// function. The spec says we should only add that property once our +// implementation complies with the specification. +// +class GIN_EXPORT ModuleRegistry { + public: + typedef base::Callback<void (v8::Handle<v8::Value>)> LoadModuleCallback; + + virtual ~ModuleRegistry(); + + static ModuleRegistry* From(v8::Handle<v8::Context> context); + + static void RegisterGlobals(v8::Isolate* isolate, + v8::Handle<v8::ObjectTemplate> templ); + + // Installs the necessary functions needed for modules. + // WARNING: this may execute script in the page. + static void InstallGlobals(v8::Isolate* isolate, v8::Handle<v8::Object> obj); + + void AddObserver(ModuleRegistryObserver* observer); + void RemoveObserver(ModuleRegistryObserver* observer); + + // The caller must have already entered our context. + void AddBuiltinModule(v8::Isolate* isolate, const std::string& id, + v8::Handle<v8::Value> module); + + // The caller must have already entered our context. + void AddPendingModule(v8::Isolate* isolate, + scoped_ptr<PendingModule> pending); + + void LoadModule(v8::Isolate* isolate, + const std::string& id, + LoadModuleCallback callback); + + // The caller must have already entered our context. + void AttemptToLoadMoreModules(v8::Isolate* isolate); + + const std::set<std::string>& available_modules() const { + return available_modules_; + } + + const std::set<std::string>& unsatisfied_dependencies() const { + return unsatisfied_dependencies_; + } + + private: + typedef ScopedVector<PendingModule> PendingModuleVector; + typedef std::multimap<std::string, LoadModuleCallback> LoadModuleCallbackMap; + + explicit ModuleRegistry(v8::Isolate* isolate); + + void Load(v8::Isolate* isolate, scoped_ptr<PendingModule> pending); + void RegisterModule(v8::Isolate* isolate, + const std::string& id, + v8::Handle<v8::Value> module); + + bool CheckDependencies(PendingModule* pending); + bool AttemptToLoad(v8::Isolate* isolate, scoped_ptr<PendingModule> pending); + + v8::Handle<v8::Value> GetModule(v8::Isolate* isolate, const std::string& id); + + std::set<std::string> available_modules_; + std::set<std::string> unsatisfied_dependencies_; + + LoadModuleCallbackMap waiting_callbacks_; + + PendingModuleVector pending_modules_; + v8::Persistent<v8::Object> modules_; + + ObserverList<ModuleRegistryObserver> observer_list_; + + DISALLOW_COPY_AND_ASSIGN(ModuleRegistry); +}; + +} // namespace gin + +#endif // GIN_MODULES_MODULE_REGISTRY_H_
diff --git a/gin/modules/module_registry_observer.h b/gin/modules/module_registry_observer.h new file mode 100644 index 0000000..68ee4ad --- /dev/null +++ b/gin/modules/module_registry_observer.h
@@ -0,0 +1,31 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GIN_MODULES_MODULE_REGISTRY_OBSERVER_H_ +#define GIN_MODULES_MODULE_REGISTRY_OBSERVER_H_ + +#include <string> +#include <vector> + +#include "gin/gin_export.h" + +namespace gin { + +// Notified of interesting events from ModuleRegistry. +class GIN_EXPORT ModuleRegistryObserver { + public: + // Called from AddPendingModule(). |id| is the id/name of the module and + // |dependencies| this list of modules |id| depends upon. + virtual void OnDidAddPendingModule( + const std::string& id, + const std::vector<std::string>& dependencies) = 0; + + protected: + virtual ~ModuleRegistryObserver() {} +}; + +} // namespace gin + +#endif // GIN_MODULES_MODULE_REGISTRY_OBSERVER_H_ +
diff --git a/gin/modules/module_registry_unittest.cc b/gin/modules/module_registry_unittest.cc new file mode 100644 index 0000000..77bf9f1 --- /dev/null +++ b/gin/modules/module_registry_unittest.cc
@@ -0,0 +1,136 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/modules/module_registry.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "gin/modules/module_registry_observer.h" +#include "gin/modules/module_runner_delegate.h" +#include "gin/public/context_holder.h" +#include "gin/public/isolate_holder.h" +#include "gin/shell_runner.h" +#include "gin/test/v8_test.h" +#include "v8/include/v8.h" + +namespace gin { + +namespace { + +struct TestHelper { + TestHelper(v8::Isolate* isolate) + : delegate(std::vector<base::FilePath>()), + runner(new ShellRunner(&delegate, isolate)), + scope(runner.get()) { + } + + base::MessageLoop message_loop; + ModuleRunnerDelegate delegate; + scoped_ptr<ShellRunner> runner; + Runner::Scope scope; +}; + +class ModuleRegistryObserverImpl : public ModuleRegistryObserver { + public: + ModuleRegistryObserverImpl() : did_add_count_(0) {} + + virtual void OnDidAddPendingModule( + const std::string& id, + const std::vector<std::string>& dependencies) OVERRIDE { + did_add_count_++; + id_ = id; + dependencies_ = dependencies; + } + + int did_add_count() { return did_add_count_; } + const std::string& id() const { return id_; } + const std::vector<std::string>& dependencies() const { return dependencies_; } + + private: + int did_add_count_; + std::string id_; + std::vector<std::string> dependencies_; + + DISALLOW_COPY_AND_ASSIGN(ModuleRegistryObserverImpl); +}; + +void NestedCallback(v8::Handle<v8::Value> value) { + FAIL() << "Should not be called"; +} + +void OnModuleLoaded(TestHelper* helper, + v8::Isolate* isolate, + int64_t* counter, + v8::Handle<v8::Value> value) { + ASSERT_TRUE(value->IsNumber()); + v8::Handle<v8::Integer> int_value = v8::Handle<v8::Integer>::Cast(value); + *counter += int_value->Value(); + ModuleRegistry::From(helper->runner->GetContextHolder()->context()) + ->LoadModule(isolate, "two", base::Bind(NestedCallback)); +} + +} // namespace + +typedef V8Test ModuleRegistryTest; + +// Verifies ModuleRegistry is not available after ContextHolder has been +// deleted. +TEST_F(ModuleRegistryTest, DestroyedWithContext) { + v8::Isolate::Scope isolate_scope(instance_->isolate()); + v8::HandleScope handle_scope(instance_->isolate()); + v8::Handle<v8::Context> context = v8::Context::New( + instance_->isolate(), NULL, v8::Handle<v8::ObjectTemplate>()); + { + ContextHolder context_holder(instance_->isolate()); + context_holder.SetContext(context); + ModuleRegistry* registry = ModuleRegistry::From(context); + EXPECT_TRUE(registry != NULL); + } + ModuleRegistry* registry = ModuleRegistry::From(context); + EXPECT_TRUE(registry == NULL); +} + +// Verifies ModuleRegistryObserver is notified appropriately. +TEST_F(ModuleRegistryTest, ModuleRegistryObserverTest) { + TestHelper helper(instance_->isolate()); + std::string source = + "define('id', ['dep1', 'dep2'], function() {" + " return function() {};" + "});"; + + ModuleRegistryObserverImpl observer; + ModuleRegistry::From(helper.runner->GetContextHolder()->context())-> + AddObserver(&observer); + helper.runner->Run(source, "script"); + ModuleRegistry::From(helper.runner->GetContextHolder()->context())-> + RemoveObserver(&observer); + EXPECT_EQ(1, observer.did_add_count()); + EXPECT_EQ("id", observer.id()); + ASSERT_EQ(2u, observer.dependencies().size()); + EXPECT_EQ("dep1", observer.dependencies()[0]); + EXPECT_EQ("dep2", observer.dependencies()[1]); +} + +// Verifies that multiple LoadModule calls for the same module are handled +// correctly. +TEST_F(ModuleRegistryTest, LoadModuleTest) { + TestHelper helper(instance_->isolate()); + int64_t counter = 0; + std::string source = + "define('one', [], function() {" + " return 1;" + "});"; + + ModuleRegistry::LoadModuleCallback callback = + base::Bind(OnModuleLoaded, &helper, instance_->isolate(), &counter); + for (int i = 0; i < 3; i++) { + ModuleRegistry::From(helper.runner->GetContextHolder()->context()) + ->LoadModule(instance_->isolate(), "one", callback); + } + EXPECT_EQ(0, counter); + helper.runner->Run(source, "script"); + EXPECT_EQ(3, counter); +} + +} // namespace gin
diff --git a/gin/modules/module_registry_unittests.js b/gin/modules/module_registry_unittests.js new file mode 100644 index 0000000..ca70148 --- /dev/null +++ b/gin/modules/module_registry_unittests.js
@@ -0,0 +1,30 @@ +// 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. + +define("module0", function() { + return { + "foo": "bar", + } +}); + +define("module2", [ + "gtest", + "module0", + "module1" + ], function(gtest, module0, module1) { + gtest.expectEqual(module0.foo, "bar", + "module0.foo is " + module0.foo); + gtest.expectFalse(module0.bar, + "module0.bar is " + module0.bar); + gtest.expectEqual(module1.baz, "qux", + "module1.baz is " + module1.baz); + gtest.expectFalse(module1.qux, + "module1.qux is " + module1.qux); + + this.result = "PASS"; +}); + +define("module1", { + "baz": "qux", +});
diff --git a/gin/modules/module_runner_delegate.cc b/gin/modules/module_runner_delegate.cc new file mode 100644 index 0000000..9d8ce2e --- /dev/null +++ b/gin/modules/module_runner_delegate.cc
@@ -0,0 +1,67 @@ +// 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. + +#include "gin/modules/module_runner_delegate.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "gin/modules/module_registry.h" +#include "gin/object_template_builder.h" +#include "gin/public/context_holder.h" + +namespace gin { + +ModuleRunnerDelegate::ModuleRunnerDelegate( + const std::vector<base::FilePath>& search_paths) + : module_provider_(search_paths) { +} + +ModuleRunnerDelegate::~ModuleRunnerDelegate() { +} + +void ModuleRunnerDelegate::AddBuiltinModule(const std::string& id, + ModuleGetter getter) { + builtin_modules_[id] = base::Bind(getter); +} + +void ModuleRunnerDelegate::AddBuiltinModule(const std::string& id, + const ModuleGetterCallback& getter) { + builtin_modules_[id] = getter; +} + +void ModuleRunnerDelegate::AttemptToLoadMoreModules(Runner* runner) { + ModuleRegistry* registry = ModuleRegistry::From( + runner->GetContextHolder()->context()); + registry->AttemptToLoadMoreModules(runner->GetContextHolder()->isolate()); + module_provider_.AttempToLoadModules( + runner, registry->unsatisfied_dependencies()); +} + +v8::Handle<v8::ObjectTemplate> ModuleRunnerDelegate::GetGlobalTemplate( + ShellRunner* runner, + v8::Isolate* isolate) { + v8::Handle<v8::ObjectTemplate> templ = ObjectTemplateBuilder(isolate).Build(); + ModuleRegistry::RegisterGlobals(isolate, templ); + return templ; +} + +void ModuleRunnerDelegate::DidCreateContext(ShellRunner* runner) { + ShellRunnerDelegate::DidCreateContext(runner); + + v8::Handle<v8::Context> context = runner->GetContextHolder()->context(); + ModuleRegistry* registry = ModuleRegistry::From(context); + + v8::Isolate* isolate = runner->GetContextHolder()->isolate(); + + for (BuiltinModuleMap::const_iterator it = builtin_modules_.begin(); + it != builtin_modules_.end(); ++it) { + registry->AddBuiltinModule(isolate, it->first, it->second.Run(isolate)); + } +} + +void ModuleRunnerDelegate::DidRunScript(ShellRunner* runner) { + AttemptToLoadMoreModules(runner); +} + +} // namespace gin
diff --git a/gin/modules/module_runner_delegate.h b/gin/modules/module_runner_delegate.h new file mode 100644 index 0000000..df91f63 --- /dev/null +++ b/gin/modules/module_runner_delegate.h
@@ -0,0 +1,56 @@ +// 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. + +#ifndef GIN_MODULES_MODULE_RUNNER_DELEGATE_H_ +#define GIN_MODULES_MODULE_RUNNER_DELEGATE_H_ + +#include <map> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "gin/gin_export.h" +#include "gin/modules/file_module_provider.h" +#include "gin/shell_runner.h" +#include "v8/include/v8.h" + +namespace gin { + +typedef v8::Local<v8::Value> (*ModuleGetter)(v8::Isolate* isolate); +typedef base::Callback<v8::Local<v8::Value>(v8::Isolate*)> ModuleGetterCallback; + +// Emebedders that use AMD modules will probably want to use a RunnerDelegate +// that inherits from ModuleRunnerDelegate. ModuleRunnerDelegate lets embedders +// register built-in modules and routes module requests to FileModuleProvider. +class GIN_EXPORT ModuleRunnerDelegate : public ShellRunnerDelegate { + public: + explicit ModuleRunnerDelegate( + const std::vector<base::FilePath>& search_paths); + virtual ~ModuleRunnerDelegate(); + + void AddBuiltinModule(const std::string& id, ModuleGetter getter); + void AddBuiltinModule(const std::string& id, + const ModuleGetterCallback& getter); + + protected: + void AttemptToLoadMoreModules(Runner* runner); + + private: + typedef std::map<std::string, ModuleGetterCallback> BuiltinModuleMap; + + // From ShellRunnerDelegate: + virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate( + ShellRunner* runner, + v8::Isolate* isolate) OVERRIDE; + virtual void DidCreateContext(ShellRunner* runner) OVERRIDE; + virtual void DidRunScript(ShellRunner* runner) OVERRIDE; + + BuiltinModuleMap builtin_modules_; + FileModuleProvider module_provider_; + + DISALLOW_COPY_AND_ASSIGN(ModuleRunnerDelegate); +}; + +} // namespace gin + +#endif // GIN_MODULES_MODULE_RUNNER_DELEGATE_H_
diff --git a/gin/modules/timer.cc b/gin/modules/timer.cc new file mode 100644 index 0000000..4ba7f91 --- /dev/null +++ b/gin/modules/timer.cc
@@ -0,0 +1,103 @@ +// 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. + +#include "gin/modules/timer.h" + +#include "base/bind.h" +#include "gin/object_template_builder.h" +#include "gin/per_context_data.h" + +namespace gin { + +namespace { + +v8::Handle<v8::String> GetHiddenPropertyName(v8::Isolate* isolate) { + return gin::StringToSymbol(isolate, "::gin::Timer"); +} + +} // namespace + +// Timer ======================================================================= + +gin::WrapperInfo Timer::kWrapperInfo = { gin::kEmbedderNativeGin }; + +// static +Handle<Timer> Timer::Create(TimerType type, v8::Isolate* isolate, int delay_ms, + v8::Handle<v8::Function> function) { + return CreateHandle(isolate, new Timer(isolate, type == TYPE_REPEATING, + delay_ms, function)); +} + +ObjectTemplateBuilder Timer::GetObjectTemplateBuilder(v8::Isolate* isolate) { + // We use Unretained() here because we directly own timer_, so we know it will + // be alive when these methods are called. + return Wrappable<Timer>::GetObjectTemplateBuilder(isolate) + .SetMethod("cancel", + base::Bind(&base::Timer::Stop, base::Unretained(&timer_))) + .SetMethod("reset", + base::Bind(&base::Timer::Reset, base::Unretained(&timer_))); +} + +Timer::Timer(v8::Isolate* isolate, bool repeating, int delay_ms, + v8::Handle<v8::Function> function) + : timer_(false, repeating), + runner_(PerContextData::From( + isolate->GetCurrentContext())->runner()->GetWeakPtr()), + weak_factory_(this) { + GetWrapper(runner_->GetContextHolder()->isolate())->SetHiddenValue( + GetHiddenPropertyName(isolate), function); + timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay_ms), + base::Bind(&Timer::OnTimerFired, weak_factory_.GetWeakPtr())); +} + +Timer::~Timer() { +} + +void Timer::OnTimerFired() { + // This can happen in spite of the weak callback because it is possible for + // a gin::Handle<> to keep this object alive past when the isolate it is part + // of is destroyed. + if (!runner_.get()) { + return; + } + + Runner::Scope scope(runner_.get()); + v8::Isolate* isolate = runner_->GetContextHolder()->isolate(); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast( + GetWrapper(isolate)->GetHiddenValue(GetHiddenPropertyName(isolate))); + runner_->Call(function, v8::Undefined(isolate), 0, NULL); +} + + +// TimerModule ================================================================= + +const char TimerModule::kName[] = "timer"; +WrapperInfo TimerModule::kWrapperInfo = { kEmbedderNativeGin }; + +// static +Handle<TimerModule> TimerModule::Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new TimerModule()); +} + +// static +v8::Local<v8::Value> TimerModule::GetModule(v8::Isolate* isolate) { + return Create(isolate)->GetWrapper(isolate); +} + +TimerModule::TimerModule() { +} + +TimerModule::~TimerModule() { +} + +ObjectTemplateBuilder TimerModule::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return Wrappable<TimerModule>::GetObjectTemplateBuilder(isolate) + .SetMethod("createOneShot", + base::Bind(&Timer::Create, Timer::TYPE_ONE_SHOT)) + .SetMethod("createRepeating", + base::Bind(&Timer::Create, Timer::TYPE_REPEATING)); +} + +} // namespace gin
diff --git a/gin/modules/timer.h b/gin/modules/timer.h new file mode 100644 index 0000000..1ec0b93 --- /dev/null +++ b/gin/modules/timer.h
@@ -0,0 +1,66 @@ +// 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. + +#ifndef GIN_MODULES_TIMER_H_ +#define GIN_MODULES_TIMER_H_ + +#include "base/memory/weak_ptr.h" +#include "base/timer/timer.h" +#include "gin/gin_export.h" +#include "gin/handle.h" +#include "gin/runner.h" +#include "gin/wrappable.h" +#include "v8/include/v8.h" + +namespace gin { + +class ObjectTemplateBuilder; + +// A simple scriptable timer that can work in one-shot or repeating mode. +class GIN_EXPORT Timer : public Wrappable<Timer> { + public: + enum TimerType { + TYPE_ONE_SHOT, + TYPE_REPEATING + }; + + static WrapperInfo kWrapperInfo; + static Handle<Timer> Create(TimerType type, v8::Isolate* isolate, + int delay_ms, v8::Handle<v8::Function> function); + + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE; + + private: + Timer(v8::Isolate* isolate, bool repeating, int delay_ms, + v8::Handle<v8::Function> function); + virtual ~Timer(); + void OnTimerFired(); + + base::Timer timer_; + base::WeakPtr<gin::Runner> runner_; + base::WeakPtrFactory<Timer> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Timer); +}; + + +class GIN_EXPORT TimerModule : public Wrappable<TimerModule> { + public: + static const char kName[]; + static WrapperInfo kWrapperInfo; + static Handle<TimerModule> Create(v8::Isolate* isolate); + static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); + + private: + TimerModule(); + virtual ~TimerModule(); + + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE; +}; + +} // namespace gin + +#endif // GIN_MODULES_TIMER_H_
diff --git a/gin/modules/timer_unittest.cc b/gin/modules/timer_unittest.cc new file mode 100644 index 0000000..f7fd8f2 --- /dev/null +++ b/gin/modules/timer_unittest.cc
@@ -0,0 +1,155 @@ +// 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. + +#include "gin/modules/timer.h" + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "gin/handle.h" +#include "gin/object_template_builder.h" +#include "gin/public/isolate_holder.h" +#include "gin/shell_runner.h" +#include "gin/test/v8_test.h" +#include "gin/try_catch.h" +#include "gin/wrappable.h" +#include "v8/include/v8.h" + +namespace gin { + +namespace { + +class Result : public Wrappable<Result> { + public: + static WrapperInfo kWrapperInfo; + static Handle<Result> Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new Result()); + } + + int count() const { return count_; } + void set_count(int count) { count_ = count; } + + void Quit() { + base::MessageLoop::current()->QuitNow(); + } + + private: + Result() : count_(0) { + } + + virtual ~Result() { + } + + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE { + return Wrappable<Result>::GetObjectTemplateBuilder(isolate) + .SetProperty("count", &Result::count, &Result::set_count) + .SetMethod("quit", &Result::Quit); + } + + int count_; +}; + +WrapperInfo Result::kWrapperInfo = { gin::kEmbedderNativeGin }; + +struct TestHelper { + TestHelper(v8::Isolate* isolate) + : runner(new ShellRunner(&delegate, isolate)), + scope(runner.get()), + timer_module(TimerModule::Create(isolate)), + result(Result::Create(isolate)) { + EXPECT_FALSE(runner->global().IsEmpty()); + runner->global()->Set(StringToV8(isolate, "timer"), + timer_module->GetWrapper(isolate)); + runner->global()->Set(StringToV8(isolate, "result"), + result->GetWrapper(isolate)); + } + + void QuitSoon() { + loop.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), + base::TimeDelta::FromMilliseconds(0)); + } + + ShellRunnerDelegate delegate; + scoped_ptr<ShellRunner> runner; + Runner::Scope scope; + Handle<TimerModule> timer_module; + Handle<Result> result; + base::MessageLoop loop; +}; + +} // namespace + +typedef V8Test TimerUnittest; + +TEST_F(TimerUnittest, OneShot) { + TestHelper helper(instance_->isolate()); + std::string source = + "timer.createOneShot(0, function() {" + " result.count++;" + "});"; + + helper.runner->Run(source, "script"); + EXPECT_EQ(0, helper.result->count()); + + helper.QuitSoon(); + helper.loop.Run(); + EXPECT_EQ(1, helper.result->count()); +} + +TEST_F(TimerUnittest, OneShotCancel) { + TestHelper helper(instance_->isolate()); + std::string source = + "var t = timer.createOneShot(0, function() {" + " result.count++;" + "});" + "t.cancel()"; + + helper.runner->Run(source, "script"); + EXPECT_EQ(0, helper.result->count()); + + helper.QuitSoon(); + helper.loop.Run(); + EXPECT_EQ(0, helper.result->count()); +} + +TEST_F(TimerUnittest, Repeating) { + TestHelper helper(instance_->isolate()); + + // TODO(aa): Cannot do: if (++result.count == 3) because of v8 bug. Create + // test case and report. + std::string source = + "timer.createRepeating(0, function() {" + " result.count++;" + " if (result.count == 3) {" + " result.quit();" + " }" + "});"; + + helper.runner->Run(source, "script"); + EXPECT_EQ(0, helper.result->count()); + + helper.loop.Run(); + EXPECT_EQ(3, helper.result->count()); +} + +TEST_F(TimerUnittest, TimerCallbackToDestroyedRunner) { + TestHelper helper(instance_->isolate()); + std::string source = + "timer.createOneShot(0, function() {" + " result.count++;" + "});"; + + helper.runner->Run(source, "script"); + EXPECT_EQ(0, helper.result->count()); + + // Destroy runner, which should destroy the timer object we created. + helper.QuitSoon(); + helper.runner.reset(NULL); + helper.loop.Run(); + + // Timer should not have run because it was deleted. + EXPECT_EQ(0, helper.result->count()); +} + +} // namespace gin
diff --git a/gin/object_template_builder.cc b/gin/object_template_builder.cc new file mode 100644 index 0000000..f649d34 --- /dev/null +++ b/gin/object_template_builder.cc
@@ -0,0 +1,181 @@ +// 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. + +#include "gin/object_template_builder.h" + +#include "gin/interceptor.h" +#include "gin/per_isolate_data.h" +#include "gin/public/wrapper_info.h" + +namespace gin { + +namespace { + +WrappableBase* WrappableFromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val) { + if (!val->IsObject()) + return NULL; + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val); + WrapperInfo* info = WrapperInfo::From(obj); + + // If this fails, the object is not managed by Gin. + if (!info) + return NULL; + + // We don't further validate the type of the object, but assume it's derived + // from WrappableBase. We look up the pointer in a global registry, to make + // sure it's actually pointed to a valid life object. + return static_cast<WrappableBase*>( + obj->GetAlignedPointerFromInternalField(kEncodedValueIndex)); +} + +NamedPropertyInterceptor* NamedInterceptorFromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val) { + WrappableBase* base = WrappableFromV8(isolate, val); + if (!base) + return NULL; + return PerIsolateData::From(isolate)->GetNamedPropertyInterceptor(base); +} + +IndexedPropertyInterceptor* IndexedInterceptorFromV8( + v8::Isolate* isolate, + v8::Handle<v8::Value> val) { + WrappableBase* base = WrappableFromV8(isolate, val); + if (!base) + return NULL; + return PerIsolateData::From(isolate)->GetIndexedPropertyInterceptor(base); +} + +void NamedPropertyGetter(v8::Local<v8::String> property, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + NamedPropertyInterceptor* interceptor = + NamedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + std::string name; + ConvertFromV8(isolate, property, &name); + info.GetReturnValue().Set(interceptor->GetNamedProperty(isolate, name)); +} + +void NamedPropertySetter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + NamedPropertyInterceptor* interceptor = + NamedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + std::string name; + ConvertFromV8(isolate, property, &name); + if (interceptor->SetNamedProperty(isolate, name, value)) + info.GetReturnValue().Set(value); +} + +void NamedPropertyQuery(v8::Local<v8::String> property, + const v8::PropertyCallbackInfo<v8::Integer>& info) { + v8::Isolate* isolate = info.GetIsolate(); + NamedPropertyInterceptor* interceptor = + NamedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + std::string name; + ConvertFromV8(isolate, property, &name); + if (interceptor->GetNamedProperty(isolate, name).IsEmpty()) + return; + info.GetReturnValue().Set(0); +} + +void NamedPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) { + v8::Isolate* isolate = info.GetIsolate(); + NamedPropertyInterceptor* interceptor = + NamedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + info.GetReturnValue().Set(v8::Handle<v8::Array>::Cast( + ConvertToV8(isolate, interceptor->EnumerateNamedProperties(isolate)))); +} + +void IndexedPropertyGetter(uint32_t index, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + IndexedPropertyInterceptor* interceptor = + IndexedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + info.GetReturnValue().Set(interceptor->GetIndexedProperty(isolate, index)); +} + +void IndexedPropertySetter(uint32_t index, + v8::Local<v8::Value> value, + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + IndexedPropertyInterceptor* interceptor = + IndexedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + if (interceptor->SetIndexedProperty(isolate, index, value)) + info.GetReturnValue().Set(value); +} + +void IndexedPropertyEnumerator( + const v8::PropertyCallbackInfo<v8::Array>& info) { + v8::Isolate* isolate = info.GetIsolate(); + IndexedPropertyInterceptor* interceptor = + IndexedInterceptorFromV8(isolate, info.Holder()); + if (!interceptor) + return; + info.GetReturnValue().Set(v8::Handle<v8::Array>::Cast( + ConvertToV8(isolate, interceptor->EnumerateIndexedProperties(isolate)))); +} + +} // namespace + +ObjectTemplateBuilder::ObjectTemplateBuilder(v8::Isolate* isolate) + : isolate_(isolate), template_(v8::ObjectTemplate::New(isolate)) { + template_->SetInternalFieldCount(kNumberOfInternalFields); +} + +ObjectTemplateBuilder::~ObjectTemplateBuilder() { +} + +ObjectTemplateBuilder& ObjectTemplateBuilder::AddNamedPropertyInterceptor() { + template_->SetNamedPropertyHandler(&NamedPropertyGetter, + &NamedPropertySetter, + &NamedPropertyQuery, + NULL, + &NamedPropertyEnumerator); + return *this; +} + +ObjectTemplateBuilder& ObjectTemplateBuilder::AddIndexedPropertyInterceptor() { + template_->SetIndexedPropertyHandler(&IndexedPropertyGetter, + &IndexedPropertySetter, + NULL, + NULL, + &IndexedPropertyEnumerator); + return *this; +} + +ObjectTemplateBuilder& ObjectTemplateBuilder::SetImpl( + const base::StringPiece& name, v8::Handle<v8::Data> val) { + template_->Set(StringToSymbol(isolate_, name), val); + return *this; +} + +ObjectTemplateBuilder& ObjectTemplateBuilder::SetPropertyImpl( + const base::StringPiece& name, v8::Handle<v8::FunctionTemplate> getter, + v8::Handle<v8::FunctionTemplate> setter) { + template_->SetAccessorProperty(StringToSymbol(isolate_, name), getter, + setter); + return *this; +} + +v8::Local<v8::ObjectTemplate> ObjectTemplateBuilder::Build() { + v8::Local<v8::ObjectTemplate> result = template_; + template_.Clear(); + return result; +} + +} // namespace gin
diff --git a/gin/object_template_builder.h b/gin/object_template_builder.h new file mode 100644 index 0000000..3d025a9 --- /dev/null +++ b/gin/object_template_builder.h
@@ -0,0 +1,147 @@ +// 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. + +#ifndef GIN_OBJECT_TEMPLATE_BUILDER_H_ +#define GIN_OBJECT_TEMPLATE_BUILDER_H_ + +#include "base/bind.h" +#include "base/callback.h" +#include "base/strings/string_piece.h" +#include "base/template_util.h" +#include "gin/converter.h" +#include "gin/function_template.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +namespace { + +// Base template - used only for non-member function pointers. Other types +// either go to one of the below specializations, or go here and fail to compile +// because of base::Bind(). +template<typename T, typename Enable = void> +struct CallbackTraits { + static v8::Handle<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate, + T callback) { + return CreateFunctionTemplate(isolate, base::Bind(callback)); + } + static void SetAsFunctionHandler(v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> tmpl, + T callback) { + CreateFunctionHandler(isolate, tmpl, base::Bind(callback)); + } +}; + +// Specialization for base::Callback. +template<typename T> +struct CallbackTraits<base::Callback<T> > { + static v8::Handle<v8::FunctionTemplate> CreateTemplate( + v8::Isolate* isolate, const base::Callback<T>& callback) { + return CreateFunctionTemplate(isolate, callback); + } + static void SetAsFunctionHandler(v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> tmpl, + const base::Callback<T>& callback) { + CreateFunctionHandler(isolate, tmpl, callback); + } +}; + +// Specialization for member function pointers. We need to handle this case +// specially because the first parameter for callbacks to MFP should typically +// come from the the JavaScript "this" object the function was called on, not +// from the first normal parameter. +template<typename T> +struct CallbackTraits<T, typename base::enable_if< + base::is_member_function_pointer<T>::value>::type> { + static v8::Handle<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate, + T callback) { + return CreateFunctionTemplate(isolate, base::Bind(callback), + HolderIsFirstArgument); + } + static void SetAsFunctionHandler(v8::Isolate* isolate, + v8::Local<v8::ObjectTemplate> tmpl, + T callback) { + CreateFunctionHandler( + isolate, tmpl, base::Bind(callback), HolderIsFirstArgument); + } +}; + +// This specialization allows people to construct function templates directly if +// they need to do fancier stuff. +template<> +struct GIN_EXPORT CallbackTraits<v8::Handle<v8::FunctionTemplate> > { + static v8::Handle<v8::FunctionTemplate> CreateTemplate( + v8::Handle<v8::FunctionTemplate> templ) { + return templ; + } +}; + +} // namespace + + +// ObjectTemplateBuilder provides a handy interface to creating +// v8::ObjectTemplate instances with various sorts of properties. +class GIN_EXPORT ObjectTemplateBuilder { + public: + explicit ObjectTemplateBuilder(v8::Isolate* isolate); + ~ObjectTemplateBuilder(); + + // It's against Google C++ style to return a non-const ref, but we take some + // poetic license here in order that all calls to Set() can be via the '.' + // operator and line up nicely. + template<typename T> + ObjectTemplateBuilder& SetValue(const base::StringPiece& name, T val) { + return SetImpl(name, ConvertToV8(isolate_, val)); + } + + // In the following methods, T and U can be function pointer, member function + // pointer, base::Callback, or v8::FunctionTemplate. Most clients will want to + // use one of the first two options. Also see gin::CreateFunctionTemplate() + // for creating raw function templates. + template<typename T> + ObjectTemplateBuilder& SetMethod(const base::StringPiece& name, + const T& callback) { + return SetImpl(name, CallbackTraits<T>::CreateTemplate(isolate_, callback)); + } + template<typename T> + ObjectTemplateBuilder& SetProperty(const base::StringPiece& name, + const T& getter) { + return SetPropertyImpl(name, + CallbackTraits<T>::CreateTemplate(isolate_, getter), + v8::Local<v8::FunctionTemplate>()); + } + template<typename T, typename U> + ObjectTemplateBuilder& SetProperty(const base::StringPiece& name, + const T& getter, const U& setter) { + return SetPropertyImpl(name, + CallbackTraits<T>::CreateTemplate(isolate_, getter), + CallbackTraits<U>::CreateTemplate(isolate_, setter)); + } + template<typename T> + ObjectTemplateBuilder& SetCallAsFunctionHandler(const T& callback) { + CallbackTraits<T>::SetAsFunctionHandler(isolate_, template_, callback); + return *this; + } + ObjectTemplateBuilder& AddNamedPropertyInterceptor(); + ObjectTemplateBuilder& AddIndexedPropertyInterceptor(); + + v8::Local<v8::ObjectTemplate> Build(); + + private: + ObjectTemplateBuilder& SetImpl(const base::StringPiece& name, + v8::Handle<v8::Data> val); + ObjectTemplateBuilder& SetPropertyImpl( + const base::StringPiece& name, v8::Handle<v8::FunctionTemplate> getter, + v8::Handle<v8::FunctionTemplate> setter); + + v8::Isolate* isolate_; + + // ObjectTemplateBuilder should only be used on the stack. + v8::Local<v8::ObjectTemplate> template_; +}; + +} // namespace gin + +#endif // GIN_OBJECT_TEMPLATE_BUILDER_H_
diff --git a/gin/per_context_data.cc b/gin/per_context_data.cc new file mode 100644 index 0000000..178c0d1 --- /dev/null +++ b/gin/per_context_data.cc
@@ -0,0 +1,33 @@ +// 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. + +#include "gin/per_context_data.h" + +#include "base/logging.h" +#include "gin/public/context_holder.h" +#include "gin/public/wrapper_info.h" + +namespace gin { + +PerContextData::PerContextData(ContextHolder* context_holder, + v8::Handle<v8::Context> context) + : context_holder_(context_holder), + runner_(NULL) { + context->SetAlignedPointerInEmbedderData( + kPerContextDataStartIndex + kEmbedderNativeGin, this); +} + +PerContextData::~PerContextData() { + v8::HandleScope handle_scope(context_holder_->isolate()); + context_holder_->context()->SetAlignedPointerInEmbedderData( + kPerContextDataStartIndex + kEmbedderNativeGin, NULL); +} + +// static +PerContextData* PerContextData::From(v8::Handle<v8::Context> context) { + return static_cast<PerContextData*>( + context->GetAlignedPointerFromEmbedderData(kEncodedValueIndex)); +} + +} // namespace gin
diff --git a/gin/per_context_data.h b/gin/per_context_data.h new file mode 100644 index 0000000..0d11653 --- /dev/null +++ b/gin/per_context_data.h
@@ -0,0 +1,48 @@ +// 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. + +#ifndef GIN_PER_CONTEXT_DATA_H_ +#define GIN_PER_CONTEXT_DATA_H_ + +#include "base/basictypes.h" +#include "base/supports_user_data.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +class ContextHolder; +class Runner; + +// There is one instance of PerContextData per v8::Context managed by Gin. This +// class stores all the Gin-related data that varies per context. Arbitrary data +// can be associated with this class by way of the SupportsUserData methods. +// Instances of this class (and any associated user data) are destroyed before +// the associated v8::Context. +class GIN_EXPORT PerContextData : public base::SupportsUserData { + public: + PerContextData(ContextHolder* context_holder, + v8::Handle<v8::Context> context); + virtual ~PerContextData(); + + // Can return NULL after the ContextHolder has detached from context. + static PerContextData* From(v8::Handle<v8::Context> context); + + // The Runner associated with this context. To execute script in this context, + // please use the appropriate API on Runner. + Runner* runner() const { return runner_; } + void set_runner(Runner* runner) { runner_ = runner; } + + ContextHolder* context_holder() { return context_holder_; } + + private: + ContextHolder* context_holder_; + Runner* runner_; + + DISALLOW_COPY_AND_ASSIGN(PerContextData); +}; + +} // namespace gin + +#endif // GIN_PER_CONTEXT_DATA_H_
diff --git a/gin/per_context_data_unittest.cc b/gin/per_context_data_unittest.cc new file mode 100644 index 0000000..4d79587 --- /dev/null +++ b/gin/per_context_data_unittest.cc
@@ -0,0 +1,34 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/per_context_data.h" + +#include "gin/public/context_holder.h" +#include "gin/public/isolate_holder.h" +#include "gin/test/v8_test.h" +#include "v8/include/v8.h" + +namespace gin { + +typedef V8Test PerContextDataTest; + +// Verifies PerContextData can be looked up by context and that it is not +// available once ContextHolder is destroyed. +TEST_F(PerContextDataTest, LookupAndDestruction) { + v8::Isolate::Scope isolate_scope(instance_->isolate()); + v8::HandleScope handle_scope(instance_->isolate()); + v8::Handle<v8::Context> context = v8::Context::New( + instance_->isolate(), NULL, v8::Handle<v8::ObjectTemplate>()); + { + ContextHolder context_holder(instance_->isolate()); + context_holder.SetContext(context); + PerContextData* per_context_data = PerContextData::From(context); + EXPECT_TRUE(per_context_data != NULL); + EXPECT_EQ(&context_holder, per_context_data->context_holder()); + } + PerContextData* per_context_data = PerContextData::From(context); + EXPECT_TRUE(per_context_data == NULL); +} + +} // namespace gin
diff --git a/gin/per_isolate_data.cc b/gin/per_isolate_data.cc new file mode 100644 index 0000000..99c928c --- /dev/null +++ b/gin/per_isolate_data.cc
@@ -0,0 +1,112 @@ +// 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. + +#include "base/logging.h" +#include "base/message_loop/message_loop_proxy.h" +#include "gin/per_isolate_data.h" +#include "gin/public/gin_embedders.h" + +using v8::ArrayBuffer; +using v8::Eternal; +using v8::Isolate; +using v8::Local; +using v8::Object; +using v8::FunctionTemplate; +using v8::ObjectTemplate; + +namespace gin { + +PerIsolateData::PerIsolateData(Isolate* isolate, + ArrayBuffer::Allocator* allocator) + : isolate_(isolate), + allocator_(allocator), + message_loop_proxy_(base::MessageLoopProxy::current()) { + isolate_->SetData(kEmbedderNativeGin, this); +} + +PerIsolateData::~PerIsolateData() { + isolate_->SetData(kEmbedderNativeGin, NULL); +} + +PerIsolateData* PerIsolateData::From(Isolate* isolate) { + return static_cast<PerIsolateData*>(isolate->GetData(kEmbedderNativeGin)); +} + +void PerIsolateData::SetObjectTemplate(WrapperInfo* info, + Local<ObjectTemplate> templ) { + object_templates_[info] = Eternal<ObjectTemplate>(isolate_, templ); +} + +void PerIsolateData::SetFunctionTemplate(WrapperInfo* info, + Local<FunctionTemplate> templ) { + function_templates_[info] = Eternal<FunctionTemplate>(isolate_, templ); +} + +v8::Local<v8::ObjectTemplate> PerIsolateData::GetObjectTemplate( + WrapperInfo* info) { + ObjectTemplateMap::iterator it = object_templates_.find(info); + if (it == object_templates_.end()) + return v8::Local<v8::ObjectTemplate>(); + return it->second.Get(isolate_); +} + +v8::Local<v8::FunctionTemplate> PerIsolateData::GetFunctionTemplate( + WrapperInfo* info) { + FunctionTemplateMap::iterator it = function_templates_.find(info); + if (it == function_templates_.end()) + return v8::Local<v8::FunctionTemplate>(); + return it->second.Get(isolate_); +} + +void PerIsolateData::SetIndexedPropertyInterceptor( + WrappableBase* base, + IndexedPropertyInterceptor* interceptor) { + indexed_interceptors_[base] = interceptor; +} + +void PerIsolateData::SetNamedPropertyInterceptor( + WrappableBase* base, + NamedPropertyInterceptor* interceptor) { + named_interceptors_[base] = interceptor; +} + +void PerIsolateData::ClearIndexedPropertyInterceptor( + WrappableBase* base, + IndexedPropertyInterceptor* interceptor) { + IndexedPropertyInterceptorMap::iterator it = indexed_interceptors_.find(base); + if (it != indexed_interceptors_.end()) + indexed_interceptors_.erase(it); + else + NOTREACHED(); +} + +void PerIsolateData::ClearNamedPropertyInterceptor( + WrappableBase* base, + NamedPropertyInterceptor* interceptor) { + NamedPropertyInterceptorMap::iterator it = named_interceptors_.find(base); + if (it != named_interceptors_.end()) + named_interceptors_.erase(it); + else + NOTREACHED(); +} + +IndexedPropertyInterceptor* PerIsolateData::GetIndexedPropertyInterceptor( + WrappableBase* base) { + IndexedPropertyInterceptorMap::iterator it = indexed_interceptors_.find(base); + if (it != indexed_interceptors_.end()) + return it->second; + else + return NULL; +} + +NamedPropertyInterceptor* PerIsolateData::GetNamedPropertyInterceptor( + WrappableBase* base) { + NamedPropertyInterceptorMap::iterator it = named_interceptors_.find(base); + if (it != named_interceptors_.end()) + return it->second; + else + return NULL; +} + +} // namespace gin
diff --git a/gin/per_isolate_data.h b/gin/per_isolate_data.h new file mode 100644 index 0000000..bffe5fb --- /dev/null +++ b/gin/per_isolate_data.h
@@ -0,0 +1,97 @@ +// 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. + +#ifndef GIN_PER_ISOLATE_DATA_H_ +#define GIN_PER_ISOLATE_DATA_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "gin/gin_export.h" +#include "gin/public/wrapper_info.h" +#include "v8/include/v8.h" + +namespace base { +class MessageLoopProxy; +} + +namespace gin { + +class IndexedPropertyInterceptor; +class NamedPropertyInterceptor; +class WrappableBase; + +// There is one instance of PerIsolateData per v8::Isolate managed by Gin. This +// class stores all the Gin-related data that varies per isolate. +class GIN_EXPORT PerIsolateData { + public: + PerIsolateData(v8::Isolate* isolate, v8::ArrayBuffer::Allocator* allocator); + ~PerIsolateData(); + + static PerIsolateData* From(v8::Isolate* isolate); + + // Each isolate is associated with a collection of v8::ObjectTemplates and + // v8::FunctionTemplates. Typically these template objects are created + // lazily. + void SetObjectTemplate(WrapperInfo* info, + v8::Local<v8::ObjectTemplate> object_template); + void SetFunctionTemplate(WrapperInfo* info, + v8::Local<v8::FunctionTemplate> function_template); + + // These are low-level functions for retrieving object or function templates + // stored in this object. Because these templates are often created lazily, + // most clients should call higher-level functions that know how to populate + // these templates if they haven't already been created. + v8::Local<v8::ObjectTemplate> GetObjectTemplate(WrapperInfo* info); + v8::Local<v8::FunctionTemplate> GetFunctionTemplate(WrapperInfo* info); + + // We maintain a map from Wrappable objects that derive from one of the + // interceptor interfaces to the interceptor interface pointers. + void SetIndexedPropertyInterceptor(WrappableBase* base, + IndexedPropertyInterceptor* interceptor); + void SetNamedPropertyInterceptor(WrappableBase* base, + NamedPropertyInterceptor* interceptor); + + void ClearIndexedPropertyInterceptor(WrappableBase* base, + IndexedPropertyInterceptor* interceptor); + void ClearNamedPropertyInterceptor(WrappableBase* base, + NamedPropertyInterceptor* interceptor); + + IndexedPropertyInterceptor* GetIndexedPropertyInterceptor( + WrappableBase* base); + NamedPropertyInterceptor* GetNamedPropertyInterceptor(WrappableBase* base); + + v8::Isolate* isolate() { return isolate_; } + v8::ArrayBuffer::Allocator* allocator() { return allocator_; } + base::MessageLoopProxy* message_loop_proxy() { + return message_loop_proxy_.get(); + } + + private: + typedef std::map< + WrapperInfo*, v8::Eternal<v8::ObjectTemplate> > ObjectTemplateMap; + typedef std::map< + WrapperInfo*, v8::Eternal<v8::FunctionTemplate> > FunctionTemplateMap; + typedef std::map<WrappableBase*, IndexedPropertyInterceptor*> + IndexedPropertyInterceptorMap; + typedef std::map<WrappableBase*, NamedPropertyInterceptor*> + NamedPropertyInterceptorMap; + + // PerIsolateData doesn't actually own |isolate_|. Instead, the isolate is + // owned by the IsolateHolder, which also owns the PerIsolateData. + v8::Isolate* isolate_; + v8::ArrayBuffer::Allocator* allocator_; + ObjectTemplateMap object_templates_; + FunctionTemplateMap function_templates_; + IndexedPropertyInterceptorMap indexed_interceptors_; + NamedPropertyInterceptorMap named_interceptors_; + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + + DISALLOW_COPY_AND_ASSIGN(PerIsolateData); +}; + +} // namespace gin + +#endif // GIN_PER_ISOLATE_DATA_H_
diff --git a/gin/public/context_holder.h b/gin/public/context_holder.h new file mode 100644 index 0000000..afbcf23 --- /dev/null +++ b/gin/public/context_holder.h
@@ -0,0 +1,52 @@ +// 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. + +#ifndef GIN_PUBLIC_CONTEXT_HOLDER_H_ +#define GIN_PUBLIC_CONTEXT_HOLDER_H_ + +#include <list> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +// Gin embedder that store embedder data in v8::Contexts must do so in a +// single field with the index kPerContextDataStartIndex + GinEmbedder-enum. +// The field at kDebugIdIndex is treated specially by V8 and is reserved for +// a V8 debugger implementation (not used by gin). +enum ContextEmbedderDataFields { + kDebugIdIndex = 0, + kPerContextDataStartIndex, +}; + +class PerContextData; + +// ContextHolder is a generic class for holding a v8::Context. +class GIN_EXPORT ContextHolder { + public: + explicit ContextHolder(v8::Isolate* isolate); + ~ContextHolder(); + + v8::Isolate* isolate() const { return isolate_; } + + v8::Handle<v8::Context> context() const { + return v8::Local<v8::Context>::New(isolate_, context_); + } + + void SetContext(v8::Handle<v8::Context> context); + + private: + v8::Isolate* isolate_; + v8::UniquePersistent<v8::Context> context_; + scoped_ptr<PerContextData> data_; + + DISALLOW_COPY_AND_ASSIGN(ContextHolder); +}; + +} // namespace gin + +#endif // GIN_PUBLIC_CONTEXT_HOLDER_H_
diff --git a/gin/public/debug.h b/gin/public/debug.h new file mode 100644 index 0000000..0c24109 --- /dev/null +++ b/gin/public/debug.h
@@ -0,0 +1,57 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GIN_PUBLIC_DEBUG_H_ +#define GIN_PUBLIC_DEBUG_H_ + +#include "build/build_config.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +class GIN_EXPORT Debug { + public: + /* Installs a callback that is invoked on entry to every V8-generated + * function. + * + * This only affects IsolateHolder instances created after + * SetFunctionEntryHook was invoked. + */ + static void SetFunctionEntryHook(v8::FunctionEntryHook entry_hook); + + /* Installs a callback that is invoked each time jit code is added, moved, + * or removed. + * + * This only affects IsolateHolder instances created after + * SetJitCodeEventHandler was invoked. + */ + static void SetJitCodeEventHandler(v8::JitCodeEventHandler event_handler); + +#if defined(OS_WIN) + typedef void (__cdecl *CodeRangeCreatedCallback)(void*, size_t); + + /* Sets a callback that is invoked for every new code range being created. + * + * On Win64, exception handling in jitted code is broken due to the fact + * that JS stack frames are not ABI compliant. It is possible to install + * custom handlers for the code range which holds the jitted code to work + * around this issue. + * + * https://code.google.com/p/v8/issues/detail?id=3598 + */ + static void SetCodeRangeCreatedCallback(CodeRangeCreatedCallback callback); + + typedef void (__cdecl *CodeRangeDeletedCallback)(void*); + + /* Sets a callback that is invoked for every previously registered code range + * when it is deleted. + */ + static void SetCodeRangeDeletedCallback(CodeRangeDeletedCallback callback); +#endif +}; + +} // namespace gin + +#endif // GIN_PUBLIC_DEBUG_H_
diff --git a/gin/public/gin_embedders.h b/gin/public/gin_embedders.h new file mode 100644 index 0000000..bffe6e0 --- /dev/null +++ b/gin/public/gin_embedders.h
@@ -0,0 +1,21 @@ +// 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. + +#ifndef GIN_PUBLIC_GIN_EMBEDDERS_H_ +#define GIN_PUBLIC_GIN_EMBEDDERS_H_ + +namespace gin { + +// The GinEmbedder is used to identify the owner of embedder data stored on +// v8 objects, and is used as in index into the embedder data slots of a +// v8::Isolate. + +enum GinEmbedder { + kEmbedderNativeGin, + kEmbedderBlink, +}; + +} // namespace gin + +#endif // GIN_PUBLIC_GIN_EMBEDDERS_H_
diff --git a/gin/public/isolate_holder.h b/gin/public/isolate_holder.h new file mode 100644 index 0000000..29cc208 --- /dev/null +++ b/gin/public/isolate_holder.h
@@ -0,0 +1,62 @@ +// 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. + +#ifndef GIN_PUBLIC_ISOLATE_HOLDER_H_ +#define GIN_PUBLIC_ISOLATE_HOLDER_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +class PerIsolateData; +class RunMicrotasksObserver; + +// To embed Gin, first initialize gin using IsolateHolder::Initialize and then +// create an instance of IsolateHolder to hold the v8::Isolate in which you +// will execute JavaScript. You might wish to subclass IsolateHolder if you +// want to tie more state to the lifetime of the isolate. +class GIN_EXPORT IsolateHolder { + public: + // Controls whether or not V8 should only accept strict mode scripts. + enum ScriptMode { + kNonStrictMode, + kStrictMode + }; + + IsolateHolder(); + ~IsolateHolder(); + + // Should be invoked once before creating IsolateHolder instances to + // initialize V8 and Gin. + static void Initialize(ScriptMode mode, + v8::ArrayBuffer::Allocator* allocator); + + v8::Isolate* isolate() { return isolate_; } + + // The implementations of Object.observe() and Promise enqueue v8 Microtasks + // that should be executed just before control is returned to the message + // loop. This method adds a MessageLoop TaskObserver which runs any pending + // Microtasks each time a Task is completed. This method should be called + // once, when a MessageLoop is created and it should be called on the + // MessageLoop's thread. + void AddRunMicrotasksObserver(); + + // This method should also only be called once, and on the MessageLoop's + // thread. + void RemoveRunMicrotasksObserver(); + + private: + v8::Isolate* isolate_; + scoped_ptr<PerIsolateData> isolate_data_; + scoped_ptr<RunMicrotasksObserver> task_observer_; + + DISALLOW_COPY_AND_ASSIGN(IsolateHolder); +}; + +} // namespace gin + +#endif // GIN_PUBLIC_ISOLATE_HOLDER_H_
diff --git a/gin/public/v8_platform.h b/gin/public/v8_platform.h new file mode 100644 index 0000000..2df0f84 --- /dev/null +++ b/gin/public/v8_platform.h
@@ -0,0 +1,38 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GIN_PUBLIC_V8_PLATFORM_H_ +#define GIN_PUBLIC_V8_PLATFORM_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/lazy_instance.h" +#include "gin/gin_export.h" +#include "v8/include/v8-platform.h" + +namespace gin { + +// A v8::Platform implementation to use with gin. +class GIN_EXPORT V8Platform : public NON_EXPORTED_BASE(v8::Platform) { + public: + static V8Platform* Get(); + + // v8::Platform implementation. + virtual void CallOnBackgroundThread( + v8::Task* task, + v8::Platform::ExpectedRuntime expected_runtime) OVERRIDE; + virtual void CallOnForegroundThread(v8::Isolate* isolate, + v8::Task* task) OVERRIDE; + private: + friend struct base::DefaultLazyInstanceTraits<V8Platform>; + + V8Platform(); + virtual ~V8Platform(); + + DISALLOW_COPY_AND_ASSIGN(V8Platform); +}; + +} // namespace gin + +#endif // GIN_PUBLIC_V8_PLATFORM_H_
diff --git a/gin/public/wrapper_info.h b/gin/public/wrapper_info.h new file mode 100644 index 0000000..31b2a98 --- /dev/null +++ b/gin/public/wrapper_info.h
@@ -0,0 +1,32 @@ +// 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. + +#ifndef GIN_PUBLIC_WRAPPER_INFO_H_ +#define GIN_PUBLIC_WRAPPER_INFO_H_ + +#include "gin/gin_export.h" +#include "gin/public/gin_embedders.h" +#include "v8/include/v8.h" + +namespace gin { + +// Gin embedder that use their own WrapperInfo-like structs must ensure that +// the first field is of type GinEmbedderId and has the correct id set. They +// also should use kWrapperInfoIndex to start their WrapperInfo-like struct +// and ensure that all objects have kNumberOfInternalFields internal fields. + +enum InternalFields { + kWrapperInfoIndex, + kEncodedValueIndex, + kNumberOfInternalFields, +}; + +struct GIN_EXPORT WrapperInfo { + static WrapperInfo* From(v8::Handle<v8::Object> object); + const GinEmbedder embedder; +}; + +} // namespace gin + +#endif // GIN_PUBLIC_WRAPPER_INFO_H_
diff --git a/gin/run_microtasks_observer.cc b/gin/run_microtasks_observer.cc new file mode 100644 index 0000000..f453a66 --- /dev/null +++ b/gin/run_microtasks_observer.cc
@@ -0,0 +1,21 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/run_microtasks_observer.h" + +namespace gin { + +RunMicrotasksObserver::RunMicrotasksObserver(v8::Isolate* isolate) + : isolate_(isolate) { +} + +void RunMicrotasksObserver::WillProcessTask(const base::PendingTask& task) { +} + +void RunMicrotasksObserver::DidProcessTask(const base::PendingTask& task) { + v8::Isolate::Scope scope(isolate_); + isolate_->RunMicrotasks(); +} + +} // namespace gin
diff --git a/gin/run_microtasks_observer.h b/gin/run_microtasks_observer.h new file mode 100644 index 0000000..d31d804 --- /dev/null +++ b/gin/run_microtasks_observer.h
@@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GIN_RUN_MICROTASKS_OBSERVER_H_ +#define GIN_RUN_MICROTASKS_OBSERVER_H_ + +#include "base/message_loop/message_loop.h" +#include "v8/include/v8.h" + +namespace gin { + +// Runs any pending v8 Microtasks each time a task is completed. +// TODO(hansmuller); At some point perhaps this can be replaced with +// the (currently experimental) Isolate::SetAutorunMicrotasks() method. + +class RunMicrotasksObserver : public base::MessageLoop::TaskObserver { + public: + RunMicrotasksObserver(v8::Isolate* isolate); + + virtual void WillProcessTask(const base::PendingTask& pending_task) override; + virtual void DidProcessTask(const base::PendingTask& pending_task) override; + + private: + v8::Isolate* isolate_; +}; + +} // namespace gin + +#endif // GIN_RUN_MICROTASKS_OBSERVER_H_
diff --git a/gin/runner.cc b/gin/runner.cc new file mode 100644 index 0000000..6f018b1 --- /dev/null +++ b/gin/runner.cc
@@ -0,0 +1,24 @@ +// 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. + +#include "gin/runner.h" + +namespace gin { + +Runner::Runner() : weak_factory_(this) { +} + +Runner::~Runner() { +} + +Runner::Scope::Scope(Runner* runner) + : isolate_scope_(runner->GetContextHolder()->isolate()), + handle_scope_(runner->GetContextHolder()->isolate()), + scope_(runner->GetContextHolder()->context()) { +} + +Runner::Scope::~Scope() { +} + +} // namespace gin
diff --git a/gin/runner.h b/gin/runner.h new file mode 100644 index 0000000..36a75d2 --- /dev/null +++ b/gin/runner.h
@@ -0,0 +1,66 @@ +// 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. + +#ifndef GIN_RUNNER_H_ +#define GIN_RUNNER_H_ + +#include <string> + +#include "base/memory/weak_ptr.h" +#include "gin/gin_export.h" +#include "gin/public/context_holder.h" +#include "v8/include/v8.h" + +namespace gin { + +// Runner is responsible for running code in a v8::Context. +class GIN_EXPORT Runner { + public: + Runner(); + virtual ~Runner(); + + // Before running script in this context, you'll need to enter the runner's + // context by creating an instance of Runner::Scope on the stack. + virtual void Run(const std::string& source, + const std::string& resource_name) = 0; + virtual v8::Handle<v8::Value> Call(v8::Handle<v8::Function> function, + v8::Handle<v8::Value> receiver, + int argc, + v8::Handle<v8::Value> argv[]) = 0; + virtual ContextHolder* GetContextHolder() = 0; + + v8::Handle<v8::Object> global() { + return GetContextHolder()->context()->Global(); + } + + // Useful for running script in this context asynchronously. Rather than + // holding a raw pointer to the runner, consider holding a WeakPtr. + base::WeakPtr<Runner> GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + + class GIN_EXPORT Scope { + public: + explicit Scope(Runner* runner); + ~Scope(); + + private: + v8::Isolate::Scope isolate_scope_; + v8::HandleScope handle_scope_; + v8::Context::Scope scope_; + + DISALLOW_COPY_AND_ASSIGN(Scope); + }; + + private: + friend class Scope; + + base::WeakPtrFactory<Runner> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Runner); +}; + +} // namespace gin + +#endif // GIN_RUNNER_H_
diff --git a/gin/shell/gin_main.cc b/gin/shell/gin_main.cc new file mode 100644 index 0000000..aa9f2b5 --- /dev/null +++ b/gin/shell/gin_main.cc
@@ -0,0 +1,88 @@ +// 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. + +#include "base/at_exit.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/i18n/icu_util.h" +#include "base/message_loop/message_loop.h" +#include "gin/array_buffer.h" +#include "gin/modules/console.h" +#include "gin/modules/module_runner_delegate.h" +#include "gin/public/isolate_holder.h" +#include "gin/test/file_runner.h" +#include "gin/try_catch.h" + +namespace gin { +namespace { + +std::string Load(const base::FilePath& path) { + std::string source; + if (!ReadFileToString(path, &source)) + LOG(FATAL) << "Unable to read " << path.LossyDisplayName(); + return source; +} + +void Run(base::WeakPtr<Runner> runner, const base::FilePath& path) { + if (!runner) + return; + Runner::Scope scope(runner.get()); + runner->Run(Load(path), path.AsUTF8Unsafe()); +} + +std::vector<base::FilePath> GetModuleSearchPaths() { + std::vector<base::FilePath> module_base(1); + CHECK(base::GetCurrentDirectory(&module_base[0])); + return module_base; +} + +class GinShellRunnerDelegate : public ModuleRunnerDelegate { + public: + GinShellRunnerDelegate() : ModuleRunnerDelegate(GetModuleSearchPaths()) { + AddBuiltinModule(Console::kModuleName, Console::GetModule); + } + + virtual void UnhandledException(ShellRunner* runner, + TryCatch& try_catch) OVERRIDE { + ModuleRunnerDelegate::UnhandledException(runner, try_catch); + LOG(ERROR) << try_catch.GetStackTrace(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(GinShellRunnerDelegate); +}; + +} // namespace +} // namespace gin + +int main(int argc, char** argv) { + base::AtExitManager at_exit; + CommandLine::Init(argc, argv); + base::i18n::InitializeICU(); + + gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode, + gin::ArrayBufferAllocator::SharedInstance()); + gin::IsolateHolder instance; + + base::MessageLoop message_loop; + + gin::GinShellRunnerDelegate delegate; + gin::ShellRunner runner(&delegate, instance.isolate()); + + { + gin::Runner::Scope scope(&runner); + v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); + } + + CommandLine::StringVector args = CommandLine::ForCurrentProcess()->GetArgs(); + for (CommandLine::StringVector::const_iterator it = args.begin(); + it != args.end(); ++it) { + base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + gin::Run, runner.GetWeakPtr(), base::FilePath(*it))); + } + + message_loop.RunUntilIdle(); + return 0; +}
diff --git a/gin/shell/gin_shell_unittest.cc b/gin/shell/gin_shell_unittest.cc new file mode 100644 index 0000000..50e2ba3 --- /dev/null +++ b/gin/shell/gin_shell_unittest.cc
@@ -0,0 +1,39 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "base/process/launch.h" +#include "base/strings/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +base::FilePath GinShellPath() { + base::FilePath dir; + PathService::Get(base::DIR_EXE, &dir); + return dir.AppendASCII("gin_shell"); +} + +base::FilePath HelloWorldPath() { + base::FilePath path; + PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path + .AppendASCII("gin") + .AppendASCII("shell") + .AppendASCII("hello_world.js"); +} + +TEST(GinShellTest, HelloWorld) { + base::FilePath gin_shell_path(GinShellPath()); + base::FilePath hello_world_path(HelloWorldPath()); + ASSERT_TRUE(base::PathExists(gin_shell_path)); + ASSERT_TRUE(base::PathExists(hello_world_path)); + + CommandLine cmd(gin_shell_path); + cmd.AppendArgPath(hello_world_path); + std::string output; + ASSERT_TRUE(base::GetAppOutput(cmd, &output)); + base::TrimWhitespaceASCII(output, base::TRIM_ALL, &output); + ASSERT_EQ("Hello World", output); +}
diff --git a/gin/shell/hello_world.js b/gin/shell/hello_world.js new file mode 100644 index 0000000..7216fbd --- /dev/null +++ b/gin/shell/hello_world.js
@@ -0,0 +1,7 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +define(["console"], function(console) { + console.log("Hello World"); +});
diff --git a/gin/shell_runner.cc b/gin/shell_runner.cc new file mode 100644 index 0000000..8d98e42 --- /dev/null +++ b/gin/shell_runner.cc
@@ -0,0 +1,112 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/shell_runner.h" + +#include "gin/converter.h" +#include "gin/modules/module_registry.h" +#include "gin/per_context_data.h" +#include "gin/public/context_holder.h" +#include "gin/try_catch.h" + +using v8::Context; +using v8::HandleScope; +using v8::Isolate; +using v8::Object; +using v8::ObjectTemplate; +using v8::Script; + +namespace gin { + +ShellRunnerDelegate::ShellRunnerDelegate() { +} + +ShellRunnerDelegate::~ShellRunnerDelegate() { +} + +v8::Handle<ObjectTemplate> ShellRunnerDelegate::GetGlobalTemplate( + ShellRunner* runner, + v8::Isolate* isolate) { + return v8::Handle<ObjectTemplate>(); +} + +void ShellRunnerDelegate::DidCreateContext(ShellRunner* runner) { +} + +void ShellRunnerDelegate::WillRunScript(ShellRunner* runner) { +} + +void ShellRunnerDelegate::DidRunScript(ShellRunner* runner) { +} + +void ShellRunnerDelegate::UnhandledException(ShellRunner* runner, + TryCatch& try_catch) { + CHECK(false) << try_catch.GetStackTrace(); +} + +ShellRunner::ShellRunner(ShellRunnerDelegate* delegate, Isolate* isolate) + : delegate_(delegate) { + v8::Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + v8::Handle<v8::Context> context = + Context::New(isolate, NULL, delegate_->GetGlobalTemplate(this, isolate)); + + context_holder_.reset(new ContextHolder(isolate)); + context_holder_->SetContext(context); + PerContextData::From(context)->set_runner(this); + + v8::Context::Scope scope(context); + delegate_->DidCreateContext(this); +} + +ShellRunner::~ShellRunner() { +} + +void ShellRunner::Run(const std::string& source, + const std::string& resource_name) { + TryCatch try_catch; + v8::Isolate* isolate = GetContextHolder()->isolate(); + v8::Handle<Script> script = Script::Compile( + StringToV8(isolate, source), StringToV8(isolate, resource_name)); + if (try_catch.HasCaught()) { + delegate_->UnhandledException(this, try_catch); + return; + } + + Run(script); +} + +v8::Handle<v8::Value> ShellRunner::Call(v8::Handle<v8::Function> function, + v8::Handle<v8::Value> receiver, + int argc, + v8::Handle<v8::Value> argv[]) { + TryCatch try_catch; + delegate_->WillRunScript(this); + + v8::Handle<v8::Value> result = function->Call(receiver, argc, argv); + + delegate_->DidRunScript(this); + if (try_catch.HasCaught()) + delegate_->UnhandledException(this, try_catch); + + return result; +} + +ContextHolder* ShellRunner::GetContextHolder() { + return context_holder_.get(); +} + +void ShellRunner::Run(v8::Handle<Script> script) { + TryCatch try_catch; + delegate_->WillRunScript(this); + + script->Run(); + + delegate_->DidRunScript(this); + if (try_catch.HasCaught()) { + delegate_->UnhandledException(this, try_catch); + } +} + +} // namespace gin
diff --git a/gin/shell_runner.h b/gin/shell_runner.h new file mode 100644 index 0000000..ca88a5d --- /dev/null +++ b/gin/shell_runner.h
@@ -0,0 +1,68 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GIN_SHELL_RUNNER_H_ +#define GIN_SHELL_RUNNER_H_ + +#include "gin/runner.h" + +namespace gin { + +class ContextHolder; +class ShellRunner; +class TryCatch; + +// Subclass ShellRunnerDelegate to customize the behavior of ShellRunner. +// Typical embedders will want to subclass one of the specialized +// ShellRunnerDelegates, such as ModuleRunnerDelegate. +class GIN_EXPORT ShellRunnerDelegate { + public: + ShellRunnerDelegate(); + virtual ~ShellRunnerDelegate(); + + // Returns the template for the global object. + virtual v8::Handle<v8::ObjectTemplate> GetGlobalTemplate( + ShellRunner* runner, + v8::Isolate* isolate); + virtual void DidCreateContext(ShellRunner* runner); + virtual void WillRunScript(ShellRunner* runner); + virtual void DidRunScript(ShellRunner* runner); + virtual void UnhandledException(ShellRunner* runner, TryCatch& try_catch); +}; + +// ShellRunner executes the script/functions directly in a v8::Context. +// ShellRunner owns a ContextHolder and v8::Context, both of which are destroyed +// when the ShellRunner is deleted. +class GIN_EXPORT ShellRunner : public Runner { + public: + ShellRunner(ShellRunnerDelegate* delegate, v8::Isolate* isolate); + virtual ~ShellRunner(); + + // Before running script in this context, you'll need to enter the runner's + // context by creating an instance of Runner::Scope on the stack. + + // Runner overrides: + virtual void Run(const std::string& source, + const std::string& resource_name) override; + virtual v8::Handle<v8::Value> Call(v8::Handle<v8::Function> function, + v8::Handle<v8::Value> receiver, + int argc, + v8::Handle<v8::Value> argv[]) override; + virtual ContextHolder* GetContextHolder() override; + + private: + friend class Scope; + + void Run(v8::Handle<v8::Script> script); + + ShellRunnerDelegate* delegate_; + + scoped_ptr<ContextHolder> context_holder_; + + DISALLOW_COPY_AND_ASSIGN(ShellRunner); +}; + +} // namespace gin + +#endif // GIN_SHELL_RUNNER_H_
diff --git a/gin/shell_runner_unittest.cc b/gin/shell_runner_unittest.cc new file mode 100644 index 0000000..07ab678 --- /dev/null +++ b/gin/shell_runner_unittest.cc
@@ -0,0 +1,40 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/shell_runner.h" + +#include "base/compiler_specific.h" +#include "gin/array_buffer.h" +#include "gin/converter.h" +#include "gin/public/isolate_holder.h" +#include "testing/gtest/include/gtest/gtest.h" + +using v8::Isolate; +using v8::Object; +using v8::Script; +using v8::String; + +namespace gin { + +TEST(RunnerTest, Run) { + std::string source = "this.result = 'PASS';\n"; + + gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode, + gin::ArrayBufferAllocator::SharedInstance()); + gin::IsolateHolder instance; + + ShellRunnerDelegate delegate; + Isolate* isolate = instance.isolate(); + ShellRunner runner(&delegate, isolate); + Runner::Scope scope(&runner); + runner.Run(source, "test_data.js"); + + std::string result; + EXPECT_TRUE(Converter<std::string>::FromV8(isolate, + runner.global()->Get(StringToV8(isolate, "result")), + &result)); + EXPECT_EQ("PASS", result); +} + +} // namespace gin
diff --git a/gin/test/expect.js b/gin/test/expect.js new file mode 100644 index 0000000..b5e0f21 --- /dev/null +++ b/gin/test/expect.js
@@ -0,0 +1,289 @@ +// 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. + +define(function() { + // Equality function based on isEqual in + // Underscore.js 1.5.2 + // http://underscorejs.org + // (c) 2009-2013 Jeremy Ashkenas, + // DocumentCloud, + // and Investigative Reporters & Editors + // Underscore may be freely distributed under the MIT license. + // + function has(obj, key) { + return obj.hasOwnProperty(key); + } + function isFunction(obj) { + return typeof obj === 'function'; + } + function isArrayBufferClass(className) { + return className == '[object ArrayBuffer]' || + className.match(/\[object \w+\d+(Clamped)?Array\]/); + } + // Internal recursive comparison function for `isEqual`. + function eq(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the Harmony `egal` proposal: + // http://wiki.ecmascript.org/doku.php?id=harmony:egal. + if (a === b) + return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) + return a === b; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) + return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; + // thus, `"5"` is equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is + // performed for other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are + // compared by their millisecond representations. Note that invalid + // dates with millisecond representations of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') + return false; + // Assume equality for cyclic structures. The algorithm for detecting + // cyclic structures is adapted from ES 5.1 section 15.12.3, abstract + // operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) + return bStack[length] == b; + } + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) && + isFunction(bCtor) && (bCtor instanceof bCtor)) + && ('constructor' in a && 'constructor' in b)) { + return false; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]' || isArrayBufferClass(className)) { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) + break; + } + } + } else { + // Deep compare objects. + for (var key in a) { + if (has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack))) + break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (has(b, key) && !(size--)) + break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + function describe(subjects) { + var descriptions = []; + Object.getOwnPropertyNames(subjects).forEach(function(name) { + if (name === "Description") + descriptions.push(subjects[name]); + else + descriptions.push(name + ": " + JSON.stringify(subjects[name])); + }); + return descriptions.join(" "); + } + + var predicates = {}; + + predicates.toBe = function(actual, expected) { + return { + "result": actual === expected, + "message": describe({ + "Actual": actual, + "Expected": expected, + }), + }; + }; + + predicates.toEqual = function(actual, expected) { + return { + "result": eq(actual, expected, [], []), + "message": describe({ + "Actual": actual, + "Expected": expected, + }), + }; + }; + + predicates.toBeDefined = function(actual) { + return { + "result": typeof actual !== "undefined", + "message": describe({ + "Actual": actual, + "Description": "Expected a defined value", + }), + }; + }; + + predicates.toBeUndefined = function(actual) { + // Recall: undefined is just a global variable. :) + return { + "result": typeof actual === "undefined", + "message": describe({ + "Actual": actual, + "Description": "Expected an undefined value", + }), + }; + }; + + predicates.toBeNull = function(actual) { + // Recall: typeof null === "object". + return { + "result": actual === null, + "message": describe({ + "Actual": actual, + "Expected": null, + }), + }; + }; + + predicates.toBeTruthy = function(actual) { + return { + "result": !!actual, + "message": describe({ + "Actual": actual, + "Description": "Expected a truthy value", + }), + }; + }; + + predicates.toBeFalsy = function(actual) { + return { + "result": !!!actual, + "message": describe({ + "Actual": actual, + "Description": "Expected a falsy value", + }), + }; + }; + + predicates.toContain = function(actual, element) { + return { + "result": (function () { + for (var i = 0; i < actual.length; ++i) { + if (eq(actual[i], element, [], [])) + return true; + } + return false; + })(), + "message": describe({ + "Actual": actual, + "Element": element, + }), + }; + }; + + predicates.toBeLessThan = function(actual, reference) { + return { + "result": actual < reference, + "message": describe({ + "Actual": actual, + "Reference": reference, + }), + }; + }; + + predicates.toBeGreaterThan = function(actual, reference) { + return { + "result": actual > reference, + "message": describe({ + "Actual": actual, + "Reference": reference, + }), + }; + }; + + predicates.toThrow = function(actual) { + return { + "result": (function () { + if (!isFunction(actual)) + throw new TypeError; + try { + actual(); + } catch (ex) { + return true; + } + return false; + })(), + "message": "Expected function to throw", + }; + } + + function negate(predicate) { + return function() { + var outcome = predicate.apply(null, arguments); + outcome.result = !outcome.result; + return outcome; + } + } + + function check(predicate) { + return function() { + var outcome = predicate.apply(null, arguments); + if (outcome.result) + return; + throw outcome.message; + }; + } + + function Condition(actual) { + this.not = {}; + Object.getOwnPropertyNames(predicates).forEach(function(name) { + var bound = predicates[name].bind(null, actual); + this[name] = check(bound); + this.not[name] = check(negate(bound)); + }, this); + } + + return function(actual) { + return new Condition(actual); + }; +});
diff --git a/gin/test/file.cc b/gin/test/file.cc new file mode 100644 index 0000000..0ed24e3 --- /dev/null +++ b/gin/test/file.cc
@@ -0,0 +1,86 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/test/file.h" + +#include <iostream> + +#include "base/bind.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "gin/arguments.h" +#include "gin/converter.h" +#include "gin/object_template_builder.h" +#include "gin/per_isolate_data.h" +#include "gin/public/wrapper_info.h" + +using v8::ObjectTemplate; + +namespace gin { + +namespace { + +v8::Handle<v8::Value> ReadFileToString(gin::Arguments* args) { + std::string filename; + if (!args->GetNext(&filename)) + return v8::Null(args->isolate()); + + const base::FilePath& path = base::FilePath::FromUTF8Unsafe(filename); + std::string contents; + if (!ReadFileToString(path, &contents)) + return v8::Null(args->isolate()); + + return gin::Converter<std::string>::ToV8(args->isolate(), contents); +} + +v8::Handle<v8::Value> GetSourceRootDirectory(gin::Arguments* args) { + base::FilePath path; + if (!PathService::Get(base::DIR_SOURCE_ROOT, &path)) + return v8::Null(args->isolate()); + return gin::Converter<std::string>::ToV8(args->isolate(), + path.AsUTF8Unsafe()); +} + +v8::Handle<v8::Value> GetFilesInDirectory(gin::Arguments* args) { + std::string filename; + if (!args->GetNext(&filename)) + return v8::Null(args->isolate()); + + const base::FilePath& path = base::FilePath::FromUTF8Unsafe(filename); + if (!base::DirectoryExists(path)) + return v8::Null(args->isolate()); + + std::vector<std::string> names; + base::FileEnumerator e(path, false, base::FileEnumerator::FILES); + for (base::FilePath name = e.Next(); !name.empty(); name = e.Next()) { + names.push_back(name.BaseName().AsUTF8Unsafe()); + } + + return gin::Converter<std::vector<std::string> >::ToV8(args->isolate(), + names); +} + +gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin }; + +} // namespace + +const char File::kModuleName[] = "file"; + +v8::Local<v8::Value> File::GetModule(v8::Isolate* isolate) { + gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); + v8::Local<ObjectTemplate> templ = data->GetObjectTemplate(&g_wrapper_info); + if (templ.IsEmpty()) { + templ = gin::ObjectTemplateBuilder(isolate) + .SetMethod("readFileToString", ReadFileToString) + .SetMethod("getFilesInDirectory", GetFilesInDirectory) + .SetMethod("getSourceRootDirectory", GetSourceRootDirectory) + .Build(); + data->SetObjectTemplate(&g_wrapper_info, templ); + } + return templ->NewInstance(); +} + +} // namespace gin
diff --git a/gin/test/file.h b/gin/test/file.h new file mode 100644 index 0000000..a4acd59 --- /dev/null +++ b/gin/test/file.h
@@ -0,0 +1,21 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GIN_TEST_FILE_H_ +#define GIN_TEST_FILE_H_ + +#include "v8/include/v8.h" + +namespace gin { + +class File { + public: + static const char kModuleName[]; + static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); +}; + +} // namespace gin + +#endif // GIN_TEST_FILE_H_ +
diff --git a/gin/test/file_runner.cc b/gin/test/file_runner.cc new file mode 100644 index 0000000..83228d6 --- /dev/null +++ b/gin/test/file_runner.cc
@@ -0,0 +1,82 @@ +// 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. + +#include "gin/test/file_runner.h" + +#include "base/files/file_util.h" +#include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "gin/array_buffer.h" +#include "gin/converter.h" +#include "gin/modules/console.h" +#include "gin/modules/module_registry.h" +#include "gin/public/context_holder.h" +#include "gin/public/isolate_holder.h" +#include "gin/test/file.h" +#include "gin/test/gc.h" +#include "gin/test/gtest.h" +#include "gin/try_catch.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gin { + +namespace { + +std::vector<base::FilePath> GetModuleSearchPaths() { + std::vector<base::FilePath> search_paths(2); + PathService::Get(base::DIR_SOURCE_ROOT, &search_paths[0]); + PathService::Get(base::DIR_EXE, &search_paths[1]); + search_paths[1] = search_paths[1].AppendASCII("gen"); + return search_paths; +} + +} // namespace + +FileRunnerDelegate::FileRunnerDelegate() + : ModuleRunnerDelegate(GetModuleSearchPaths()) { + AddBuiltinModule(Console::kModuleName, Console::GetModule); + AddBuiltinModule(GTest::kModuleName, GTest::GetModule); + AddBuiltinModule(GC::kModuleName, GC::GetModule); + AddBuiltinModule(File::kModuleName, File::GetModule); +} + +FileRunnerDelegate::~FileRunnerDelegate() { +} + +void FileRunnerDelegate::UnhandledException(ShellRunner* runner, + TryCatch& try_catch) { + ModuleRunnerDelegate::UnhandledException(runner, try_catch); + FAIL() << try_catch.GetStackTrace(); +} + +void RunTestFromFile(const base::FilePath& path, FileRunnerDelegate* delegate, + bool run_until_idle) { + ASSERT_TRUE(base::PathExists(path)) << path.LossyDisplayName(); + std::string source; + ASSERT_TRUE(ReadFileToString(path, &source)); + + base::MessageLoop message_loop; + + gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode, + gin::ArrayBufferAllocator::SharedInstance()); + gin::IsolateHolder instance; + gin::ShellRunner runner(delegate, instance.isolate()); + { + gin::Runner::Scope scope(&runner); + v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); + runner.Run(source, path.AsUTF8Unsafe()); + + if (run_until_idle) { + message_loop.RunUntilIdle(); + } else { + message_loop.Run(); + } + + v8::Handle<v8::Value> result = runner.global()->Get( + StringToSymbol(runner.GetContextHolder()->isolate(), "result")); + EXPECT_EQ("PASS", V8ToString(result)); + } +} + +} // namespace gin
diff --git a/gin/test/file_runner.h b/gin/test/file_runner.h new file mode 100644 index 0000000..f248fb5 --- /dev/null +++ b/gin/test/file_runner.h
@@ -0,0 +1,38 @@ +// 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. + +#ifndef GIN_TEST_FILE_RUNNER_H_ +#define GIN_TEST_FILE_RUNNER_H_ + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "gin/modules/module_runner_delegate.h" +#include "gin/runner.h" + +namespace gin { + +// FileRunnerDelegate is a simple RunnerDelegate that's useful for running +// tests. The FileRunnerDelegate provides built-in modules for "console" and +// "gtest" that are useful when writing unit tests. +// +// TODO(abarth): Rename FileRunnerDelegate to TestRunnerDelegate. +class FileRunnerDelegate : public ModuleRunnerDelegate { + public: + FileRunnerDelegate(); + virtual ~FileRunnerDelegate(); + + private: + // From ModuleRunnerDelegate: + virtual void UnhandledException(ShellRunner* runner, + TryCatch& try_catch) OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(FileRunnerDelegate); +}; + +void RunTestFromFile(const base::FilePath& path, FileRunnerDelegate* delegate, + bool run_until_idle = true); + +} // namespace gin + +#endif // GIN_TEST_FILE_RUNNER_H_
diff --git a/gin/test/file_unittests.js b/gin/test/file_unittests.js new file mode 100644 index 0000000..8c25806 --- /dev/null +++ b/gin/test/file_unittests.js
@@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +define([ + "gin/test/expect", + "file" + ], function(expect, file) { + + function isString(x) { + return toString.call(x) === '[object String]' + } + + var rootDir = file.getSourceRootDirectory(); + expect(isString(rootDir)).toBeTruthy(); + + var noArgsNull = file.getFilesInDirectory(); + expect(noArgsNull).toBeNull(); + + var files = file.getFilesInDirectory(rootDir); + expect(Array.isArray(files)).toBeTruthy(); + + var nsdNull = file.getFilesInDirectory(rootDir + "/no_such_dir"); + expect(nsdNull).toBeNull(); + + var owners = file.readFileToString(rootDir + "/OWNERS"); + expect(isString(owners)).toBeTruthy(); + expect(owners.length).toBeGreaterThan(0); + + noArgsNull = file.readFileToString(); + expect(noArgsNull).toBeNull(); + + var nsfNull = file.readFileToString(rootDir + "/no_such_file"); + expect(nsfNull).toBeNull(); + + this.result = "PASS"; +});
diff --git a/gin/test/gc.cc b/gin/test/gc.cc new file mode 100644 index 0000000..4cd67e1 --- /dev/null +++ b/gin/test/gc.cc
@@ -0,0 +1,41 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/test/gc.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "gin/arguments.h" +#include "gin/converter.h" +#include "gin/function_template.h" +#include "gin/object_template_builder.h" +#include "gin/per_isolate_data.h" +#include "gin/public/wrapper_info.h" +#include "gin/wrappable.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gin { + +namespace { +WrapperInfo g_wrapper_info = { kEmbedderNativeGin }; +} // namespace + +const char GC::kModuleName[] = "gc"; + +v8::Local<v8::Value> GC::GetModule(v8::Isolate* isolate) { + PerIsolateData* data = PerIsolateData::From(isolate); + v8::Local<v8::ObjectTemplate> templ = + data->GetObjectTemplate(&g_wrapper_info); + if (templ.IsEmpty()) { + templ = ObjectTemplateBuilder(isolate) + .SetMethod("collectGarbage", + base::Bind(&v8::Isolate::LowMemoryNotification, + base::Unretained(isolate))) + .Build(); + data->SetObjectTemplate(&g_wrapper_info, templ); + } + return templ->NewInstance(); +} + +} // namespace gin
diff --git a/gin/test/gc.h b/gin/test/gc.h new file mode 100644 index 0000000..25917ef --- /dev/null +++ b/gin/test/gc.h
@@ -0,0 +1,21 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GIN_TEST_GC_H_ +#define GIN_TEST_GC_H_ + +#include "v8/include/v8.h" + +namespace gin { + +// This module provides bindings to the garbage collector. +class GC { + public: + static const char kModuleName[]; + static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); +}; + +} // namespace gin + +#endif // GIN_TEST_GC_H_
diff --git a/gin/test/gtest.cc b/gin/test/gtest.cc new file mode 100644 index 0000000..b4060a3 --- /dev/null +++ b/gin/test/gtest.cc
@@ -0,0 +1,62 @@ +// 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. + +#include "gin/test/gtest.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "gin/arguments.h" +#include "gin/converter.h" +#include "gin/function_template.h" +#include "gin/object_template_builder.h" +#include "gin/per_isolate_data.h" +#include "gin/public/wrapper_info.h" +#include "gin/wrappable.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gin { + +namespace { + +void Fail(const std::string& description) { + FAIL() << description; +} + +void ExpectTrue(bool condition, const std::string& description) { + EXPECT_TRUE(condition) << description; +} + +void ExpectFalse(bool condition, const std::string& description) { + EXPECT_FALSE(condition) << description; +} + +void ExpectEqual(const v8::Handle<v8::Value> expected, + const v8::Handle<v8::Value> actual, + const std::string& description) { + EXPECT_TRUE(expected->StrictEquals(actual)) << description; +} + +WrapperInfo g_wrapper_info = { kEmbedderNativeGin }; + +} // namespace + +const char GTest::kModuleName[] = "gtest"; + +v8::Local<v8::Value> GTest::GetModule(v8::Isolate* isolate) { + PerIsolateData* data = PerIsolateData::From(isolate); + v8::Local<v8::ObjectTemplate> templ = + data->GetObjectTemplate(&g_wrapper_info); + if (templ.IsEmpty()) { + templ = ObjectTemplateBuilder(isolate) + .SetMethod("fail", Fail) + .SetMethod("expectTrue", ExpectTrue) + .SetMethod("expectFalse", ExpectFalse) + .SetMethod("expectEqual", ExpectEqual) + .Build(); + data->SetObjectTemplate(&g_wrapper_info, templ); + } + return templ->NewInstance(); +} + +} // namespace gin
diff --git a/gin/test/gtest.h b/gin/test/gtest.h new file mode 100644 index 0000000..8f4332d --- /dev/null +++ b/gin/test/gtest.h
@@ -0,0 +1,23 @@ +// 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. + +#ifndef GIN_TEST_GTEST_H_ +#define GIN_TEST_GTEST_H_ + +#include "v8/include/v8.h" + +namespace gin { + +// This module provides bindings to gtest. Most tests should use an idiomatic +// JavaScript testing API, but this module is available for tests that need a +// low-level integration with gtest. +class GTest { + public: + static const char kModuleName[]; + static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); +}; + +} // namespace gin + +#endif // GIN_TEST_GTEST_H_
diff --git a/gin/test/gtest_unittests.js b/gin/test/gtest_unittests.js new file mode 100644 index 0000000..1d566d5 --- /dev/null +++ b/gin/test/gtest_unittests.js
@@ -0,0 +1,11 @@ +// 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. + +define(["gtest"], function(gtest) { + gtest.expectTrue(true, "true is true"); + gtest.expectFalse(false, "false is false"); + gtest.expectTrue(this, "this is " + this); + + this.result = "PASS"; +});
diff --git a/gin/test/run_all_unittests.cc b/gin/test/run_all_unittests.cc new file mode 100644 index 0000000..25500a6 --- /dev/null +++ b/gin/test/run_all_unittests.cc
@@ -0,0 +1,15 @@ +// 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. + +#include "base/bind.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" + +int main(int argc, char** argv) { + base::TestSuite test_suite(argc, argv); + + return base::LaunchUnitTests( + argc, argv, base::Bind(&base::TestSuite::Run, + base::Unretained(&test_suite))); +}
diff --git a/gin/test/run_js_tests.cc b/gin/test/run_js_tests.cc new file mode 100644 index 0000000..b83dc9f --- /dev/null +++ b/gin/test/run_js_tests.cc
@@ -0,0 +1,43 @@ +// 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. + +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "gin/test/file_runner.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gin { +namespace { + +base::FilePath BasePath() { + base::FilePath path; + PathService::Get(base::DIR_SOURCE_ROOT, &path); + return path.AppendASCII("gin"); +} + +void RunTest(const base::FilePath& path) { + FileRunnerDelegate delegate; + RunTestFromFile(path, &delegate); +} + +TEST(JSTest, File) { + RunTest(BasePath() + .AppendASCII("test") + .AppendASCII("file_unittests.js")); +} + +TEST(JSTest, GTest) { + RunTest(BasePath() + .AppendASCII("test") + .AppendASCII("gtest_unittests.js")); +} + +TEST(JSTest, ModuleRegistry) { + RunTest(BasePath() + .AppendASCII("modules") + .AppendASCII("module_registry_unittests.js")); +} + +} // namespace +} // gin
diff --git a/gin/test/v8_test.cc b/gin/test/v8_test.cc new file mode 100644 index 0000000..cb6d573 --- /dev/null +++ b/gin/test/v8_test.cc
@@ -0,0 +1,42 @@ +// 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. + +#include "gin/test/v8_test.h" + +#include "gin/array_buffer.h" +#include "gin/public/isolate_holder.h" + +using v8::Context; +using v8::Local; +using v8::HandleScope; + +namespace gin { + +V8Test::V8Test() { +} + +V8Test::~V8Test() { +} + +void V8Test::SetUp() { + gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode, + gin::ArrayBufferAllocator::SharedInstance()); + instance_.reset(new gin::IsolateHolder); + instance_->isolate()->Enter(); + HandleScope handle_scope(instance_->isolate()); + context_.Reset(instance_->isolate(), Context::New(instance_->isolate())); + Local<Context>::New(instance_->isolate(), context_)->Enter(); +} + +void V8Test::TearDown() { + { + HandleScope handle_scope(instance_->isolate()); + Local<Context>::New(instance_->isolate(), context_)->Exit(); + context_.Reset(); + } + instance_->isolate()->Exit(); + instance_.reset(); +} + +} // namespace gin
diff --git a/gin/test/v8_test.h b/gin/test/v8_test.h new file mode 100644 index 0000000..e62aa57 --- /dev/null +++ b/gin/test/v8_test.h
@@ -0,0 +1,38 @@ +// 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. + +#ifndef GIN_TEST_V8_TEST_H_ +#define GIN_TEST_V8_TEST_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "v8/include/v8.h" + +namespace gin { + +class IsolateHolder; + +// V8Test is a simple harness for testing interactions with V8. V8Test doesn't +// have any dependencies on Gin's module system. +class V8Test : public testing::Test { + public: + V8Test(); + virtual ~V8Test(); + + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + + protected: + scoped_ptr<IsolateHolder> instance_; + v8::Persistent<v8::Context> context_; + + private: + DISALLOW_COPY_AND_ASSIGN(V8Test); +}; + +} // namespace gin + +#endif // GIN_TEST_V8_TEST_H_
diff --git a/gin/try_catch.cc b/gin/try_catch.cc new file mode 100644 index 0000000..a44e28e --- /dev/null +++ b/gin/try_catch.cc
@@ -0,0 +1,49 @@ +// 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. + +#include "gin/try_catch.h" + +#include <sstream> + +#include "gin/converter.h" + +namespace gin { + +TryCatch::TryCatch() { +} + +TryCatch::~TryCatch() { +} + +bool TryCatch::HasCaught() { + return try_catch_.HasCaught(); +} + +std::string TryCatch::GetStackTrace() { + if (!HasCaught()) { + return ""; + } + + std::stringstream ss; + v8::Handle<v8::Message> message = try_catch_.Message(); + ss << V8ToString(message->Get()) << std::endl + << V8ToString(message->GetSourceLine()) << std::endl; + + v8::Handle<v8::StackTrace> trace = message->GetStackTrace(); + if (trace.IsEmpty()) + return ss.str(); + + int len = trace->GetFrameCount(); + for (int i = 0; i < len; ++i) { + v8::Handle<v8::StackFrame> frame = trace->GetFrame(i); + ss << V8ToString(frame->GetScriptName()) << ":" + << frame->GetLineNumber() << ":" + << frame->GetColumn() << ": " + << V8ToString(frame->GetFunctionName()) + << std::endl; + } + return ss.str(); +} + +} // namespace gin
diff --git a/gin/try_catch.h b/gin/try_catch.h new file mode 100644 index 0000000..633b909 --- /dev/null +++ b/gin/try_catch.h
@@ -0,0 +1,33 @@ +// 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. + +#ifndef GIN_TRY_CATCH_H_ +#define GIN_TRY_CATCH_H_ + +#include <string> + +#include "base/basictypes.h" +#include "gin/gin_export.h" +#include "v8/include/v8.h" + +namespace gin { + +// TryCatch is a convenient wrapper around v8::TryCatch. +class GIN_EXPORT TryCatch { + public: + TryCatch(); + ~TryCatch(); + + bool HasCaught(); + std::string GetStackTrace(); + + private: + v8::TryCatch try_catch_; + + DISALLOW_COPY_AND_ASSIGN(TryCatch); +}; + +} // namespace gin + +#endif // GIN_TRY_CATCH_H_
diff --git a/gin/v8_platform.cc b/gin/v8_platform.cc new file mode 100644 index 0000000..d50ff24 --- /dev/null +++ b/gin/v8_platform.cc
@@ -0,0 +1,42 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gin/public/v8_platform.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/threading/worker_pool.h" +#include "gin/per_isolate_data.h" + +namespace gin { + +namespace { + +base::LazyInstance<V8Platform>::Leaky g_v8_platform = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +// static +V8Platform* V8Platform::Get() { return g_v8_platform.Pointer(); } + +V8Platform::V8Platform() {} + +V8Platform::~V8Platform() {} + +void V8Platform::CallOnBackgroundThread( + v8::Task* task, + v8::Platform::ExpectedRuntime expected_runtime) { + base::WorkerPool::PostTask( + FROM_HERE, + base::Bind(&v8::Task::Run, base::Owned(task)), + expected_runtime == v8::Platform::kLongRunningTask); +} + +void V8Platform::CallOnForegroundThread(v8::Isolate* isolate, v8::Task* task) { + PerIsolateData::From(isolate)->message_loop_proxy()->PostTask( + FROM_HERE, base::Bind(&v8::Task::Run, base::Owned(task))); +} + +} // namespace gin
diff --git a/gin/wrappable.cc b/gin/wrappable.cc new file mode 100644 index 0000000..a330fef --- /dev/null +++ b/gin/wrappable.cc
@@ -0,0 +1,87 @@ +// 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. + +#include "gin/wrappable.h" + +#include "base/logging.h" +#include "gin/object_template_builder.h" +#include "gin/per_isolate_data.h" + +namespace gin { + +WrappableBase::WrappableBase() { +} + +WrappableBase::~WrappableBase() { + wrapper_.Reset(); +} + +ObjectTemplateBuilder WrappableBase::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return ObjectTemplateBuilder(isolate); +} + +void WrappableBase::WeakCallback( + const v8::WeakCallbackData<v8::Object, WrappableBase>& data) { + WrappableBase* wrappable = data.GetParameter(); + wrappable->wrapper_.Reset(); + delete wrappable; +} + +v8::Handle<v8::Object> WrappableBase::GetWrapperImpl(v8::Isolate* isolate, + WrapperInfo* info) { + if (!wrapper_.IsEmpty()) { + return v8::Local<v8::Object>::New(isolate, wrapper_); + } + + PerIsolateData* data = PerIsolateData::From(isolate); + v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(info); + if (templ.IsEmpty()) { + templ = GetObjectTemplateBuilder(isolate).Build(); + CHECK(!templ.IsEmpty()); + data->SetObjectTemplate(info, templ); + } + CHECK_EQ(kNumberOfInternalFields, templ->InternalFieldCount()); + v8::Handle<v8::Object> wrapper = templ->NewInstance(); + // |wrapper| may be empty in some extreme cases, e.g., when + // Object.prototype.constructor is overwritten. + if (wrapper.IsEmpty()) { + // The current wrappable object will be no longer managed by V8. Delete this + // now. + delete this; + return wrapper; + } + wrapper->SetAlignedPointerInInternalField(kWrapperInfoIndex, info); + wrapper->SetAlignedPointerInInternalField(kEncodedValueIndex, this); + wrapper_.Reset(isolate, wrapper); + wrapper_.SetWeak(this, WeakCallback); + return wrapper; +} + +namespace internal { + +void* FromV8Impl(v8::Isolate* isolate, v8::Handle<v8::Value> val, + WrapperInfo* wrapper_info) { + if (!val->IsObject()) + return NULL; + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val); + WrapperInfo* info = WrapperInfo::From(obj); + + // If this fails, the object is not managed by Gin. It is either a normal JS + // object that's not wrapping any external C++ object, or it is wrapping some + // C++ object, but that object isn't managed by Gin (maybe Blink). + if (!info) + return NULL; + + // If this fails, the object is managed by Gin, but it's not wrapping an + // instance of the C++ class associated with wrapper_info. + if (info != wrapper_info) + return NULL; + + return obj->GetAlignedPointerFromInternalField(kEncodedValueIndex); +} + +} // namespace internal + +} // namespace gin
diff --git a/gin/wrappable.h b/gin/wrappable.h new file mode 100644 index 0000000..ff52b19 --- /dev/null +++ b/gin/wrappable.h
@@ -0,0 +1,116 @@ +// 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. + +#ifndef GIN_WRAPPABLE_H_ +#define GIN_WRAPPABLE_H_ + +#include "base/template_util.h" +#include "gin/converter.h" +#include "gin/gin_export.h" +#include "gin/public/wrapper_info.h" + +namespace gin { + +namespace internal { + +GIN_EXPORT void* FromV8Impl(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + WrapperInfo* info); + +} // namespace internal + + +// Wrappable is a base class for C++ objects that have corresponding v8 wrapper +// objects. To retain a Wrappable object on the stack, use a gin::Handle. +// +// USAGE: +// // my_class.h +// class MyClass : Wrappable<MyClass> { +// public: +// static WrapperInfo kWrapperInfo; +// +// // Optional, only required if non-empty template should be used. +// virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder( +// v8::Isolate* isolate); +// ... +// }; +// +// // my_class.cc +// WrapperInfo MyClass::kWrapperInfo = {kEmbedderNativeGin}; +// +// gin::ObjectTemplateBuilder MyClass::GetObjectTemplateBuilder( +// v8::Isolate* isolate) { +// return Wrappable<MyClass>::GetObjectTemplateBuilder(isolate) +// .SetValue("foobar", 42); +// } +// +// Subclasses should also typically have private constructors and expose a +// static Create function that returns a gin::Handle. Forcing creators through +// this static Create function will enforce that clients actually create a +// wrapper for the object. If clients fail to create a wrapper for a wrappable +// object, the object will leak because we use the weak callback from the +// wrapper as the signal to delete the wrapped object. +template<typename T> +class Wrappable; + +class ObjectTemplateBuilder; + +// Non-template base class to share code between templates instances. +class GIN_EXPORT WrappableBase { + protected: + WrappableBase(); + virtual ~WrappableBase(); + + virtual ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate); + + v8::Handle<v8::Object> GetWrapperImpl(v8::Isolate* isolate, + WrapperInfo* wrapper_info); + + private: + static void WeakCallback( + const v8::WeakCallbackData<v8::Object, WrappableBase>& data); + + v8::Persistent<v8::Object> wrapper_; // Weak + + DISALLOW_COPY_AND_ASSIGN(WrappableBase); +}; + + +template<typename T> +class Wrappable : public WrappableBase { + public: + // Retrieve (or create) the v8 wrapper object cooresponding to this object. + // To customize the wrapper created for a subclass, override GetWrapperInfo() + // instead of overriding this function. + v8::Handle<v8::Object> GetWrapper(v8::Isolate* isolate) { + return GetWrapperImpl(isolate, &T::kWrapperInfo); + } + + protected: + Wrappable() {} + virtual ~Wrappable() {} + + private: + DISALLOW_COPY_AND_ASSIGN(Wrappable); +}; + + +// This converter handles any subclass of Wrappable. +template<typename T> +struct Converter<T*, typename base::enable_if< + base::is_convertible<T*, WrappableBase*>::value>::type> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, T* val) { + return val->GetWrapper(isolate); + } + + static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, T** out) { + *out = static_cast<T*>(static_cast<WrappableBase*>( + internal::FromV8Impl(isolate, val, &T::kWrapperInfo))); + return *out != NULL; + } +}; + +} // namespace gin + +#endif // GIN_WRAPPABLE_H_
diff --git a/gin/wrappable_unittest.cc b/gin/wrappable_unittest.cc new file mode 100644 index 0000000..0e10c32 --- /dev/null +++ b/gin/wrappable_unittest.cc
@@ -0,0 +1,273 @@ +// 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. + +#include "base/logging.h" +#include "gin/arguments.h" +#include "gin/handle.h" +#include "gin/object_template_builder.h" +#include "gin/per_isolate_data.h" +#include "gin/public/isolate_holder.h" +#include "gin/test/v8_test.h" +#include "gin/try_catch.h" +#include "gin/wrappable.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gin { + +class BaseClass { + public: + BaseClass() : value_(23) {} + virtual ~BaseClass() {} + + private: + int value_; + + DISALLOW_COPY_AND_ASSIGN(BaseClass); +}; + +class MyObject : public BaseClass, + public Wrappable<MyObject> { + public: + static WrapperInfo kWrapperInfo; + + static gin::Handle<MyObject> Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new MyObject()); + } + + int value() const { return value_; } + void set_value(int value) { value_ = value; } + + protected: + MyObject() : value_(0) {} + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override; + virtual ~MyObject() {} + + private: + int value_; +}; + +class MyObjectSubclass : public MyObject { + public: + static gin::Handle<MyObjectSubclass> Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new MyObjectSubclass()); + } + + void SayHello(const std::string& name) { + result = std::string("Hello, ") + name; + } + + std::string result; + + private: + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override { + return MyObject::GetObjectTemplateBuilder(isolate) + .SetMethod("sayHello", &MyObjectSubclass::SayHello); + } + + MyObjectSubclass() { + } + + virtual ~MyObjectSubclass() { + } +}; + +class MyCallableObject : public Wrappable<MyCallableObject> { + public: + static WrapperInfo kWrapperInfo; + + static gin::Handle<MyCallableObject> Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new MyCallableObject()); + } + + int result() { return result_; } + + private: + virtual ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override { + return Wrappable<MyCallableObject>::GetObjectTemplateBuilder(isolate) + .SetCallAsFunctionHandler(&MyCallableObject::Call); + } + + MyCallableObject() : result_(0) { + } + + virtual ~MyCallableObject() { + } + + void Call(int val, const gin::Arguments& arguments) { + if (arguments.IsConstructCall()) + arguments.ThrowTypeError("Cannot be called as constructor."); + else + result_ = val; + } + + int result_; +}; + +class MyObject2 : public Wrappable<MyObject2> { + public: + static WrapperInfo kWrapperInfo; +}; + +class MyObjectBlink : public Wrappable<MyObjectBlink> { + public: + static WrapperInfo kWrapperInfo; +}; + +WrapperInfo MyObject::kWrapperInfo = { kEmbedderNativeGin }; +ObjectTemplateBuilder MyObject::GetObjectTemplateBuilder(v8::Isolate* isolate) { + return Wrappable<MyObject>::GetObjectTemplateBuilder(isolate) + .SetProperty("value", &MyObject::value, &MyObject::set_value); +} + +WrapperInfo MyCallableObject::kWrapperInfo = { kEmbedderNativeGin }; +WrapperInfo MyObject2::kWrapperInfo = { kEmbedderNativeGin }; +WrapperInfo MyObjectBlink::kWrapperInfo = { kEmbedderNativeGin }; + +typedef V8Test WrappableTest; + +TEST_F(WrappableTest, WrapAndUnwrap) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + Handle<MyObject> obj = MyObject::Create(isolate); + + v8::Handle<v8::Value> wrapper = ConvertToV8(isolate, obj.get()); + EXPECT_FALSE(wrapper.IsEmpty()); + + MyObject* unwrapped = NULL; + EXPECT_TRUE(ConvertFromV8(isolate, wrapper, &unwrapped)); + EXPECT_EQ(obj.get(), unwrapped); +} + +TEST_F(WrappableTest, UnwrapFailures) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + // Something that isn't an object. + v8::Handle<v8::Value> thing = v8::Number::New(isolate, 42); + MyObject* unwrapped = NULL; + EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped)); + EXPECT_FALSE(unwrapped); + + // An object that's not wrapping anything. + thing = v8::Object::New(isolate); + EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped)); + EXPECT_FALSE(unwrapped); + + // An object that's wrapping a C++ object from Blink. + thing.Clear(); + thing = ConvertToV8(isolate, new MyObjectBlink()); + EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped)); + EXPECT_FALSE(unwrapped); + + // An object that's wrapping a C++ object of the wrong type. + thing.Clear(); + thing = ConvertToV8(isolate, new MyObject2()); + EXPECT_FALSE(ConvertFromV8(isolate, thing, &unwrapped)); + EXPECT_FALSE(unwrapped); +} + +TEST_F(WrappableTest, GetAndSetProperty) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + gin::Handle<MyObject> obj = MyObject::Create(isolate); + + obj->set_value(42); + EXPECT_EQ(42, obj->value()); + + v8::Handle<v8::String> source = StringToV8(isolate, + "(function (obj) {" + " if (obj.value !== 42) throw 'FAIL';" + " else obj.value = 191; })"); + EXPECT_FALSE(source.IsEmpty()); + + gin::TryCatch try_catch; + v8::Handle<v8::Script> script = v8::Script::Compile(source); + EXPECT_FALSE(script.IsEmpty()); + v8::Handle<v8::Value> val = script->Run(); + EXPECT_FALSE(val.IsEmpty()); + v8::Handle<v8::Function> func; + EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); + v8::Handle<v8::Value> argv[] = { + ConvertToV8(isolate, obj.get()), + }; + func->Call(v8::Undefined(isolate), 1, argv); + EXPECT_FALSE(try_catch.HasCaught()); + EXPECT_EQ("", try_catch.GetStackTrace()); + + EXPECT_EQ(191, obj->value()); +} + +TEST_F(WrappableTest, WrappableSubclass) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + gin::Handle<MyObjectSubclass> object(MyObjectSubclass::Create(isolate)); + v8::Handle<v8::String> source = StringToV8(isolate, + "(function(obj) {" + "obj.sayHello('Lily');" + "})"); + gin::TryCatch try_catch; + v8::Handle<v8::Script> script = v8::Script::Compile(source); + v8::Handle<v8::Value> val = script->Run(); + v8::Handle<v8::Function> func; + EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); + v8::Handle<v8::Value> argv[] = { + ConvertToV8(isolate, object.get()) + }; + func->Call(v8::Undefined(isolate), 1, argv); + EXPECT_FALSE(try_catch.HasCaught()); + EXPECT_EQ("Hello, Lily", object->result); +} + +TEST_F(WrappableTest, CallAsFunction) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + gin::Handle<MyCallableObject> object(MyCallableObject::Create(isolate)); + EXPECT_EQ(0, object->result()); + v8::Handle<v8::String> source = StringToV8(isolate, + "(function(obj) {" + "obj(42);" + "})"); + gin::TryCatch try_catch; + v8::Handle<v8::Script> script = v8::Script::Compile(source); + v8::Handle<v8::Value> val = script->Run(); + v8::Handle<v8::Function> func; + EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); + v8::Handle<v8::Value> argv[] = { + ConvertToV8(isolate, object.get()) + }; + func->Call(v8::Undefined(isolate), 1, argv); + EXPECT_FALSE(try_catch.HasCaught()); + EXPECT_EQ(42, object->result()); +} + +TEST_F(WrappableTest, CallAsConstructor) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + gin::Handle<MyCallableObject> object(MyCallableObject::Create(isolate)); + EXPECT_EQ(0, object->result()); + v8::Handle<v8::String> source = StringToV8(isolate, + "(function(obj) {" + "new obj(42);" + "})"); + gin::TryCatch try_catch; + v8::Handle<v8::Script> script = v8::Script::Compile(source); + v8::Handle<v8::Value> val = script->Run(); + v8::Handle<v8::Function> func; + EXPECT_TRUE(ConvertFromV8(isolate, val, &func)); + v8::Handle<v8::Value> argv[] = { + ConvertToV8(isolate, object.get()) + }; + func->Call(v8::Undefined(isolate), 1, argv); + EXPECT_TRUE(try_catch.HasCaught()); +} + +} // namespace gin
diff --git a/gin/wrapper_info.cc b/gin/wrapper_info.cc new file mode 100644 index 0000000..6bf8316 --- /dev/null +++ b/gin/wrapper_info.cc
@@ -0,0 +1,17 @@ +// 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. + +#include "gin/public/wrapper_info.h" + +namespace gin { + +WrapperInfo* WrapperInfo::From(v8::Handle<v8::Object> object) { + if (object->InternalFieldCount() != kNumberOfInternalFields) + return NULL; + WrapperInfo* info = static_cast<WrapperInfo*>( + object->GetAlignedPointerFromInternalField(kWrapperInfoIndex)); + return info->embedder == kEmbedderNativeGin ? info : NULL; +} + +} // namespace gin
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn index b77dfca..8239940 100644 --- a/gpu/BUILD.gn +++ b/gpu/BUILD.gn
@@ -43,6 +43,7 @@ "//gpu/command_buffer/common", "//gpu/command_buffer/service", "//gpu/config", + "//gpu/ipc", ] }
diff --git a/gpu/config/gpu_info.cc b/gpu/config/gpu_info.cc index f560dcb..c7951f9 100644 --- a/gpu/config/gpu_info.cc +++ b/gpu/config/gpu_info.cc
@@ -17,6 +17,19 @@ enumerator->EndGPUDevice(); } +void EnumerateVideoEncodeAcceleratorSupportedProfile( + gpu::GPUInfo::Enumerator* enumerator, + const media::VideoEncodeAccelerator::SupportedProfile profile) { + enumerator->BeginVideoEncodeAcceleratorSupportedProfile(); + enumerator->AddInt("profile", profile.profile); + enumerator->AddInt("maxResolutionWidth", profile.max_resolution.width()); + enumerator->AddInt("maxResolutionHeight", profile.max_resolution.height()); + enumerator->AddInt("maxFramerateNumerator", profile.max_framerate_numerator); + enumerator->AddInt("maxFramerateDenominator", + profile.max_framerate_denominator); + enumerator->EndVideoEncodeAcceleratorSupportedProfile(); +} + } // namespace namespace gpu { @@ -88,6 +101,8 @@ CollectInfoResult dx_diagnostics_info_state; DxDiagNode dx_diagnostics; #endif + std::vector<media::VideoEncodeAccelerator::SupportedProfile> + video_encode_accelerator_supported_profiles; }; // If this assert fails then most likely something below needs to be updated. @@ -142,6 +157,12 @@ #if defined(OS_WIN) enumerator->AddInt("DxDiagnosticsInfoState", dx_diagnostics_info_state); #endif + // TODO(kbr): add dx_diagnostics on Windows. + for (size_t ii = 0; ii < video_encode_accelerator_supported_profiles.size(); + ++ii) { + EnumerateVideoEncodeAcceleratorSupportedProfile( + enumerator, video_encode_accelerator_supported_profiles[ii]); + } enumerator->EndAuxAttributes(); }
diff --git a/gpu/config/gpu_info.h b/gpu/config/gpu_info.h index 8f5479b..bdbb205 100644 --- a/gpu/config/gpu_info.h +++ b/gpu/config/gpu_info.h
@@ -18,6 +18,7 @@ #include "gpu/config/dx_diag_node.h" #include "gpu/config/gpu_performance_stats.h" #include "gpu/gpu_export.h" +#include "media/video/video_encode_accelerator.h" namespace gpu { @@ -177,6 +178,8 @@ DxDiagNode dx_diagnostics; #endif + std::vector<media::VideoEncodeAccelerator::SupportedProfile> + video_encode_accelerator_supported_profiles; // Note: when adding new members, please remember to update EnumerateFields // in gpu_info.cc.
diff --git a/gpu/config/gpu_info_collector.cc b/gpu/config/gpu_info_collector.cc index 78dfe22..5d25546 100644 --- a/gpu/config/gpu_info_collector.cc +++ b/gpu/config/gpu_info_collector.cc
@@ -156,6 +156,8 @@ basic_gpu_info->direct_rendering = context_gpu_info.direct_rendering; basic_gpu_info->context_info_state = context_gpu_info.context_info_state; basic_gpu_info->initialization_time = context_gpu_info.initialization_time; + basic_gpu_info->video_encode_accelerator_supported_profiles = + context_gpu_info.video_encode_accelerator_supported_profiles; } } // namespace gpu
diff --git a/gpu/config/gpu_info_unittest.cc b/gpu/config/gpu_info_unittest.cc index 48d476f..71d4e5c 100644 --- a/gpu/config/gpu_info_unittest.cc +++ b/gpu/config/gpu_info_unittest.cc
@@ -32,6 +32,7 @@ #if defined(OS_WIN) EXPECT_EQ(gpu_info.dx_diagnostics_info_state, kCollectInfoNone); #endif + EXPECT_EQ(gpu_info.video_encode_accelerator_supported_profiles.size(), 0u); } } // namespace gpu
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn index 36f90a1..7db56f7 100644 --- a/mojo/BUILD.gn +++ b/mojo/BUILD.gn
@@ -12,6 +12,7 @@ } deps = [ ":tests", + "//mojo/apps/js", "//mojo/common", "//mojo/examples", "//mojo/public", @@ -20,6 +21,12 @@ "//mojo/tools/package_manager", ] + if (is_android) { + deps += [ + "//mojo/android", + ] + } + if (is_linux) { deps += [ "//mojo/python", @@ -36,6 +43,8 @@ testonly = true deps = [ "//mojo/application_manager:mojo_application_manager_unittests", + "//mojo/apps/js/test:mojo_apps_js_unittests", + "//mojo/bindings/js/tests:mojo_js_unittests", "//mojo/common:mojo_common_unittests", "//mojo/edk/system:mojo_message_pipe_perftests", "//mojo/edk/system:mojo_system_unittests",
diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn new file mode 100644 index 0000000..c2cfbfe --- /dev/null +++ b/mojo/android/BUILD.gn
@@ -0,0 +1,64 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +group("android") { + testonly = true + deps = [ + ":system_impl", + ":mojo_javatests", + ] +} + +android_library("system_impl") { + java_files = [ + "system/src/org/chromium/mojo/system/impl/CoreImpl.java", + "system/src/org/chromium/mojo/system/impl/DataPipeConsumerHandleImpl.java", + "system/src/org/chromium/mojo/system/impl/DataPipeProducerHandleImpl.java", + "system/src/org/chromium/mojo/system/impl/HandleBase.java", + "system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java", + "system/src/org/chromium/mojo/system/impl/SharedBufferHandleImpl.java", + "system/src/org/chromium/mojo/system/impl/UntypedHandleImpl.java", + ] + + deps = [ + "//base:base_java", + "//mojo/public/java:system", + ] +} + +android_library("mojo_javatests") { + testonly = true + java_files = [ + "javatests/src/org/chromium/mojo/HandleMock.java", + "javatests/src/org/chromium/mojo/MojoTestCase.java", + "javatests/src/org/chromium/mojo/TestUtils.java", + "javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java", + "javatests/src/org/chromium/mojo/bindings/BindingsTest.java", + "javatests/src/org/chromium/mojo/bindings/BindingsTestUtils.java", + "javatests/src/org/chromium/mojo/bindings/CallbacksTest.java", + "javatests/src/org/chromium/mojo/bindings/ConnectorTest.java", + "javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java", + "javatests/src/org/chromium/mojo/bindings/InterfacesTest.java", + "javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java", + "javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java", + "javatests/src/org/chromium/mojo/bindings/RouterTest.java", + "javatests/src/org/chromium/mojo/bindings/SerializationTest.java", + "javatests/src/org/chromium/mojo/bindings/ValidationTest.java", + "javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java", + "javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java", + "javatests/src/org/chromium/mojo/bindings/test/mojom/mojo/IntegrationTestInterface2TestHelper.java", + "javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java", + ] + + deps = [ + ":system_impl", + "//base:base_java", + "//base:base_java_test_support", + "//mojo/public/interfaces/bindings/tests:test_interfaces_java", + "//mojo/public/java:bindings", + "//mojo/public/java:system", + ] +}
diff --git a/mojo/common/message_pump_mojo.cc b/mojo/common/message_pump_mojo.cc index 91b78d1..6d21af2 100644 --- a/mojo/common/message_pump_mojo.cc +++ b/mojo/common/message_pump_mojo.cc
@@ -139,11 +139,7 @@ bool more_work_is_plausible = true; for (;;) { const bool block = !more_work_is_plausible; - DoInternalWork(*run_state, block); - - // There isn't a good way to know if there are more handles ready, we assume - // not. - more_work_is_plausible = false; + more_work_is_plausible = DoInternalWork(*run_state, block); if (run_state->should_quit) break; @@ -166,15 +162,15 @@ } } -void MessagePumpMojo::DoInternalWork(const RunState& run_state, bool block) { +bool MessagePumpMojo::DoInternalWork(const RunState& run_state, bool block) { const MojoDeadline deadline = block ? GetDeadlineForWait(run_state) : 0; const WaitState wait_state = GetWaitState(run_state); const MojoResult result = WaitMany(wait_state.handles, wait_state.wait_signals, deadline); + bool did_work = true; if (result == 0) { // Control pipe was written to. - uint32_t num_bytes = 0; - ReadMessageRaw(run_state.read_handle.get(), NULL, &num_bytes, NULL, NULL, + ReadMessageRaw(run_state.read_handle.get(), NULL, NULL, NULL, NULL, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); } else if (result > 0) { const size_t index = static_cast<size_t>(result); @@ -188,6 +184,7 @@ RemoveFirstInvalidHandle(wait_state); break; case MOJO_RESULT_DEADLINE_EXCEEDED: + did_work = false; break; default: base::debug::Alias(&result); @@ -208,8 +205,11 @@ handlers_.find(i->first) != handlers_.end() && handlers_[i->first].id == i->second.id) { i->second.handler->OnHandleError(i->first, MOJO_RESULT_DEADLINE_EXCEEDED); + handlers_.erase(i->first); + did_work = true; } } + return did_work; } void MessagePumpMojo::RemoveFirstInvalidHandle(const WaitState& wait_state) {
diff --git a/mojo/common/message_pump_mojo.h b/mojo/common/message_pump_mojo.h index 3ec3457..26e28b1 100644 --- a/mojo/common/message_pump_mojo.h +++ b/mojo/common/message_pump_mojo.h
@@ -73,8 +73,9 @@ void DoRunLoop(RunState* run_state, Delegate* delegate); // Services the set of handles ready. If |block| is true this waits for a - // handle to become ready, otherwise this does not block. - void DoInternalWork(const RunState& run_state, bool block); + // handle to become ready, otherwise this does not block. Returns |true| if a + // handle has become ready, |false| otherwise. + bool DoInternalWork(const RunState& run_state, bool block); // Removes the first invalid handle. This is called if MojoWaitMany finds an // invalid handle.
diff --git a/mojo/common/message_pump_mojo_handler.h b/mojo/common/message_pump_mojo_handler.h index 5809051..ba40f80 100644 --- a/mojo/common/message_pump_mojo_handler.h +++ b/mojo/common/message_pump_mojo_handler.h
@@ -12,7 +12,7 @@ namespace common { // Used by MessagePumpMojo to notify when a handle is either ready or has become -// invalid. +// invalid. In case of error, the handler will be removed. class MOJO_COMMON_EXPORT MessagePumpMojoHandler { public: virtual void OnHandleReady(const Handle& handle) = 0;
diff --git a/mojo/common/message_pump_mojo_unittest.cc b/mojo/common/message_pump_mojo_unittest.cc index fb5ae24..d552942 100644 --- a/mojo/common/message_pump_mojo_unittest.cc +++ b/mojo/common/message_pump_mojo_unittest.cc
@@ -5,6 +5,9 @@ #include "mojo/common/message_pump_mojo.h" #include "base/message_loop/message_loop_test.h" +#include "base/run_loop.h" +#include "mojo/common/message_pump_mojo_handler.h" +#include "mojo/public/cpp/system/core.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { @@ -17,6 +20,66 @@ RUN_MESSAGE_LOOP_TESTS(Mojo, &CreateMojoMessagePump); +class CountingMojoHandler : public MessagePumpMojoHandler { + public: + CountingMojoHandler() : success_count_(0), error_count_(0) {} + + virtual void OnHandleReady(const Handle& handle) override { + ReadMessageRaw(static_cast<const MessagePipeHandle&>(handle), + NULL, + NULL, + NULL, + NULL, + MOJO_READ_MESSAGE_FLAG_NONE); + ++success_count_; + } + virtual void OnHandleError(const Handle& handle, MojoResult result) override { + ++error_count_; + } + + int success_count() { return success_count_; } + int error_count() { return error_count_; } + + private: + int success_count_; + int error_count_; + + DISALLOW_COPY_AND_ASSIGN(CountingMojoHandler); +}; + +TEST(MessagePumpMojo, RunUntilIdle) { + base::MessageLoop message_loop(MessagePumpMojo::Create()); + CountingMojoHandler handler; + MessagePipe handles; + MessagePumpMojo::current()->AddHandler(&handler, + handles.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, + base::TimeTicks()); + WriteMessageRaw( + handles.handle1.get(), NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE); + WriteMessageRaw( + handles.handle1.get(), NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE); + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + EXPECT_EQ(2, handler.success_count()); +} + +TEST(MessagePumpMojo, UnregisterAfterDeadline) { + base::MessageLoop message_loop(MessagePumpMojo::Create()); + CountingMojoHandler handler; + MessagePipe handles; + MessagePumpMojo::current()->AddHandler( + &handler, + handles.handle0.get(), + MOJO_HANDLE_SIGNAL_READABLE, + base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1)); + for (int i = 0; i < 2; ++i) { + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + } + EXPECT_EQ(1, handler.error_count()); +} + } // namespace test } // namespace common } // namespace mojo
diff --git a/mojo/examples/BUILD.gn b/mojo/examples/BUILD.gn index 7708161..bb42f4d 100644 --- a/mojo/examples/BUILD.gn +++ b/mojo/examples/BUILD.gn
@@ -12,6 +12,7 @@ "//mojo/examples/compositor_app", "//mojo/examples/content_handler_demo", "//mojo/examples/echo", + "//mojo/examples/pepper_container_app", "//mojo/examples/png_viewer", "//mojo/examples/sample_app", "//mojo/examples/surfaces_app",
diff --git a/mojo/examples/surfaces_app/surfaces_app.cc b/mojo/examples/surfaces_app/surfaces_app.cc index a779c16..2086407 100644 --- a/mojo/examples/surfaces_app/surfaces_app.cc +++ b/mojo/examples/surfaces_app/surfaces_app.cc
@@ -14,9 +14,9 @@ #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/system/core.h" -#include "mojo/services/gles2/command_buffer.mojom.h" #include "mojo/services/public/cpp/geometry/geometry_type_converters.h" #include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h" +#include "mojo/services/public/interfaces/gpu/command_buffer.mojom.h" #include "mojo/services/public/interfaces/gpu/gpu.mojom.h" #include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h" #include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h"
diff --git a/mojo/gles2/BUILD.gn b/mojo/gles2/BUILD.gn index 14e1b8e..1f33403 100644 --- a/mojo/gles2/BUILD.gn +++ b/mojo/gles2/BUILD.gn
@@ -20,13 +20,13 @@ "//base", "//base/third_party/dynamic_annotations", "//gpu/command_buffer/client", - "//gpu/command_buffer/common", "//gpu/command_buffer/client:gles2_implementation", "//gpu/command_buffer/client:gles2_interface", + "//gpu/command_buffer/common", + "//mojo/environment:chromium", "//mojo/public/c/system:for_component", "//mojo/services/gles2:bindings", - "//mojo/services/gles2:interfaces", - "//mojo/environment:chromium", + "//mojo/services/public/interfaces/gpu", ] defines = [
diff --git a/mojo/gles2/command_buffer_client_impl.h b/mojo/gles2/command_buffer_client_impl.h index 1c8ca53..65de5a7 100644 --- a/mojo/gles2/command_buffer_client_impl.h +++ b/mojo/gles2/command_buffer_client_impl.h
@@ -13,7 +13,7 @@ #include "gpu/command_buffer/common/command_buffer.h" #include "gpu/command_buffer/common/command_buffer_shared.h" #include "mojo/public/cpp/bindings/error_handler.h" -#include "mojo/services/gles2/command_buffer.mojom.h" +#include "mojo/services/public/interfaces/gpu/command_buffer.mojom.h" namespace base { class RunLoop;
diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp index 30ffcf2..091d0b3 100644 --- a/mojo/mojo.gyp +++ b/mojo/mojo.gyp
@@ -151,10 +151,10 @@ 'mojo_application_manager', 'mojo_base.gyp:mojo_application_bindings', 'mojo_base.gyp:mojo_common_lib', - 'mojo_base.gyp:mojo_gles2_impl', 'mojo_base.gyp:mojo_system_impl', 'mojo_base.gyp:mojo_application_chromium', 'mojo_external_service_bindings', + 'mojo_gles2_impl', 'mojo_network_bindings', 'mojo_spy', ], @@ -375,6 +375,50 @@ ], }, { + # GN version: //mojo/gles2 + 'target_name': 'mojo_gles2_impl', + 'type': '<(component)', + 'dependencies': [ + '../base/base.gyp:base', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../gpu/gpu.gyp:command_buffer_client', + '../gpu/gpu.gyp:command_buffer_common', + '../gpu/gpu.gyp:gles2_cmd_helper', + '../gpu/gpu.gyp:gles2_implementation', + 'mojo_base.gyp:mojo_environment_chromium', + 'mojo_gles2_bindings', + 'mojo_gpu_bindings', + '<(mojo_system_for_component)', + ], + 'defines': [ + 'GLES2_USE_MOJO', + 'GL_GLEXT_PROTOTYPES', + 'MOJO_GLES2_IMPLEMENTATION', + 'MOJO_GLES2_IMPL_IMPLEMENTATION', + 'MOJO_USE_GLES2_IMPL' + ], + 'direct_dependent_settings': { + 'defines': [ + 'GLES2_USE_MOJO', + ], + }, + 'export_dependent_settings': [ + 'mojo_gpu_bindings', + ], + 'sources': [ + 'gles2/command_buffer_client_impl.cc', + 'gles2/command_buffer_client_impl.h', + 'gles2/gles2_impl_export.h', + 'gles2/gles2_impl.cc', + 'gles2/gles2_context.cc', + 'gles2/gles2_context.h', + ], + 'all_dependent_settings': { + # Ensures that dependent projects import the core functions on Windows. + 'defines': ['MOJO_USE_GLES2_IMPL'], + } + }, + { # GN version: //mojo/bindings/js/tests:mojo_js_unittests 'target_name': 'mojo_js_unittests', 'type': 'executable',
diff --git a/mojo/mojo_apps.gypi b/mojo/mojo_apps.gypi index de1919a..ea597e3 100644 --- a/mojo/mojo_apps.gypi +++ b/mojo/mojo_apps.gypi
@@ -18,8 +18,8 @@ '../v8/tools/gyp/v8.gyp:v8', 'mojo_base.gyp:mojo_common_lib', 'mojo_base.gyp:mojo_environment_chromium', - 'mojo_base.gyp:mojo_gles2_bindings', 'mojo_base.gyp:mojo_js_bindings_lib', + 'mojo_gles2_bindings', 'mojo_native_viewport_bindings', ], 'includes': [ @@ -29,7 +29,7 @@ '../base/base.gyp:base', '../gin/gin.gyp:gin', 'mojo_base.gyp:mojo_common_lib', - 'mojo_base.gyp:mojo_gles2_bindings', + 'mojo_gles2_bindings', 'mojo_native_viewport_bindings', ], 'sources': [
diff --git a/mojo/mojo_base.gyp b/mojo/mojo_base.gyp index 0cf8766..fd942f4 100644 --- a/mojo/mojo_base.gyp +++ b/mojo/mojo_base.gyp
@@ -404,71 +404,6 @@ ], }, { - # GN version: //mojo/services/gles2:interfaces (for files generated from - # the mojom file) - # GN version: //mojo/services/gles2:bindings - 'target_name': 'mojo_gles2_bindings', - 'type': 'static_library', - 'sources': [ - 'services/gles2/command_buffer.mojom', - 'services/gles2/command_buffer_type_conversions.cc', - 'services/gles2/command_buffer_type_conversions.h', - 'services/gles2/mojo_buffer_backing.cc', - 'services/gles2/mojo_buffer_backing.h', - ], - 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], - 'export_dependent_settings': [ - 'mojo_cpp_bindings', - ], - 'dependencies': [ - 'mojo_cpp_bindings', - '../gpu/gpu.gyp:command_buffer_common', - ], - }, - { - # GN version: //mojo/gles2 - 'target_name': 'mojo_gles2_impl', - 'type': '<(component)', - 'dependencies': [ - '../base/base.gyp:base', - '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', - '../gpu/gpu.gyp:command_buffer_client', - '../gpu/gpu.gyp:command_buffer_common', - '../gpu/gpu.gyp:gles2_cmd_helper', - '../gpu/gpu.gyp:gles2_implementation', - 'mojo_environment_chromium', - 'mojo_gles2_bindings', - '<(mojo_system_for_component)', - ], - 'defines': [ - 'GLES2_USE_MOJO', - 'GL_GLEXT_PROTOTYPES', - 'MOJO_GLES2_IMPLEMENTATION', - 'MOJO_GLES2_IMPL_IMPLEMENTATION', - 'MOJO_USE_GLES2_IMPL' - ], - 'direct_dependent_settings': { - 'defines': [ - 'GLES2_USE_MOJO', - ], - }, - 'export_dependent_settings': [ - 'mojo_gles2_bindings', - ], - 'sources': [ - 'gles2/command_buffer_client_impl.cc', - 'gles2/command_buffer_client_impl.h', - 'gles2/gles2_impl_export.h', - 'gles2/gles2_impl.cc', - 'gles2/gles2_context.cc', - 'gles2/gles2_context.h', - ], - 'all_dependent_settings': { - # Ensures that dependent projects import the core functions on Windows. - 'defines': ['MOJO_USE_GLES2_IMPL'], - } - }, - { # GN version: //mojo/application 'target_name': 'mojo_application_chromium', 'type': 'static_library',
diff --git a/mojo/mojo_public_gles2_for_loadable_module.gypi b/mojo/mojo_public_gles2_for_loadable_module.gypi index 42421dc..54c1a49 100644 --- a/mojo/mojo_public_gles2_for_loadable_module.gypi +++ b/mojo/mojo_public_gles2_for_loadable_module.gypi
@@ -10,7 +10,7 @@ 'conditions': [ ['component=="shared_library"', { 'dependencies': [ - 'mojo_base.gyp:mojo_gles2_impl', + 'mojo.gyp:mojo_gles2_impl', ], }, { # component!="shared_library" 'defines': [
diff --git a/mojo/mojo_services.gypi b/mojo/mojo_services.gypi index 96f69f7..4c0e103 100644 --- a/mojo/mojo_services.gypi +++ b/mojo/mojo_services.gypi
@@ -58,6 +58,27 @@ ], }, { + # GN version: //mojo/services/gles2:bindings + 'target_name': 'mojo_gles2_bindings', + 'type': 'static_library', + 'sources': [ + 'services/gles2/command_buffer_type_conversions.cc', + 'services/gles2/command_buffer_type_conversions.h', + 'services/gles2/mojo_buffer_backing.cc', + 'services/gles2/mojo_buffer_backing.h', + ], + 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], + 'dependencies': [ + 'mojo_base.gyp:mojo_cpp_bindings', + 'mojo_gpu_bindings', + '../gpu/gpu.gyp:command_buffer_common', + ], + 'export_dependent_settings': [ + 'mojo_base.gyp:mojo_cpp_bindings', + 'mojo_gpu_bindings', + ], + }, + { # GN version: //mojo/services/html_viewer 'target_name': 'mojo_html_viewer', 'type': 'loadable_module', @@ -228,6 +249,7 @@ 'mojo_base.gyp:mojo_environment_chromium', 'mojo_geometry_lib', 'mojo_surfaces_bindings', + 'mojo_gpu_bindings', '<(mojo_system_for_component)', ], 'export_dependent_settings': [ @@ -277,10 +299,10 @@ '../ui/gfx/gfx.gyp:gfx', '../ui/gfx/gfx.gyp:gfx_geometry', '../ui/gl/gl.gyp:gl', - 'mojo_base.gyp:mojo_gles2_bindings', + 'mojo_gles2_bindings', ], 'export_dependent_settings': [ - 'mojo_base.gyp:mojo_gles2_bindings', + 'mojo_gles2_bindings', ], 'sources': [ 'services/gles2/command_buffer_impl.cc', @@ -292,17 +314,16 @@ 'target_name': 'mojo_gpu_bindings', 'type': 'static_library', 'sources': [ + 'services/public/interfaces/gpu/command_buffer.mojom', 'services/public/interfaces/gpu/gpu.mojom', ], 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], 'dependencies': [ 'mojo_base.gyp:mojo_cpp_bindings', - 'mojo_base.gyp:mojo_gles2_bindings', 'mojo_geometry_bindings', ], 'export_dependent_settings': [ 'mojo_base.gyp:mojo_cpp_bindings', - 'mojo_base.gyp:mojo_gles2_bindings', 'mojo_geometry_bindings', ], }, @@ -316,15 +337,15 @@ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], 'dependencies': [ 'mojo_base.gyp:mojo_cpp_bindings', - 'mojo_base.gyp:mojo_gles2_bindings', 'mojo_geometry_bindings', + 'mojo_gles2_bindings', 'mojo_input_events_bindings', 'mojo_surface_id_bindings', ], 'export_dependent_settings': [ 'mojo_base.gyp:mojo_cpp_bindings', - 'mojo_base.gyp:mojo_gles2_bindings', 'mojo_geometry_bindings', + 'mojo_gles2_bindings', 'mojo_input_events_bindings', 'mojo_surface_id_bindings', ], @@ -682,13 +703,12 @@ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], 'dependencies': [ 'mojo_base.gyp:mojo_cpp_bindings', - 'mojo_base.gyp:mojo_gles2_bindings', 'mojo_geometry_bindings', + 'mojo_gles2_bindings', 'mojo_surface_id_bindings', ], 'export_dependent_settings': [ 'mojo_base.gyp:mojo_cpp_bindings', - 'mojo_base.gyp:mojo_gles2_bindings', 'mojo_geometry_bindings', 'mojo_surface_id_bindings', ],
diff --git a/mojo/public/BUILD.gn b/mojo/public/BUILD.gn index 8eb4647..1874a6c 100644 --- a/mojo/public/BUILD.gn +++ b/mojo/public/BUILD.gn
@@ -37,6 +37,7 @@ "//mojo/public/cpp/environment:standalone", "//mojo/public/cpp/utility", "//mojo/public/interfaces/application", + "//mojo/public/js/bindings", ] }
diff --git a/mojo/public/interfaces/application/application.mojom b/mojo/public/interfaces/application/application.mojom index 758a053..84417be 100644 --- a/mojo/public/interfaces/application/application.mojom +++ b/mojo/public/interfaces/application/application.mojom
@@ -8,7 +8,6 @@ // Applications vend Services through the ServiceProvider interface. Services // implement Interfaces. -[Client=Shell] interface Application { // Initialize is guaranteed to be called before any AcceptConnection calls. Initialize(array<string>? args);
diff --git a/mojo/public/interfaces/application/shell.mojom b/mojo/public/interfaces/application/shell.mojom index a9ce05f..014fa3d 100644 --- a/mojo/public/interfaces/application/shell.mojom +++ b/mojo/public/interfaces/application/shell.mojom
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import "mojo/public/interfaces/application/application.mojom" import "mojo/public/interfaces/application/service_provider.mojom" module mojo {
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni index e12d8e0..5f65c27 100644 --- a/mojo/public/tools/bindings/mojom.gni +++ b/mojo/public/tools/bindings/mojom.gni
@@ -12,8 +12,10 @@ # List of source .mojom files to compile. # # deps (optional) +# Note: this can contain only other mojom targets. # # public_deps (optional) +# Note: this can contain only other mojom targets. # # testonly (optional) # @@ -45,6 +47,16 @@ "$generator_root/generators/cpp_templates/struct_macros.tmpl", "$generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl", "$generator_root/generators/cpp_templates/wrapper_class_definition.tmpl", + "$generator_root/generators/java_templates/constant_definition.tmpl", + "$generator_root/generators/java_templates/constants.java.tmpl", + "$generator_root/generators/java_templates/enum.java.tmpl", + "$generator_root/generators/java_templates/enum_definition.tmpl", + "$generator_root/generators/java_templates/header.java.tmpl", + "$generator_root/generators/java_templates/interface.java.tmpl", + "$generator_root/generators/java_templates/interface_definition.tmpl", + "$generator_root/generators/java_templates/interface_internal.java.tmpl", + "$generator_root/generators/java_templates/struct.java.tmpl", + "$generator_root/generators/java_templates/struct_definition.tmpl", "$generator_root/generators/js_templates/enum_definition.tmpl", "$generator_root/generators/js_templates/interface_definition.tmpl", "$generator_root/generators/js_templates/module.js.tmpl", @@ -53,6 +65,7 @@ "$generator_root/generators/python_templates/module.py.tmpl", "$generator_root/generators/mojom_cpp_generator.py", "$generator_root/generators/mojom_js_generator.py", + "$generator_root/generators/mojom_java_generator.py", "$generator_root/generators/mojom_python_generator.py", "$generator_root/pylib/mojom/__init__.py", "$generator_root/pylib/mojom/error.py", @@ -73,6 +86,9 @@ "{{source_gen_dir}}/{{source_name_part}}.mojom.h", "{{source_gen_dir}}/{{source_name_part}}.mojom-internal.h", ] + generator_java_outputs = [ + "{{source_gen_dir}}/{{source_name_part}}.mojom.srcjar", + ] generator_js_outputs = [ "{{source_gen_dir}}/{{source_name_part}}.mojom.js", ] @@ -96,6 +112,7 @@ inputs = generator_sources sources = invoker.sources outputs = generator_cpp_outputs + + generator_java_outputs + generator_js_outputs + generator_python_outputs args = [ @@ -127,4 +144,48 @@ public_deps = invoker.public_deps } } + + all_deps = [] + if (defined(invoker.deps)) { + all_deps += invoker.deps + } + if (defined(invoker.public_deps)) { + all_deps += invoker.public_deps + } + + group("${target_name}__is_mojom") { + } + + # Explicitly ensure that all dependencies (invoker.deps and + # invoker.public_deps) are mojom targets themselves. + group("${target_name}__check_deps_are_all_mojom") { + deps = [] + foreach(d, all_deps) { + name = get_label_info(d, "label_no_toolchain") + toolchain = get_label_info(d, "toolchain") + deps += [ "${name}__is_mojom(${toolchain})" ] + } + } + + if (is_android) { + import("//build/config/android/rules.gni") + + java_target_name = target_name + "_java" + android_library(java_target_name) { + deps = [ + "//mojo/public/java:bindings", + "//mojo/public/java:system", + ] + + foreach(d, all_deps) { + # Resolve the name, so that a target //mojo/something becomes + # //mojo/something:something and we can append "_java" to get the java + # dependency name. + full_name = get_label_info(d, "label_no_toolchain") + deps += [ "${full_name}_java" ] + } + + srcjars = process_file_template(invoker.sources, generator_java_outputs) + } + } }
diff --git a/mojo/services/BUILD.gn b/mojo/services/BUILD.gn index cdf3752..5b4498c 100644 --- a/mojo/services/BUILD.gn +++ b/mojo/services/BUILD.gn
@@ -8,6 +8,7 @@ deps = [ "//mojo/services/clipboard", "//mojo/services/gles2:bindings", + "//mojo/services/html_viewer", "//mojo/services/network", "//mojo/services/public/interfaces/clipboard", "//mojo/services/public/interfaces/content_handler",
diff --git a/mojo/services/gles2/BUILD.gn b/mojo/services/gles2/BUILD.gn index 173d7df..9bf840e 100644 --- a/mojo/services/gles2/BUILD.gn +++ b/mojo/services/gles2/BUILD.gn
@@ -24,13 +24,6 @@ } # GYP version: mojo/mojo_services.gypi:mojo_gles2_bindings -mojom("interfaces") { - sources = [ - "command_buffer.mojom", - ] -} - -# GYP version: mojo/mojo_services.gypi:mojo_gles2_bindings source_set("bindings") { sources = [ "command_buffer_type_conversions.cc", @@ -39,13 +32,10 @@ "mojo_buffer_backing.h", ] - public_deps = [ - ":interfaces", - ] deps = [ "//base", "//gpu/command_buffer/common", "//mojo/public/cpp/bindings", - "//mojo/services/gles2:interfaces", + "//mojo/services/public/interfaces/gpu", ] }
diff --git a/mojo/services/gles2/DEPS b/mojo/services/gles2/DEPS index acd992d..0549188 100644 --- a/mojo/services/gles2/DEPS +++ b/mojo/services/gles2/DEPS
@@ -1,5 +1,6 @@ include_rules = [ "+gpu/command_buffer", + "+mojo/services/public/interfaces/gpu", "+ui/gfx", "+ui/gl", ]
diff --git a/mojo/services/gles2/command_buffer_impl.h b/mojo/services/gles2/command_buffer_impl.h index 5233e90..a304e88 100644 --- a/mojo/services/gles2/command_buffer_impl.h +++ b/mojo/services/gles2/command_buffer_impl.h
@@ -9,7 +9,7 @@ #include "base/memory/scoped_ptr.h" #include "base/timer/timer.h" #include "mojo/public/cpp/system/core.h" -#include "mojo/services/gles2/command_buffer.mojom.h" +#include "mojo/services/public/interfaces/gpu/command_buffer.mojom.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/size.h"
diff --git a/mojo/services/gles2/command_buffer_type_conversions.cc b/mojo/services/gles2/command_buffer_type_conversions.cc index a144f88..a279f15 100644 --- a/mojo/services/gles2/command_buffer_type_conversions.cc +++ b/mojo/services/gles2/command_buffer_type_conversions.cc
@@ -4,7 +4,7 @@ #include "mojo/services/gles2/command_buffer_type_conversions.h" -#include "mojo/services/gles2/command_buffer.mojom.h" +#include "mojo/services/public/interfaces/gpu/command_buffer.mojom.h" namespace mojo {
diff --git a/mojo/services/gles2/command_buffer_type_conversions.h b/mojo/services/gles2/command_buffer_type_conversions.h index 2579681..4fb4f7e 100644 --- a/mojo/services/gles2/command_buffer_type_conversions.h +++ b/mojo/services/gles2/command_buffer_type_conversions.h
@@ -7,7 +7,7 @@ #include "gpu/command_buffer/common/command_buffer.h" #include "mojo/public/cpp/bindings/type_converter.h" -#include "mojo/services/gles2/command_buffer.mojom.h" +#include "mojo/services/public/interfaces/gpu/command_buffer.mojom.h" namespace mojo {
diff --git a/mojo/services/native_viewport/BUILD.gn b/mojo/services/native_viewport/BUILD.gn index 036e8e9..72901f9 100644 --- a/mojo/services/native_viewport/BUILD.gn +++ b/mojo/services/native_viewport/BUILD.gn
@@ -31,11 +31,11 @@ "//mojo/common", "//mojo/environment:chromium", "//mojo/services/gles2", - "//mojo/services/gles2:interfaces", "//mojo/services/public/cpp/geometry", "//mojo/services/public/cpp/input_events", "//mojo/services/public/cpp/surfaces", "//mojo/services/public/interfaces/geometry", + "//mojo/services/public/interfaces/gpu", "//mojo/services/public/interfaces/native_viewport", "//mojo/services/public/interfaces/surfaces", "//ui/events",
diff --git a/mojo/services/native_viewport/gpu_impl.h b/mojo/services/native_viewport/gpu_impl.h index ac0f435..4aeb53b 100644 --- a/mojo/services/native_viewport/gpu_impl.h +++ b/mojo/services/native_viewport/gpu_impl.h
@@ -6,8 +6,8 @@ #include "base/memory/ref_counted.h" #include "mojo/public/cpp/bindings/interface_impl.h" #include "mojo/public/cpp/bindings/interface_request.h" -#include "mojo/services/gles2/command_buffer.mojom.h" #include "mojo/services/public/interfaces/geometry/geometry.mojom.h" +#include "mojo/services/public/interfaces/gpu/command_buffer.mojom.h" #include "mojo/services/public/interfaces/gpu/gpu.mojom.h" namespace gfx {
diff --git a/mojo/services/public/cpp/surfaces/lib/surfaces_type_converters.cc b/mojo/services/public/cpp/surfaces/lib/surfaces_type_converters.cc index 7d1798e..dee0996 100644 --- a/mojo/services/public/cpp/surfaces/lib/surfaces_type_converters.cc +++ b/mojo/services/public/cpp/surfaces/lib/surfaces_type_converters.cc
@@ -239,7 +239,7 @@ // This is intentionally left set to an invalid value here. It's set when // converting an entire pass since it's an index into the pass' shared quad // state list. - quad->shared_quad_state_index = -1; + quad->shared_quad_state_index = UINT32_MAX; switch (input.material) { case cc::DrawQuad::RENDER_PASS: { const cc::RenderPassDrawQuad* render_pass_quad = @@ -357,25 +357,26 @@ Array<QuadPtr> quads(input.quad_list.size()); Array<SharedQuadStatePtr> shared_quad_state( input.shared_quad_state_list.size()); - int sqs_i = -1; - const cc::SharedQuadState* last_sqs = NULL; - size_t i = 0; + const cc::SharedQuadState* last_sqs = nullptr; + cc::SharedQuadStateList::ConstIterator next_sqs_iter = + input.shared_quad_state_list.begin(); for (cc::QuadList::ConstIterator iter = input.quad_list.begin(); iter != input.quad_list.end(); ++iter) { const cc::DrawQuad& quad = *iter; - quads[i] = Quad::From(quad); + quads[iter.index()] = Quad::From(quad); if (quad.shared_quad_state != last_sqs) { - sqs_i++; - shared_quad_state[sqs_i] = - SharedQuadState::From(*input.shared_quad_state_list[sqs_i]); - last_sqs = quad.shared_quad_state; + shared_quad_state[next_sqs_iter.index()] = + SharedQuadState::From(*next_sqs_iter); + last_sqs = &*next_sqs_iter; + ++next_sqs_iter; } - quads[i]->shared_quad_state_index = sqs_i; - ++i; + DCHECK_LE(next_sqs_iter.index() - 1, UINT32_MAX); + quads[iter.index()]->shared_quad_state_index = + static_cast<uint32_t>(next_sqs_iter.index() - 1); } // We should copy all shared quad states. - DCHECK_EQ(static_cast<size_t>(sqs_i + 1), shared_quad_state.size()); + DCHECK_EQ(next_sqs_iter.index(), shared_quad_state.size()); pass->quads = quads.Pass(); pass->shared_quad_states = shared_quad_state.Pass(); return pass.Pass(); @@ -385,23 +386,24 @@ scoped_ptr<cc::RenderPass> TypeConverter<scoped_ptr<cc::RenderPass>, PassPtr>::Convert( const PassPtr& input) { - // TODO(weiliangc): RenderPass will have a constructor that takes in preset - // size of quad list and shared quad state list size in upcoming CL. - scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create(); + scoped_ptr<cc::RenderPass> pass = cc::RenderPass::Create( + input->shared_quad_states.size(), input->quads.size()); pass->SetAll(cc::RenderPassId(1, input->id), input->output_rect.To<gfx::Rect>(), input->damage_rect.To<gfx::Rect>(), input->transform_to_root_target.To<gfx::Transform>(), input->has_transparent_background); - cc::SharedQuadStateList& sqs_list = pass->shared_quad_state_list; - sqs_list.reserve(input->shared_quad_states.size()); for (size_t i = 0; i < input->shared_quad_states.size(); ++i) { ConvertSharedQuadState(input->shared_quad_states[i], pass.get()); } + cc::SharedQuadStateList::Iterator sqs_iter = + pass->shared_quad_state_list.begin(); for (size_t i = 0; i < input->quads.size(); ++i) { QuadPtr quad = input->quads[i].Pass(); - if (!ConvertDrawQuad( - quad, sqs_list[quad->shared_quad_state_index], pass.get())) + while (quad->shared_quad_state_index > sqs_iter.index()) { + ++sqs_iter; + } + if (!ConvertDrawQuad(quad, &*sqs_iter, pass.get())) return scoped_ptr<cc::RenderPass>(); } return pass.Pass();
diff --git a/mojo/services/public/cpp/surfaces/tests/surface_unittest.cc b/mojo/services/public/cpp/surfaces/tests/surface_unittest.cc index 93cf571..bf04195 100644 --- a/mojo/services/public/cpp/surfaces/tests/surface_unittest.cc +++ b/mojo/services/public/cpp/surfaces/tests/surface_unittest.cc
@@ -290,7 +290,7 @@ EXPECT_EQ(has_transparent_background, mojo_pass->has_transparent_background); ASSERT_EQ(1u, mojo_pass->shared_quad_states.size()); ASSERT_EQ(3u, mojo_pass->quads.size()); - EXPECT_EQ(0, mojo_pass->quads[0]->shared_quad_state_index); + EXPECT_EQ(0u, mojo_pass->quads[0]->shared_quad_state_index); scoped_ptr<cc::RenderPass> round_trip_pass = mojo_pass.To<scoped_ptr<cc::RenderPass> >();
diff --git a/mojo/services/public/interfaces/gpu/BUILD.gn b/mojo/services/public/interfaces/gpu/BUILD.gn index 1e56abc..6541063 100644 --- a/mojo/services/public/interfaces/gpu/BUILD.gn +++ b/mojo/services/public/interfaces/gpu/BUILD.gn
@@ -7,11 +7,11 @@ # GYP version: mojo/mojo_services.gypi:mojo_gpu_bindings mojom("gpu") { sources = [ - "gpu.mojom", + "command_buffer.mojom", + "gpu.mojom", ] deps = [ - "//mojo/services/gles2:interfaces", "//mojo/services/public/interfaces/geometry", ] }
diff --git a/mojo/services/gles2/command_buffer.mojom b/mojo/services/public/interfaces/gpu/command_buffer.mojom similarity index 100% rename from mojo/services/gles2/command_buffer.mojom rename to mojo/services/public/interfaces/gpu/command_buffer.mojom
diff --git a/mojo/services/public/interfaces/gpu/gpu.mojom b/mojo/services/public/interfaces/gpu/gpu.mojom index 881c6be..07e813d 100644 --- a/mojo/services/public/interfaces/gpu/gpu.mojom +++ b/mojo/services/public/interfaces/gpu/gpu.mojom
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import "mojo/services/gles2/command_buffer.mojom" import "mojo/services/public/interfaces/geometry/geometry.mojom" +import "mojo/services/public/interfaces/gpu/command_buffer.mojom" module mojo {
diff --git a/mojo/services/public/interfaces/native_viewport/native_viewport.mojom b/mojo/services/public/interfaces/native_viewport/native_viewport.mojom index 6f05b72..990ad03 100644 --- a/mojo/services/public/interfaces/native_viewport/native_viewport.mojom +++ b/mojo/services/public/interfaces/native_viewport/native_viewport.mojom
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import "mojo/services/gles2/command_buffer.mojom" import "mojo/services/public/interfaces/geometry/geometry.mojom" +import "mojo/services/public/interfaces/gpu/command_buffer.mojom" import "mojo/services/public/interfaces/input_events/input_events.mojom" import "mojo/services/public/interfaces/surfaces/surface_id.mojom"
diff --git a/mojo/services/public/interfaces/surfaces/BUILD.gn b/mojo/services/public/interfaces/surfaces/BUILD.gn index cd00cd5..791b76d 100644 --- a/mojo/services/public/interfaces/surfaces/BUILD.gn +++ b/mojo/services/public/interfaces/surfaces/BUILD.gn
@@ -14,8 +14,8 @@ deps = [ ":surface_id", - "//mojo/services/gles2:interfaces", "//mojo/services/public/interfaces/geometry", + "//mojo/services/public/interfaces/gpu", "//mojo/services/public/interfaces/native_viewport", ] }
diff --git a/mojo/services/public/interfaces/surfaces/quads.mojom b/mojo/services/public/interfaces/surfaces/quads.mojom index 4f51be4..6d5cccd 100644 --- a/mojo/services/public/interfaces/surfaces/quads.mojom +++ b/mojo/services/public/interfaces/surfaces/quads.mojom
@@ -123,7 +123,7 @@ // Index into the containing pass' shared quad state array which has state // (transforms etc) shared by multiple quads. - int32 shared_quad_state_index; + uint32 shared_quad_state_index; // Only one of the following will be set, depending on the material. CheckerboardQuadState? checkerboard_quad_state;
diff --git a/mojo/services/public/interfaces/surfaces/surfaces.mojom b/mojo/services/public/interfaces/surfaces/surfaces.mojom index 1c5b0b9..0d93ee4 100644 --- a/mojo/services/public/interfaces/surfaces/surfaces.mojom +++ b/mojo/services/public/interfaces/surfaces/surfaces.mojom
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import "mojo/services/gles2/command_buffer.mojom" import "mojo/services/public/interfaces/geometry/geometry.mojom" +import "mojo/services/public/interfaces/gpu/command_buffer.mojom" import "mojo/services/public/interfaces/surfaces/quads.mojom" import "mojo/services/public/interfaces/surfaces/surface_id.mojom"
diff --git a/mojo/services/surfaces/BUILD.gn b/mojo/services/surfaces/BUILD.gn index 8c43ca5..9f154a5 100644 --- a/mojo/services/surfaces/BUILD.gn +++ b/mojo/services/surfaces/BUILD.gn
@@ -16,10 +16,10 @@ "//mojo/environment:chromium", "//mojo/public/c/system:for_shared_library", "//mojo/public/gles2:for_shared_library", - "//mojo/services/gles2:interfaces", "//mojo/services/public/cpp/geometry", "//mojo/services/public/cpp/surfaces", "//mojo/services/public/interfaces/geometry", + "//mojo/services/public/interfaces/gpu", "//mojo/services/public/interfaces/surfaces", ]
diff --git a/mojo/services/surfaces/surfaces_impl.h b/mojo/services/surfaces/surfaces_impl.h index fb6ce53..5ee6161 100644 --- a/mojo/services/surfaces/surfaces_impl.h +++ b/mojo/services/surfaces/surfaces_impl.h
@@ -10,7 +10,7 @@ #include "cc/surfaces/surface_factory.h" #include "cc/surfaces/surface_factory_client.h" #include "mojo/public/cpp/application/application_connection.h" -#include "mojo/services/gles2/command_buffer.mojom.h" +#include "mojo/services/public/interfaces/gpu/command_buffer.mojom.h" #include "mojo/services/public/interfaces/surfaces/surfaces.mojom.h" namespace cc {
diff --git a/net/BUILD.gn b/net/BUILD.gn index 0a39d0b..70bc62a 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -41,13 +41,11 @@ # WebSockets and socket stream code are used everywhere except iOS. enable_websockets = !is_ios +use_v8_in_net = !is_ios enable_built_in_dns = !is_ios disable_ftp_support = is_ios declare_args() { - # Enables proxy auto config support. Disabled by default on iOS. - use_v8_in_net = !is_ios - # Disables support for file URLs. File URL support requires use of icu. disable_file_support = false } @@ -60,9 +58,6 @@ if (disable_file_support) { defines += [ "DISABLE_FILE_SUPPORT" ] } - if (!use_v8_in_net) { - defines += [ "DISABLE_V8_IN_NET" ] - } } # Disables Windows warning about size to int truncations. @@ -556,8 +551,6 @@ ] } -if (false) { - static_library("extras") { sources = gypi_values.net_extras_sources configs += [ "//build/config/compiler:wexit_time_destructors" ] @@ -567,8 +560,6 @@ ] } -} # if (false) - static_library("http_server") { sources = [ "server/http_connection.cc", @@ -1112,14 +1103,11 @@ test("net_unittests") { sources = gypi_values.net_test_sources - sources -= [ - "extras/sqlite/sqlite_channel_id_store_unittest.cc", - ] - configs += [ ":net_win_size_truncation" ] defines = [] deps = [ + ":extras", ":http_server", ":net", ":quic_tools",
diff --git a/net/base/mime_util.cc b/net/base/mime_util.cc index 37e630f..27f3c70 100644 --- a/net/base/mime_util.cc +++ b/net/base/mime_util.cc
@@ -924,6 +924,39 @@ allow_proprietary_codecs_ = false; } +// Returns true iff |profile_str| conforms to hex string "42y0", where y is one +// of [8..F]. Requiring constraint_set0_flag be set and profile_idc be 0x42 is +// taken from ISO-14496-10 7.3.2.1, 7.4.2.1, and Annex A.2.1. +// +// |profile_str| is the first four characters of the H.264 suffix string +// (ignoring the last 2 characters of the full 6 character suffix that are +// level_idc). From ISO-14496-10 7.3.2.1, it consists of: +// 8 bits: profile_idc: required to be 0x42 here. +// 1 bit: constraint_set0_flag : required to be true here. +// 1 bit: constraint_set1_flag : ignored here. +// 1 bit: constraint_set2_flag : ignored here. +// 1 bit: constraint_set3_flag : ignored here. +// 4 bits: reserved : required to be 0 here. +// +// The spec indicates other ways, not implemented here, that a |profile_str| +// can indicate a baseline conforming decoder is sufficient for decode in Annex +// A.2.1: "[profile_idc not necessarily 0x42] with constraint_set0_flag set and +// in which level_idc and constraint_set3_flag represent a level less than or +// equal to the specified level." +static bool IsValidH264BaselineProfile(const std::string& profile_str) { + uint32 constraint_set_bits; + if (profile_str.size() != 4 || + profile_str[0] != '4' || + profile_str[1] != '2' || + profile_str[3] != '0' || + !base::HexStringToUInt(base::StringPiece(profile_str.c_str() + 2, 1), + &constraint_set_bits)) { + return false; + } + + return constraint_set_bits >= 8; +} + static bool IsValidH264Level(const std::string& level_str) { uint32 level; if (level_str.size() != 2 || !base::HexStringToUInt(level_str, &level)) @@ -938,13 +971,16 @@ (level >= 50 && level <= 51)); } -// Handle parsing H.264 codec IDs as outlined in RFC 6381 -// avc1.42E0xx - H.264 Baseline -// avc1.4D40xx - H.264 Main -// avc1.6400xx - H.264 High +// Handle parsing H.264 codec IDs as outlined in RFC 6381 and ISO-14496-10. +// avc1.42y0xx, y >= 8 - H.264 Baseline +// avc1.4D40xx - H.264 Main +// avc1.6400xx - H.264 High // -// avc1.xxxxxx & avc3.xxxxxx are considered ambiguous forms that -// are trying to signal H.264 Baseline. +// avc1.xxxxxx & avc3.xxxxxx are considered ambiguous forms that are trying to +// signal H.264 Baseline. For example, the idc_level, profile_idc and +// constraint_set3_flag pieces may explicitly require decoder to conform to +// baseline profile at the specified level (see Annex A and constraint_set0 in +// ISO-14496-10). static bool ParseH264CodecID(const std::string& codec_id, MimeUtil::Codec* codec, bool* is_ambiguous) { @@ -956,7 +992,7 @@ } std::string profile = StringToUpperASCII(codec_id.substr(5, 4)); - if (profile == "42E0") { + if (IsValidH264BaselineProfile(profile)) { *codec = MimeUtil::H264_BASELINE; } else if (profile == "4D40") { *codec = MimeUtil::H264_MAIN;
diff --git a/net/net.gyp b/net/net.gyp index d6ee1fd..e570e8e 100644 --- a/net/net.gyp +++ b/net/net.gyp
@@ -693,7 +693,6 @@ 'proxy/proxy_resolver_v8_unittest.cc', 'proxy/proxy_resolver_v8_tracing_unittest.cc', ], - 'defines': [ 'DISABLE_V8_IN_NET' ], }, ], @@ -991,10 +990,11 @@ ], }], [ 'use_v8_in_net==1', { - 'dependencies': [ - 'net_with_v8', - ], - }], + 'dependencies': [ + 'net_with_v8', + ], + }, + ], [ 'enable_mdns != 1', { 'sources!' : [ 'dns/mock_mdns_socket_factory.cc', @@ -1174,14 +1174,6 @@ 'msvs_disabled_warnings': [4267, ], }, ], - }, { - 'defines': [ 'DISABLE_V8_IN_NET' ], - 'targets': [ - { - 'target_name': 'net_with_v8', - 'type': 'none', - } - ] }], ['OS != "ios" and OS != "android"', { 'targets': [
diff --git a/net/proxy/proxy_resolver_perftest.cc b/net/proxy/proxy_resolver_perftest.cc index 94b5143..3d7854c 100644 --- a/net/proxy/proxy_resolver_perftest.cc +++ b/net/proxy/proxy_resolver_perftest.cc
@@ -11,6 +11,7 @@ #include "net/base/net_errors.h" #include "net/dns/mock_host_resolver.h" #include "net/proxy/proxy_info.h" +#include "net/proxy/proxy_resolver_v8.h" #include "net/test/spawned_test_server/spawned_test_server.h" #include "testing/gtest/include/gtest/gtest.h" @@ -20,10 +21,6 @@ #include "net/proxy/proxy_resolver_mac.h" #endif -#if !defined(DISABLE_V8_IN_NET) -#include "net/proxy/proxy_resolver_v8.h" -#endif - // This class holds the URL to use for resolving, and the expected result. // We track the expected result in order to make sure the performance // test is actually resolving URLs properly, otherwise the perf numbers @@ -198,8 +195,6 @@ } #endif -#if !defined(DISABLE_V8_IN_NET) - class MockJSBindings : public net::ProxyResolverV8::JSBindings { public: MockJSBindings() {} @@ -231,5 +226,3 @@ PacPerfSuiteRunner runner(&resolver, "ProxyResolverV8"); runner.RunAllTests(); } - -#endif // !defined(DISABLE_V8_IN_NET)
diff --git a/net/test/run_all_unittests.cc b/net/test/run_all_unittests.cc index 53c1456..a035057 100644 --- a/net/test/run_all_unittests.cc +++ b/net/test/run_all_unittests.cc
@@ -19,7 +19,7 @@ #include "url/android/url_jni_registrar.h" #endif -#if !defined(DISABLE_V8_IN_NET) +#if !defined(OS_IOS) #include "net/proxy/proxy_resolver_v8.h" #endif @@ -57,7 +57,7 @@ // single-threaded. net::EnableSSLServerSockets(); -#if !defined(DISABLE_V8_IN_NET) +#if !defined(OS_IOS) net::ProxyResolverV8::EnsureIsolateCreated(); #endif
diff --git a/net/url_request/test_url_fetcher_factory.cc b/net/url_request/test_url_fetcher_factory.cc index c595a4e..af76576 100644 --- a/net/url_request/test_url_fetcher_factory.cc +++ b/net/url_request/test_url_fetcher_factory.cc
@@ -59,6 +59,7 @@ void TestURLFetcher::SetUploadData(const std::string& upload_content_type, const std::string& upload_content) { + upload_content_type_ = upload_content_type; upload_data_ = upload_content; }
diff --git a/net/url_request/test_url_fetcher_factory.h b/net/url_request/test_url_fetcher_factory.h index f94b8a9..5d4f3d9 100644 --- a/net/url_request/test_url_fetcher_factory.h +++ b/net/url_request/test_url_fetcher_factory.h
@@ -160,6 +160,9 @@ int id() const { return id_; } // Returns the data uploaded on this URLFetcher. + const std::string& upload_content_type() const { + return upload_content_type_; + } const std::string& upload_data() const { return upload_data_; } const base::FilePath& upload_file_path() const { return upload_file_path_; } @@ -200,6 +203,7 @@ const GURL original_url_; URLFetcherDelegate* delegate_; DelegateForTests* delegate_for_tests_; + std::string upload_content_type_; std::string upload_data_; base::FilePath upload_file_path_; std::list<std::string> chunks_;
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index e6bfa04..d26b080 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -95,53 +95,52 @@ "Linux ChromiumOS Ozone Tests (1)": { "gtest_tests": [ "accessibility_unittests", + "app_list_unittests", + "app_shell_browsertests", "app_shell_unittests", "ash_unittests", + "athena_unittests", + "aura_unittests", "base_unittests", "cacheinvalidation_unittests", "chromeos_unittests", + "chromevox_tests", "components_unittests", + "content_browsertests", + "content_unittests", "crypto_unittests", "dbus_unittests", + "device_unittests", "display_unittests", + "events_unittests", "extensions_unittests", "gcm_unit_tests", "google_apis_unittests", "gpu_unittests", - "url_unittests", + "interactive_ui_tests", + "ipc_tests", "jingle_unittests", - "content_unittests", - "device_unittests", "media_unittests", + "message_center_unittests", + "nacl_loader_unittests", "net_unittests", "ozone_unittests", "ppapi_unittests", "printing_unittests", "remoting_unittests", "sandbox_linux_unittests", - "ui_unittests", - "views_unittests", - "wm_unittests", - "aura_unittests", - "app_list_unittests", - "message_center_unittests", - "compositor_unittests", - "events_unittests", - "ipc_tests", - "sync_unit_tests", - "unit_tests", "sql_unittests", - "nacl_loader_unittests", - "athena_unittests" + "sync_unit_tests", + "ui_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" ] }, "Linux ChromiumOS Ozone Tests (2)": { "gtest_tests": [ - "interactive_ui_tests", - "browser_tests", - "content_browsertests", - "app_shell_browsertests", - "chromevox_tests" + "browser_tests" ] }, "Linux ChromiumOS Tests (dbg)(1)": {
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json index e2751cd..0f02193 100644 --- a/testing/buildbot/chromium.win.json +++ b/testing/buildbot/chromium.win.json
@@ -1,61 +1,99 @@ { "XP Tests (1)": { "gtest_tests": [ - "interactive_ui_tests", + "accessibility_unittests", + { + "test": "base_unittests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + { + "test": "browser_tests", + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 7 + } + }, "cacheinvalidation_unittests", "cast_unittests", "cc_unittests", "chromedriver_unittests", + "chrome_elf_unittests", + "components_unittests", + "compositor_unittests", + { + "test": "content_browsertests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, + { + "test": "content_unittests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, "courgette_unittests", "crypto_unittests", + "events_unittests", + "extensions_unittests", + "gcm_unit_tests", "gfx_unittests", + "google_apis_unittests", "gpu_unittests", - "url_unittests", + "installer_util_unittests", + { + "test": "interactive_ui_tests", + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + } + }, + "ipc_tests", "jingle_unittests", "media_unittests", + { + "test": "net_unittests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, "ppapi_unittests", "printing_unittests", "remoting_unittests", "sbox_unittests", "sbox_integration_tests", "sbox_validation_tests", - {"test": "browser_tests", "shard_index": 0, "total_shards": 3}, - "content_browsertests", - "installer_util_unittests" + "sql_unittests", + { + "test": "sync_integration_tests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, + "sync_unit_tests", + { + "test": "unit_tests", + "swarming": { + "can_use_on_swarming_builders": true + } + }, + "ui_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" ] }, "XP Tests (2)": { "gtest_tests": [ - "accessibility_unittests", - "base_unittests", - "net_unittests", - "views_unittests", - "wm_unittests", - "aura_unittests", - "ash_unittests", - "compositor_unittests", - "events_unittests", - "sync_integration_tests", - {"test": "browser_tests", "shard_index": 1, "total_shards": 3} ] }, "XP Tests (3)": { "gtest_tests": [ - "app_shell_unittests", - "chrome_elf_unittests", - "components_unittests", - "extensions_unittests", - "gcm_unit_tests", - "google_apis_unittests", - "ipc_tests", - "sync_unit_tests", - "unit_tests", - "sql_unittests", - "ui_unittests", - "content_unittests", - "views_unittests", - "wm_unittests", - {"test": "browser_tests", "shard_index": 2, "total_shards": 3} ] }, "Vista Tests (1)": { @@ -120,8 +158,6 @@ "sql_unittests", "ui_unittests", "content_unittests", - "views_unittests", - "wm_unittests", {"test": "browser_tests", "shard_index": 2, "total_shards": 3} ] },
diff --git a/third_party/yasm/BUILD.gn b/third_party/yasm/BUILD.gn index 9429bc0..700e745 100644 --- a/third_party/yasm/BUILD.gn +++ b/third_party/yasm/BUILD.gn
@@ -31,14 +31,18 @@ # Various files referenced by multiple targets. yasm_gen_include_dir = "$target_gen_dir/include" - config_makefile = "source/config/$os/Makefile" + yasm_os = os + if (is_chromeos) { + yasm_os = "linux" + } + config_makefile = "source/config/$yasm_os/Makefile" version_file = "version.mac" import("//build/compiled_action.gni") config("yasm_config") { include_dirs = [ - "source/config/$os", + "source/config/$yasm_os", "source/patched-yasm", ] defines = [ "HAVE_CONFIG_H" ]
diff --git a/tools/clang/CMakeLists.txt b/tools/clang/CMakeLists.txt index 1b16704..21b5a9f 100644 --- a/tools/clang/CMakeLists.txt +++ b/tools/clang/CMakeLists.txt
@@ -5,21 +5,10 @@ list(APPEND CMAKE_MODULE_PATH "${LLVM_BUILD_DIR}/share/llvm/cmake") # These tools are built using LLVM's build system, not Chromium's. -# It expects LLVM_SRC_DIR and LLVM_BUILD_DIR to be set. -# For example: -# -# cmake -GNinja \ -# -DLLVM_BUILD_DIR=$CHROMIUM_SRC_DIR/third_party/llvm-build/Release+Asserts \ -# -DLLVM_SRC_DIR=$CHROMIUM_SRC_DIR/third_party/llvm \ -# -DCHROMIUM_TOOLS=blink_gc_plugin;plugin \ -# $CHROMIUM_SRC_DIR/tools/clang/ -# ninja +# The build script generates a shim CMakeLists.txt in the LLVM source tree, +# which simply forwards to this file. -include(LLVMConfig) -include(AddLLVM) -include(HandleLLVMOptions) - # Use rpath to find the bundled standard C++ library. set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) if (APPLE) @@ -29,18 +18,15 @@ set(CMAKE_INSTALL_RPATH "\$ORIGIN/../lib") endif() -set(LLVM_RUNTIME_OUTPUT_INTDIR "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin") -set(LLVM_LIBRARY_OUTPUT_INTDIR "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib") +include_directories("${CMAKE_SOURCE_DIR}/include" + "${CMAKE_SOURCE_DIR}/tools/clang/include" + "${CMAKE_BINARY_DIR}/include" + "${CMAKE_BINARY_DIR}/tools/clang/include") -include_directories("${LLVM_SRC_DIR}/include" - "${LLVM_SRC_DIR}/tools/clang/include" - "${LLVM_BUILD_DIR}/include" - "${LLVM_BUILD_DIR}/tools/clang/include") - -link_directories("${LLVM_SRC_DIR}/lib" - "${LLVM_SRC_DIR}/tools/clang/lib" - "${LLVM_BUILD_DIR}/lib" - "${LLVM_BUILD_DIR}/tools/clang/lib") +link_directories("${CMAKE_SOURCE_DIR}/lib" + "${CMAKE_SOURCE_DIR}/tools/clang/lib" + "${CMAKE_BINARY_DIR}/lib" + "${CMAKE_BINARY_DIR}/tools/clang/lib") # cr_add_test( # name @@ -49,11 +35,20 @@ # ) function(cr_add_test name testprog) add_test(NAME ${name} COMMAND ${testprog} ${ARGN}) - add_dependencies(check-all ${name}) + add_dependencies(cr-check-all ${name}) endfunction(cr_add_test) # Tests for all enabled tools can be run by building this target. -add_custom_target(check-all COMMAND ${CMAKE_CTEST_COMMAND} -V) +add_custom_target(cr-check-all COMMAND ${CMAKE_CTEST_COMMAND} -V) + +function(cr_install) + install(${ARGN} COMPONENT chrome-tools OPTIONAL) +endfunction(cr_install) + +# Custom install target, so the chrome tools can be installed without installing +# all the other LLVM targets. +add_custom_target(cr-install COMMAND + ${CMAKE_COMMAND} -D COMPONENT=chrome-tools -P cmake_install.cmake) foreach(tool ${CHROMIUM_TOOLS}) add_subdirectory(${tool})
diff --git a/tools/clang/blink_gc_plugin/CMakeLists.txt b/tools/clang/blink_gc_plugin/CMakeLists.txt index 9dab269..df2bc63 100644 --- a/tools/clang/blink_gc_plugin/CMakeLists.txt +++ b/tools/clang/blink_gc_plugin/CMakeLists.txt
@@ -8,10 +8,10 @@ RecordInfo.cpp ) -install(TARGETS "lib${LIBRARYNAME}" LIBRARY DESTINATION lib) +cr_install(TARGETS "lib${LIBRARYNAME}" LIBRARY DESTINATION lib) cr_add_test(blink_gc_plugin_test ${CMAKE_CURRENT_SOURCE_DIR}/tests/test.sh - ${LLVM_BUILD_DIR}/bin/clang + ${CMAKE_BINARY_DIR}/bin/clang $<TARGET_FILE:lib${LIBRARYNAME}> )
diff --git a/tools/clang/empty_string/CMakeLists.txt b/tools/clang/empty_string/CMakeLists.txt index 49b0234..4697a80 100644 --- a/tools/clang/empty_string/CMakeLists.txt +++ b/tools/clang/empty_string/CMakeLists.txt
@@ -23,4 +23,4 @@ clangTooling ) -install(TARGETS empty_string RUNTIME DESTINATION bin) +cr_install(TARGETS empty_string RUNTIME DESTINATION bin)
diff --git a/tools/clang/plugins/CMakeLists.txt b/tools/clang/plugins/CMakeLists.txt index df30cdd..7dcdb48 100644 --- a/tools/clang/plugins/CMakeLists.txt +++ b/tools/clang/plugins/CMakeLists.txt
@@ -4,10 +4,10 @@ FindBadConstructsConsumer.cpp ) -install(TARGETS libFindBadConstructs LIBRARY DESTINATION lib) +cr_install(TARGETS libFindBadConstructs LIBRARY DESTINATION lib) cr_add_test(plugins_test ${CMAKE_CURRENT_SOURCE_DIR}/tests/test.sh - ${LLVM_BUILD_DIR}/bin/clang + ${CMAKE_BINARY_DIR}/bin/clang $<TARGET_FILE:libFindBadConstructs> )
diff --git a/tools/clang/rewrite_scoped_refptr/CMakeLists.txt b/tools/clang/rewrite_scoped_refptr/CMakeLists.txt index 87ce4d8..9b0184d 100644 --- a/tools/clang/rewrite_scoped_refptr/CMakeLists.txt +++ b/tools/clang/rewrite_scoped_refptr/CMakeLists.txt
@@ -23,4 +23,4 @@ clangTooling ) -install(TARGETS rewrite_scoped_refptr RUNTIME DESTINATION bin) +cr_install(TARGETS rewrite_scoped_refptr RUNTIME DESTINATION bin)
diff --git a/tools/clang/scripts/update.sh b/tools/clang/scripts/update.sh index eabed4b..bfd586e 100755 --- a/tools/clang/scripts/update.sh +++ b/tools/clang/scripts/update.sh
@@ -22,7 +22,9 @@ LIBCXXABI_DIR="${LLVM_DIR}/projects/libcxxabi" ANDROID_NDK_DIR="${THIS_DIR}/../../../third_party/android_tools/ndk" STAMP_FILE="${LLVM_DIR}/../llvm-build/cr_build_revision" +CHROMIUM_TOOLS_DIR="${THIS_DIR}/.." +ABS_CHROMIUM_TOOLS_DIR="${PWD}/${CHROMIUM_TOOLS_DIR}" ABS_LIBCXX_DIR="${PWD}/${LIBCXX_DIR}" ABS_LIBCXXABI_DIR="${PWD}/${LIBCXXABI_DIR}" ABS_LLVM_DIR="${PWD}/${LLVM_DIR}" @@ -430,6 +432,22 @@ LDFLAGS+="-stdlib=libc++ -L${PWD}/libcxxbuild" fi +# Hook the Chromium tools into the LLVM build. Several Chromium tools have +# dependencies on LLVM/Clang libraries. The LLVM build detects implicit tools +# in the tools subdirectory, so install a shim CMakeLists.txt that forwards to +# the real directory for the Chromium tools. +# Note that the shim directory name intentionally has no _ or _. The implicit +# tool detection logic munges them in a weird way. +CHROME_TOOLS_SHIM_DIR=${ABS_LLVM_DIR}/tools/chrometools +rm -rfv ${CHROME_TOOLS_SHIM_DIR} +mkdir -v ${CHROME_TOOLS_SHIM_DIR} +cat > ${CHROME_TOOLS_SHIM_DIR}/CMakeLists.txt << EOF +# Since tools/clang isn't actually a subdirectory, use the two argument version +# to specify where build artifacts go. CMake doesn't allow reusing the same +# binary dir for multiple source dirs, so the build artifacts have to go into a +# subdirectory... +add_subdirectory(\${CHROMIUM_TOOLS_SRC} \${CMAKE_CURRENT_BINARY_DIR}/a) +EOF rm -fv CMakeCache.txt MACOSX_DEPLOYMENT_TARGET=${deployment_target} cmake -GNinja \ -DCMAKE_BUILD_TYPE=Release \ @@ -442,6 +460,9 @@ -DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" \ -DCMAKE_SHARED_LINKER_FLAGS="${LDFLAGS}" \ -DCMAKE_MODULE_LINKER_FLAGS="${LDFLAGS}" \ + -DCMAKE_INSTALL_PREFIX="${ABS_LLVM_BUILD_DIR}" \ + -DCHROMIUM_TOOLS_SRC="${ABS_CHROMIUM_TOOLS_DIR}" \ + -DCHROMIUM_TOOLS="${chrome_tools}" \ "${ABS_LLVM_DIR}" env @@ -452,6 +473,10 @@ fi ninja +# If any Chromium tools were built, install those now. +if [[ -n "${chrome_tools}" ]]; then + ninja cr-install +fi STRIP_FLAGS= if [ "${OS}" = "Darwin" ]; then @@ -528,34 +553,9 @@ popd fi -# Build Chrome-specific clang tools. Paths in this list should be relative to -# tools/clang. -TOOL_SRC_DIR="${PWD}/${THIS_DIR}/../" -TOOL_BUILD_DIR="${ABS_LLVM_BUILD_DIR}/tools/clang/tools/chrome-extras" - -rm -rf "${TOOL_BUILD_DIR}" -mkdir -p "${TOOL_BUILD_DIR}" -pushd "${TOOL_BUILD_DIR}" -rm -fv CMakeCache.txt -MACOSX_DEPLOYMENT_TARGET=${deployment_target} cmake -GNinja \ - -DLLVM_BUILD_DIR="${ABS_LLVM_BUILD_DIR}" \ - -DLLVM_SRC_DIR="${ABS_LLVM_DIR}" \ - -DCMAKE_C_COMPILER="${CC}" \ - -DCMAKE_CXX_COMPILER="${CXX}" \ - -DCMAKE_C_FLAGS="${CFLAGS}" \ - -DCMAKE_CXX_FLAGS="${CXXFLAGS}" \ - -DCMAKE_EXE_LINKER_FLAGS="${LDFLAGS}" \ - -DCMAKE_SHARED_LINKER_FLAGS="${LDFLAGS}" \ - -DCMAKE_MODULE_LINKER_FLAGS="${LDFLAGS}" \ - -DCMAKE_INSTALL_PREFIX="${ABS_LLVM_BUILD_DIR}" \ - -DCHROMIUM_TOOLS="${chrome_tools}" \ - "${TOOL_SRC_DIR}" -popd -ninja -C "${TOOL_BUILD_DIR}" install - if [[ -n "$run_tests" ]]; then # Run Chrome tool tests. - ninja -C "${TOOL_BUILD_DIR}" check-all + ninja -C "${LLVM_BUILD_DIR}" cr-check-all # Run the LLVM and Clang tests. ninja -C "${LLVM_BUILD_DIR}" check-all fi
diff --git a/tools/valgrind/drmemory/suppressions.txt b/tools/valgrind/drmemory/suppressions.txt index eec7064..5840dc1 100644 --- a/tools/valgrind/drmemory/suppressions.txt +++ b/tools/valgrind/drmemory/suppressions.txt
@@ -658,6 +658,12 @@ content.dll!content::RenderFrameProxy::~RenderFrameProxy UNADDRESSABLE ACCESS -name=http://crbug.com/420013 -content.dll!content::RenderFrameImpl::OnSwapOut -content.dll!FrameMsg_SwapOut::Dispatch<> +name=http://crbug.com/420311 +blink_web.dll!blink::Frame::deprecatedLocalOwner +blink_web.dll!blink::Document::topDocument +blink_web.dll!blink::Document::axObjectCacheOwner +blink_web.dll!blink::Document::existingAXObjectCache +blink_web.dll!blink::FrameView::removeFromAXObjectCache +blink_web.dll!blink::FrameView::prepareForDetach +blink_web.dll!blink::LocalFrame::setView +blink_web.dll!blink::FrameTree::~FrameTree
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt index 8d83b61..fad0fa3 100644 --- a/tools/valgrind/memcheck/suppressions.txt +++ b/tools/valgrind/memcheck/suppressions.txt
@@ -2931,12 +2931,11 @@ Memcheck:Leak fun:_Znw* ... - fun:_ZN5blink18ModulesInitializer20registerEventFactoryEv - fun:_ZN5blink15CoreInitializer4initEv + fun:_ZN5blink18ModulesInitializer4initEv fun:_ZN5blink19initializeWithoutV8EPNS_8PlatformE fun:_ZN5blink10initializeEPNS_8PlatformE - fun:_ZN7content25TestWebKitPlatformSupportC2Ev - fun:_ZN7content25TestWebKitPlatformSupportC1Ev + fun:_ZN7content27TestBlinkWebUnitTestSupportC2Ev + fun:_ZN7content27TestBlinkWebUnitTestSupportC1Ev fun:_ZN7content17UnitTestTestSuiteC2EPN4base9TestSuiteE fun:_ZN7content17UnitTestTestSuiteC1EPN4base9TestSuiteE }
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn index c4ed603..4f97f80 100644 --- a/ui/app_list/BUILD.gn +++ b/ui/app_list/BUILD.gn
@@ -180,6 +180,8 @@ "test/app_list_test_model.h", "test/app_list_test_view_delegate.cc", "test/app_list_test_view_delegate.h", + "test/test_search_result.cc", + "test/test_search_result.h", ] deps = [
diff --git a/ui/app_list/app_list.gyp b/ui/app_list/app_list.gyp index 39675a8..c15be36 100644 --- a/ui/app_list/app_list.gyp +++ b/ui/app_list/app_list.gyp
@@ -204,6 +204,8 @@ 'test/app_list_test_model.h', 'test/app_list_test_view_delegate.cc', 'test/app_list_test_view_delegate.h', + 'test/test_search_result.cc', + 'test/test_search_result.h', ], }, {
diff --git a/ui/app_list/cocoa/apps_search_results_controller_unittest.mm b/ui/app_list/cocoa/apps_search_results_controller_unittest.mm index 0b3ae52..b6f2f95 100644 --- a/ui/app_list/cocoa/apps_search_results_controller_unittest.mm +++ b/ui/app_list/cocoa/apps_search_results_controller_unittest.mm
@@ -10,8 +10,8 @@ #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #import "testing/gtest_mac.h" -#include "ui/app_list/search_result.h" #include "ui/app_list/test/app_list_test_model.h" +#include "ui/app_list/test/test_search_result.h" #include "ui/base/models/simple_menu_model.h" #import "ui/events/test/cocoa_test_event_utils.h" #include "ui/gfx/image/image_skia_util_mac.h" @@ -60,7 +60,7 @@ const int kDefaultResultsCount = 3; -class SearchResultWithMenu : public SearchResult { +class SearchResultWithMenu : public TestSearchResult { public: SearchResultWithMenu(const std::string& title, const std::string& details) : menu_model_(NULL),
diff --git a/ui/app_list/search_provider.cc b/ui/app_list/search_provider.cc index 916825f..0a652f5 100644 --- a/ui/app_list/search_provider.cc +++ b/ui/app_list/search_provider.cc
@@ -13,10 +13,6 @@ SearchProvider::~SearchProvider() { } -void SearchProvider::ReleaseResult(std::vector<SearchResult*>* results) { - results_.release(results); -} - void SearchProvider::Add(scoped_ptr<SearchResult> result) { results_.push_back(result.release()); FireResultChanged();
diff --git a/ui/app_list/search_provider.h b/ui/app_list/search_provider.h index f8ebfd0..d092c7e 100644 --- a/ui/app_list/search_provider.h +++ b/ui/app_list/search_provider.h
@@ -34,9 +34,6 @@ result_changed_callback_ = callback; } - // TODO(mukai): Fix the ownership and copying of the results. - void ReleaseResult(std::vector<SearchResult*>* results); - const Results& results() const { return results_; } protected:
diff --git a/ui/app_list/search_result.h b/ui/app_list/search_result.h index 7e7524f..9db0f63 100644 --- a/ui/app_list/search_result.h +++ b/ui/app_list/search_result.h
@@ -118,10 +118,14 @@ void AddObserver(SearchResultObserver* observer); void RemoveObserver(SearchResultObserver* observer); + // TODO(mukai): Remove this method and really simplify the ownership of + // SearchResult. Ideally, SearchResult will be copyable. + virtual scoped_ptr<SearchResult> Duplicate() = 0; + // Opens the result. virtual void Open(int event_flags); - // Invokes a custom action on the result. + // Invokes a custom action on the result. It does nothing by default. virtual void InvokeAction(int action_index, int event_flags); // Returns the context menu model for this item, or NULL if there is currently
diff --git a/ui/app_list/test/test_search_result.cc b/ui/app_list/test/test_search_result.cc new file mode 100644 index 0000000..361d670 --- /dev/null +++ b/ui/app_list/test/test_search_result.cc
@@ -0,0 +1,20 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/app_list/test/test_search_result.h" + +namespace app_list { + +TestSearchResult::TestSearchResult() { +} + +TestSearchResult::~TestSearchResult() { +} + +scoped_ptr<SearchResult> TestSearchResult::Duplicate() { + NOTREACHED(); + return scoped_ptr<SearchResult>(); +} + +} // namespace app_list
diff --git a/ui/app_list/test/test_search_result.h b/ui/app_list/test/test_search_result.h new file mode 100644 index 0000000..b54908d --- /dev/null +++ b/ui/app_list/test/test_search_result.h
@@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_APP_LIST_TEST_TEST_SEARCH_RESULT_H_ +#define UI_APP_LIST_TEST_TEST_SEARCH_RESULT_H_ + +#include "ui/app_list/search_result.h" + +namespace app_list { + +// A test search result which does nothing. +class TestSearchResult : public SearchResult { + public: + TestSearchResult(); + virtual ~TestSearchResult(); + + // SearchResult: + virtual scoped_ptr<SearchResult> Duplicate() override; + + private: + DISALLOW_COPY_AND_ASSIGN(TestSearchResult); +}; + +} // namespace app_list + +#endif // UI_APP_LIST_TEST_TEST_SEARCH_RESULT_H_
diff --git a/ui/app_list/views/app_list_view_unittest.cc b/ui/app_list/views/app_list_view_unittest.cc index 1238e25..29428ea 100644 --- a/ui/app_list/views/app_list_view_unittest.cc +++ b/ui/app_list/views/app_list_view_unittest.cc
@@ -14,6 +14,7 @@ #include "ui/app_list/search_box_model.h" #include "ui/app_list/test/app_list_test_model.h" #include "ui/app_list/test/app_list_test_view_delegate.h" +#include "ui/app_list/test/test_search_result.h" #include "ui/app_list/views/app_list_folder_view.h" #include "ui/app_list/views/app_list_main_view.h" #include "ui/app_list/views/apps_container_view.h" @@ -61,7 +62,7 @@ // Choose a set that is 3 regular app list pages and 2 landscape app list pages. const int kInitialItems = 34; -class TestTileSearchResult : public SearchResult { +class TestTileSearchResult : public TestSearchResult { public: TestTileSearchResult() { set_display_type(DISPLAY_TILE); } virtual ~TestTileSearchResult() {}
diff --git a/ui/app_list/views/search_result_list_view_unittest.cc b/ui/app_list/views/search_result_list_view_unittest.cc index 19ba7f7..e48a760 100644 --- a/ui/app_list/views/search_result_list_view_unittest.cc +++ b/ui/app_list/views/search_result_list_view_unittest.cc
@@ -8,8 +8,8 @@ #include "base/strings/utf_string_conversions.h" #include "ui/app_list/app_list_model.h" -#include "ui/app_list/search_result.h" #include "ui/app_list/test/app_list_test_view_delegate.h" +#include "ui/app_list/test/test_search_result.h" #include "ui/app_list/views/progress_bar_view.h" #include "ui/app_list/views/search_result_list_view_delegate.h" #include "ui/app_list/views/search_result_view.h" @@ -55,7 +55,7 @@ void SetUpSearchResults() { AppListModel::SearchResults* results = GetResults(); for (int i = 0; i < kDefaultSearchItems; ++i) - results->Add(new SearchResult()); + results->Add(new TestSearchResult()); // Adding results will schedule Update(). RunPendingMessages(); @@ -78,7 +78,7 @@ } void AddTestResultAtIndex(int index) { - GetResults()->Add(new SearchResult()); + GetResults()->Add(new TestSearchResult()); } void DeleteResultAt(int index) { GetResults()->DeleteAt(index); }
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn index 4f9a4a8..8b22fbb 100644 --- a/ui/aura/BUILD.gn +++ b/ui/aura/BUILD.gn
@@ -142,8 +142,6 @@ } } -if (false) { - source_set("test_support") { testonly = true sources = [ @@ -305,5 +303,3 @@ ] } } - -} # if (false)
diff --git a/ui/aura/client/aura_constants.cc b/ui/aura/client/aura_constants.cc index 554b611..f903bff 100644 --- a/ui/aura/client/aura_constants.cc +++ b/ui/aura/client/aura_constants.cc
@@ -21,6 +21,7 @@ DEFINE_WINDOW_PROPERTY_KEY(bool, kAlwaysOnTopKey, false); DEFINE_WINDOW_PROPERTY_KEY(bool, kAnimationsDisabledKey, false); DEFINE_WINDOW_PROPERTY_KEY(bool, kCanMaximizeKey, false); +DEFINE_WINDOW_PROPERTY_KEY(bool, kCanMinimizeKey, false); DEFINE_WINDOW_PROPERTY_KEY(bool, kCanResizeKey, true); DEFINE_WINDOW_PROPERTY_KEY(bool, kConstrainedWindowKey, false); DEFINE_WINDOW_PROPERTY_KEY(bool, kDrawAttentionKey, false);
diff --git a/ui/aura/client/aura_constants.h b/ui/aura/client/aura_constants.h index a751049..d346098 100644 --- a/ui/aura/client/aura_constants.h +++ b/ui/aura/client/aura_constants.h
@@ -28,6 +28,9 @@ // A property key to store the can-maximize flag. AURA_EXPORT extern const WindowProperty<bool>* const kCanMaximizeKey; +// A property key to store the can-minimize flag. +AURA_EXPORT extern const WindowProperty<bool>* const kCanMinimizeKey; + // A property key to store the can-resize flag. AURA_EXPORT extern const WindowProperty<bool>* const kCanResizeKey;
diff --git a/ui/chromeos/touch_exploration_controller.cc b/ui/chromeos/touch_exploration_controller.cc index 3aed357..e9d3e4f 100644 --- a/ui/chromeos/touch_exploration_controller.cc +++ b/ui/chromeos/touch_exploration_controller.cc
@@ -1149,7 +1149,7 @@ BindKeyEventWithFlags(VKEY_BROWSER_REFRESH, ui::EF_NONE); } -const float TouchExplorationController::GetSplitTapTouchSlop() { +float TouchExplorationController::GetSplitTapTouchSlop() { return gesture_detector_config_.touch_slop * 3; }
diff --git a/ui/chromeos/touch_exploration_controller.h b/ui/chromeos/touch_exploration_controller.h index c62237e..3cf4be0 100644 --- a/ui/chromeos/touch_exploration_controller.h +++ b/ui/chromeos/touch_exploration_controller.h
@@ -280,7 +280,7 @@ // The split tap slop is a bit more generous since keeping two // fingers in place is a bit harder. - const float GetSplitTapTouchSlop(); + float GetSplitTapTouchSlop(); enum State { // No fingers are down and no events are pending.
diff --git a/ui/compositor/BUILD.gn b/ui/compositor/BUILD.gn index 6e89292..ada7f9e 100644 --- a/ui/compositor/BUILD.gn +++ b/ui/compositor/BUILD.gn
@@ -77,8 +77,6 @@ } } -if (false) { - source_set("test_support") { testonly = true sources = [ @@ -134,8 +132,6 @@ } } -} # if (false) - # TODO(GYP) enable this when all dependencies are complete and it links. #test("compositor_unittests") { # sources = [
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc index 1aac1a1..b08a3d0 100644 --- a/ui/compositor/layer.cc +++ b/ui/compositor/layer.cc
@@ -510,6 +510,9 @@ cc_layer_->SetForceRenderSurface(force_render_surface_); cc_layer_->SetIsDrawable(type_ != LAYER_NOT_DRAWN); cc_layer_->SetHideLayerAndSubtree(!visible_); + + SetLayerFilters(); + SetLayerBackgroundFilters(); } void Layer::SwitchCCLayerForTest() {
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc index 71f5314..e83eb33 100644 --- a/ui/compositor/layer_unittest.cc +++ b/ui/compositor/layer_unittest.cc
@@ -1522,6 +1522,33 @@ EXPECT_NE(before.get(), child->cc_layer()); } +// Verifies that layer filters still attached after changing implementation +// layer. +TEST_F(LayerWithDelegateTest, LayerFiltersSurvival) { + scoped_ptr<Layer> layer(CreateLayer(LAYER_TEXTURED)); + layer->SetBounds(gfx::Rect(0, 0, 10, 10)); + EXPECT_TRUE(layer->cc_layer()); + EXPECT_EQ(0u, layer->cc_layer()->filters().size()); + + layer->SetLayerGrayscale(0.5f); + EXPECT_EQ(layer->layer_grayscale(), 0.5f); + EXPECT_EQ(1u, layer->cc_layer()->filters().size()); + + scoped_refptr<cc::DelegatedFrameResourceCollection> resource_collection = + new cc::DelegatedFrameResourceCollection; + scoped_refptr<cc::DelegatedFrameProvider> frame_provider = + new cc::DelegatedFrameProvider(resource_collection.get(), + MakeFrameData(gfx::Size(10, 10))); + + // Showing delegated content changes the underlying cc layer. + scoped_refptr<cc::Layer> before = layer->cc_layer(); + layer->SetShowDelegatedContent(frame_provider.get(), gfx::Size(10, 10)); + EXPECT_EQ(layer->layer_grayscale(), 0.5f); + EXPECT_TRUE(layer->cc_layer()); + EXPECT_NE(before.get(), layer->cc_layer()); + EXPECT_EQ(1u, layer->cc_layer()->filters().size()); +} + // Tests Layer::AddThreadedAnimation and Layer::RemoveThreadedAnimation. TEST_F(LayerWithRealCompositorTest, AddRemoveThreadedAnimations) { scoped_ptr<Layer> root(CreateLayer(LAYER_TEXTURED));
diff --git a/ui/events/gestures/gesture_recognizer_impl.cc b/ui/events/gestures/gesture_recognizer_impl.cc index 9737fd8..5f6cfda 100644 --- a/ui/events/gestures/gesture_recognizer_impl.cc +++ b/ui/events/gestures/gesture_recognizer_impl.cc
@@ -95,7 +95,7 @@ GestureConfiguration::max_separation_for_gesture_touches_in_pixels(); gfx::PointF closest_point; - int closest_touch_id; + int closest_touch_id = 0; float closest_distance_squared = std::numeric_limits<float>::infinity(); std::map<GestureConsumer*, GestureProviderAura*>::iterator i;
diff --git a/ui/file_manager/file_manager/background/js/compiled_resources.gyp b/ui/file_manager/file_manager/background/js/compiled_resources.gyp index 323a78f..a44b1e0 100644 --- a/ui/file_manager/file_manager/background/js/compiled_resources.gyp +++ b/ui/file_manager/file_manager/background/js/compiled_resources.gyp
@@ -25,7 +25,11 @@ 'volume_manager.js', '../../../../webui/resources/js/cr/ui/dialogs.js', ], - 'externs': ['<(CLOSURE_DIR)/externs/chrome_send_externs.js'], + 'externs': [ + '<(CLOSURE_DIR)/externs/chrome_send_externs.js', + '<(CLOSURE_DIR)/externs/chrome_extensions.js', + '<(CLOSURE_DIR)/externs/file_manager_private.js', + ], }, 'includes': [ '../../../../../third_party/closure_compiler/compile_js.gypi'
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js index 31d2e12..480b417 100644 --- a/ui/file_manager/file_manager/common/js/util.js +++ b/ui/file_manager/file_manager/common/js/util.js
@@ -10,37 +10,6 @@ var util = {}; /** - * Returns a function that console.log's its arguments, prefixed by |msg|. - * - * @param {string} msg The message prefix to use in the log. - * @param {function(...string)=} opt_callback A function to invoke after - * logging. - * @return {function(...string)} Function that logs. - */ -util.flog = function(msg, opt_callback) { - return function() { - var ary = Array.apply(null, arguments); - console.log(msg + ': ' + ary.join(', ')); - if (opt_callback) - opt_callback.apply(null, arguments); - }; -}; - -/** - * Returns a function that throws an exception that includes its arguments - * prefixed by |msg|. - * - * @param {string} msg The message prefix to use in the exception. - * @return {function(...string)} Function that throws. - */ -util.ferr = function(msg) { - return function() { - var ary = Array.apply(null, arguments); - throw new Error(msg + ': ' + ary.join(', ')); - }; -}; - -/** * @param {string} name File error name. * @return {string} Translated file error string. */ @@ -126,164 +95,6 @@ }; /** - * Iterates the entries contained by dirEntry, and invokes callback once for - * each entry. On completion, successCallback will be invoked. - * - * @param {DirectoryEntry} dirEntry The entry of the directory. - * @param {function(Entry, function())} callback Invoked for each entry. - * @param {function()} successCallback Invoked on completion. - * @param {function(FileError)} errorCallback Invoked if an error is found on - * directory entry reading. - */ -util.forEachDirEntry = function( - dirEntry, callback, successCallback, errorCallback) { - var reader = dirEntry.createReader(); - var iterate = function() { - reader.readEntries(function(entries) { - if (entries.length == 0) { - successCallback(); - return; - } - - AsyncUtil.forEach( - entries, - function(forEachCallback, entry) { - // Do not pass index nor entries. - callback(entry, forEachCallback); - }, - iterate); - }, errorCallback); - }; - iterate(); -}; - -/** - * Reads contents of directory. - * @param {DirectoryEntry} root Root entry. - * @param {string} path Directory path. - * @param {function(Array.<Entry>)} callback List of entries passed to callback. - */ -util.readDirectory = function(root, path, callback) { - var onError = function(e) { - callback([], e); - }; - root.getDirectory(path, {create: false}, function(entry) { - var reader = entry.createReader(); - var r = []; - var readNext = function() { - reader.readEntries(function(results) { - if (results.length == 0) { - callback(r, null); - return; - } - r.push.apply(r, results); - readNext(); - }, onError); - }; - readNext(); - }, onError); -}; - -/** - * Utility function to resolve multiple directories with a single call. - * - * The successCallback will be invoked once for each directory object - * found. The errorCallback will be invoked once for each - * path that could not be resolved. - * - * The successCallback is invoked with a null entry when all paths have - * been processed. - * - * @param {DirEntry} dirEntry The base directory. - * @param {Object} params The parameters to pass to the underlying - * getDirectory calls. - * @param {Array.<string>} paths The list of directories to resolve. - * @param {function(!DirEntry)} successCallback The function to invoke for - * each DirEntry found. Also invoked once with null at the end of the - * process. - * @param {function(FileError)} errorCallback The function to invoke - * for each path that cannot be resolved. - */ -util.getDirectories = function(dirEntry, params, paths, successCallback, - errorCallback) { - - // Copy the params array, since we're going to destroy it. - params = [].slice.call(params); - - var onComplete = function() { - successCallback(null); - }; - - var getNextDirectory = function() { - var path = paths.shift(); - if (!path) - return onComplete(); - - dirEntry.getDirectory( - path, params, - function(entry) { - successCallback(entry); - getNextDirectory(); - }, - function(err) { - errorCallback(err); - getNextDirectory(); - }); - }; - - getNextDirectory(); -}; - -/** - * Utility function to resolve multiple files with a single call. - * - * The successCallback will be invoked once for each directory object - * found. The errorCallback will be invoked once for each - * path that could not be resolved. - * - * The successCallback is invoked with a null entry when all paths have - * been processed. - * - * @param {DirEntry} dirEntry The base directory. - * @param {Object} params The parameters to pass to the underlying - * getFile calls. - * @param {Array.<string>} paths The list of files to resolve. - * @param {function(!FileEntry)} successCallback The function to invoke for - * each FileEntry found. Also invoked once with null at the end of the - * process. - * @param {function(FileError)} errorCallback The function to invoke - * for each path that cannot be resolved. - */ -util.getFiles = function(dirEntry, params, paths, successCallback, - errorCallback) { - // Copy the params array, since we're going to destroy it. - params = [].slice.call(params); - - var onComplete = function() { - successCallback(null); - }; - - var getNextFile = function() { - var path = paths.shift(); - if (!path) - return onComplete(); - - dirEntry.getFile( - path, params, - function(entry) { - successCallback(entry); - getNextFile(); - }, - function(err) { - errorCallback(err); - getNextFile(); - }); - }; - - getNextFile(); -}; - -/** * Renames the entry to newName. * @param {Entry} entry The entry to be renamed. * @param {string} newName The new name. @@ -406,30 +217,6 @@ }; /** - * Write a blob to a file. - * Truncates the file first, so the previous content is fully overwritten. - * @param {FileEntry} entry File entry. - * @param {Blob} blob The blob to write. - * @param {function(Event)} onSuccess Completion callback. The first argument is - * a 'writeend' event. - * @param {function(FileError)} onError Error handler. - */ -util.writeBlobToFile = function(entry, blob, onSuccess, onError) { - var truncate = function(writer) { - writer.onerror = onError; - writer.onwriteend = write.bind(null, writer); - writer.truncate(0); - }; - - var write = function(writer) { - writer.onwriteend = onSuccess; - writer.write(blob); - }; - - entry.createWriter(truncate, onError); -}; - -/** * Returns a string '[Ctrl-][Alt-][Shift-][Meta-]' depending on the event * modifiers. Convenient for writing out conditions in keyboard handlers. * @@ -471,33 +258,6 @@ }; /** - * Traverses a directory tree whose root is the given entry, and invokes - * callback for each entry. Upon completion, successCallback will be called. - * On error, errorCallback will be called. - * - * @param {Entry} entry The root entry. - * @param {function(Entry):boolean} callback Callback invoked for each entry. - * If this returns false, entries under it won't be traversed. Note that - * its siblings (and their children) will be still traversed. - * @param {function()} successCallback Called upon successful completion. - * @param {function(error)} errorCallback Called upon error. - */ -util.traverseTree = function(entry, callback, successCallback, errorCallback) { - if (!callback(entry)) { - successCallback(); - return; - } - - util.forEachDirEntry( - entry, - function(child, iterationCallback) { - util.traverseTree(child, callback, iterationCallback, errorCallback); - }, - successCallback, - errorCallback); -}; - -/** * A shortcut function to create a child element with given tag and class. * * @param {HTMLElement} parent Parent element. @@ -743,34 +503,6 @@ }; /** - * Finds proerty descriptor in the object prototype chain. - * @param {Object} object The object. - * @param {string} propertyName The property name. - * @return {Object} Property descriptor. - */ -util.findPropertyDescriptor = function(object, propertyName) { - for (var p = object; p; p = Object.getPrototypeOf(p)) { - var d = Object.getOwnPropertyDescriptor(p, propertyName); - if (d) - return d; - } - return null; -}; - -/** - * Calls inherited property setter (useful when property is - * overridden). - * @param {Object} object The object. - * @param {string} propertyName The property name. - * @param {*} value Value to set. - */ -util.callInheritedSetter = function(object, propertyName, value) { - var d = util.findPropertyDescriptor(Object.getPrototypeOf(object), - propertyName); - d.set.call(object, value); -}; - -/** * Returns true if the board of the device matches the given prefix. * @param {string} boardPrefix The board prefix to match against. * (ex. "x86-mario". Prefix is used as the actual board name comes with @@ -808,18 +540,6 @@ }; /** - * Makes a redirect to the specified Files.app's window from another window. - * @param {number} id Window id. - * @param {string} url Target url. - * @return {boolean} True if the window has been found. False otherwise. - */ -util.redirectMainWindow = function(id, url) { - // TODO(mtomasz): Implement this for Apps V2, once the photo importer is - // restored. - return false; -}; - -/** * Checks, if the Files.app's window is in a full screen mode. * * @param {AppWindow} appWindow App window to be maximized. @@ -1168,35 +888,6 @@ }; /** - * Returns the localized name for the root type. If not available, then returns - * null. - * - * @param {VolumeManagerCommon.RootType} rootType The root type. - * @return {?string} The localized name, or null if not available. - */ -util.getRootTypeLabel = function(rootType) { - var str = function(id) { - return loadTimeData.getString(id); - }; - - switch (rootType) { - case VolumeManagerCommon.RootType.DOWNLOADS: - return str('DOWNLOADS_DIRECTORY_LABEL'); - case VolumeManagerCommon.RootType.DRIVE: - return str('DRIVE_MY_DRIVE_LABEL'); - case VolumeManagerCommon.RootType.DRIVE_OFFLINE: - return str('DRIVE_OFFLINE_COLLECTION_LABEL'); - case VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME: - return str('DRIVE_SHARED_WITH_ME_COLLECTION_LABEL'); - case VolumeManagerCommon.RootType.DRIVE_RECENT: - return str('DRIVE_RECENT_COLLECTION_LABEL'); - } - - // Translation not found. - return null; -}; - -/** * Extracts the extension of the path. * * Examples:
diff --git a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp index 3dcdf1f..57ff0ac 100644 --- a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp +++ b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
@@ -9,7 +9,12 @@ 'depends': [ '../../../../../ui/webui/resources/js/cr/ui/dialogs.js', ], - 'externs': ['<(CLOSURE_DIR)/externs/chrome_send_externs.js'], + 'externs': [ + '<(CLOSURE_DIR)/externs/chrome_send_externs.js', + '<(CLOSURE_DIR)/externs/chrome_extensions.js', + '<(CLOSURE_DIR)/externs/file_manager_private.js', + '<(CLOSURE_DIR)/externs/metrics_private.js', + ], }, 'includes': [ '../../../../../third_party/closure_compiler/compile_js.gypi'
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js index 40200c7..dc66f71 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -2167,7 +2167,8 @@ // does not have listIndex, and therefore is not found in the list. if (entry) visibleEntries.push(entry); } - this.metadataCache_.get(visibleEntries, 'thumbnail', null); + // Refreshes the metadata. + this.metadataCache_.getLatest(visibleEntries, 'thumbnail', null); }; /** @@ -3994,6 +3995,16 @@ // consistency, the current directory is always changed regardless of // the file type. entry.getParent(function(parentEntry) { + // Check if the parent entry points /drive/other or not. + // If so it just opens the file. + var locationInfo = self.volumeManager_.getLocationInfo(parentEntry); + if (!locationInfo || + (locationInfo.isRootEntry && + locationInfo.rootType === + VolumeManagerCommon.RootType.DRIVE_OTHER)) { + openIt(); + return; + } var onDirectoryChanged = function(event) { self.directoryModel_.removeEventListener('scan-completed', onDirectoryChanged);
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js index 248b956..d007f5f 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js +++ b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
@@ -67,8 +67,9 @@ var boxes = this.querySelectorAll('.img-container'); for (var i = 0; i < boxes.length; i++) { var box = boxes[i]; - var entry = this.dataModel.item(this.getListItemAncestor(box)); - if (!entry || !(entry.toURL() in urls)) + var listItem = this.getListItemAncestor(box); + var entry = listItem && this.dataModel.item(listItem.listIndex); + if (!entry || urls.indexOf(entry.toURL()) === -1) continue; FileGrid.decorateThumbnailBox(box,
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table.js b/ui/file_manager/file_manager/foreground/js/ui/file_table.js index 9224176..f15674e 100644 --- a/ui/file_manager/file_manager/foreground/js/ui/file_table.js +++ b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
@@ -101,21 +101,37 @@ * Handles to the start of column resizing by splitters. */ FileTableColumnModel.prototype.handleSplitterDragStart = function() { - this.columnPos_ = [0]; - for (var i = 0; i < this.columns_.length; i++) { - this.columnPos_[i + 1] = this.columns_[i].width + this.columnPos_[i]; - } + this.initializeColumnPos(); }; /** * Handles to the end of column resizing by splitters. */ FileTableColumnModel.prototype.handleSplitterDragEnd = function() { - this.columnPos_ = null; + this.destroyColumnPos(); }; /** + * Initialize columnPos_ which is used in setWidthAndKeepTotal(). + */ +FileTableColumnModel.prototype.initializeColumnPos = function() { + this.columnPos_ = [0]; + for (var i = 0; i < this.columns_.length; i++) { + this.columnPos_[i + 1] = this.columns_[i].width + this.columnPos_[i]; + } +} + +/** + * Destroy columnPos_ which is used in setWidthAndKeepTotal(). + */ +FileTableColumnModel.prototype.destroyColumnPos = function() { + this.columnPos_ = null; +} + +/** * Sets the width of column with keeping the total width of table. + * Before and after calling this method, you must initialize and destroy + * columnPos with initializeColumnPos() and destroyColumnPos(). * @param {number} columnIndex Index of column that is resized. * @param {number} columnWidth New width of the column. */ @@ -351,6 +367,56 @@ }; /** + * Adjust column width to fit its content. + * @param {number} index Index of the column to adjust width. + * @override + */ +FileTable.prototype.fitColumn = function(index) { + var list = this.list_; + var listHeight = list.clientHeight; + + var cm = this.columnModel; + var dm = this.dataModel; + var columnId = cm.getId(index); + var doc = this.ownerDocument; + var render = cm.getRenderFunction(index); + var table = this; + var MAXIMUM_ROWS_TO_MEASURE = 1000; + + // Create a temporaty list item, put all cells into it and measure its + // width. Then remove the item. It fits "list > *" CSS rules. + var container = doc.createElement('li'); + container.style.display = 'inline-block'; + container.style.textAlign = 'start'; + // The container will have width of the longest cell. + container.style.webkitBoxOrient = 'vertical'; + + // Ensure all needed data available. + dm.prepareSort(columnId, function() { + // Select at most MAXIMUM_ROWS_TO_MEASURE items around visible area. + var items = list.getItemsInViewPort(list.scrollTop, listHeight); + var firstIndex = Math.floor(Math.max(0, + (items.last + items.first - MAXIMUM_ROWS_TO_MEASURE) / 2)); + var lastIndex = Math.min(dm.length, + firstIndex + MAXIMUM_ROWS_TO_MEASURE); + for (var i = firstIndex; i < lastIndex; i++) { + var item = dm.item(i); + var div = doc.createElement('div'); + div.className = 'table-row-cell'; + div.appendChild(render(item, columnId, table)); + container.appendChild(div); + } + list.appendChild(container); + var width = parseFloat(window.getComputedStyle(container).width); + list.removeChild(container); + + cm.initializeColumnPos(); + cm.setWidthAndKeepTotal(index, Math.ceil(width)); + cm.destroyColumnPos(); + }); +} + +/** * Sets date and time format. * @param {boolean} use12hourClock True if 12 hours clock, False if 24 hours. */
diff --git a/ui/file_manager/gallery/js/compiled_resources.gyp b/ui/file_manager/gallery/js/compiled_resources.gyp index c1c0859..1461d61 100644 --- a/ui/file_manager/gallery/js/compiled_resources.gyp +++ b/ui/file_manager/gallery/js/compiled_resources.gyp
@@ -18,7 +18,11 @@ '../../file_manager/common/js/error_util.js', '../../file_manager/foreground/js/file_type.js' ], - 'externs': ['<(CLOSURE_DIR)/externs/chrome_send_externs.js'], + 'externs': [ + '<(CLOSURE_DIR)/externs/chrome_send_externs.js', + '<(CLOSURE_DIR)/externs/chrome_extensions.js', + '<(CLOSURE_DIR)/externs/file_manager_private.js', + ], }, 'includes': [ '../../../../third_party/closure_compiler/compile_js.gypi' @@ -29,7 +33,12 @@ 'variables': { 'depends': [ ], - 'externs': ['<(CLOSURE_DIR)/externs/chrome_send_externs.js'], + 'externs': [ + '<(CLOSURE_DIR)/externs/chrome_send_externs.js', + '<(CLOSURE_DIR)/externs/chrome_extensions.js', + '<(CLOSURE_DIR)/externs/file_manager_private.js', + '<(CLOSURE_DIR)/externs/metrics_private.js', + ], }, 'includes': [ '../../../../third_party/closure_compiler/compile_js.gypi'
diff --git a/ui/file_manager/gallery/js/gallery.js b/ui/file_manager/gallery/js/gallery.js index 3641ccc..011fa33 100644 --- a/ui/file_manager/gallery/js/gallery.js +++ b/ui/file_manager/gallery/js/gallery.js
@@ -712,7 +712,8 @@ var entry = itemsToRemove.pop().getEntry(); entry.remove(deleteNext, function() { - util.flog('Error deleting: ' + entry.name, deleteNext); + console.error('Error deleting: ' + entry.name); + deleteNext(); }); }
diff --git a/ui/file_manager/image_loader/compiled_resources.gyp b/ui/file_manager/image_loader/compiled_resources.gyp index d269d9f..c6488fa 100644 --- a/ui/file_manager/image_loader/compiled_resources.gyp +++ b/ui/file_manager/image_loader/compiled_resources.gyp
@@ -12,7 +12,11 @@ 'scheduler.js', 'request.js', ], - 'externs': ['<(CLOSURE_DIR)/externs/chrome_send_externs.js'], + 'externs': [ + '<(CLOSURE_DIR)/externs/chrome_send_externs.js', + '<(CLOSURE_DIR)/externs/chrome_extensions.js', + '<(CLOSURE_DIR)/externs/file_manager_private.js', + ], }, 'includes': [ '../../../third_party/closure_compiler/compile_js.gypi'
diff --git a/ui/file_manager/video_player/js/compiled_resources.gyp b/ui/file_manager/video_player/js/compiled_resources.gyp index 7bb6fe5..933ec66 100644 --- a/ui/file_manager/video_player/js/compiled_resources.gyp +++ b/ui/file_manager/video_player/js/compiled_resources.gyp
@@ -19,7 +19,11 @@ 'error_util.js', 'test_util.js', ], - 'externs': ['<(CLOSURE_DIR)/externs/chrome_send_externs.js'], + 'externs': [ + '<(CLOSURE_DIR)/externs/chrome_send_externs.js', + '<(CLOSURE_DIR)/externs/chrome_extensions.js', + '<(CLOSURE_DIR)/externs/file_manager_private.js', + ], }, 'includes': [ '../../../../third_party/closure_compiler/compile_js.gypi' @@ -30,7 +34,12 @@ 'variables': { 'depends': [ ], - 'externs': ['<(CLOSURE_DIR)/externs/chrome_send_externs.js'], + 'externs': [ + '<(CLOSURE_DIR)/externs/chrome_send_externs.js', + '<(CLOSURE_DIR)/externs/chrome_extensions.js', + '<(CLOSURE_DIR)/externs/file_manager_private.js', + '<(CLOSURE_DIR)/externs/media_player_private.js', + ], }, 'includes': [ '../../../../third_party/closure_compiler/compile_js.gypi'
diff --git a/ui/login/account_picker/screen_account_picker.js b/ui/login/account_picker/screen_account_picker.js index fc87cc4..865d64d 100644 --- a/ui/login/account_picker/screen_account_picker.js +++ b/ui/login/account_picker/screen_account_picker.js
@@ -271,12 +271,8 @@ * Shows a custom icon in the user pod of |username|. This function * is used by the chrome.screenlockPrivate API. * @param {string} username Username of pod to add button - * @param {!{resourceUrl: (string | undefined), - * data: ({scale1x: string, scale2x: string} | undefined), - * size: ({width: number, height: number} | undefined), - * animation: ({resourceWidth: number, frameLength: number} | - * undefined), - * opacity: (number | undefined), + * @param {!{id: !string, + * hardlockOnClick: boolean, * tooltip: ({text: string, autoshow: boolean} | undefined)}} icon * The icon parameters. */
diff --git a/ui/login/account_picker/user_pod_row.css b/ui/login/account_picker/user_pod_row.css index 089dc5e..bb52a3e 100644 --- a/ui/login/account_picker/user_pod_row.css +++ b/ui/login/account_picker/user_pod_row.css
@@ -214,6 +214,7 @@ display: block; flex: auto; margin-top: 11px; + outline: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -225,6 +226,8 @@ background-position: center; background-repeat: no-repeat; flex: none; + height: 27px; + width: 27px; } .custom-icon.faded { @@ -232,6 +235,75 @@ visibility 200ms ease-in-out; } +.custom-icon-hardlocked { + background-image: url('chrome://theme/IDR_EASY_UNLOCK_HARDLOCKED'); +} + +.custom-icon-hardlocked.icon-with-tooltip:hover { + background-image: url('chrome://theme/IDR_EASY_UNLOCK_HARDLOCKED_HOVER'); +} + +.custom-icon-hardlocked.interactive-custom-icon:active { + background-image: url('chrome://theme/IDR_EASY_UNLOCK_HARDLOCKED_PRESSED'); +} + +.custom-icon-locked { + background-image: url('chrome://theme/IDR_EASY_UNLOCK_LOCKED'); +} + +.custom-icon-locked.icon-with-tooltip:hover { + background-image: url('chrome://theme/IDR_EASY_UNLOCK_LOCKED_HOVER'); +} + +.custom-icon-locked.interactive-custom-icon:active { + background-image: url('chrome://theme/IDR_EASY_UNLOCK_LOCKED_PRESSED'); +} + +.custom-icon-unlocked { + background-image: url('chrome://theme/IDR_EASY_UNLOCK_UNLOCKED'); +} + +.custom-icon-unlocked.icon-with-tooltip:hover { + background-image: url('chrome://theme/IDR_EASY_UNLOCK_UNLOCKED_HOVER'); +} + +.custom-icon-unlocked.interactive-custom-icon:active { + background-image: url('chrome://theme/IDR_EASY_UNLOCK_UNLOCKED_PRESSED'); +} + +/** + * Preloads resources for custom icon. Without this, the resources will be + * loaded when CSS properties using them are first applied, which has visible + * delay and may cause a short white flash when the icon background changes. + */ +.custom-icon::after { + content: + url('chrome://theme/IDR_EASY_UNLOCK_HARDLOCKED') + url('chrome://theme/IDR_EASY_UNLOCK_HARDLOCKED_HOVER') + url('chrome://theme/IDR_EASY_UNLOCK_HARDLOCKED_PRESSED') + url('chrome://theme/IDR_EASY_UNLOCK_LOCKED') + url('chrome://theme/IDR_EASY_UNLOCK_LOCKED_HOVER') + url('chrome://theme/IDR_EASY_UNLOCK_LOCKED_PRESSED') + url('chrome://theme/IDR_EASY_UNLOCK_UNLOCKED') + url('chrome://theme/IDR_EASY_UNLOCK_UNLOCKED_HOVER') + url('chrome://theme/IDR_EASY_UNLOCK_UNLOCKED_PRESSED'); + display: none; +} + +.custom-icon-spinner { + -webkit-animation: easy-unlock-spinner-animation 2s steps(45) infinite; + background-image: url('chrome://theme/IDR_EASY_UNLOCK_SPINNER'); +} + +@-webkit-keyframes easy-unlock-spinner-animation { + from { background-position: 0 } + to { background-position: -1215px } +} + +.interactive-custom-icon { + cursor: pointer; +} + .custom-icon-container { display: flex; flex: none;
diff --git a/ui/login/account_picker/user_pod_row.js b/ui/login/account_picker/user_pod_row.js index 1024dfa..c6af9a8 100644 --- a/ui/login/account_picker/user_pod_row.js +++ b/ui/login/account_picker/user_pod_row.js
@@ -183,24 +183,23 @@ return node; }); + /** + * The supported user pod custom icons. + * {@code id} properties should be in sync with values set by C++ side. + * {@code class} properties are CSS classes used to set the icons' background. + * @const {Array.<{id: !string, class: !string}>} + */ + UserPodCustomIcon.ICONS = [ + {id: 'locked', class: 'custom-icon-locked'}, + {id: 'unlocked', class: 'custom-icon-unlocked'}, + {id: 'hardlocked', class: 'custom-icon-hardlocked'}, + {id: 'spinner', class: 'custom-icon-spinner'} + ]; + UserPodCustomIcon.prototype = { __proto__: HTMLDivElement.prototype, /** - * The icon height. - * @type {number} - * @private - */ - height_: 0, - - /** - * The icon width. - * @type {number} - * @private - */ - width_: 0, - - /** * Tooltip to be shown when the user hovers over the icon. The icon * properties may be set so the tooltip is shown automatically when the icon * is updated. The tooltip is shown in a bubble attached to the icon @@ -236,29 +235,6 @@ showTooltipTimeout_: null, /** - * If animation is set, the current horizontal background offset for the - * icon resource. - * @type {number} - * @private - */ - lastAnimationOffset_: 0, - - /** - * The reference to interval for progressing the animation. - * @type {?number} - * @private - */ - animationInterval_: null, - - /** - * The width of the resource that contains representations for different - * animation stages. - * @type {number} - * @private - */ - animationResourceSize_: 0, - - /** * When {@code fadeOut} is called, the element gets hidden using fadeout * animation. This is reference to the listener for transition end added to * the icon element while it's fading out. @@ -280,9 +256,9 @@ this.iconElement.addEventListener('mouseover', this.showTooltipSoon_.bind(this)); this.iconElement.addEventListener('mouseout', - this.hideTooltip_.bind(this, false)); + this.hideTooltip_.bind(this, false)); this.iconElement.addEventListener('mousedown', - this.hideTooltip_.bind(this, false)); + this.handleMouseDown_.bind(this)); this.iconElement.addEventListener('click', this.handleClick_.bind(this)); this.iconElement.addEventListener('keydown', @@ -304,14 +280,15 @@ }, /** - * Sets the icon background image to a chrome://theme URL. - * @param {!string} iconUrl The icon's background image URL. + * Updates the icon element class list to properly represent the provided + * icon. + * @param {!string} id The id of the icon that should be shown. Should be + * one of the ids listed in {@code UserPodCustomIcon.ICONS}. */ - setIconAsResourceUrl: function(iconUrl) { - this.iconElement.style.backgroundImage = - '-webkit-image-set(' + - 'url(' + iconUrl + '@1x) 1x,' + - 'url(' + iconUrl + '@2x) 2x)'; + setIcon: function(id) { + UserPodCustomIcon.ICONS.forEach(function(icon) { + this.iconElement.classList.toggle(icon.class, id == icon.id); + }, this); }, /** @@ -339,35 +316,6 @@ }, /** - * Sets the icon size element size. - * @param {!{width: number, height: number}} size The icon size. - */ - setSize: function(size) { - this.height_ = size.height < CUSTOM_ICON_CONTAINER_SIZE ? - size.height : CUSTOM_ICON_COTAINER_SIZE; - this.iconElement.style.height = this.height_ + 'px'; - - this.width_ = size.width < CUSTOM_ICON_CONTAINER_SIZE ? - size.width : CUSTOM_ICON_COTAINER_SIZE; - this.iconElement.style.width = this.width_ + 'px'; - this.style.width = this.width_ + 'px'; - }, - - /** - * Sets the icon opacity. - * @param {number} opacity The icon opacity in [0-100] scale. - */ - setOpacity: function(opacity) { - if (opacity > 100) { - this.style.opacity = 1; - } else if (opacity < 0) { - this.style.opacity = 0; - } else { - this.style.opacity = opacity / 100; - } - }, - - /** * Updates the icon tooltip. If {@code autoshow} parameter is set the * tooltip is immediatelly shown. If tooltip text is not set, the method * ensures the tooltip gets hidden. If tooltip is shown prior to this call, @@ -376,6 +324,8 @@ * parameters. */ setTooltip: function(tooltip) { + this.iconElement.classList.toggle('icon-with-tooltip', !!tooltip); + if (this.tooltip_ == tooltip.text && !tooltip.autoshow) return; this.tooltip_ = tooltip.text; @@ -408,29 +358,6 @@ }, /** - * Sets the icon animation parameter and starts the animation. - * The animation is set using the resource containing all animation frames - * concatenated horizontally. The animator offsets the background image in - * regural intervals. - * @param {?{resourceWidth: number, frameLengthMs: number}} animation - * |resourceWidth|: Total width for the resource containing the - * animation frames. - * |frameLengthMs|: Time interval for which a single animation frame is - * shown. - */ - setAnimation: function(animation) { - if (this.animationInterval_) - clearInterval(this.animationInterval_); - this.iconElement.style.backgroundPosition = 'center'; - if (!animation) - return; - this.lastAnimationOffset_ = 0; - this.animationResourceSize_ = animation.resourceWidth; - this.animationInterval_ = setInterval(this.progressAnimation_.bind(this), - animation.frameLengthMs); - }, - - /** * Sets up icon tabIndex attribute and handler for click and 'Enter' key * down events. * @param {?function()} callback If icon should be interactive, the @@ -438,6 +365,8 @@ * be null to make the icon non interactive. */ setInteractive: function(callback) { + this.iconElement.classList.toggle('interactive-custom-icon', !!callback); + // Update tabIndex property if needed. if (!!this.actionHandler_ != !!callback) { if (callback) { @@ -453,12 +382,11 @@ }, /** - * Hides the icon. Makes sure the tooltip is hidden and animation reset. + * Hides the icon and cleans its state. */ hide: function() { this.hideTooltip_(true); this.hidden = true; - this.setAnimation(null); this.setInteractive(null); this.resetHideTransitionState_(); }, @@ -478,6 +406,18 @@ }, /** + * Handles mouse down event in the icon element. + * @param {Event} e The mouse down event. + * @private + */ + handleMouseDown_: function(e) { + this.hideTooltip_(false); + // Stop the event propagation so in the case the click ends up on the + // user pod (outside the custom icon) auth is not attempted. + stopEventPropagation(e); + }, + + /** * Handles click event on the icon element. No-op if * {@code this.actionHandler_} is not set. * @param {Event} e The click event. @@ -569,19 +509,6 @@ this.showTooltipTimeout_ = null; } }, - - /** - * Horizontally offsets the animated icon's background for a single icon - * size width. - * @private - */ - progressAnimation_: function() { - this.lastAnimationOffset_ += this.width_; - if (this.lastAnimationOffset_ >= this.animationResourceSize_) - this.lastAnimationOffset_ = 0; - this.iconElement.style.backgroundPosition = - '-' + this.lastAnimationOffset_ + 'px center'; - } }; /** @@ -594,6 +521,15 @@ UserPod.prototype = { __proto__: HTMLDivElement.prototype, + /** + * Whether click on the pod can issue a user click auth attempt. The + * attempt can be issued iff the pod was focused when the click + * started (i.e. on mouse down event). + * @type {boolean} + * @private + */ + userClickAuthAllowed_: false, + /** @override */ decorate: function() { this.tabIndex = UserPodTabOrder.POD_INPUT; @@ -601,6 +537,7 @@ this.addEventListener('keydown', this.handlePodKeyDown_.bind(this)); this.addEventListener('click', this.handleClickOnPod_.bind(this)); + this.addEventListener('mousedown', this.handlePodMouseDown_.bind(this)); this.signinButtonElement.addEventListener('click', this.activate.bind(this)); @@ -644,6 +581,8 @@ var initialAuthType = this.user.initialAuthType || AUTH_TYPE.OFFLINE_PASSWORD; this.setAuthType(initialAuthType, null); + + this.userClickAuthAllowed_ = false; }, /** @@ -981,7 +920,7 @@ } else if (this.isAuthTypeOnlineSignIn) { return this.signinButtonElement; } else if (this.isAuthTypeUserClick) { - return this; + return this.passwordLabelElement; } }, @@ -1365,6 +1304,16 @@ }, /** + * Handles mouse down event. It sets whether the user click auth will be + * allowed on the next mouse click event. The auth is allowed iff the pod + * was focused on the mouse down event starting the click. + * @param {Event} e The mouse down event. + */ + handlePodMouseDown_: function(e) { + this.userClickAuthAllowed_ = this.parentNode.isFocused(this); + }, + + /** * Handles click event on a user pod. * @param {Event} e Click event. */ @@ -1375,7 +1324,9 @@ if (!this.isActionBoxMenuActive) { if (this.isAuthTypeOnlineSignIn) { this.showSigninUI(); - } else if (this.isAuthTypeUserClick) { + } else if (this.isAuthTypeUserClick && this.userClickAuthAllowed_) { + // Note that this.userClickAuthAllowed_ is set in mouse down event + // handler. this.parentNode.setActivatedPod(this); } @@ -2280,12 +2231,8 @@ /** * Shows a custom icon on a user pod besides the input field. * @param {string} username Username of pod to add button - * @param {!{resourceUrl: (string | undefined), - * data: ({scale1x: string, scale2x: string} | undefined), - * size: ({width: number, height: number} | undefined), - * animation: ({resourceWidth: number, frameLength: number} | - * undefined), - * opacity: (number | undefined), + * @param {!{id: !string, + * hardlockOnClick: boolean, * tooltip: ({text: string, autoshow: boolean} | undefined)}} icon * The icon parameters. */ @@ -2297,20 +2244,20 @@ return; } - if (!icon.resourceUrl) + if (!icon.id) return; - pod.customIconElement.setIconAsResourceUrl(icon.resourceUrl); - pod.customIconElement.setSize(icon.size || {width: 0, height: 0}); - pod.customIconElement.setAnimation(icon.animation || null); - pod.customIconElement.setOpacity(icon.opacity || 100); + pod.customIconElement.setIcon(icon.id); + if (icon.hardlockOnClick) { pod.customIconElement.setInteractive( this.hardlockUserPod_.bind(this, username)); } else { pod.customIconElement.setInteractive(null); } + pod.customIconElement.show(); + // This has to be called after |show| in case the tooltip should be shown // immediatelly. pod.customIconElement.setTooltip(
diff --git a/ui/resources/default_100_percent/common/easy_unlock_hardlocked_hover.png b/ui/resources/default_100_percent/common/easy_unlock_hardlocked_hover.png new file mode 100644 index 0000000..7792ee6 --- /dev/null +++ b/ui/resources/default_100_percent/common/easy_unlock_hardlocked_hover.png Binary files differ
diff --git a/ui/resources/default_100_percent/common/easy_unlock_hardlocked_pressed.png b/ui/resources/default_100_percent/common/easy_unlock_hardlocked_pressed.png new file mode 100644 index 0000000..371a40e --- /dev/null +++ b/ui/resources/default_100_percent/common/easy_unlock_hardlocked_pressed.png Binary files differ
diff --git a/ui/resources/default_100_percent/common/easy_unlock_locked_hover.png b/ui/resources/default_100_percent/common/easy_unlock_locked_hover.png new file mode 100644 index 0000000..33f2ec6 --- /dev/null +++ b/ui/resources/default_100_percent/common/easy_unlock_locked_hover.png Binary files differ
diff --git a/ui/resources/default_100_percent/common/easy_unlock_locked_pressed.png b/ui/resources/default_100_percent/common/easy_unlock_locked_pressed.png new file mode 100644 index 0000000..49ccf28 --- /dev/null +++ b/ui/resources/default_100_percent/common/easy_unlock_locked_pressed.png Binary files differ
diff --git a/ui/resources/default_100_percent/common/easy_unlock_unlocked_hover.png b/ui/resources/default_100_percent/common/easy_unlock_unlocked_hover.png new file mode 100644 index 0000000..7b2ef72 --- /dev/null +++ b/ui/resources/default_100_percent/common/easy_unlock_unlocked_hover.png Binary files differ
diff --git a/ui/resources/default_100_percent/common/easy_unlock_unlocked_pressed.png b/ui/resources/default_100_percent/common/easy_unlock_unlocked_pressed.png new file mode 100644 index 0000000..7f8e3e2 --- /dev/null +++ b/ui/resources/default_100_percent/common/easy_unlock_unlocked_pressed.png Binary files differ
diff --git a/ui/resources/default_200_percent/common/easy_unlock_hardlocked_hover.png b/ui/resources/default_200_percent/common/easy_unlock_hardlocked_hover.png new file mode 100644 index 0000000..90fb3b2 --- /dev/null +++ b/ui/resources/default_200_percent/common/easy_unlock_hardlocked_hover.png Binary files differ
diff --git a/ui/resources/default_200_percent/common/easy_unlock_hardlocked_pressed.png b/ui/resources/default_200_percent/common/easy_unlock_hardlocked_pressed.png new file mode 100644 index 0000000..e730f2a --- /dev/null +++ b/ui/resources/default_200_percent/common/easy_unlock_hardlocked_pressed.png Binary files differ
diff --git a/ui/resources/default_200_percent/common/easy_unlock_locked_hover.png b/ui/resources/default_200_percent/common/easy_unlock_locked_hover.png new file mode 100644 index 0000000..e7ee8e9 --- /dev/null +++ b/ui/resources/default_200_percent/common/easy_unlock_locked_hover.png Binary files differ
diff --git a/ui/resources/default_200_percent/common/easy_unlock_locked_pressed.png b/ui/resources/default_200_percent/common/easy_unlock_locked_pressed.png new file mode 100644 index 0000000..2eebf9b --- /dev/null +++ b/ui/resources/default_200_percent/common/easy_unlock_locked_pressed.png Binary files differ
diff --git a/ui/resources/default_200_percent/common/easy_unlock_unlocked_hover.png b/ui/resources/default_200_percent/common/easy_unlock_unlocked_hover.png new file mode 100644 index 0000000..01d3f97 --- /dev/null +++ b/ui/resources/default_200_percent/common/easy_unlock_unlocked_hover.png Binary files differ
diff --git a/ui/resources/default_200_percent/common/easy_unlock_unlocked_pressed.png b/ui/resources/default_200_percent/common/easy_unlock_unlocked_pressed.png new file mode 100644 index 0000000..b7e1b52 --- /dev/null +++ b/ui/resources/default_200_percent/common/easy_unlock_unlocked_pressed.png Binary files differ
diff --git a/ui/resources/ui_resources.grd b/ui/resources/ui_resources.grd index 18394f4..1bae8be 100644 --- a/ui/resources/ui_resources.grd +++ b/ui/resources/ui_resources.grd
@@ -260,9 +260,15 @@ <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_32" file="common/default_favicon_32.png" /> <structure type="chrome_scaled_image" name="IDR_DEFAULT_FAVICON_64" file="common/default_favicon_64.png" /> <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_HARDLOCKED" file="common/easy_unlock_hardlocked.png" /> + <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_HARDLOCKED_HOVER" file="common/easy_unlock_hardlocked_hover.png" /> + <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_HARDLOCKED_PRESSED" file="common/easy_unlock_hardlocked_pressed.png" /> <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED" file="common/easy_unlock_locked.png" /> + <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_HOVER" file="common/easy_unlock_locked_hover.png" /> + <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_LOCKED_PRESSED" file="common/easy_unlock_locked_pressed.png" /> <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_SPINNER" file="common/easy_unlock_spinner.png" /> <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED" file="common/easy_unlock_unlocked.png" /> + <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED_HOVER" file="common/easy_unlock_unlocked_hover.png" /> + <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED_PRESSED" file="common/easy_unlock_unlocked_pressed.png" /> <structure type="chrome_scaled_image" name="IDR_FOLDER_CLOSED" file="common/folder_closed.png" /> <structure type="chrome_scaled_image" name="IDR_FOLDER_CLOSED_RTL" file="common/folder_closed_rtl.png" /> <if expr="toolkit_views">
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn index 93abc4d..1b1b6bf 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn
@@ -104,8 +104,6 @@ } } -if (false) { - static_library("test_support") { testonly = true sources = gypi_values.views_test_support_sources @@ -115,6 +113,7 @@ ] deps = [ "//base", + "//ipc:test_support", "//skia", "//testing/gtest", "//ui/aura", @@ -263,5 +262,3 @@ ] } } - -} # if (false)
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index 6c7c0cb..5dae218 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -936,6 +936,8 @@ void DesktopNativeWidgetAura::OnSizeConstraintsChanged() { content_window_->SetProperty(aura::client::kCanMaximizeKey, GetWidget()->widget_delegate()->CanMaximize()); + content_window_->SetProperty(aura::client::kCanMinimizeKey, + GetWidget()->widget_delegate()->CanMinimize()); content_window_->SetProperty(aura::client::kCanResizeKey, GetWidget()->widget_delegate()->CanResize()); desktop_window_tree_host_->SizeConstraintsChanged();
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc index 41a58c5..2889355 100644 --- a/ui/views/widget/native_widget_aura.cc +++ b/ui/views/widget/native_widget_aura.cc
@@ -700,6 +700,8 @@ void NativeWidgetAura::OnSizeConstraintsChanged() { window_->SetProperty(aura::client::kCanMaximizeKey, GetWidget()->widget_delegate()->CanMaximize()); + window_->SetProperty(aura::client::kCanMinimizeKey, + GetWidget()->widget_delegate()->CanMinimize()); window_->SetProperty(aura::client::kCanResizeKey, GetWidget()->widget_delegate()->CanResize()); }
diff --git a/ui/views/widget/native_widget_aura_unittest.cc b/ui/views/widget/native_widget_aura_unittest.cc index 282f8ee..be0041d 100644 --- a/ui/views/widget/native_widget_aura_unittest.cc +++ b/ui/views/widget/native_widget_aura_unittest.cc
@@ -193,6 +193,7 @@ // aura::LayoutManager: virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE { EXPECT_TRUE(child->GetProperty(aura::client::kCanMaximizeKey)); + EXPECT_TRUE(child->GetProperty(aura::client::kCanMinimizeKey)); EXPECT_TRUE(child->GetProperty(aura::client::kCanResizeKey)); added_ = true; } @@ -232,8 +233,8 @@ DISALLOW_COPY_AND_ASSIGN(PropertyTestWidgetDelegate); }; -// Verifies that the kCanMaximizeKey/kCanReizeKey have the correct -// value when added to the layout manager. +// Verifies that the kCanMaximizeKey/kCanMinimizeKey/kCanResizeKey have the +// correct value when added to the layout manager. TEST_F(NativeWidgetAuraTest, TestPropertiesWhenAddedToLayout) { root_window()->SetBounds(gfx::Rect(0, 0, 640, 480)); PropertyTestLayoutManager* layout_manager = new PropertyTestLayoutManager();
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc index ae24d04..744b9ea 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc
@@ -1549,8 +1549,15 @@ POINT cursor_pos = {0}; ::GetCursorPos(&cursor_pos); ::ScreenToClient(hwnd(), &cursor_pos); + // The code below exists for child windows like NPAPI plugins etc which need + // to be activated whenever we receive a WM_MOUSEACTIVATE message. Don't put + // transparent child windows in this bucket as they are not supposed to grab + // activation. + // TODO(ananta) + // Get rid of this code when we deprecate NPAPI plugins. HWND child = ::RealChildWindowFromPoint(hwnd(), cursor_pos); - if (::IsWindow(child) && child != hwnd() && ::IsWindowVisible(child)) + if (::IsWindow(child) && child != hwnd() && ::IsWindowVisible(child) && + !(::GetWindowLong(child, GWL_EXSTYLE) & WS_EX_TRANSPARENT)) PostProcessActivateMessage(WA_INACTIVE, false); // TODO(beng): resolve this with the GetWindowLong() check on the subsequent
diff --git a/ui/webui/resources/js/cr/ui/list.js b/ui/webui/resources/js/cr/ui/list.js index a628cb8..2f7180c 100644 --- a/ui/webui/resources/js/cr/ui/list.js +++ b/ui/webui/resources/js/cr/ui/list.js
@@ -521,9 +521,13 @@ * event. * @param {Event} e The blur event. * @private + * @suppress {checkTypes} + * TODO(dbeam): remove suppression when the extern + * Node.prototype.contains() will be fixed. */ handleElementBlur_: function(e) { - this.hasElementFocus = false; + if (!this.contains(e.relatedTarget)) + this.hasElementFocus = false; }, /**
diff --git a/ui/wm/BUILD.gn b/ui/wm/BUILD.gn index e01ee94..67248f4 100644 --- a/ui/wm/BUILD.gn +++ b/ui/wm/BUILD.gn
@@ -88,8 +88,6 @@ ] } -if (false) { - static_library("test_support") { testonly = true sources = [ @@ -142,5 +140,3 @@ "//ui/gl", ] } - -} # if (false)