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)