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)