Revert 185709
> Set up V8 bindings for extension/app APIs when they're first used, not on
> context creation. This should gives us a significant reduction in extension/app
> startup time and slightly better memory usage.
> 
> It also gives us better error messages, the chance to complete the 
> implementation of API features, and eventually the ability to expose select
> extension APIs (e.g. extension.sendMessage) to web pages.
> 
> BUG=163678,120070,55316,177163
> [email protected]
> 
> Review URL: https://chromiumcodereview.appspot.com/11571014

[email protected]

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@185815 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/renderer/extensions/api_definitions_natives.cc b/chrome/renderer/extensions/api_definitions_natives.cc
index 85f86b3..1bdd767 100644
--- a/chrome/renderer/extensions/api_definitions_natives.cc
+++ b/chrome/renderer/extensions/api_definitions_natives.cc
@@ -12,10 +12,8 @@
 
 namespace extensions {
 
-ApiDefinitionsNatives::ApiDefinitionsNatives(Dispatcher* dispatcher,
-                                             ChromeV8Context* context)
-    : ChromeV8Extension(dispatcher, context->v8_context()),
-      context_(context) {
+ApiDefinitionsNatives::ApiDefinitionsNatives(Dispatcher* dispatcher)
+    : ChromeV8Extension(dispatcher) {
   RouteFunction("GetExtensionAPIDefinition",
                 base::Bind(&ApiDefinitionsNatives::GetExtensionAPIDefinition,
                            base::Unretained(this)));
@@ -23,7 +21,10 @@
 
 v8::Handle<v8::Value> ApiDefinitionsNatives::GetExtensionAPIDefinition(
     const v8::Arguments& args) {
-  std::set<std::string> available_apis(context_->GetAvailableExtensionAPIs());
+  ChromeV8Context* v8_context = dispatcher()->v8_context_set().GetCurrent();
+  CHECK(v8_context);
+
+  std::set<std::string> available_apis(v8_context->GetAvailableExtensionAPIs());
   if (args.Length() == 0)
     return dispatcher()->v8_schema_registry()->GetSchemas(available_apis);
 
diff --git a/chrome/renderer/extensions/api_definitions_natives.h b/chrome/renderer/extensions/api_definitions_natives.h
index f8801e5..45b46b6 100644
--- a/chrome/renderer/extensions/api_definitions_natives.h
+++ b/chrome/renderer/extensions/api_definitions_natives.h
@@ -10,14 +10,12 @@
 
 #include "v8/include/v8.h"
 
-class ChromeV8Context;
-
 namespace extensions {
 
 // Native functions for JS to get access to the schemas for extension APIs.
 class ApiDefinitionsNatives : public ChromeV8Extension {
  public:
-  ApiDefinitionsNatives(Dispatcher* dispatcher, ChromeV8Context* context);
+  explicit ApiDefinitionsNatives(Dispatcher* dispatcher);
 
  private:
   // Returns the list of schemas that are available to the calling context
@@ -25,8 +23,6 @@
   // of all schemas that are available to the calling context.
   v8::Handle<v8::Value> GetExtensionAPIDefinition(const v8::Arguments& args);
 
-  ChromeV8Context* context_;
-
   DISALLOW_COPY_AND_ASSIGN(ApiDefinitionsNatives);
 };
 
diff --git a/chrome/renderer/extensions/app_bindings.cc b/chrome/renderer/extensions/app_bindings.cc
index 97472b9..aea0606 100644
--- a/chrome/renderer/extensions/app_bindings.cc
+++ b/chrome/renderer/extensions/app_bindings.cc
@@ -57,8 +57,9 @@
 
 }  // namespace
 
-AppBindings::AppBindings(Dispatcher* dispatcher, ChromeV8Context* context)
-    : ChromeV8Extension(dispatcher, context->v8_context()),
+AppBindings::AppBindings(Dispatcher* dispatcher,
+                         ChromeV8Context* context)
+    : ChromeV8Extension(dispatcher),
       ChromeV8ExtensionHandler(context) {
   RouteFunction("GetIsInstalled",
       base::Bind(&AppBindings::GetIsInstalled, base::Unretained(this)));
diff --git a/chrome/renderer/extensions/app_bindings.h b/chrome/renderer/extensions/app_bindings.h
index 99028e4..2fff69c 100644
--- a/chrome/renderer/extensions/app_bindings.h
+++ b/chrome/renderer/extensions/app_bindings.h
@@ -24,7 +24,8 @@
 class AppBindings : public ChromeV8Extension,
                     public ChromeV8ExtensionHandler {
  public:
-  AppBindings(Dispatcher* dispatcher, ChromeV8Context* context);
+  explicit AppBindings(Dispatcher* dispatcher,
+                       ChromeV8Context* context);
 
  private:
   // IPC::Listener
diff --git a/chrome/renderer/extensions/app_runtime_custom_bindings.cc b/chrome/renderer/extensions/app_runtime_custom_bindings.cc
index 60fbace9..0dd73c57 100644
--- a/chrome/renderer/extensions/app_runtime_custom_bindings.cc
+++ b/chrome/renderer/extensions/app_runtime_custom_bindings.cc
@@ -55,9 +55,8 @@
 
 namespace extensions {
 
-AppRuntimeCustomBindings::AppRuntimeCustomBindings(
-    Dispatcher* dispatcher,
-    v8::Handle<v8::Context> context) : ChromeV8Extension(dispatcher, context) {
+AppRuntimeCustomBindings::AppRuntimeCustomBindings()
+    : ChromeV8Extension(NULL) {
   RouteStaticFunction("DeserializeString", &DeserializeString);
   RouteStaticFunction("SerializeToString", &SerializeToString);
   RouteStaticFunction("CreateBlob", &CreateBlob);
diff --git a/chrome/renderer/extensions/app_runtime_custom_bindings.h b/chrome/renderer/extensions/app_runtime_custom_bindings.h
index c691300..5dd83467 100644
--- a/chrome/renderer/extensions/app_runtime_custom_bindings.h
+++ b/chrome/renderer/extensions/app_runtime_custom_bindings.h
@@ -12,8 +12,7 @@
 // The native component of custom bindings for the chrome.app.runtime API.
 class AppRuntimeCustomBindings : public ChromeV8Extension {
  public:
-  AppRuntimeCustomBindings(Dispatcher* dispatcher,
-                           v8::Handle<v8::Context> context);
+  AppRuntimeCustomBindings();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AppRuntimeCustomBindings);
diff --git a/chrome/renderer/extensions/app_window_custom_bindings.cc b/chrome/renderer/extensions/app_window_custom_bindings.cc
index 67388cf..29325d5 100644
--- a/chrome/renderer/extensions/app_window_custom_bindings.cc
+++ b/chrome/renderer/extensions/app_window_custom_bindings.cc
@@ -49,9 +49,8 @@
   Dispatcher* dispatcher_;
 };
 
-AppWindowCustomBindings::AppWindowCustomBindings(
-    Dispatcher* dispatcher,
-    v8::Handle<v8::Context> context) : ChromeV8Extension(dispatcher, context) {
+AppWindowCustomBindings::AppWindowCustomBindings(Dispatcher* dispatcher)
+    : ChromeV8Extension(dispatcher) {
   RouteFunction("GetView",
       base::Bind(&AppWindowCustomBindings::GetView,
                  base::Unretained(this)));
@@ -159,7 +158,7 @@
   // need to make sure the security origin is set up before returning the DOM
   // reference. A better way to do this would be to have the browser pass the
   // opener through so opener_id is set in RenderViewImpl's constructor.
-  content::RenderView* render_view = GetRenderView();
+  content::RenderView* render_view = GetCurrentRenderView();
   if (!render_view)
     return v8::Undefined();
   WebKit::WebFrame* opener = render_view->GetWebView()->mainFrame();
diff --git a/chrome/renderer/extensions/app_window_custom_bindings.h b/chrome/renderer/extensions/app_window_custom_bindings.h
index be84aa0..6333097 100644
--- a/chrome/renderer/extensions/app_window_custom_bindings.h
+++ b/chrome/renderer/extensions/app_window_custom_bindings.h
@@ -13,8 +13,7 @@
 // Implements custom bindings for the app.window API.
 class AppWindowCustomBindings : public ChromeV8Extension {
  public:
-  AppWindowCustomBindings(Dispatcher* dispatcher,
-                          v8::Handle<v8::Context> context);
+  explicit AppWindowCustomBindings(Dispatcher* dispatcher);
 
  private:
   v8::Handle<v8::Value> GetView(const v8::Arguments& args);
diff --git a/chrome/renderer/extensions/binding_generating_native_handler.cc b/chrome/renderer/extensions/binding_generating_native_handler.cc
deleted file mode 100644
index 22c45c58..0000000
--- a/chrome/renderer/extensions/binding_generating_native_handler.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/renderer/extensions/binding_generating_native_handler.h"
-
-#include "chrome/renderer/extensions/module_system.h"
-
-namespace extensions {
-
-BindingGeneratingNativeHandler::BindingGeneratingNativeHandler(
-    ModuleSystem* module_system,
-    const std::string& api_name,
-    const std::string& bind_to)
-    : module_system_(module_system),
-      api_name_(api_name),
-      bind_to_(bind_to) {
-}
-
-v8::Handle<v8::Object> BindingGeneratingNativeHandler::NewInstance() {
-  v8::HandleScope scope;
-  v8::Handle<v8::Object> binding = v8::Handle<v8::Object>::Cast(
-      v8::Handle<v8::Object>::Cast(module_system_->Require(
-          "binding"))->Get(v8::String::New("Binding")));
-  v8::Handle<v8::Function> create = v8::Handle<v8::Function>::Cast(
-      binding->Get(v8::String::New("create")));
-  v8::Handle<v8::Value> argv[] = { v8::String::New(api_name_.c_str()) };
-  v8::Handle<v8::Object> binding_instance = v8::Handle<v8::Object>::Cast(
-      create->Call(binding, 1, argv));
-  v8::Handle<v8::Function> generate = v8::Handle<v8::Function>::Cast(
-      binding_instance->Get(v8::String::New("generate")));
-  v8::Handle<v8::Value> compiled_schema =
-      generate->Call(binding_instance, 0, NULL);
-  v8::Handle<v8::Object> object = v8::Object::New();
-  object->Set(v8::String::New(bind_to_.c_str()), compiled_schema);
-  return scope.Close(object);
-}
-
-} // extensions
diff --git a/chrome/renderer/extensions/binding_generating_native_handler.h b/chrome/renderer/extensions/binding_generating_native_handler.h
deleted file mode 100644
index 9fefb30..0000000
--- a/chrome/renderer/extensions/binding_generating_native_handler.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_RENDERER_EXTENSIONS_BINDING_GENERATING_NATIVE_HANDLER_H_
-#define CHROME_RENDERER_EXTENSIONS_BINDING_GENERATING_NATIVE_HANDLER_H_
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "chrome/renderer/extensions/native_handler.h"
-
-namespace extensions {
-
-class ModuleSystem;
-
-// Generates API bindings based on the JSON/IDL schemas. This is done by
-// creating a |Binding| (from binding.js) for the schema and generating the
-// bindings from that.
-class BindingGeneratingNativeHandler : public NativeHandler {
- public:
-  // Generates binding for |api_name|, and sets the |bind_to| property on the
-  // Object returned by |NewInstance| to the generated binding.
-  BindingGeneratingNativeHandler(ModuleSystem* module_system,
-                                 const std::string& api_name,
-                                 const std::string& bind_to);
-
-  virtual v8::Handle<v8::Object> NewInstance() OVERRIDE;
-
- private:
-  ModuleSystem* module_system_;
-  std::string api_name_;
-  std::string bind_to_;
-};
-
-} // extensions
-
-#endif  // CHROME_RENDERER_EXTENSIONS_BINDING_GENERATING_NATIVE_HANDLER_H_
diff --git a/chrome/renderer/extensions/chrome_v8_context.cc b/chrome/renderer/extensions/chrome_v8_context.cc
index 3d326ed..2abc772 100644
--- a/chrome/renderer/extensions/chrome_v8_context.cc
+++ b/chrome/renderer/extensions/chrome_v8_context.cc
@@ -12,7 +12,6 @@
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_set.h"
 #include "chrome/renderer/extensions/chrome_v8_extension.h"
-#include "chrome/renderer/extensions/module_system.h"
 #include "chrome/renderer/extensions/user_script_slave.h"
 #include "content/public/renderer/render_view.h"
 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
@@ -24,10 +23,6 @@
 namespace {
 
 const char kChromeHidden[] = "chromeHidden";
-const char kUnavailableMessage[] = "You do not have permission to access this "
-                                   "API. Ensure that the required permission "
-                                   "or manifest property is included in your "
-                                   "manifest.json.";
 
 const char kValidateCallbacks[] = "validateCallbacks";
 const char kValidateAPI[] = "validateAPI";
@@ -42,8 +37,7 @@
                                                    v8_context)),
       web_frame_(web_frame),
       extension_(extension),
-      context_type_(context_type),
-      available_extension_apis_initialized_(false) {
+      context_type_(context_type) {
   VLOG(1) << "Created context:\n"
           << "  extension id: " << GetExtensionID() << "\n"
           << "  frame:        " << web_frame_ << "\n"
@@ -56,12 +50,6 @@
   v8_context_.Dispose(v8_context_->GetIsolate());
 }
 
-void ChromeV8Context::Invalidate() {
-  if (module_system_)
-    module_system_->Invalidate();
-  web_frame_ = NULL;
-}
-
 std::string ChromeV8Context::GetExtensionID() {
   return extension_ ? extension_->id() : "";
 }
@@ -78,11 +66,11 @@
     global->SetHiddenValue(v8::String::New(kChromeHidden), hidden);
 
     if (DCHECK_IS_ON()) {
-      // Tell bindings.js to validate callbacks and events against their schema
-      // definitions.
+      // Tell schema_generated_bindings.js to validate callbacks and events
+      // against their schema definitions.
       v8::Local<v8::Object>::Cast(hidden)->Set(
           v8::String::New(kValidateCallbacks), v8::True());
-      // Tell bindings.js to validate API for ambiguity.
+      // Tell schema_generated_bindings.js to validate API for ambiguity.
       v8::Local<v8::Object>::Cast(hidden)->Set(
           v8::String::New(kValidateAPI), v8::True());
     }
@@ -111,9 +99,9 @@
     v8::Handle<v8::Value>* result) const {
   v8::Context::Scope context_scope(v8_context_);
 
-  // ChromeV8ContextSet calls Invalidate() and then schedules a task to delete
-  // this object. This check prevents a race from attempting to execute script
-  // on a NULL web_frame_.
+  // ChromeV8ContextSet calls clear_web_frame() and then schedules a task to
+  // delete this object. This check prevents a race from attempting to execute
+  // script on a NULL web_frame_.
   if (!web_frame_)
     return false;
 
@@ -153,28 +141,15 @@
 }
 
 const std::set<std::string>& ChromeV8Context::GetAvailableExtensionAPIs() {
-  if (!available_extension_apis_initialized_) {
+  if (!available_extension_apis_.get()) {
     available_extension_apis_ =
         ExtensionAPI::GetSharedInstance()->GetAPIsForContext(
             context_type_,
             extension_,
-            UserScriptSlave::GetDataSourceURLForFrame(web_frame_));
-    available_extension_apis_initialized_ = true;
+            UserScriptSlave::GetDataSourceURLForFrame(
+                web_frame_)).Pass();
   }
-  return available_extension_apis_;
-}
-
-Feature::Availability ChromeV8Context::GetAvailability(
-    const std::string& api_name) {
-  const std::set<std::string>& available_apis = GetAvailableExtensionAPIs();
-
-  // TODO(cduvall/kalman): Switch to ExtensionAPI::IsAvailable() once Features
-  // are complete.
-  if (available_apis.find(api_name) != available_apis.end())
-    return Feature::CreateAvailability(Feature::IS_AVAILABLE, "");
-
-  return Feature::CreateAvailability(Feature::INVALID_CONTEXT,
-                                     kUnavailableMessage);
+  return *(available_extension_apis_.get());
 }
 
 void ChromeV8Context::DispatchOnLoadEvent(bool is_incognito_process,
diff --git a/chrome/renderer/extensions/chrome_v8_context.h b/chrome/renderer/extensions/chrome_v8_context.h
index e170f5c..8c71c04 100644
--- a/chrome/renderer/extensions/chrome_v8_context.h
+++ b/chrome/renderer/extensions/chrome_v8_context.h
@@ -36,10 +36,6 @@
                   Feature::Context context_type);
   ~ChromeV8Context();
 
-  // Clears the WebFrame for this contexts and invalidates the associated
-  // ModuleSystem.
-  void Invalidate();
-
   v8::Handle<v8::Context> v8_context() const {
     return v8_context_;
   }
@@ -51,6 +47,9 @@
   WebKit::WebFrame* web_frame() const {
     return web_frame_;
   }
+  void clear_web_frame() {
+    web_frame_ = NULL;
+  }
 
   Feature::Context context_type() const {
     return context_type_;
@@ -101,8 +100,6 @@
   // APIs are available, returns an empty set.
   const std::set<std::string>& GetAvailableExtensionAPIs();
 
-  Feature::Availability GetAvailability(const std::string& api_name);
-
   // Returns a string description of the type of context this is.
   std::string GetContextTypeDescription();
 
@@ -131,8 +128,7 @@
   scoped_ptr<ModuleSystem> module_system_;
 
   // The extension APIs available to this context.
-  std::set<std::string> available_extension_apis_;
-  bool available_extension_apis_initialized_;
+  scoped_ptr<std::set<std::string> > available_extension_apis_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeV8Context);
 };
diff --git a/chrome/renderer/extensions/chrome_v8_context_set.cc b/chrome/renderer/extensions/chrome_v8_context_set.cc
index 88f7f98..0be3ece0 100644
--- a/chrome/renderer/extensions/chrome_v8_context_set.cc
+++ b/chrome/renderer/extensions/chrome_v8_context_set.cc
@@ -75,7 +75,7 @@
 
 void ChromeV8ContextSet::Remove(ChromeV8Context* context) {
   if (contexts_.erase(context)) {
-    context->Invalidate();
+    context->clear_web_frame();
     MessageLoop::current()->DeleteSoon(FROM_HERE, context);
   }
 }
diff --git a/chrome/renderer/extensions/chrome_v8_extension.cc b/chrome/renderer/extensions/chrome_v8_extension.cc
index 4d2a048..a5cbf03e 100644
--- a/chrome/renderer/extensions/chrome_v8_extension.cc
+++ b/chrome/renderer/extensions/chrome_v8_extension.cc
@@ -25,33 +25,61 @@
 
 namespace extensions {
 
-ChromeV8Extension::ChromeV8Extension(Dispatcher* dispatcher)
-    : ObjectBackedNativeHandler(v8::Context::GetCurrent()),
-      dispatcher_(dispatcher) {
+namespace {
+
+static base::LazyInstance<ChromeV8Extension::InstanceSet> g_instances =
+    LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+// static
+content::RenderView* ChromeV8Extension::GetCurrentRenderView() {
+  WebFrame* webframe = WebFrame::frameForCurrentContext();
+  DCHECK(webframe) << "RetrieveCurrentFrame called when not in a V8 context.";
+  if (!webframe)
+    return NULL;
+
+  WebView* webview = webframe->view();
+  if (!webview)
+    return NULL;  // can happen during closing
+
+  content::RenderView* renderview = content::RenderView::FromWebView(webview);
+  DCHECK(renderview) << "Encountered a WebView without a WebViewDelegate";
+  return renderview;
 }
 
-ChromeV8Extension::ChromeV8Extension(Dispatcher* dispatcher,
-                                     v8::Handle<v8::Context> context)
-    : ObjectBackedNativeHandler(context),
+ChromeV8Extension::ChromeV8Extension(Dispatcher* dispatcher)
+    // TODO(svenpanne) It would be nice to remove the GetCurrent() call and use
+    // an additional constructor parameter instead, but this would involve too
+    // many changes for now.
+    : NativeHandler(v8::Isolate::GetCurrent()),
       dispatcher_(dispatcher) {
+  g_instances.Get().insert(this);
 }
 
 ChromeV8Extension::~ChromeV8Extension() {
+  g_instances.Get().erase(this);
 }
 
-ChromeV8Context* ChromeV8Extension::GetContext() {
-  CHECK(dispatcher_);
-  return dispatcher_->v8_context_set().GetByV8Context(v8_context());
+// static
+const ChromeV8Extension::InstanceSet& ChromeV8Extension::GetAll() {
+  return g_instances.Get();
 }
 
-content::RenderView* ChromeV8Extension::GetRenderView() {
-  ChromeV8Context* context = GetContext();
-  return context ? context->GetRenderView() : NULL;
-}
+const Extension* ChromeV8Extension::GetExtensionForCurrentRenderView() const {
+  content::RenderView* renderview = GetCurrentRenderView();
+  if (!renderview)
+    return NULL;  // this can happen as a tab is closing.
 
-const Extension* ChromeV8Extension::GetExtensionForRenderView() {
-  ChromeV8Context* context = GetContext();
-  return context ? context->extension() : NULL;
+  WebDocument document = renderview->GetWebView()->mainFrame()->document();
+  GURL url = document.url();
+  const ExtensionSet* extensions = dispatcher_->extensions();
+  if (!extensions->ExtensionBindingsAllowed(
+      ExtensionURLInfo(document.securityOrigin(), url)))
+    return NULL;
+
+  return extensions->GetExtensionOrAppByURL(
+      ExtensionURLInfo(document.securityOrigin(), url));
 }
 
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/chrome_v8_extension.h b/chrome/renderer/extensions/chrome_v8_extension.h
index 28251e9..e12af64 100644
--- a/chrome/renderer/extensions/chrome_v8_extension.h
+++ b/chrome/renderer/extensions/chrome_v8_extension.h
@@ -9,7 +9,7 @@
 #include "base/memory/linked_ptr.h"
 #include "base/string_piece.h"
 #include "chrome/renderer/extensions/chrome_v8_extension_handler.h"
-#include "chrome/renderer/extensions/object_backed_native_handler.h"
+#include "chrome/renderer/extensions/native_handler.h"
 #include "v8/include/v8.h"
 
 #include <map>
@@ -26,30 +26,19 @@
 class Dispatcher;
 class Extension;
 
-// DEPRECATED.
-//
 // This is a base class for chrome extension bindings.  Common features that
 // are shared by different modules go here.
-//
-// TODO(kalman): Delete this class entirely, it has no value anymore.
-//               Custom bindings should extend ObjectBackedNativeHandler.
-class ChromeV8Extension : public ObjectBackedNativeHandler {
+// TODO(koz): Rename this to ExtensionNativeModule.
+class ChromeV8Extension : public NativeHandler {
  public:
-  // EXTRA DEPRECATED. DO NOT USE THIS CONSTRUCTOR. It doesn't work in subtle
-  // cases.
-  explicit ChromeV8Extension(Dispatcher* dispatcher);
+  typedef std::set<ChromeV8Extension*> InstanceSet;
+  static const InstanceSet& GetAll();
 
-  ChromeV8Extension(Dispatcher* dispatcher, v8::Handle<v8::Context> context);
+  explicit ChromeV8Extension(Dispatcher* dispatcher);
   virtual ~ChromeV8Extension();
 
   Dispatcher* dispatcher() { return dispatcher_; }
 
-  ChromeV8Context* GetContext();
-
-  // Shortcuts through to the context's render view and extension.
-  content::RenderView* GetRenderView();
-  const Extension* GetExtensionForRenderView();
-
  protected:
   template<class T>
   static T* GetFromArguments(const v8::Arguments& args) {
@@ -58,6 +47,17 @@
     return result;
   }
 
+  // Gets the render view for the current v8 context.
+  static content::RenderView* GetCurrentRenderView();
+
+  // Note: do not call this function before or during the chromeHidden.onLoad
+  // event dispatch. The URL might not have been committed yet and might not
+  // be an extension URL.
+  const Extension* GetExtensionForCurrentRenderView() const;
+
+  // Returns the chromeHidden object for the current context.
+  static v8::Handle<v8::Value> GetChromeHidden(const v8::Arguments& args);
+
   Dispatcher* dispatcher_;
 
  private:
diff --git a/chrome/renderer/extensions/dispatcher.cc b/chrome/renderer/extensions/dispatcher.cc
index c085cbd..770a771 100644
--- a/chrome/renderer/extensions/dispatcher.cc
+++ b/chrome/renderer/extensions/dispatcher.cc
@@ -8,7 +8,6 @@
 #include "base/command_line.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/string_piece.h"
-#include "base/string_split.h"
 #include "chrome/common/child_process_logging.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/chrome_version_info.h"
@@ -16,7 +15,6 @@
 #include "chrome/common/extensions/background_info.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_messages.h"
-#include "chrome/common/extensions/features/feature.h"
 #include "chrome/common/extensions/manifest.h"
 #include "chrome/common/extensions/permissions/permission_set.h"
 #include "chrome/common/url_constants.h"
@@ -26,7 +24,6 @@
 #include "chrome/renderer/extensions/app_bindings.h"
 #include "chrome/renderer/extensions/app_runtime_custom_bindings.h"
 #include "chrome/renderer/extensions/app_window_custom_bindings.h"
-#include "chrome/renderer/extensions/binding_generating_native_handler.h"
 #include "chrome/renderer/extensions/chrome_v8_context.h"
 #include "chrome/renderer/extensions/chrome_v8_extension.h"
 #include "chrome/renderer/extensions/content_watcher.h"
@@ -42,7 +39,7 @@
 #include "chrome/renderer/extensions/media_galleries_custom_bindings.h"
 #include "chrome/renderer/extensions/miscellaneous_bindings.h"
 #include "chrome/renderer/extensions/module_system.h"
-#include "chrome/renderer/extensions/object_backed_native_handler.h"
+#include "chrome/renderer/extensions/native_handler.h"
 #include "chrome/renderer/extensions/page_actions_custom_bindings.h"
 #include "chrome/renderer/extensions/page_capture_custom_bindings.h"
 #include "chrome/renderer/extensions/request_sender.h"
@@ -93,97 +90,24 @@
 static const char kOnSuspendEvent[] = "runtime.onSuspend";
 static const char kOnSuspendCanceledEvent[] = "runtime.onSuspendCanceled";
 
-static v8::Handle<v8::Object> GetOrCreateChrome(
-    v8::Handle<v8::Context> context) {
-  v8::Handle<v8::String> chrome_string(v8::String::New("chrome"));
-  v8::Handle<v8::Object> global(context->Global());
-  v8::Handle<v8::Value> chrome(global->Get(chrome_string));
-  if (chrome.IsEmpty() || chrome->IsUndefined()) {
-    v8::Handle<v8::Object> chrome_object(v8::Object::New());
-    global->Set(chrome_string, chrome_object);
-    return chrome_object;
-  }
-  CHECK(chrome->IsObject());
-  return chrome->ToObject();
-}
-
-class SchemaRegistryNativeHandler : public ObjectBackedNativeHandler {
+class ChromeHiddenNativeHandler : public NativeHandler {
  public:
-  SchemaRegistryNativeHandler(V8SchemaRegistry* registry,
-                              v8::Handle<v8::Context> context)
-      : ObjectBackedNativeHandler(context),
-        registry_(registry) {
-    RouteFunction("GetSchema",
-        base::Bind(&SchemaRegistryNativeHandler::GetSchema,
-                   base::Unretained(this)));
-  }
-
- private:
-  v8::Handle<v8::Value> GetSchema(const v8::Arguments& args) {
-    return registry_->GetSchema(*v8::String::AsciiValue(args[0]));
-  }
-
-  V8SchemaRegistry* registry_;
-};
-
-class V8ContextNativeHandler : public ObjectBackedNativeHandler {
- public:
-  explicit V8ContextNativeHandler(ChromeV8Context* context)
-      : ObjectBackedNativeHandler(context->v8_context()),
-        context_(context) {
-    RouteFunction("GetAvailability",
-        base::Bind(&V8ContextNativeHandler::GetAvailability,
-                   base::Unretained(this)));
-  }
-
- private:
-  v8::Handle<v8::Value> GetAvailability(const v8::Arguments& args) {
-    CHECK_EQ(args.Length(), 1);
-    std::string api_name = *v8::String::AsciiValue(args[0]->ToString());
-    Feature::Availability availability = context_->GetAvailability(api_name);
-
-    v8::Handle<v8::Object> ret = v8::Object::New();
-    ret->Set(v8::String::New("is_available"),
-             v8::Boolean::New(availability.is_available()));
-    ret->Set(v8::String::New("message"),
-             v8::String::New(availability.message().c_str()));
-    return ret;
-  }
-
-  ChromeV8Context* context_;
-};
-
-class ChromeHiddenNativeHandler : public ObjectBackedNativeHandler {
- public:
-  explicit ChromeHiddenNativeHandler(v8::Handle<v8::Context> context)
-      : ObjectBackedNativeHandler(context) {
+  explicit ChromeHiddenNativeHandler(v8::Isolate* isolate)
+      : NativeHandler(isolate) {
     RouteFunction("GetChromeHidden",
         base::Bind(&ChromeHiddenNativeHandler::GetChromeHidden,
                    base::Unretained(this)));
   }
 
   v8::Handle<v8::Value> GetChromeHidden(const v8::Arguments& args) {
-    return ChromeV8Context::GetOrCreateChromeHidden(v8_context());
+    return ChromeV8Context::GetOrCreateChromeHidden(v8::Context::GetCurrent());
   }
 };
 
-class ChromeNativeHandler : public ObjectBackedNativeHandler {
+class PrintNativeHandler : public NativeHandler {
  public:
-  explicit ChromeNativeHandler(v8::Handle<v8::Context> context)
-      : ObjectBackedNativeHandler(context) {
-    RouteFunction("GetChrome",
-        base::Bind(&ChromeNativeHandler::GetChrome, base::Unretained(this)));
-  }
-
-  v8::Handle<v8::Value> GetChrome(const v8::Arguments& args) {
-    return GetOrCreateChrome(v8_context());
-  }
-};
-
-class PrintNativeHandler : public ObjectBackedNativeHandler {
- public:
-  explicit PrintNativeHandler(v8::Handle<v8::Context> context)
-      : ObjectBackedNativeHandler(context) {
+  explicit PrintNativeHandler(v8::Isolate* isolate)
+      : NativeHandler(isolate) {
     RouteFunction("Print",
         base::Bind(&PrintNativeHandler::Print,
                    base::Unretained(this)));
@@ -215,8 +139,7 @@
   }
 
   v8::Handle<v8::Value> IncrementKeepaliveCount(const v8::Arguments& args) {
-    ChromeV8Context* context =
-        dispatcher()->v8_context_set().GetByV8Context(v8_context());
+    ChromeV8Context* context = dispatcher()->v8_context_set().GetCurrent();
     if (!context)
       return v8::Undefined();
     RenderView* render_view = context->GetRenderView();
@@ -228,8 +151,7 @@
   }
 
   v8::Handle<v8::Value> DecrementKeepaliveCount(const v8::Arguments& args) {
-    ChromeV8Context* context =
-        dispatcher()->v8_context_set().GetByV8Context(v8_context());
+    ChromeV8Context* context = dispatcher()->v8_context_set().GetCurrent();
     if (!context)
       return v8::Undefined();
     RenderView* render_view = context->GetRenderView();
@@ -317,12 +239,13 @@
   bool send_request_disabled_;
 };
 
-class LoggingNativeHandler : public ObjectBackedNativeHandler {
+class LoggingNativeHandler : public NativeHandler {
  public:
-  explicit LoggingNativeHandler(v8::Handle<v8::Context> context)
-      : ObjectBackedNativeHandler(context) {
+  explicit LoggingNativeHandler(v8::Isolate* isolate)
+      : NativeHandler(isolate) {
     RouteFunction("DCHECK",
-        base::Bind(&LoggingNativeHandler::Dcheck, base::Unretained(this)));
+        base::Bind(&LoggingNativeHandler::Dcheck,
+                   base::Unretained(this)));
   }
 
   v8::Handle<v8::Value> Dcheck(const v8::Arguments& args) {
@@ -348,6 +271,7 @@
       }
     }
     DCHECK(check_value) << error_message;
+    LOG(WARNING) << error_message;
     return v8::Undefined();
   }
 
@@ -379,6 +303,20 @@
                               "chromeHiddenWebstore");
 }
 
+static v8::Handle<v8::Object> GetOrCreateChrome(
+    v8::Handle<v8::Context> context) {
+  v8::Handle<v8::String> chrome_string(v8::String::New("chrome"));
+  v8::Handle<v8::Object> global(context->Global());
+  v8::Handle<v8::Value> chrome(global->Get(chrome_string));
+  if (chrome.IsEmpty() || chrome->IsUndefined()) {
+    v8::Handle<v8::Object> chrome_object(v8::Object::New());
+    global->Set(chrome_string, chrome_object);
+    return chrome_object;
+  }
+  CHECK(chrome->IsObject());
+  return chrome->ToObject();
+}
+
 }  // namespace
 
 Dispatcher::Dispatcher()
@@ -399,7 +337,7 @@
   }
 
   user_script_slave_.reset(new UserScriptSlave(&extensions_));
-  request_sender_.reset(new RequestSender(this));
+  request_sender_.reset(new RequestSender(this, &v8_context_set_));
   PopulateSourceMap();
   PopulateLazyBindingsMap();
 }
@@ -623,128 +561,58 @@
   return true;
 }
 
-v8::Handle<v8::Object> Dispatcher::GetOrCreateObject(
-    v8::Handle<v8::Object> object,
-    const std::string& field) {
-  v8::HandleScope handle_scope;
-  v8::Handle<v8::String> key = v8::String::New(field.c_str());
-  // This little dance is for APIs that may be unavailable but have available
-  // children. For example, chrome.app can be unavailable, while
-  // chrome.app.runtime is available. The lazy getter for chrome.app must be
-  // deleted, so that there isn't an error when accessing chrome.app.runtime.
-  if (object->Has(key)) {
-    v8::Handle<v8::Value> value = object->Get(key);
-    if (value->IsObject())
-      return handle_scope.Close(v8::Handle<v8::Object>::Cast(value));
-    else
-      object->Delete(key);
-  }
-
-  v8::Handle<v8::Object> new_object = v8::Object::New();
-  object->Set(key, new_object);
-  return handle_scope.Close(new_object);
-}
-
-void Dispatcher::RegisterSchemaGeneratedBindings(
-    ModuleSystem* module_system,
-    ChromeV8Context* context,
-    v8::Handle<v8::Context> v8_context) {
-  std::set<std::string> apis =
-      ExtensionAPI::GetSharedInstance()->GetAllAPINames();
-  for (std::set<std::string>::iterator it = apis.begin();
-       it != apis.end(); ++it) {
-    const std::string& api_name = *it;
-
-    std::vector<std::string> split;
-    base::SplitString(api_name, '.', &split);
-
-    v8::Handle<v8::Object> bind_object = GetOrCreateChrome(v8_context);
-    for (size_t i = 0; i < split.size() - 1; ++i)
-      bind_object = GetOrCreateObject(bind_object, split[i]);
-
-    if (lazy_bindings_map_.find(api_name) != lazy_bindings_map_.end()) {
-      InstallBindings(module_system, v8_context, api_name);
-    } else if (!source_map_.Contains(api_name)) {
-      module_system->RegisterNativeHandler(
-          api_name,
-          scoped_ptr<NativeHandler>(new BindingGeneratingNativeHandler(
-              module_system,
-              api_name,
-              "binding")));
-      module_system->SetNativeLazyField(bind_object,
-                                        split.back(),
-                                        api_name,
-                                        "binding");
-    } else {
-      module_system->SetLazyField(bind_object,
-                                  split.back(),
-                                  api_name,
-                                  "binding");
-    }
-  }
-}
-
 void Dispatcher::RegisterNativeHandlers(ModuleSystem* module_system,
                                         ChromeV8Context* context) {
   module_system->RegisterNativeHandler("event_bindings",
-      scoped_ptr<NativeHandler>(
-          EventBindings::Create(this, context->v8_context())));
+      scoped_ptr<NativeHandler>(EventBindings::Get(this)));
   module_system->RegisterNativeHandler("miscellaneous_bindings",
-      scoped_ptr<NativeHandler>(
-          MiscellaneousBindings::Get(this, context->v8_context())));
+      scoped_ptr<NativeHandler>(MiscellaneousBindings::Get(this)));
   module_system->RegisterNativeHandler("apiDefinitions",
-      scoped_ptr<NativeHandler>(new ApiDefinitionsNatives(this, context)));
+      scoped_ptr<NativeHandler>(new ApiDefinitionsNatives(this)));
   module_system->RegisterNativeHandler("sendRequest",
       scoped_ptr<NativeHandler>(
-          new SendRequestNatives(this, request_sender_.get(), context)));
+          new SendRequestNatives(this, request_sender_.get())));
   module_system->RegisterNativeHandler("setIcon",
       scoped_ptr<NativeHandler>(
-          new SetIconNatives(this, request_sender_.get(), context)));
+          new SetIconNatives(this, request_sender_.get())));
   module_system->RegisterNativeHandler("contentWatcherNative",
                                        content_watcher_->MakeNatives());
 
   // Natives used by multiple APIs.
   module_system->RegisterNativeHandler("file_system_natives",
-      scoped_ptr<NativeHandler>(new FileSystemNatives(context->v8_context())));
+      scoped_ptr<NativeHandler>(new FileSystemNatives()));
 
   // Custom bindings.
   module_system->RegisterNativeHandler("app",
       scoped_ptr<NativeHandler>(new AppBindings(this, context)));
   module_system->RegisterNativeHandler("app_runtime",
-      scoped_ptr<NativeHandler>(
-          new AppRuntimeCustomBindings(this, context->v8_context())));
+      scoped_ptr<NativeHandler>(new AppRuntimeCustomBindings()));
   module_system->RegisterNativeHandler("app_window",
-      scoped_ptr<NativeHandler>(
-          new AppWindowCustomBindings(this, context->v8_context())));
+      scoped_ptr<NativeHandler>(new AppWindowCustomBindings(this)));
   module_system->RegisterNativeHandler("context_menus",
       scoped_ptr<NativeHandler>(new ContextMenusCustomBindings()));
   module_system->RegisterNativeHandler("extension",
       scoped_ptr<NativeHandler>(
-          new ExtensionCustomBindings(this, context->v8_context())));
+          new ExtensionCustomBindings(this)));
   module_system->RegisterNativeHandler("sync_file_system",
       scoped_ptr<NativeHandler>(new SyncFileSystemCustomBindings()));
   module_system->RegisterNativeHandler("file_browser_handler",
-      scoped_ptr<NativeHandler>(new FileBrowserHandlerCustomBindings(
-          context->v8_context())));
+      scoped_ptr<NativeHandler>(new FileBrowserHandlerCustomBindings()));
   module_system->RegisterNativeHandler("file_browser_private",
-      scoped_ptr<NativeHandler>(new FileBrowserPrivateCustomBindings(
-          context->v8_context())));
+      scoped_ptr<NativeHandler>(new FileBrowserPrivateCustomBindings()));
   module_system->RegisterNativeHandler("i18n",
-      scoped_ptr<NativeHandler>(
-          new I18NCustomBindings(this, context->v8_context())));
+      scoped_ptr<NativeHandler>(new I18NCustomBindings()));
   module_system->RegisterNativeHandler("mediaGalleries",
       scoped_ptr<NativeHandler>(new MediaGalleriesCustomBindings()));
   module_system->RegisterNativeHandler("page_actions",
       scoped_ptr<NativeHandler>(
           new PageActionsCustomBindings(this)));
   module_system->RegisterNativeHandler("page_capture",
-      scoped_ptr<NativeHandler>(
-          new PageCaptureCustomBindings(this, context->v8_context())));
+      scoped_ptr<NativeHandler>(new PageCaptureCustomBindings()));
   module_system->RegisterNativeHandler("runtime",
-      scoped_ptr<NativeHandler>(new RuntimeCustomBindings(this, context)));
+      scoped_ptr<NativeHandler>(new RuntimeCustomBindings(context)));
   module_system->RegisterNativeHandler("tabs",
-      scoped_ptr<NativeHandler>(
-          new TabsCustomBindings(this, context->v8_context())));
+      scoped_ptr<NativeHandler>(new TabsCustomBindings()));
   module_system->RegisterNativeHandler("tts",
       scoped_ptr<NativeHandler>(new TTSCustomBindings()));
   module_system->RegisterNativeHandler("web_request",
@@ -757,9 +625,11 @@
   source_map_.RegisterSource("event_bindings", IDR_EVENT_BINDINGS_JS);
   source_map_.RegisterSource("miscellaneous_bindings",
       IDR_MISCELLANEOUS_BINDINGS_JS);
+  source_map_.RegisterSource("schema_generated_bindings",
+      IDR_SCHEMA_GENERATED_BINDINGS_JS);
   source_map_.RegisterSource("json", IDR_JSON_JS);
   source_map_.RegisterSource("json_schema", IDR_JSON_SCHEMA_JS);
-  source_map_.RegisterSource("test", IDR_TEST_CUSTOM_BINDINGS_JS);
+  source_map_.RegisterSource("apitest", IDR_EXTENSION_APITEST_JS);
 
   // Libraries.
   source_map_.RegisterSource("contentWatcher", IDR_CONTENT_WATCHER_JS);
@@ -828,16 +698,13 @@
   source_map_.RegisterSource("webRequestInternal",
                              IDR_WEB_REQUEST_INTERNAL_CUSTOM_BINDINGS_JS);
   source_map_.RegisterSource("webstore", IDR_WEBSTORE_CUSTOM_BINDINGS_JS);
-  source_map_.RegisterSource("binding", IDR_BINDING_JS);
 
   // Platform app sources that are not API-specific..
   source_map_.RegisterSource("tagWatcher", IDR_TAG_WATCHER_JS);
-  // Note: webView not webview so that this doesn't interfere with the
-  // chrome.webview API bindings.
-  source_map_.RegisterSource("webView", IDR_WEB_VIEW_JS);
-  source_map_.RegisterSource("webViewExperimental",
+  source_map_.RegisterSource("webview", IDR_WEB_VIEW_JS);
+  source_map_.RegisterSource("webview.experimental",
                              IDR_WEB_VIEW_EXPERIMENTAL_JS);
-  source_map_.RegisterSource("denyWebView", IDR_WEB_VIEW_DENY_JS);
+  source_map_.RegisterSource("denyWebview", IDR_WEB_VIEW_DENY_JS);
   source_map_.RegisterSource("platformApp", IDR_PLATFORM_APP_JS);
   source_map_.RegisterSource("injectAppTitlebar", IDR_INJECT_APP_TITLEBAR_JS);
 }
@@ -909,21 +776,15 @@
 
   RegisterNativeHandlers(module_system.get(), context);
 
-  module_system->RegisterNativeHandler("chrome",
-      scoped_ptr<NativeHandler>(new ChromeNativeHandler(v8_context)));
+  v8::Isolate* isolate = v8_context->GetIsolate();
   module_system->RegisterNativeHandler("chrome_hidden",
-      scoped_ptr<NativeHandler>(new ChromeHiddenNativeHandler(v8_context)));
+      scoped_ptr<NativeHandler>(new ChromeHiddenNativeHandler(isolate)));
   module_system->RegisterNativeHandler("print",
-      scoped_ptr<NativeHandler>(new PrintNativeHandler(v8_context)));
+      scoped_ptr<NativeHandler>(new PrintNativeHandler(isolate)));
   module_system->RegisterNativeHandler("lazy_background_page",
       scoped_ptr<NativeHandler>(new LazyBackgroundPageNativeHandler(this)));
   module_system->RegisterNativeHandler("logging",
-      scoped_ptr<NativeHandler>(new LoggingNativeHandler(v8_context)));
-  module_system->RegisterNativeHandler("schema_registry",
-      scoped_ptr<NativeHandler>(
-          new SchemaRegistryNativeHandler(v8_schema_registry(), v8_context)));
-  module_system->RegisterNativeHandler("v8_context",
-      scoped_ptr<NativeHandler>(new V8ContextNativeHandler(context)));
+      scoped_ptr<NativeHandler>(new LoggingNativeHandler(isolate)));
 
   int manifest_version = extension ? extension->manifest_version() : 1;
   bool send_request_disabled =
@@ -947,20 +808,25 @@
       InstallBindings(module_system.get(), v8_context, "app");
       InstallBindings(module_system.get(), v8_context, "webstore");
       break;
+
     case Feature::BLESSED_EXTENSION_CONTEXT:
     case Feature::UNBLESSED_EXTENSION_CONTEXT:
     case Feature::CONTENT_SCRIPT_CONTEXT: {
-      if (extension && !extension->is_platform_app())
-        module_system->Require("miscellaneous_bindings");
       module_system->Require("json");  // see paranoid comment in json.js
+      module_system->Require("miscellaneous_bindings");
+      module_system->Require("schema_generated_bindings");
+      module_system->Require("apitest");
 
       // TODO(kalman): move this code back out of the switch and execute it
       // regardless of |context_type|. ExtensionAPI knows how to return the
       // correct APIs, however, until it doesn't have a 2MB overhead we can't
       // load it in every process.
-      RegisterSchemaGeneratedBindings(module_system.get(),
-                                      context,
-                                      v8_context);
+      const std::set<std::string>& apis = context->GetAvailableExtensionAPIs();
+      for (std::set<std::string>::const_iterator i = apis.begin();
+           i != apis.end(); ++i) {
+        InstallBindings(module_system.get(), v8_context, *i);
+      }
+
       break;
     }
   }
@@ -970,14 +836,11 @@
     module_system->Require("platformApp");
 
   if (context_type == Feature::BLESSED_EXTENSION_CONTEXT) {
-    // Note: setting up the WebView class here, not the chrome.webview API.
-    // The API will be automatically set up when first used.
-    if (extension->HasAPIPermission(APIPermission::kWebView)) {
-      module_system->Require("webView");
-      if (Feature::GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV)
-        module_system->Require("webViewExperimental");
-    } else {
-      module_system->Require("denyWebView");
+    bool has_permission = extension->HasAPIPermission(APIPermission::kWebView);
+    module_system->Require(has_permission ? "webview" : "denyWebview");
+    if (has_permission &&
+        Feature::GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV) {
+      module_system->Require("webview.experimental");
     }
   }
 
@@ -1019,8 +882,6 @@
     return;
 
   context->DispatchOnUnloadEvent();
-  // TODO(kalman): add an invalidation observer interface to ChromeV8Context.
-  request_sender_->InvalidateContext(context);
 
   v8_context_set_.Remove(context);
   VLOG(1) << "Num tracked contexts: " << v8_context_set_.size();
@@ -1257,8 +1118,9 @@
   request_sender_->HandleResponse(request_id, success, response, error);
 }
 
-bool Dispatcher::CheckContextAccessToExtensionAPI(
-    const std::string& function_name, ChromeV8Context* context) const {
+bool Dispatcher::CheckCurrentContextAccessToExtensionAPI(
+    const std::string& function_name) const {
+  ChromeV8Context* context = v8_context_set().GetCurrent();
   if (!context) {
     DLOG(ERROR) << "Not in a v8::Context";
     return false;
diff --git a/chrome/renderer/extensions/dispatcher.h b/chrome/renderer/extensions/dispatcher.h
index c6c0e7a..a3a5275 100644
--- a/chrome/renderer/extensions/dispatcher.h
+++ b/chrome/renderer/extensions/dispatcher.h
@@ -120,8 +120,8 @@
   // Checks that the current context contains an extension that has permission
   // to execute the specified function. If it does not, a v8 exception is thrown
   // and the method returns false. Otherwise returns true.
-  bool CheckContextAccessToExtensionAPI(
-      const std::string& function_name, ChromeV8Context* context) const;
+  bool CheckCurrentContextAccessToExtensionAPI(
+      const std::string& function_name) const;
 
  private:
   friend class RenderViewTest;
@@ -189,9 +189,6 @@
 
   void RegisterNativeHandlers(ModuleSystem* module_system,
                               ChromeV8Context* context);
-  void RegisterSchemaGeneratedBindings(ModuleSystem* module_system,
-                                       ChromeV8Context* context,
-                                       v8::Handle<v8::Context> v8_context);
 
   // Inserts static source code into |source_map_|.
   void PopulateSourceMap();
@@ -214,11 +211,6 @@
                                              int extension_group,
                                              const ExtensionURLInfo& url_info);
 
-  // Gets |field| from |object| or creates it as an empty object if it doesn't
-  // exist.
-  v8::Handle<v8::Object> GetOrCreateObject(v8::Handle<v8::Object> object,
-                                           const std::string& field);
-
   // True if this renderer is running extensions.
   bool is_extension_process_;
 
diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc
index bbf16f5e..12435a53e 100644
--- a/chrome/renderer/extensions/event_bindings.cc
+++ b/chrome/renderer/extensions/event_bindings.cc
@@ -72,9 +72,8 @@
 // TODO(koz): Merge this into EventBindings.
 class ExtensionImpl : public ChromeV8Extension {
  public:
-  explicit ExtensionImpl(Dispatcher* dispatcher,
-                         v8::Handle<v8::Context> v8_context)
-      : ChromeV8Extension(dispatcher, v8_context) {
+  explicit ExtensionImpl(Dispatcher* dispatcher)
+      : ChromeV8Extension(dispatcher) {
     RouteStaticFunction("AttachEvent", &AttachEvent);
     RouteStaticFunction("DetachEvent", &DetachEvent);
     RouteStaticFunction("AttachFilteredEvent", &AttachFilteredEvent);
@@ -95,10 +94,10 @@
       std::string event_name = *v8::String::AsciiValue(args[0]->ToString());
       Dispatcher* dispatcher = self->dispatcher();
       const ChromeV8ContextSet& context_set = dispatcher->v8_context_set();
-      ChromeV8Context* context = context_set.GetByV8Context(self->v8_context());
+      ChromeV8Context* context = context_set.GetCurrent();
       CHECK(context);
 
-      if (!dispatcher->CheckContextAccessToExtensionAPI(event_name, context))
+      if (!dispatcher->CheckCurrentContextAccessToExtensionAPI(event_name))
         return v8::Undefined();
 
       std::string extension_id = context->GetExtensionID();
@@ -112,7 +111,7 @@
       // This is called the first time the page has added a listener. Since
       // the background page is the only lazy page, we know this is the first
       // time this listener has been registered.
-      if (IsLazyBackgroundPage(self->GetRenderView(), context->extension())) {
+      if (IsLazyBackgroundPage(context->extension())) {
         content::RenderThread::Get()->Send(
             new ExtensionHostMsg_AddLazyListener(extension_id, event_name));
       }
@@ -132,7 +131,7 @@
       ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args);
       Dispatcher* dispatcher = self->dispatcher();
       const ChromeV8ContextSet& context_set = dispatcher->v8_context_set();
-      ChromeV8Context* context = context_set.GetByV8Context(self->v8_context());
+      ChromeV8Context* context = context_set.GetCurrent();
       if (!context)
         return v8::Undefined();
 
@@ -149,8 +148,7 @@
       // removed. If the context is the background page, and it removes the
       // last listener manually, then we assume that it is no longer interested
       // in being awakened for this event.
-      if (is_manual && IsLazyBackgroundPage(self->GetRenderView(),
-                                            context->extension())) {
+      if (is_manual && IsLazyBackgroundPage(context->extension())) {
         content::RenderThread::Get()->Send(
             new ExtensionHostMsg_RemoveLazyListener(extension_id, event_name));
       }
@@ -171,14 +169,14 @@
     ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args);
     Dispatcher* dispatcher = self->dispatcher();
     const ChromeV8ContextSet& context_set = dispatcher->v8_context_set();
-    ChromeV8Context* context = context_set.GetByV8Context(self->v8_context());
+    ChromeV8Context* context = context_set.GetCurrent();
     DCHECK(context);
     if (!context)
       return v8::Integer::New(-1);
 
     std::string event_name = *v8::String::AsciiValue(args[0]);
     // This method throws an exception if it returns false.
-    if (!dispatcher->CheckContextAccessToExtensionAPI(event_name, context))
+    if (!dispatcher->CheckCurrentContextAccessToExtensionAPI(event_name))
       return v8::Undefined();
 
     std::string extension_id = context->GetExtensionID();
@@ -190,8 +188,8 @@
         content::V8ValueConverter::create());
 
     base::DictionaryValue* filter_dict = NULL;
-    base::Value* filter_value =
-        converter->FromV8Value(args[1]->ToObject(), context->v8_context());
+    base::Value* filter_value = converter->FromV8Value(args[1]->ToObject(),
+        v8::Context::GetCurrent());
     if (!filter_value)
       return v8::Integer::New(-1);
     if (!filter_value->GetAsDictionary(&filter_dict)) {
@@ -206,8 +204,7 @@
 
     // Only send IPCs the first time a filter gets added.
     if (AddFilter(event_name, extension_id, filter.get())) {
-      bool lazy = IsLazyBackgroundPage(self->GetRenderView(),
-                                       context->extension());
+      bool lazy = IsLazyBackgroundPage(context->extension());
       content::RenderThread::Get()->Send(
           new ExtensionHostMsg_AddFilteredListener(extension_id, event_name,
                                                    *filter, lazy));
@@ -256,7 +253,7 @@
     ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args);
     Dispatcher* dispatcher = self->dispatcher();
     const ChromeV8ContextSet& context_set = dispatcher->v8_context_set();
-    ChromeV8Context* context = context_set.GetByV8Context(self->v8_context());
+    ChromeV8Context* context = context_set.GetCurrent();
     if (!context)
       return v8::Undefined();
 
@@ -273,8 +270,7 @@
 
     // Only send IPCs the last time a filter gets removed.
     if (RemoveFilter(event_name, extension_id, event_matcher->value())) {
-      bool lazy = is_manual && IsLazyBackgroundPage(self->GetRenderView(),
-                                                    context->extension());
+      bool lazy = is_manual && IsLazyBackgroundPage(context->extension());
       content::RenderThread::Get()->Send(
           new ExtensionHostMsg_RemoveFilteredListener(extension_id, event_name,
                                                       *event_matcher->value(),
@@ -315,10 +311,11 @@
   }
 
  private:
-  static bool IsLazyBackgroundPage(content::RenderView* render_view,
-                                   const Extension* extension) {
+  static bool IsLazyBackgroundPage(const Extension* extension) {
+    content::RenderView* render_view = GetCurrentRenderView();
     if (!render_view)
       return false;
+
     ExtensionHelper* helper = ExtensionHelper::Get(render_view);
     return (extension && BackgroundInfo::HasLazyBackgroundPage(extension) &&
             helper->view_type() == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
@@ -334,9 +331,8 @@
 }  // namespace
 
 // static
-ChromeV8Extension* EventBindings::Create(Dispatcher* dispatcher,
-                                         v8::Handle<v8::Context> context) {
-  return new ExtensionImpl(dispatcher, context);
+ChromeV8Extension* EventBindings::Get(Dispatcher* dispatcher) {
+  return new ExtensionImpl(dispatcher);
 }
 
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h
index a263ad3..9051dae 100644
--- a/chrome/renderer/extensions/event_bindings.h
+++ b/chrome/renderer/extensions/event_bindings.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_
 #define CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_
 
-#include "v8/include/v8.h"
+namespace v8 {
+class Extension;
+}
 
 namespace extensions {
 class ChromeV8Extension;
@@ -15,8 +17,7 @@
 // This class deals with the javascript bindings related to Event objects.
 class EventBindings {
  public:
-  static ChromeV8Extension* Create(Dispatcher* dispatcher,
-                                   v8::Handle<v8::Context> context);
+  static ChromeV8Extension* Get(Dispatcher* dispatcher);
 };
 
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/event_unittest.cc b/chrome/renderer/extensions/event_unittest.cc
index 8258b42e..edd47c0 100644
--- a/chrome/renderer/extensions/event_unittest.cc
+++ b/chrome/renderer/extensions/event_unittest.cc
@@ -62,9 +62,6 @@
     OverrideNativeHandler("chrome_hidden",
         "var chromeHidden = {};"
         "exports.GetChromeHidden = function() { return chromeHidden; };");
-    OverrideNativeHandler("chrome",
-        "var chrome = {};"
-        "exports.GetChrome = function() { return chrome; };");
     OverrideNativeHandler("sendRequest",
         "exports.sendRequest = function() {};");
     OverrideNativeHandler("apiDefinitions",
@@ -82,10 +79,9 @@
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
       "var assert = requireNative('assert');"
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var eventBindings = requireNative('event_bindings');"
-      "var myEvent = new Event('named-event');"
+      "var myEvent = new event.Event('named-event');"
       "var cb1 = function() {};"
       "var cb2 = function() {};"
       "myEvent.addListener(cb1);"
@@ -101,11 +97,10 @@
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
       "var assert = requireNative('assert');"
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var eventBindings = requireNative('event_bindings');"
       "var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();"
-      "var myEvent = new Event('named-event');"
+      "var myEvent = new event.Event('named-event');"
       "var cb1 = function() {};"
       "var cb2 = function() {};"
       "myEvent.addListener(cb1);"
@@ -119,11 +114,10 @@
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
       "var assert = requireNative('assert');"
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var eventBindings = requireNative('event_bindings');"
       "var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();"
-      "var myEvent = new Event('named-event');"
+      "var myEvent = new event.Event('named-event');"
       "var cb1 = function() {};"
       "myEvent.addListener(cb1);"
       "myEvent.addListener(cb1);"
@@ -135,13 +129,12 @@
 TEST_F(EventUnittest, EventsThatSupportRulesMustHaveAName) {
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var eventOpts = {supportsRules: true};"
       "var assert = requireNative('assert');"
       "var caught = false;"
       "try {"
-      "  var myEvent = new Event(undefined, undefined, eventOpts);"
+      "  var myEvent = new event.Event(undefined, undefined, eventOpts);"
       "} catch (e) {"
       "  caught = true;"
       "}"
@@ -152,11 +145,10 @@
 TEST_F(EventUnittest, NamedEventDispatch) {
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();"
       "var assert = requireNative('assert');"
-      "var e = new Event('myevent');"
+      "var e = new event.Event('myevent');"
       "var called = false;"
       "e.addListener(function() { called = true; });"
       "chromeHidden.Event.dispatchEvent('myevent', []);"
@@ -167,10 +159,9 @@
 TEST_F(EventUnittest, AddListenerWithFiltersThrowsErrorByDefault) {
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var assert = requireNative('assert');"
-      "var e = new Event('myevent');"
+      "var e = new event.Event('myevent');"
       "var filter = [{"
       "  url: {hostSuffix: 'google.com'},"
       "}];"
@@ -187,12 +178,11 @@
 TEST_F(EventUnittest, FilteredEventsAttachment) {
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var assert = requireNative('assert');"
       "var bindings = requireNative('event_bindings');"
       "var eventOpts = {supportsListeners: true, supportsFilters: true};"
-      "var e = new Event('myevent', undefined, eventOpts);"
+      "var e = new event.Event('myevent', undefined, eventOpts);"
       "var cb = function() {};"
       "var filters = {url: [{hostSuffix: 'google.com'}]};"
       "e.addListener(cb, filters);"
@@ -205,12 +195,11 @@
 TEST_F(EventUnittest, DetachFilteredEvent) {
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var assert = requireNative('assert');"
       "var bindings = requireNative('event_bindings');"
       "var eventOpts = {supportsListeners: true, supportsFilters: true};"
-      "var e = new Event('myevent', undefined, eventOpts);"
+      "var e = new event.Event('myevent', undefined, eventOpts);"
       "var cb1 = function() {};"
       "var cb2 = function() {};"
       "var filters = {url: [{hostSuffix: 'google.com'}]};"
@@ -224,12 +213,11 @@
 TEST_F(EventUnittest, AttachAndRemoveSameFilteredEventListener) {
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var assert = requireNative('assert');"
       "var bindings = requireNative('event_bindings');"
       "var eventOpts = {supportsListeners: true, supportsFilters: true};"
-      "var e = new Event('myevent', undefined, eventOpts);"
+      "var e = new event.Event('myevent', undefined, eventOpts);"
       "var cb = function() {};"
       "var filters = {url: [{hostSuffix: 'google.com'}]};"
       "e.addListener(cb, filters);"
@@ -245,11 +233,10 @@
 TEST_F(EventUnittest, AddingFilterWithUrlFieldNotAListThrowsException) {
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var assert = requireNative('assert');"
       "var eventOpts = {supportsListeners: true, supportsFilters: true};"
-      "var e = new Event('myevent', undefined, eventOpts);"
+      "var e = new event.Event('myevent', undefined, eventOpts);"
       "var cb = function() {};"
       "var filters = {url: {hostSuffix: 'google.com'}};"
       "var caught = false;"
@@ -265,11 +252,10 @@
 TEST_F(EventUnittest, MaxListeners) {
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   RegisterModule("test",
-      "require('event');"
-      "var Event = requireNative('chrome').GetChrome().Event;"
+      "var event = require('event');"
       "var assert = requireNative('assert');"
       "var eventOpts = {supportsListeners: true, maxListeners: 1};"
-      "var e = new Event('myevent', undefined, eventOpts);"
+      "var e = new event.Event('myevent', undefined, eventOpts);"
       "var cb = function() {};"
       "var caught = false;"
       "try {"
diff --git a/chrome/renderer/extensions/extension_custom_bindings.cc b/chrome/renderer/extensions/extension_custom_bindings.cc
index 2c290e52..1cc5e099 100644
--- a/chrome/renderer/extensions/extension_custom_bindings.cc
+++ b/chrome/renderer/extensions/extension_custom_bindings.cc
@@ -26,10 +26,8 @@
 
 }  // namespace
 
-ExtensionCustomBindings::ExtensionCustomBindings(
-    Dispatcher* dispatcher,
-    v8::Handle<v8::Context> context)
-    : ChromeV8Extension(dispatcher, context) {
+ExtensionCustomBindings::ExtensionCustomBindings(Dispatcher* dispatcher)
+    : ChromeV8Extension(dispatcher) {
   RouteStaticFunction("GetExtensionViews", &GetExtensionViews);
 }
 
@@ -74,7 +72,7 @@
   ExtensionCustomBindings* v8_extension =
       GetFromArguments<ExtensionCustomBindings>(args);
   const Extension* extension =
-      v8_extension->GetExtensionForRenderView();
+      v8_extension->GetExtensionForCurrentRenderView();
   if (!extension)
     return v8::Undefined();
 
diff --git a/chrome/renderer/extensions/extension_custom_bindings.h b/chrome/renderer/extensions/extension_custom_bindings.h
index 5123a6c4..01d27df3 100644
--- a/chrome/renderer/extensions/extension_custom_bindings.h
+++ b/chrome/renderer/extensions/extension_custom_bindings.h
@@ -13,8 +13,7 @@
 // Implements custom bindings for the extension API.
 class ExtensionCustomBindings : public ChromeV8Extension {
  public:
-  explicit ExtensionCustomBindings(Dispatcher* dispatcher,
-                                   v8::Handle<v8::Context> context);
+  explicit ExtensionCustomBindings(Dispatcher* dispatcher);
 
  private:
   static v8::Handle<v8::Value> GetExtensionViews(const v8::Arguments& args);
diff --git a/chrome/renderer/extensions/extension_helper.cc b/chrome/renderer/extensions/extension_helper.cc
index c0e513d..c97c22a 100644
--- a/chrome/renderer/extensions/extension_helper.cc
+++ b/chrome/renderer/extensions/extension_helper.cc
@@ -509,7 +509,8 @@
     if (current_size > kMaxIconSize - actual_icon_size) {
       AddMessageToRootConsole(
           content::CONSOLE_MESSAGE_LEVEL_ERROR,
-          "Icons are too large. Maximum total size for app icons is 128 KB.");
+          ASCIIToUTF16("Icons are too large. "
+              "Maximum total size for app icons is 128 KB."));
       return;
     }
     actual_icon_size += current_size;
@@ -521,11 +522,6 @@
 }
 
 void ExtensionHelper::AddMessageToRootConsole(ConsoleMessageLevel level,
-                                              const std::string& message) {
-  AddMessageToRootConsole(level, ASCIIToUTF16(message));
-}
-
-void ExtensionHelper::AddMessageToRootConsole(ConsoleMessageLevel level,
                                               const string16& message) {
   if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
     WebConsoleMessage::Level target_level = WebConsoleMessage::LevelLog;
diff --git a/chrome/renderer/extensions/extension_helper.h b/chrome/renderer/extensions/extension_helper.h
index 350f4a037..8eb414f 100644
--- a/chrome/renderer/extensions/extension_helper.h
+++ b/chrome/renderer/extensions/extension_helper.h
@@ -64,10 +64,6 @@
   int browser_window_id() const { return browser_window_id_; }
   chrome::ViewType view_type() const { return view_type_; }
 
-  // Helper to add a logging message to the root frame's console.
-  void AddMessageToRootConsole(content::ConsoleMessageLevel level,
-                               const std::string& message);
-
  private:
   // RenderViewObserver implementation.
   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
@@ -115,7 +111,7 @@
   void DidDownloadApplicationIcon(webkit_glue::ImageResourceFetcher* fetcher,
                                   const SkBitmap& image);
 
-  // Helper to add a logging message to the root frame's console.
+  // Helper to add an logging message to the root frame's console.
   void AddMessageToRootConsole(content::ConsoleMessageLevel level,
                                const string16& message);
 
diff --git a/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc b/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc
index ad0238c..78800dbb 100644
--- a/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc
+++ b/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc
@@ -13,19 +13,9 @@
 #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h"
 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
 
-namespace extensions {
+namespace {
 
-FileBrowserHandlerCustomBindings::FileBrowserHandlerCustomBindings(
-    v8::Handle<v8::Context> context)
-    : ChromeV8Extension(NULL, context) {
-  RouteFunction(
-      "GetExternalFileEntry",
-      base::Bind(&FileBrowserHandlerCustomBindings::GetExternalFileEntry,
-                 base::Unretained(this)));
-}
-
-v8::Handle<v8::Value> FileBrowserHandlerCustomBindings::GetExternalFileEntry(
-    const v8::Arguments& args) {
+v8::Handle<v8::Value> GetExternalFileEntry(const v8::Arguments& args) {
   // TODO(zelidrag): Make this magic work on other platforms when file browser
   // matures enough on ChromeOS.
 #if defined(OS_CHROMEOS)
@@ -43,8 +33,7 @@
             v8::String::New("fileFullPath"))));
     bool is_directory =
         file_def->Get(v8::String::New("fileIsDirectory"))->ToBoolean()->Value();
-    WebKit::WebFrame* webframe =
-        WebKit::WebFrame::frameForContext(v8_context());
+    WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext();
     return webframe->createFileEntry(
         WebKit::WebFileSystem::TypeExternal,
         WebKit::WebString::fromUTF8(file_system_name.c_str()),
@@ -56,4 +45,14 @@
 #endif
 }
 
+}  // namespace
+
+namespace extensions {
+
+FileBrowserHandlerCustomBindings::FileBrowserHandlerCustomBindings()
+    : ChromeV8Extension(NULL) {
+  RouteStaticFunction("GetExternalFileEntry", &GetExternalFileEntry);
+}
+
+
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/file_browser_handler_custom_bindings.h b/chrome/renderer/extensions/file_browser_handler_custom_bindings.h
index cf9c1ad..a34769d6 100644
--- a/chrome/renderer/extensions/file_browser_handler_custom_bindings.h
+++ b/chrome/renderer/extensions/file_browser_handler_custom_bindings.h
@@ -13,11 +13,9 @@
 // Custom bindings for the fileBrowserHandler API.
 class FileBrowserHandlerCustomBindings : public ChromeV8Extension {
  public:
-  explicit FileBrowserHandlerCustomBindings(v8::Handle<v8::Context> context);
+  FileBrowserHandlerCustomBindings();
 
  private:
-  v8::Handle<v8::Value> GetExternalFileEntry(const v8::Arguments& args);
-
   DISALLOW_COPY_AND_ASSIGN(FileBrowserHandlerCustomBindings);
 };
 
diff --git a/chrome/renderer/extensions/file_browser_private_custom_bindings.cc b/chrome/renderer/extensions/file_browser_private_custom_bindings.cc
index 364dfa4..355e8383 100644
--- a/chrome/renderer/extensions/file_browser_private_custom_bindings.cc
+++ b/chrome/renderer/extensions/file_browser_private_custom_bindings.cc
@@ -13,18 +13,9 @@
 #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h"
 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
 
-namespace extensions {
+namespace {
 
-FileBrowserPrivateCustomBindings::FileBrowserPrivateCustomBindings(
-    v8::Handle<v8::Context> context)
-    : ChromeV8Extension(NULL, context) {
-  RouteFunction(
-      "GetLocalFileSystem",
-       base::Bind(&FileBrowserPrivateCustomBindings::GetLocalFileSystem,
-                  base::Unretained(this)));
-}
-
-v8::Handle<v8::Value> FileBrowserPrivateCustomBindings::GetLocalFileSystem(
+static v8::Handle<v8::Value> GetLocalFileSystem(
     const v8::Arguments& args) {
   DCHECK(args.Length() == 2);
   DCHECK(args[0]->IsString());
@@ -32,7 +23,7 @@
   std::string name(*v8::String::Utf8Value(args[0]));
   std::string path(*v8::String::Utf8Value(args[1]));
 
-  WebKit::WebFrame* webframe = WebKit::WebFrame::frameForContext(v8_context());
+  WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext();
   DCHECK(webframe);
   return webframe->createFileSystem(
       WebKit::WebFileSystem::TypeExternal,
@@ -40,4 +31,13 @@
       WebKit::WebString::fromUTF8(path.c_str()));
 }
 
+}  // namespace
+
+namespace extensions {
+
+FileBrowserPrivateCustomBindings::FileBrowserPrivateCustomBindings()
+    : ChromeV8Extension(NULL) {
+  RouteStaticFunction("GetLocalFileSystem", &GetLocalFileSystem);
+}
+
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/file_browser_private_custom_bindings.h b/chrome/renderer/extensions/file_browser_private_custom_bindings.h
index 20c7065..f1fce16ad 100644
--- a/chrome/renderer/extensions/file_browser_private_custom_bindings.h
+++ b/chrome/renderer/extensions/file_browser_private_custom_bindings.h
@@ -13,9 +13,7 @@
 // Custom bindings for the fileBrowserPrivate API.
 class FileBrowserPrivateCustomBindings : public ChromeV8Extension {
  public:
-  explicit FileBrowserPrivateCustomBindings(v8::Handle<v8::Context> context);
-
-  v8::Handle<v8::Value> GetLocalFileSystem(const v8::Arguments& args);
+  FileBrowserPrivateCustomBindings();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(FileBrowserPrivateCustomBindings);
diff --git a/chrome/renderer/extensions/file_system_natives.cc b/chrome/renderer/extensions/file_system_natives.cc
index 7812fe09..81fbf929 100644
--- a/chrome/renderer/extensions/file_system_natives.cc
+++ b/chrome/renderer/extensions/file_system_natives.cc
@@ -18,23 +18,14 @@
 #include "webkit/fileapi/file_system_types.h"
 #include "webkit/fileapi/file_system_util.h"
 
-namespace extensions {
+namespace {
 
-FileSystemNatives::FileSystemNatives(v8::Handle<v8::Context> context)
-    : ObjectBackedNativeHandler(context) {
-  RouteFunction("GetFileEntry",
-      base::Bind(&FileSystemNatives::GetFileEntry, base::Unretained(this)));
-  RouteFunction("GetIsolatedFileSystem",
-      base::Bind(&FileSystemNatives::GetIsolatedFileSystem,
-                 base::Unretained(this)));
-}
-
-v8::Handle<v8::Value> FileSystemNatives::GetIsolatedFileSystem(
+static v8::Handle<v8::Value> GetIsolatedFileSystem(
     const v8::Arguments& args) {
   DCHECK(args.Length() == 1 || args.Length() == 2);
   DCHECK(args[0]->IsString());
   std::string file_system_id(*v8::String::Utf8Value(args[0]));
-  WebKit::WebFrame* webframe = WebKit::WebFrame::frameForContext(v8_context());
+  WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext();
   DCHECK(webframe);
 
   GURL context_url =
@@ -63,8 +54,7 @@
       WebKit::WebString::fromUTF8(root));
 }
 
-v8::Handle<v8::Value> FileSystemNatives::GetFileEntry(
-    const v8::Arguments& args) {
+static v8::Handle<v8::Value> GetFileEntry(const v8::Arguments& args) {
   DCHECK(args.Length() == 5);
   DCHECK(args[0]->IsString());
   std::string type_string = *v8::String::Utf8Value(args[0]->ToString());
@@ -87,7 +77,7 @@
   DCHECK(args[4]->IsBoolean());
   bool is_directory = args[4]->BooleanValue();
 
-  WebKit::WebFrame* webframe = WebKit::WebFrame::frameForContext(v8_context());
+  WebKit::WebFrame* webframe = WebKit::WebFrame::frameForCurrentContext();
   DCHECK(webframe);
   return webframe->createFileEntry(
       type,
@@ -97,4 +87,14 @@
       is_directory);
 }
 
+}  // namespace
+
+namespace extensions {
+
+FileSystemNatives::FileSystemNatives()
+    : ChromeV8Extension(NULL) {
+  RouteStaticFunction("GetFileEntry", &GetFileEntry);
+  RouteStaticFunction("GetIsolatedFileSystem", &GetIsolatedFileSystem);
+}
+
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/file_system_natives.h b/chrome/renderer/extensions/file_system_natives.h
index 6768eae8..c57a9a9 100644
--- a/chrome/renderer/extensions/file_system_natives.h
+++ b/chrome/renderer/extensions/file_system_natives.h
@@ -6,19 +6,16 @@
 #define CHROME_RENDERER_EXTENSIONS_FILE_SYSTEM_NATIVES_H_
 
 #include "base/compiler_specific.h"
-#include "chrome/renderer/extensions/object_backed_native_handler.h"
+#include "chrome/renderer/extensions/chrome_v8_extension.h"
 
 namespace extensions {
 
 // Custom bindings for the nativeFileSystem API.
-class FileSystemNatives : public ObjectBackedNativeHandler {
+class FileSystemNatives : public ChromeV8Extension {
  public:
-  explicit FileSystemNatives(v8::Handle<v8::Context> context);
+  FileSystemNatives();
 
  private:
-  v8::Handle<v8::Value> GetFileEntry(const v8::Arguments& args);
-  v8::Handle<v8::Value> GetIsolatedFileSystem(const v8::Arguments& args);
-
   DISALLOW_COPY_AND_ASSIGN(FileSystemNatives);
 };
 
diff --git a/chrome/renderer/extensions/i18n_custom_bindings.cc b/chrome/renderer/extensions/i18n_custom_bindings.cc
index 44ee27b..7dfcb0f 100644
--- a/chrome/renderer/extensions/i18n_custom_bindings.cc
+++ b/chrome/renderer/extensions/i18n_custom_bindings.cc
@@ -12,17 +12,14 @@
 
 namespace extensions {
 
-I18NCustomBindings::I18NCustomBindings(Dispatcher* dispatcher,
-                                       v8::Handle<v8::Context> context)
-    : ChromeV8Extension(dispatcher, context) {
+I18NCustomBindings::I18NCustomBindings()
+    : ChromeV8Extension(NULL) {
   RouteStaticFunction("GetL10nMessage", &GetL10nMessage);
 }
 
 // static
 v8::Handle<v8::Value> I18NCustomBindings::GetL10nMessage(
     const v8::Arguments& args) {
-  I18NCustomBindings* self = GetFromArguments<I18NCustomBindings>(args);
-
   if (args.Length() != 3 || !args[0]->IsString()) {
     NOTREACHED() << "Bad arguments";
     return v8::Undefined();
@@ -41,7 +38,7 @@
   if (!l10n_messages) {
     // Get the current RenderView so that we can send a routed IPC message
     // from the correct source.
-    content::RenderView* renderview = self->GetRenderView();
+    content::RenderView* renderview = GetCurrentRenderView();
     if (!renderview)
       return v8::Undefined();
 
diff --git a/chrome/renderer/extensions/i18n_custom_bindings.h b/chrome/renderer/extensions/i18n_custom_bindings.h
index faa9db7..beab1731 100644
--- a/chrome/renderer/extensions/i18n_custom_bindings.h
+++ b/chrome/renderer/extensions/i18n_custom_bindings.h
@@ -12,7 +12,7 @@
 // Implements custom bindings for the i18n API.
 class I18NCustomBindings : public ChromeV8Extension {
  public:
-  I18NCustomBindings(Dispatcher* dispatcher, v8::Handle<v8::Context> context);
+  I18NCustomBindings();
 
  private:
   static v8::Handle<v8::Value> GetL10nMessage(const v8::Arguments& args);
diff --git a/chrome/renderer/extensions/json_schema_unittest.cc b/chrome/renderer/extensions/json_schema_unittest.cc
index 32c7fab..08c12d20 100644
--- a/chrome/renderer/extensions/json_schema_unittest.cc
+++ b/chrome/renderer/extensions/json_schema_unittest.cc
@@ -27,17 +27,12 @@
     std::string code = ResourceBundle::GetSharedInstance().GetRawDataResource(
         IDR_JSON_SCHEMA_JS).as_string();
 
-    // json_schema.js expects to have require() and requireNative() defined.
+    // json_schema.js expects to have requireNative() defined.
     ExecuteScriptInContext(
         "function requireNative(id) {"
         "  return {"
         "    GetChromeHidden: function() { return {}; },"
         "  };"
-        "}"
-        "function require(id) {"
-        "  return {"
-        "    loadRefDependency: function(foo) { return {}; },"
-        "  };"
         "}",
         "test-code");
     ExecuteScriptInContext(code, kJsonSchema);
diff --git a/chrome/renderer/extensions/miscellaneous_bindings.cc b/chrome/renderer/extensions/miscellaneous_bindings.cc
index 35d24bd..facdec7 100644
--- a/chrome/renderer/extensions/miscellaneous_bindings.cc
+++ b/chrome/renderer/extensions/miscellaneous_bindings.cc
@@ -65,9 +65,8 @@
 
 class ExtensionImpl : public extensions::ChromeV8Extension {
  public:
-  explicit ExtensionImpl(extensions::Dispatcher* dispatcher,
-                         v8::Handle<v8::Context> context)
-      : extensions::ChromeV8Extension(dispatcher, context) {
+  explicit ExtensionImpl(extensions::Dispatcher* dispatcher)
+      : extensions::ChromeV8Extension(dispatcher) {
     RouteStaticFunction("CloseChannel", &CloseChannel);
     RouteStaticFunction("PortAddRef", &PortAddRef);
     RouteStaticFunction("PortRelease", &PortRelease);
@@ -79,8 +78,7 @@
 
   // Sends a message along the given channel.
   static v8::Handle<v8::Value> PostMessage(const v8::Arguments& args) {
-    ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args);
-    content::RenderView* renderview = self->GetRenderView();
+    content::RenderView* renderview = GetCurrentRenderView();
     if (!renderview)
       return v8::Undefined();
 
@@ -180,10 +178,8 @@
 
 namespace extensions {
 
-ChromeV8Extension* MiscellaneousBindings::Get(
-    Dispatcher* dispatcher,
-    v8::Handle<v8::Context> context) {
-  return new ExtensionImpl(dispatcher, context);
+ChromeV8Extension* MiscellaneousBindings::Get(Dispatcher* dispatcher) {
+  return new ExtensionImpl(dispatcher);
 }
 
 // static
diff --git a/chrome/renderer/extensions/miscellaneous_bindings.h b/chrome/renderer/extensions/miscellaneous_bindings.h
index d6ecd4a..c1cb1ef 100644
--- a/chrome/renderer/extensions/miscellaneous_bindings.h
+++ b/chrome/renderer/extensions/miscellaneous_bindings.h
@@ -30,8 +30,7 @@
 class MiscellaneousBindings {
  public:
   // Creates an instance of the extension.
-  static ChromeV8Extension* Get(Dispatcher* dispatcher,
-                                v8::Handle<v8::Context> context);
+  static ChromeV8Extension* Get(Dispatcher* dispatcher);
 
   // Dispatches the Port.onConnect content script messaging event to some
   // contexts in |contexts|. If |restrict_to_render_view| is specified, only
diff --git a/chrome/renderer/extensions/module_system.cc b/chrome/renderer/extensions/module_system.cc
index 74e7d3d..31146c8 100644
--- a/chrome/renderer/extensions/module_system.cc
+++ b/chrome/renderer/extensions/module_system.cc
@@ -6,10 +6,6 @@
 
 #include "base/bind.h"
 #include "base/stl_util.h"
-#include "base/stringprintf.h"
-#include "chrome/common/extensions/extension_messages.h"
-#include "chrome/renderer/extensions/chrome_v8_context.h"
-#include "content/public/renderer/render_view.h"
 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedMicrotaskSuppression.h"
 
 namespace {
@@ -25,35 +21,28 @@
 
 ModuleSystem::ModuleSystem(v8::Handle<v8::Context> context,
                            SourceMap* source_map)
-    : ObjectBackedNativeHandler(context),
+    : NativeHandler(context->GetIsolate()),
+      context_(v8::Persistent<v8::Context>::New(context->GetIsolate(),
+                                                context)),
       source_map_(source_map),
-      natives_enabled_(0),
-      is_valid_(true) {
+      natives_enabled_(0) {
   RouteFunction("require",
       base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this)));
   RouteFunction("requireNative",
-      base::Bind(&ModuleSystem::RequireNative, base::Unretained(this)));
+      base::Bind(&ModuleSystem::GetNative, base::Unretained(this)));
 
-  v8::Handle<v8::Object> global(context->Global());
+  v8::Handle<v8::Object> global(context_->Global());
   global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New());
   global->SetHiddenValue(v8::String::New(kModuleSystem),
                          v8::External::New(this));
 }
 
 ModuleSystem::~ModuleSystem() {
-  Invalidate();
-}
-
-bool ModuleSystem::Invalidate() {
-  if (!ObjectBackedNativeHandler::Invalidate())
-    return false;
-
   v8::HandleScope handle_scope;
   // Deleting this value here prevents future lazy field accesses from
   // referencing ModuleSystem after it has been freed.
-  v8_context()->Global()->DeleteHiddenValue(v8::String::New(kModuleSystem));
-
-  return true;
+  context_->Global()->DeleteHiddenValue(v8::String::New(kModuleSystem));
+  context_.Dispose(context_->GetIsolate());
 }
 
 ModuleSystem::NativesEnabledScope::NativesEnabledScope(
@@ -69,7 +58,6 @@
 
 // static
 bool ModuleSystem::IsPresentInCurrentContext() {
-  // XXX(kalman): Not sure if this is safe. Investigate.
   v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global());
   if (global.IsEmpty())
     return false;
@@ -85,10 +73,13 @@
 }
 
 // static
-std::string ModuleSystem::CreateExceptionString(const v8::TryCatch& try_catch) {
+void ModuleSystem::DumpException(const v8::TryCatch& try_catch) {
+  v8::HandleScope handle_scope;
+
   v8::Handle<v8::Message> message(try_catch.Message());
   if (message.IsEmpty()) {
-    return "try_catch has no message";
+    LOG(ERROR) << "try_catch has no message";
+    return;
   }
 
   std::string resource_name = "<unknown resource>";
@@ -101,16 +92,6 @@
   if (!message->Get().IsEmpty())
     error_message = *v8::String::Utf8Value(message->Get());
 
-  return base::StringPrintf("%s:%d: %s",
-                            resource_name.c_str(),
-                            message->GetLineNumber(),
-                            error_message.c_str());
-}
-
-// static
-void ModuleSystem::DumpException(const v8::TryCatch& try_catch) {
-  v8::HandleScope handle_scope;
-
   std::string stack_trace = "<stack trace unavailable>";
   if (!try_catch.StackTrace().IsEmpty()) {
     v8::String::Utf8Value stack_value(try_catch.StackTrace());
@@ -120,13 +101,14 @@
       stack_trace = "<could not convert stack trace to string>";
   }
 
-  LOG(ERROR) << CreateExceptionString(try_catch) << "{" << stack_trace << "}";
+  LOG(ERROR) << "[" << resource_name << "(" << message->GetLineNumber() << ")] "
+             << error_message
+             << "{" << stack_trace << "}";
 }
 
-v8::Handle<v8::Value> ModuleSystem::Require(const std::string& module_name) {
+void ModuleSystem::Require(const std::string& module_name) {
   v8::HandleScope handle_scope;
-  return handle_scope.Close(
-      RequireForJsInner(v8::String::New(module_name.c_str())));
+  RequireForJsInner(v8::String::New(module_name.c_str()));
 }
 
 v8::Handle<v8::Value> ModuleSystem::RequireForJs(const v8::Arguments& args) {
@@ -137,9 +119,8 @@
 
 v8::Handle<v8::Value> ModuleSystem::RequireForJsInner(
     v8::Handle<v8::String> module_name) {
-  CHECK(is_valid_);
   v8::HandleScope handle_scope;
-  v8::Handle<v8::Object> global(v8_context()->Global());
+  v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global());
   v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast(
       global->GetHiddenValue(v8::String::New(kModulesField))));
   v8::Handle<v8::Value> exports(modules->Get(module_name));
@@ -179,11 +160,10 @@
   return handle_scope.Close(exports);
 }
 
-v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
-    const std::string& module_name,
-    const std::string& method_name) {
+void ModuleSystem::CallModuleMethod(const std::string& module_name,
+                                    const std::string& method_name) {
   std::vector<v8::Handle<v8::Value> > args;
-  return CallModuleMethod(module_name, method_name, &args);
+  CallModuleMethod(module_name, method_name, &args);
 }
 
 v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
@@ -203,7 +183,8 @@
     return v8::Local<v8::Value>();
   v8::Handle<v8::Function> func =
       v8::Handle<v8::Function>::Cast(value);
-  v8::Handle<v8::Object> global(v8_context()->Global());
+  // TODO(jeremya/koz): refer to context_ here, not the current context.
+  v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global());
   v8::Local<v8::Value> result;
   {
     WebKit::WebScopedMicrotaskSuppression suppression;
@@ -216,10 +197,6 @@
   return handle_scope.Close(result);
 }
 
-bool ModuleSystem::HasNativeHandler(const std::string& name) {
-  return native_handler_map_.find(name) != native_handler_map_.end();
-}
-
 void ModuleSystem::RegisterNativeHandler(const std::string& name,
     scoped_ptr<NativeHandler> native_handler) {
   native_handler_map_[name] =
@@ -236,29 +213,13 @@
 }
 
 // static
-v8::Handle<v8::Value> ModuleSystem::NativeLazyFieldGetter(
-    v8::Local<v8::String> property, const v8::AccessorInfo& info) {
-  return LazyFieldGetterInner(property,
-                              info,
-                              &ModuleSystem::RequireNativeFromString);
-}
-
-// static
 v8::Handle<v8::Value> ModuleSystem::LazyFieldGetter(
     v8::Local<v8::String> property, const v8::AccessorInfo& info) {
-  return LazyFieldGetterInner(property, info, &ModuleSystem::Require);
-}
-
-// static
-v8::Handle<v8::Value> ModuleSystem::LazyFieldGetterInner(
-    v8::Local<v8::String> property,
-    const v8::AccessorInfo& info,
-    GetModuleFunc get_module) {
   CHECK(!info.Data().IsEmpty());
   CHECK(info.Data()->IsObject());
   v8::HandleScope handle_scope;
   v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data());
-  v8::Handle<v8::Object> global(parameters->CreationContext()->Global());
+  v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global());
   v8::Handle<v8::Value> module_system_value =
       global->GetHiddenValue(v8::String::New(kModuleSystem));
   if (module_system_value.IsEmpty() || module_system_value->IsUndefined()) {
@@ -268,78 +229,38 @@
   ModuleSystem* module_system = static_cast<ModuleSystem*>(
       v8::Handle<v8::External>::Cast(module_system_value)->Value());
 
-  std::string name = *v8::String::AsciiValue(
-      parameters->Get(v8::String::New(kModuleName))->ToString());
-
-  // HACK(kalman): Switch to the context of the owner module system while
-  // lazily requiring modules.
-  //
-  // It seems to be a common incorrect assumption throughout code that the
-  // current context is the owner context. This makes that assumption true for
-  // at least the period where the JavaScript is first evaluated, which is when
-  // things are most likely to go wrong.
-  v8::Context::Scope context_scope(parameters->CreationContext());
-
-  NativesEnabledScope natives_enabled_scope(module_system);
-
-  v8::TryCatch try_catch;
-  v8::Handle<v8::Object> module = v8::Handle<v8::Object>::Cast(
-      (module_system->*get_module)(name));
-  if (try_catch.HasCaught()) {
-    module_system->HandleException(try_catch);
-    return handle_scope.Close(v8::Handle<v8::Value>());
+  v8::Handle<v8::Object> module;
+  {
+    NativesEnabledScope scope(module_system);
+    module = v8::Handle<v8::Object>::Cast(module_system->RequireForJsInner(
+        parameters->Get(v8::String::New(kModuleName))->ToString()));
   }
-
   if (module.IsEmpty())
     return handle_scope.Close(v8::Handle<v8::Value>());
 
   v8::Handle<v8::String> field =
       parameters->Get(v8::String::New(kModuleField))->ToString();
 
-  v8::Local<v8::Value> new_field = module->Get(field);
-  v8::Handle<v8::Object> object = info.This();
-  // Delete the getter and set this field to |new_field| so the same object is
-  // returned every time a certain API is accessed.
-  if (!new_field->IsUndefined()) {
-    object->Delete(property);
-    object->Set(property, new_field);
-  }
-  return handle_scope.Close(new_field);
+  return handle_scope.Close(module->Get(field));
 }
 
 void ModuleSystem::SetLazyField(v8::Handle<v8::Object> object,
                                 const std::string& field,
                                 const std::string& module_name,
                                 const std::string& module_field) {
-  SetLazyField(object, field, module_name, module_field,
-      &ModuleSystem::LazyFieldGetter);
-}
-
-void ModuleSystem::SetLazyField(v8::Handle<v8::Object> object,
-                                const std::string& field,
-                                const std::string& module_name,
-                                const std::string& module_field,
-                                v8::AccessorGetter getter) {
   v8::HandleScope handle_scope;
   v8::Handle<v8::Object> parameters = v8::Object::New();
   parameters->Set(v8::String::New(kModuleName),
                   v8::String::New(module_name.c_str()));
   parameters->Set(v8::String::New(kModuleField),
                   v8::String::New(module_field.c_str()));
+
   object->SetAccessor(v8::String::New(field.c_str()),
-                      getter,
+                      &ModuleSystem::LazyFieldGetter,
                       NULL,
                       parameters);
 }
 
-void ModuleSystem::SetNativeLazyField(v8::Handle<v8::Object> object,
-                                      const std::string& field,
-                                      const std::string& module_name,
-                                      const std::string& module_field) {
-  SetLazyField(object, field, module_name, module_field,
-      &ModuleSystem::NativeLazyFieldGetter);
-}
-
 v8::Handle<v8::Value> ModuleSystem::RunString(v8::Handle<v8::String> code,
                                               v8::Handle<v8::String> name) {
   v8::HandleScope handle_scope;
@@ -369,18 +290,13 @@
   return handle_scope.Close(source_map_->GetSource(module_name));
 }
 
-v8::Handle<v8::Value> ModuleSystem::RequireNative(const v8::Arguments& args) {
+v8::Handle<v8::Value> ModuleSystem::GetNative(const v8::Arguments& args) {
   CHECK_EQ(1, args.Length());
-  std::string native_name = *v8::String::AsciiValue(args[0]->ToString());
-  return RequireNativeFromString(native_name);
-}
-
-v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString(
-    const std::string& native_name) {
   if (natives_enabled_ == 0)
     return ThrowException("Natives disabled");
+  std::string native_name = *v8::String::AsciiValue(args[0]->ToString());
   if (overridden_native_handlers_.count(native_name) > 0u)
-    return RequireForJsInner(v8::String::New(native_name.c_str()));
+    return RequireForJs(args);
   NativeHandlerMap::iterator i = native_handler_map_.find(native_name);
   if (i == native_handler_map_.end())
     return v8::Undefined();
diff --git a/chrome/renderer/extensions/module_system.h b/chrome/renderer/extensions/module_system.h
index 2521d67..be03f2ad 100644
--- a/chrome/renderer/extensions/module_system.h
+++ b/chrome/renderer/extensions/module_system.h
@@ -8,7 +8,7 @@
 #include "base/compiler_specific.h"
 #include "base/memory/linked_ptr.h"
 #include "base/memory/scoped_ptr.h"
-#include "chrome/renderer/extensions/object_backed_native_handler.h"
+#include "chrome/renderer/extensions/native_handler.h"
 #include "v8/include/v8.h"
 
 #include <map>
@@ -34,7 +34,7 @@
 // Note that a ModuleSystem must be used only in conjunction with a single
 // v8::Context.
 // TODO(koz): Rename this to JavaScriptModuleSystem.
-class ModuleSystem : public ObjectBackedNativeHandler {
+class ModuleSystem : public NativeHandler {
  public:
   class SourceMap {
    public:
@@ -61,7 +61,7 @@
   };
 
   // |source_map| is a weak pointer.
-  ModuleSystem(v8::Handle<v8::Context> context, SourceMap* source_map);
+  explicit ModuleSystem(v8::Handle<v8::Context> context, SourceMap* source_map);
   virtual ~ModuleSystem();
 
   // Returns true if the current context has a ModuleSystem installed in it.
@@ -72,13 +72,15 @@
 
   // Require the specified module. This is the equivalent of calling
   // require('module_name') from the loaded JS files.
-  v8::Handle<v8::Value> Require(const std::string& module_name);
+  void Require(const std::string& module_name);
   v8::Handle<v8::Value> Require(const v8::Arguments& args);
+  v8::Handle<v8::Value> RequireForJs(const v8::Arguments& args);
+  v8::Handle<v8::Value> RequireForJsInner(v8::Handle<v8::String> module_name);
 
   // Calls the specified method exported by the specified module. This is
   // equivalent to calling require('module_name').method_name() from JS.
-  v8::Local<v8::Value> CallModuleMethod(const std::string& module_name,
-                                        const std::string& method_name);
+  void CallModuleMethod(const std::string& module_name,
+                        const std::string& method_name);
 
   // Calls the specified method exported by the specified module. This is
   // equivalent to calling require('module_name').method_name(args) from JS.
@@ -92,8 +94,6 @@
   // |native_handler|.
   void RegisterNativeHandler(const std::string& name,
                              scoped_ptr<NativeHandler> native_handler);
-  // Check if a native handler has been registered for this |name|.
-  bool HasNativeHandler(const std::string& name);
 
   // Causes requireNative(|name|) to look for its module in |source_map_|
   // instead of using a registered native handler. This can be used in unit
@@ -106,11 +106,6 @@
   // Retrieves the lazily defined field specified by |property|.
   static v8::Handle<v8::Value> LazyFieldGetter(v8::Local<v8::String> property,
                                                const v8::AccessorInfo& info);
-  // Retrieves the lazily defined field specified by |property| on a native
-  // object.
-  static v8::Handle<v8::Value> NativeLazyFieldGetter(
-      v8::Local<v8::String> property,
-      const v8::AccessorInfo& info);
 
   // Make |object|.|field| lazily evaluate to the result of
   // require(|module_name|)[|module_field|].
@@ -119,27 +114,16 @@
                     const std::string& module_name,
                     const std::string& module_field);
 
-  // Make |object|.|field| lazily evaluate to the result of
-  // requireNative(|module_name|)[|module_field|].
-  void SetNativeLazyField(v8::Handle<v8::Object> object,
-                          const std::string& field,
-                          const std::string& module_name,
-                          const std::string& module_field);
-
   void set_exception_handler(scoped_ptr<ExceptionHandler> handler) {
     exception_handler_ = handler.Pass();
   }
 
-  virtual bool Invalidate() OVERRIDE;
-
  private:
   typedef std::map<std::string, linked_ptr<NativeHandler> > NativeHandlerMap;
 
   // Called when an exception is thrown but not caught.
   void HandleException(const v8::TryCatch& try_catch);
 
-  static std::string CreateExceptionString(const v8::TryCatch& try_catch);
-
   // Ensure that require_ has been evaluated from require.js.
   void EnsureRequireLoaded();
 
@@ -148,25 +132,6 @@
   v8::Handle<v8::Value> RunString(v8::Handle<v8::String> code,
                                   v8::Handle<v8::String> name);
 
-  v8::Handle<v8::Value> RequireForJs(const v8::Arguments& args);
-  v8::Handle<v8::Value> RequireForJsInner(v8::Handle<v8::String> module_name);
-
-  // Sets a lazy field using the specified |getter|.
-  void SetLazyField(v8::Handle<v8::Object> object,
-                    const std::string& field,
-                    const std::string& module_name,
-                    const std::string& module_field,
-                    v8::AccessorGetter getter);
-
-  typedef v8::Handle<v8::Value> (ModuleSystem::*GetModuleFunc)(
-      const std::string&);
-  // Base implementation of a LazyFieldGetter that can be customized by passing
-  // in a |get_module| function.
-  static v8::Handle<v8::Value> LazyFieldGetterInner(
-      v8::Local<v8::String> property,
-      const v8::AccessorInfo& info,
-      GetModuleFunc get_module);
-
   // Return the named source file stored in the source map.
   // |args[0]| - the name of a source file in source_map_.
   v8::Handle<v8::Value> GetSource(v8::Handle<v8::String> source_name);
@@ -174,8 +139,7 @@
   // Return an object that contains the native methods defined by the named
   // NativeHandler.
   // |args[0]| - the name of a native handler object.
-  v8::Handle<v8::Value> RequireNativeFromString(const std::string& native_name);
-  v8::Handle<v8::Value> RequireNative(const v8::Arguments& args);
+  v8::Handle<v8::Value> GetNative(const v8::Arguments& args);
 
   // Wraps |source| in a (function(require, requireNative, exports) {...}).
   v8::Handle<v8::String> WrapSource(v8::Handle<v8::String> source);
@@ -183,6 +147,9 @@
   // Throws an exception in the calling JS context.
   v8::Handle<v8::Value> ThrowException(const std::string& message);
 
+  // The context that this ModuleSystem is for.
+  v8::Persistent<v8::Context> context_;
+
   // A map from module names to the JS source for that module. GetSource()
   // performs a lookup on this map.
   SourceMap* source_map_;
@@ -194,9 +161,6 @@
   // pinned natives as enabled.
   int natives_enabled_;
 
-  // Set to false if |context_| has been deleted and this should not be used.
-  bool is_valid_;
-
   // Called when an exception is thrown but not caught in JS.
   scoped_ptr<ExceptionHandler> exception_handler_;
 
diff --git a/chrome/renderer/extensions/module_system_unittest.cc b/chrome/renderer/extensions/module_system_unittest.cc
index 4c88cca4..f5f7db6d 100644
--- a/chrome/renderer/extensions/module_system_unittest.cc
+++ b/chrome/renderer/extensions/module_system_unittest.cc
@@ -6,15 +6,13 @@
 #include "base/memory/scoped_ptr.h"
 #include "chrome/renderer/extensions/module_system.h"
 
-// TODO(cduvall/kalman): Put this file in extensions namespace.
 using extensions::ModuleSystem;
 using extensions::NativeHandler;
-using extensions::ObjectBackedNativeHandler;
 
-class CounterNatives : public ObjectBackedNativeHandler {
+class CounterNatives : public NativeHandler {
  public:
-  explicit CounterNatives(v8::Handle<v8::Context> context)
-      : ObjectBackedNativeHandler(context), counter_(0) {
+  explicit CounterNatives(v8::Isolate* isolate)
+      : NativeHandler(isolate), counter_(0) {
     RouteFunction("Get", base::Bind(&CounterNatives::Get,
         base::Unretained(this)));
     RouteFunction("Increment", base::Bind(&CounterNatives::Increment,
@@ -175,7 +173,7 @@
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   module_system_->RegisterNativeHandler(
       "counter",
-      scoped_ptr<NativeHandler>(new CounterNatives(v8::Context::GetCurrent())));
+      scoped_ptr<NativeHandler>(new CounterNatives(v8::Isolate::GetCurrent())));
   RegisterModule("lazy",
       "requireNative('counter').Increment();"
       "exports.x = 5;");
@@ -229,7 +227,7 @@
   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get());
   module_system_->RegisterNativeHandler(
       "counter",
-      scoped_ptr<NativeHandler>(new CounterNatives(v8::Context::GetCurrent())));
+      scoped_ptr<NativeHandler>(new CounterNatives(v8::Isolate::GetCurrent())));
 
   RegisterModule("incrementsWhenEvaled",
       "requireNative('counter').Increment();");
diff --git a/chrome/renderer/extensions/object_backed_native_handler.cc b/chrome/renderer/extensions/native_handler.cc
similarity index 68%
rename from chrome/renderer/extensions/object_backed_native_handler.cc
rename to chrome/renderer/extensions/native_handler.cc
index f57d579..3c41ea53 100644
--- a/chrome/renderer/extensions/object_backed_native_handler.cc
+++ b/chrome/renderer/extensions/native_handler.cc
@@ -2,34 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/renderer/extensions/object_backed_native_handler.h"
+#include "chrome/renderer/extensions/native_handler.h"
 
-#include "base/logging.h"
 #include "base/memory/linked_ptr.h"
+#include "base/logging.h"
 #include "chrome/renderer/extensions/module_system.h"
 #include "v8/include/v8.h"
 
 namespace extensions {
 
-ObjectBackedNativeHandler::ObjectBackedNativeHandler(
-    v8::Handle<v8::Context> context)
-    : v8_context_(context),
+NativeHandler::NativeHandler(v8::Isolate* isolate)
+    : isolate_(isolate),
       object_template_(
-          v8::Persistent<v8::ObjectTemplate>::New(context->GetIsolate(),
-                                                  v8::ObjectTemplate::New())),
-      is_valid_(true) {
+          v8::Persistent<v8::ObjectTemplate>::New(isolate,
+                                                  v8::ObjectTemplate::New())) {
 }
 
-ObjectBackedNativeHandler::~ObjectBackedNativeHandler() {
+NativeHandler::~NativeHandler() {
+  object_template_.Dispose(isolate_);
 }
 
-v8::Handle<v8::Object> ObjectBackedNativeHandler::NewInstance() {
+v8::Handle<v8::Object> NativeHandler::NewInstance() {
   return object_template_->NewInstance();
 }
 
 // static
-v8::Handle<v8::Value> ObjectBackedNativeHandler::Router(
-    const v8::Arguments& args) {
+v8::Handle<v8::Value> NativeHandler::Router(const v8::Arguments& args) {
   // It is possible for JS code to execute after ModuleSystem has been deleted
   // in which case the native handlers will also have been deleted, making
   // HandlerFunction below point to freed memory.
@@ -42,7 +40,7 @@
   return handler_function->Run(args);
 }
 
-void ObjectBackedNativeHandler::RouteFunction(const std::string& name,
+void NativeHandler::RouteFunction(const std::string& name,
     const HandlerFunction& handler_function) {
   linked_ptr<HandlerFunction> function(new HandlerFunction(handler_function));
   // TODO(koz): Investigate using v8's MakeWeak() function instead of holding
@@ -54,21 +52,11 @@
   object_template_->Set(name.c_str(), function_template);
 }
 
-void ObjectBackedNativeHandler::RouteStaticFunction(const std::string& name,
+void NativeHandler::RouteStaticFunction(const std::string& name,
                                         const HandlerFunc handler_func) {
   v8::Handle<v8::FunctionTemplate> function_template =
       v8::FunctionTemplate::New(handler_func, v8::External::New(this));
   object_template_->Set(name.c_str(), function_template);
 }
 
-bool ObjectBackedNativeHandler::Invalidate() {
-  if (!is_valid_)
-    return false;
-
-  object_template_.Dispose(v8_context_->GetIsolate());
-
-  is_valid_ = false;
-  return true;
-}
-
 }   // extensions
diff --git a/chrome/renderer/extensions/native_handler.h b/chrome/renderer/extensions/native_handler.h
index 36ef9918..fd6bda69 100644
--- a/chrome/renderer/extensions/native_handler.h
+++ b/chrome/renderer/extensions/native_handler.h
@@ -5,21 +5,55 @@
 #ifndef CHROME_RENDERER_EXTENSIONS_NATIVE_HANDLER_H_
 #define CHROME_RENDERER_EXTENSIONS_NATIVE_HANDLER_H_
 
+#include "base/bind.h"
+#include "base/memory/linked_ptr.h"
 #include "v8/include/v8.h"
 
+#include <string>
+#include <vector>
+
 namespace extensions {
 
+// A NativeHandler is a factory for JS objects with functions on them that map
+// to native C++ functions. Subclasses should call RouteFunction() in their
+// constructor to define functions on the created JS objects.
+//
 // NativeHandlers are intended to be used with a ModuleSystem. The ModuleSystem
 // will assume ownership of the NativeHandler, and as a ModuleSystem is tied to
 // a single v8::Context, this implies that NativeHandlers will also be tied to
-// a single v8::Context.
+// a single v8::context.
 // TODO(koz): Rename this to NativeJavaScriptModule.
 class NativeHandler {
  public:
-  virtual ~NativeHandler() {}
+  explicit NativeHandler(v8::Isolate* isolate);
+  virtual ~NativeHandler();
 
-  // Create a new instance of the object this handler specifies.
-  virtual v8::Handle<v8::Object> NewInstance() = 0;
+  // Create an object with bindings to the native functions defined through
+  // RouteFunction().
+  virtual v8::Handle<v8::Object> NewInstance();
+
+ protected:
+  typedef v8::Handle<v8::Value> (*HandlerFunc)(const v8::Arguments&);
+  typedef base::Callback<v8::Handle<v8::Value>(const v8::Arguments&)>
+      HandlerFunction;
+
+  // Installs a new 'route' from |name| to |handler_function|. This means that
+  // NewInstance()s of this NativeHandler will have a property |name| which
+  // will be handled by |handler_function|.
+  void RouteFunction(const std::string& name,
+                     const HandlerFunction& handler_function);
+
+  void RouteStaticFunction(const std::string& name,
+                           const HandlerFunc handler_func);
+
+ private:
+  static v8::Handle<v8::Value> Router(const v8::Arguments& args);
+
+  std::vector<linked_ptr<HandlerFunction> > handler_functions_;
+  v8::Isolate* isolate_;
+  v8::Persistent<v8::ObjectTemplate> object_template_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeHandler);
 };
 
 }  // extensions
diff --git a/chrome/renderer/extensions/object_backed_native_handler.h b/chrome/renderer/extensions/object_backed_native_handler.h
deleted file mode 100644
index 8c7e658..0000000
--- a/chrome/renderer/extensions/object_backed_native_handler.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_RENDERER_EXTENSIONS_OBJECT_BACKED_NATIVE_HANDLER_H_
-#define CHROME_RENDERER_EXTENSIONS_OBJECT_BACKED_NATIVE_HANDLER_H_
-
-#include <string>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/memory/linked_ptr.h"
-#include "chrome/renderer/extensions/native_handler.h"
-#include "v8/include/v8.h"
-
-namespace extensions {
-
-// An ObjectBackedNativeHandler is a factory for JS objects with functions on
-// them that map to native C++ functions. Subclasses should call RouteFunction()
-// in their constructor to define functions on the created JS objects.
-class ObjectBackedNativeHandler : public NativeHandler {
- public:
-  explicit ObjectBackedNativeHandler(v8::Handle<v8::Context> context);
-  virtual ~ObjectBackedNativeHandler();
-
-  // Create an object with bindings to the native functions defined through
-  // RouteFunction().
-  virtual v8::Handle<v8::Object> NewInstance() OVERRIDE;
-
- protected:
-  typedef v8::Handle<v8::Value> (*HandlerFunc)(const v8::Arguments&);
-  typedef base::Callback<v8::Handle<v8::Value>(const v8::Arguments&)>
-      HandlerFunction;
-
-  // Installs a new 'route' from |name| to |handler_function|. This means that
-  // NewInstance()s of this ObjectBackedNativeHandler will have a property
-  // |name| which will be handled by |handler_function|.
-  void RouteFunction(const std::string& name,
-                     const HandlerFunction& handler_function);
-
-  void RouteStaticFunction(const std::string& name,
-                           const HandlerFunc handler_func);
-
-  // Invalidate this object so it cannot be used any more. This is needed
-  // because it's possible for this to outlive |v8_context_|. Invalidate must
-  // be called before this happens.
-  //
-  // Subclasses may override to invalidate their own V8 state, but always
-  // call superclass eventually.
-  //
-  // This must be idempotent. Returns true if it was the first time this was
-  // called, false otherwise.
-  virtual bool Invalidate();
-
-  v8::Handle<v8::Context> v8_context() { return v8_context_; }
-
- private:
-  static v8::Handle<v8::Value> Router(const v8::Arguments& args);
-
-  std::vector<linked_ptr<HandlerFunction> > handler_functions_;
-  v8::Handle<v8::Context> v8_context_;
-  v8::Persistent<v8::ObjectTemplate> object_template_;
-  bool is_valid_;
-
-  DISALLOW_COPY_AND_ASSIGN(ObjectBackedNativeHandler);
-};
-
-}  // extensions
-
-#endif  // CHROME_RENDERER_EXTENSIONS_OBJECT_BACKED_NATIVE_HANDLER_H_
diff --git a/chrome/renderer/extensions/page_capture_custom_bindings.cc b/chrome/renderer/extensions/page_capture_custom_bindings.cc
index 3b684b9..c7e41ea 100644
--- a/chrome/renderer/extensions/page_capture_custom_bindings.cc
+++ b/chrome/renderer/extensions/page_capture_custom_bindings.cc
@@ -13,10 +13,8 @@
 
 namespace extensions {
 
-PageCaptureCustomBindings::PageCaptureCustomBindings(
-    Dispatcher* dispatcher,
-    v8::Handle<v8::Context> context)
-    : ChromeV8Extension(dispatcher, context) {
+PageCaptureCustomBindings::PageCaptureCustomBindings()
+    : ChromeV8Extension(NULL) {
   RouteStaticFunction("CreateBlob", &CreateBlob);
   RouteStaticFunction("SendResponseAck", &SendResponseAck);
 }
@@ -39,9 +37,7 @@
   CHECK(args.Length() == 1);
   CHECK(args[0]->IsInt32());
 
-  PageCaptureCustomBindings* self =
-      GetFromArguments<PageCaptureCustomBindings>(args);
-  content::RenderView* render_view = self->GetRenderView();
+  content::RenderView* render_view = GetCurrentRenderView();
   if (render_view) {
     render_view->Send(new ExtensionHostMsg_ResponseAck(
         render_view->GetRoutingID(), args[0]->Int32Value()));
diff --git a/chrome/renderer/extensions/page_capture_custom_bindings.h b/chrome/renderer/extensions/page_capture_custom_bindings.h
index 2881769..d6ac69d 100644
--- a/chrome/renderer/extensions/page_capture_custom_bindings.h
+++ b/chrome/renderer/extensions/page_capture_custom_bindings.h
@@ -12,8 +12,7 @@
 // Implements custom bindings for the pageCapture API.
 class PageCaptureCustomBindings : public ChromeV8Extension {
  public:
-  PageCaptureCustomBindings(Dispatcher* dispatcher,
-                            v8::Handle<v8::Context> context);
+  PageCaptureCustomBindings();
 
  private:
   // Creates a Blob with the content of the specified file.
diff --git a/chrome/renderer/extensions/request_sender.cc b/chrome/renderer/extensions/request_sender.cc
index a9269ae..e77aebf 100644
--- a/chrome/renderer/extensions/request_sender.cc
+++ b/chrome/renderer/extensions/request_sender.cc
@@ -7,6 +7,7 @@
 #include "base/values.h"
 #include "chrome/common/extensions/extension_messages.h"
 #include "chrome/renderer/extensions/chrome_v8_context.h"
+#include "chrome/renderer/extensions/chrome_v8_context_set.h"
 #include "chrome/renderer/extensions/dispatcher.h"
 #include "content/public/renderer/render_view.h"
 #include "content/public/renderer/v8_value_converter.h"
@@ -21,18 +22,23 @@
 // Contains info relevant to a pending API request.
 struct PendingRequest {
  public :
-  PendingRequest(ChromeV8Context* context,
-                 ChromeV8Context* caller_context,
-                 const std::string& name)
-      : name(name), context(context), caller_context(caller_context) {
+  PendingRequest(v8::Persistent<v8::Context> context, const std::string& name,
+                 const std::string& extension_id)
+      : context(context), name(name), extension_id(extension_id) {
   }
 
+  ~PendingRequest() {
+    context.Dispose(context->GetIsolate());
+  }
+
+  v8::Persistent<v8::Context> context;
   std::string name;
-  ChromeV8Context* context;
-  ChromeV8Context* caller_context;
+  std::string extension_id;
 };
 
-RequestSender::RequestSender(Dispatcher* dispatcher) : dispatcher_(dispatcher) {
+RequestSender::RequestSender(Dispatcher* dispatcher,
+                             ChromeV8ContextSet* context_set)
+    : dispatcher_(dispatcher), context_set_(context_set) {
 }
 
 RequestSender::~RequestSender() {
@@ -53,15 +59,18 @@
   return result;
 }
 
-void RequestSender::StartRequest(ChromeV8Context* context,
-                                 const std::string& name,
+void RequestSender::StartRequest(const std::string& name,
                                  int request_id,
                                  bool has_callback,
                                  bool for_io_thread,
                                  base::ListValue* value_args) {
+  ChromeV8Context* current_context = context_set_->GetCurrent();
+  if (!current_context)
+    return;
+
   // Get the current RenderView so that we can send a routed IPC message from
   // the correct source.
-  content::RenderView* renderview = context->GetRenderView();
+  content::RenderView* renderview = current_context->GetRenderView();
   if (!renderview)
     return;
 
@@ -73,25 +82,25 @@
   }
 
   // TODO(koz): See if we can make this a CHECK.
-  if (!dispatcher_->CheckContextAccessToExtensionAPI(name, context))
+  if (!dispatcher_->CheckCurrentContextAccessToExtensionAPI(name))
     return;
 
   GURL source_url;
   WebKit::WebSecurityOrigin source_origin;
-  WebKit::WebFrame* webframe = context->web_frame();
+  WebKit::WebFrame* webframe = current_context->web_frame();
   if (webframe) {
     source_url = webframe->document().url();
     source_origin = webframe->document().securityOrigin();
   }
 
-  std::string extension_id = context->GetExtensionID();
-  // Insert the current context into the PendingRequest because that's the
-  // context that we call back on.
-  InsertRequest(
-      request_id,
-      new PendingRequest(context,
-                         dispatcher_->v8_context_set().GetCurrent(),
-                         name));
+  v8::Local<v8::Context> ctx = v8::Context::GetCurrent();
+  v8::Persistent<v8::Context> v8_context =
+      v8::Persistent<v8::Context>::New(ctx->GetIsolate(), ctx);
+  DCHECK(!v8_context.IsEmpty());
+
+  std::string extension_id = current_context->GetExtensionID();
+  InsertRequest(request_id, new PendingRequest(
+      v8_context, name, extension_id));
 
   ExtensionHostMsg_Request_Params params;
   params.name = name;
@@ -119,10 +128,16 @@
   linked_ptr<PendingRequest> request = RemoveRequest(request_id);
 
   if (!request.get()) {
-    // This can happen if a context is destroyed while a request is in flight.
+    // This should not be able to happen since we only remove requests when
+    // they are handled.
+    LOG(ERROR) << "Could not find specified request id: " << request_id;
     return;
   }
 
+  ChromeV8Context* v8_context = context_set_->GetByV8Context(request->context);
+  if (!v8_context)
+    return;  // The frame went away.
+
   v8::HandleScope handle_scope;
 
   scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
@@ -130,15 +145,15 @@
     v8::Integer::New(request_id),
     v8::String::New(request->name.c_str()),
     v8::Boolean::New(success),
-    converter->ToV8Value(&responseList, request->context->v8_context()),
+    converter->ToV8Value(&responseList, v8_context->v8_context()),
     v8::String::New(error.c_str())
   };
 
   v8::Handle<v8::Value> retval;
-  CHECK(request->context->CallChromeHiddenMethod("handleResponse",
-                                                  arraysize(argv),
-                                                  argv,
-                                                  &retval));
+  CHECK(v8_context->CallChromeHiddenMethod("handleResponse",
+                                           arraysize(argv),
+                                           argv,
+                                           &retval));
   // In debug, the js will validate the callback parameters and return a
   // string if a validation error has occured.
   if (DCHECK_IS_ON()) {
@@ -149,14 +164,4 @@
   }
 }
 
-void RequestSender::InvalidateContext(ChromeV8Context* context) {
-  for (PendingRequestMap::iterator it = pending_requests_.begin();
-       it != pending_requests_.end();) {
-    if (it->second->context == context)
-      pending_requests_.erase(it++);
-    else
-      ++it;
-  }
-}
-
 }  // namespace extensions
diff --git a/chrome/renderer/extensions/request_sender.h b/chrome/renderer/extensions/request_sender.h
index b1463fd..a7bbdb8 100644
--- a/chrome/renderer/extensions/request_sender.h
+++ b/chrome/renderer/extensions/request_sender.h
@@ -16,7 +16,7 @@
 }
 
 namespace extensions {
-class ChromeV8Context;
+class ChromeV8ContextSet;
 class Dispatcher;
 
 struct PendingRequest;
@@ -25,17 +25,15 @@
 // extension host and routing the responses back to the caller.
 class RequestSender {
  public:
-  explicit RequestSender(Dispatcher* dispatcher);
+  explicit RequestSender(Dispatcher* dispatcher,
+                         ChromeV8ContextSet* context_set);
   ~RequestSender();
 
   // Makes a call to the API function |name| that is to be handled by the
   // extension host. The response to this request will be received in
   // HandleResponse().
   // TODO(koz): Remove |request_id| and generate that internally.
-  //            There are multiple of these per render view though, so we'll
-  //            need to vend the IDs centrally.
-  void StartRequest(ChromeV8Context* target_context,
-                    const std::string& name,
+  void StartRequest(const std::string& name,
                     int request_id,
                     bool has_callback,
                     bool for_io_thread,
@@ -47,9 +45,6 @@
                       const base::ListValue& response,
                       const std::string& error);
 
-  // Notifies this that a context is no longer valid.
-  // TODO(kalman): Do this in a generic/safe way.
-  void InvalidateContext(ChromeV8Context* context);
 
  private:
   typedef std::map<int, linked_ptr<PendingRequest> > PendingRequestMap;
@@ -59,6 +54,7 @@
 
   Dispatcher* dispatcher_;
   PendingRequestMap pending_requests_;
+  ChromeV8ContextSet* context_set_;
 
   DISALLOW_COPY_AND_ASSIGN(RequestSender);
 };
diff --git a/chrome/renderer/extensions/runtime_custom_bindings.cc b/chrome/renderer/extensions/runtime_custom_bindings.cc
index 22af19c..211ad62 100644
--- a/chrome/renderer/extensions/runtime_custom_bindings.cc
+++ b/chrome/renderer/extensions/runtime_custom_bindings.cc
@@ -18,10 +18,8 @@
 
 namespace extensions {
 
-RuntimeCustomBindings::RuntimeCustomBindings(Dispatcher* dispatcher,
-                                             ChromeV8Context* context)
-    : ChromeV8Extension(dispatcher, context->v8_context()),
-      context_(context) {
+RuntimeCustomBindings::RuntimeCustomBindings(ChromeV8Context* context)
+    : ChromeV8Extension(NULL), context_(context) {
   RouteFunction("GetManifest",
       base::Bind(&RuntimeCustomBindings::GetManifest, base::Unretained(this)));
   RouteStaticFunction("OpenChannelToExtension", &OpenChannelToExtension);
@@ -35,8 +33,7 @@
     const v8::Arguments& args) {
   // Get the current RenderView so that we can send a routed IPC message from
   // the correct source.
-  RuntimeCustomBindings* self = GetFromArguments<RuntimeCustomBindings>(args);
-  content::RenderView* renderview = self->GetRenderView();
+  content::RenderView* renderview = GetCurrentRenderView();
   if (!renderview)
     return v8::Undefined();
 
@@ -64,8 +61,7 @@
     const v8::Arguments& args) {
   // Get the current RenderView so that we can send a routed IPC message from
   // the correct source.
-  RuntimeCustomBindings* self = GetFromArguments<RuntimeCustomBindings>(args);
-  content::RenderView* renderview = self->GetRenderView();
+  content::RenderView* renderview = GetCurrentRenderView();
   if (!renderview)
     return v8::Undefined();
 
diff --git a/chrome/renderer/extensions/runtime_custom_bindings.h b/chrome/renderer/extensions/runtime_custom_bindings.h
index 84ddf04a..c2d2509 100644
--- a/chrome/renderer/extensions/runtime_custom_bindings.h
+++ b/chrome/renderer/extensions/runtime_custom_bindings.h
@@ -17,7 +17,7 @@
 // The native component of custom bindings for the chrome.runtime API.
 class RuntimeCustomBindings : public ChromeV8Extension {
  public:
-  RuntimeCustomBindings(Dispatcher* dispatcher, ChromeV8Context* context);
+  explicit RuntimeCustomBindings(ChromeV8Context* context);
 
   virtual ~RuntimeCustomBindings();
 
diff --git a/chrome/renderer/extensions/send_request_natives.cc b/chrome/renderer/extensions/send_request_natives.cc
index f0249b15..b788a8a 100644
--- a/chrome/renderer/extensions/send_request_natives.cc
+++ b/chrome/renderer/extensions/send_request_natives.cc
@@ -13,11 +13,8 @@
 namespace extensions {
 
 SendRequestNatives::SendRequestNatives(Dispatcher* dispatcher,
-                                       RequestSender* request_sender,
-                                       ChromeV8Context* context)
-    : ChromeV8Extension(dispatcher, context->v8_context()),
-      request_sender_(request_sender),
-      context_(context) {
+                                       RequestSender* request_sender)
+    : ChromeV8Extension(dispatcher), request_sender_(request_sender) {
   RouteFunction("GetNextRequestId",
                 base::Bind(&SendRequestNatives::GetNextRequestId,
                            base::Unretained(this)));
@@ -45,21 +42,21 @@
   scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
 
   // See http://crbug.com/149880. The context menus APIs relies on this, but
-  // we shouldn't really be doing it (e.g. for the sake of the storage API).
+  // we shouln't really be doing it (e.g. for the sake of the storage API).
   converter->SetFunctionAllowed(true);
 
   if (!preserve_null_in_objects)
     converter->SetStripNullFromObjects(true);
 
-  scoped_ptr<Value> value_args(converter->FromV8Value(args[1], v8_context()));
+  scoped_ptr<Value> value_args(
+      converter->FromV8Value(args[1], v8::Context::GetCurrent()));
   if (!value_args.get() || !value_args->IsType(Value::TYPE_LIST)) {
     NOTREACHED() << "Unable to convert args passed to StartRequest";
     return v8::Undefined();
   }
 
-  request_sender_->StartRequest(
-      context_, name, request_id, has_callback, for_io_thread,
-      static_cast<ListValue*>(value_args.get()));
+  request_sender_->StartRequest(name, request_id, has_callback, for_io_thread,
+                                static_cast<ListValue*>(value_args.get()));
   return v8::Undefined();
 }
 
diff --git a/chrome/renderer/extensions/send_request_natives.h b/chrome/renderer/extensions/send_request_natives.h
index d8546f3..d92ffe3 100644
--- a/chrome/renderer/extensions/send_request_natives.h
+++ b/chrome/renderer/extensions/send_request_natives.h
@@ -17,9 +17,7 @@
 // the browser.
 class SendRequestNatives : public ChromeV8Extension {
  public:
-  SendRequestNatives(Dispatcher* dispatcher,
-                     RequestSender* request_sender,
-                     ChromeV8Context* context);
+  SendRequestNatives(Dispatcher* dispatcher, RequestSender* request_sender);
 
  private:
   v8::Handle<v8::Value> GetNextRequestId(const v8::Arguments& args);
@@ -29,8 +27,6 @@
 
   RequestSender* request_sender_;
 
-  ChromeV8Context* context_;
-
   DISALLOW_COPY_AND_ASSIGN(SendRequestNatives);
 };
 
diff --git a/chrome/renderer/extensions/set_icon_natives.cc b/chrome/renderer/extensions/set_icon_natives.cc
index 084c5a8..369cb07 100644
--- a/chrome/renderer/extensions/set_icon_natives.cc
+++ b/chrome/renderer/extensions/set_icon_natives.cc
@@ -24,11 +24,9 @@
 namespace extensions {
 
 SetIconNatives::SetIconNatives(Dispatcher* dispatcher,
-                               RequestSender* request_sender,
-                               ChromeV8Context* context)
-    : ChromeV8Extension(dispatcher, context->v8_context()),
-      request_sender_(request_sender),
-      context_(context) {
+                               RequestSender* request_sender)
+    : ChromeV8Extension(dispatcher),
+      request_sender_(request_sender) {
   RouteFunction(
       "SetIconCommon",
       base::Bind(&SetIconNatives::SetIconCommon, base::Unretained(this)));
@@ -138,8 +136,7 @@
   bool has_callback = args[3]->BooleanValue();
   bool for_io_thread = args[4]->BooleanValue();
 
-  request_sender_->StartRequest(context_,
-                                name,
+  request_sender_->StartRequest(name,
                                 request_id,
                                 has_callback,
                                 for_io_thread,
diff --git a/chrome/renderer/extensions/set_icon_natives.h b/chrome/renderer/extensions/set_icon_natives.h
index 7e0352a..b03bcee 100644
--- a/chrome/renderer/extensions/set_icon_natives.h
+++ b/chrome/renderer/extensions/set_icon_natives.h
@@ -20,9 +20,7 @@
 // Functions exposed to extension JS to implement the setIcon extension API.
 class SetIconNatives : public ChromeV8Extension {
  public:
-  SetIconNatives(Dispatcher* dispatcher,
-                 RequestSender* request_sender,
-                 ChromeV8Context* context);
+  SetIconNatives(Dispatcher* dispatcher, RequestSender* request_sender);
 
  private:
   bool ConvertImageDataToBitmapValue(const v8::Local<v8::Object> image_data,
@@ -30,11 +28,8 @@
   bool ConvertImageDataSetToBitmapValueSet(const v8::Arguments& args,
                                            DictionaryValue* bitmap_value);
   v8::Handle<v8::Value> SetIconCommon(const v8::Arguments& args);
-
   RequestSender* request_sender_;
 
-  ChromeV8Context* context_;
-
   DISALLOW_COPY_AND_ASSIGN(SetIconNatives);
 };
 
diff --git a/chrome/renderer/extensions/tabs_custom_bindings.cc b/chrome/renderer/extensions/tabs_custom_bindings.cc
index 7d16093..5492d34 100644
--- a/chrome/renderer/extensions/tabs_custom_bindings.cc
+++ b/chrome/renderer/extensions/tabs_custom_bindings.cc
@@ -13,9 +13,8 @@
 
 namespace extensions {
 
-TabsCustomBindings::TabsCustomBindings(Dispatcher* dispatcher,
-                                       v8::Handle<v8::Context> context)
-    : ChromeV8Extension(dispatcher, context) {
+TabsCustomBindings::TabsCustomBindings()
+    : ChromeV8Extension(NULL) {
   RouteStaticFunction("OpenChannelToTab", &OpenChannelToTab);
 }
 
@@ -24,8 +23,7 @@
     const v8::Arguments& args) {
   // Get the current RenderView so that we can send a routed IPC message from
   // the correct source.
-  TabsCustomBindings* self = GetFromArguments<TabsCustomBindings>(args);
-  content::RenderView* renderview = self->GetRenderView();
+  content::RenderView* renderview = GetCurrentRenderView();
   if (!renderview)
     return v8::Undefined();
 
diff --git a/chrome/renderer/extensions/tabs_custom_bindings.h b/chrome/renderer/extensions/tabs_custom_bindings.h
index 2ac19bb..0576212 100644
--- a/chrome/renderer/extensions/tabs_custom_bindings.h
+++ b/chrome/renderer/extensions/tabs_custom_bindings.h
@@ -12,7 +12,7 @@
 // Implements custom bindings for the tabs API.
 class TabsCustomBindings : public ChromeV8Extension {
  public:
-  TabsCustomBindings(Dispatcher* dispatcher, v8::Handle<v8::Context> context);
+  TabsCustomBindings();
 
  private:
   // Creates a new messaging channel to the tab with the given ID.
diff --git a/chrome/renderer/extensions/v8_schema_registry.h b/chrome/renderer/extensions/v8_schema_registry.h
index 17eb667..ab3ecf0 100644
--- a/chrome/renderer/extensions/v8_schema_registry.h
+++ b/chrome/renderer/extensions/v8_schema_registry.h
@@ -24,10 +24,10 @@
   // Returns a v8::Array with all the schemas for the APIs in |apis|.
   v8::Handle<v8::Array> GetSchemas(const std::set<std::string>& apis);
 
+ private:
   // Returns a v8::Object for the schema for |api|, possibly from the cache.
   v8::Handle<v8::Object> GetSchema(const std::string& api);
 
- private:
   // Cache of schemas.
   typedef std::map<std::string, v8::Persistent<v8::Object> > SchemaCache;
   SchemaCache schema_cache_;
diff --git a/chrome/renderer/resources/extensions/apitest.js b/chrome/renderer/resources/extensions/apitest.js
new file mode 100644
index 0000000..52414c2
--- /dev/null
+++ b/chrome/renderer/resources/extensions/apitest.js
@@ -0,0 +1,274 @@
+// 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.
+
+// extension_apitest.js
+// mini-framework for ExtensionApiTest browser tests
+
+  chrome.test = chrome.test || {};
+
+  chrome.test.tests = chrome.test.tests || [];
+
+  var currentTest = null;
+  var lastTest = null;
+  var testsFailed = 0;
+  var testCount = 1;
+  var failureException = 'chrome.test.failure';
+
+  // Helper function to get around the fact that function names in javascript
+  // are read-only, and you can't assign one to anonymous functions.
+  function testName(test) {
+    return test ? (test.name || test.generatedName) : "(no test)";
+  }
+
+  function testDone() {
+    // Use setTimeout here to allow previous test contexts to be
+    // eligible for garbage collection.
+    setTimeout(chrome.test.runNextTest, 0);
+  }
+
+  function allTestsDone() {
+    if (testsFailed == 0) {
+      chrome.test.notifyPass();
+    } else {
+      chrome.test.notifyFail('Failed ' + testsFailed + ' of ' +
+                             testCount + ' tests');
+    }
+
+    // Try to get the script to stop running immediately.
+    // This isn't an error, just an attempt at saying "done".
+    throw "completed";
+  }
+
+  var pendingCallbacks = 0;
+
+  chrome.test.callbackAdded = function() {
+    pendingCallbacks++;
+
+    var called = false;
+    return function() {
+      chrome.test.assertFalse(called, 'callback has already been run');
+      called = true;
+
+      pendingCallbacks--;
+      if (pendingCallbacks == 0) {
+        chrome.test.succeed();
+      }
+    };
+  };
+
+  chrome.test.runNextTest = function() {
+    // There may have been callbacks which were interrupted by failure
+    // exceptions.
+    pendingCallbacks = 0;
+
+    lastTest = currentTest;
+    currentTest = chrome.test.tests.shift();
+
+    if (!currentTest) {
+      allTestsDone();
+      return;
+    }
+
+    try {
+      chrome.test.log("( RUN      ) " + testName(currentTest));
+      currentTest.call();
+    } catch (e) {
+      if (e !== failureException)
+        chrome.test.fail('uncaught exception: ' + e);
+    }
+  };
+
+  chrome.test.fail = function(message) {
+    chrome.test.log("(  FAILED  ) " + testName(currentTest));
+
+    var stack = {};
+    Error.captureStackTrace(stack, chrome.test.fail);
+
+    if (!message)
+      message = "FAIL (no message)";
+
+    message += "\n" + stack.stack;
+    console.log("[FAIL] " + testName(currentTest) + ": " + message);
+    testsFailed++;
+    testDone();
+
+    // Interrupt the rest of the test.
+    throw failureException;
+  };
+
+  chrome.test.succeed = function() {
+    console.log("[SUCCESS] " + testName(currentTest));
+    chrome.test.log("(  SUCCESS )");
+    testDone();
+  };
+
+  chrome.test.assertTrue = function(test, message) {
+    chrome.test.assertBool(test, true, message);
+  };
+
+  chrome.test.assertFalse = function(test, message) {
+    chrome.test.assertBool(test, false, message);
+  };
+
+  chrome.test.assertBool = function(test, expected, message) {
+    if (test !== expected) {
+      if (typeof(test) == "string") {
+        if (message)
+          message = test + "\n" + message;
+        else
+          message = test;
+      }
+      chrome.test.fail(message);
+    }
+  };
+
+  chrome.test.checkDeepEq = function (expected, actual) {
+    if ((expected === null) != (actual === null))
+      return false;
+
+    if (expected === actual)
+      return true;
+
+    if (typeof(expected) !== typeof(actual))
+      return false;
+
+    for (var p in actual) {
+      if (actual.hasOwnProperty(p) && !expected.hasOwnProperty(p))
+        return false;
+    }
+    for (var p in expected) {
+      if (expected.hasOwnProperty(p) && !actual.hasOwnProperty(p))
+        return false;
+    }
+
+    for (var p in expected) {
+      var eq = true;
+      switch (typeof(expected[p])) {
+        case 'object':
+          eq = chrome.test.checkDeepEq(expected[p], actual[p]);
+          break;
+        case 'function':
+          eq = (typeof(actual[p]) != 'undefined' &&
+                expected[p].toString() == actual[p].toString());
+          break;
+        default:
+          eq = (expected[p] == actual[p] &&
+                typeof(expected[p]) == typeof(actual[p]));
+          break;
+      }
+      if (!eq)
+        return false;
+    }
+    return true;
+  };
+
+  chrome.test.assertEq = function(expected, actual, message) {
+    var error_msg = "API Test Error in " + testName(currentTest);
+    if (message)
+      error_msg += ": " + message;
+    if (typeof(expected) == 'object') {
+      if (!chrome.test.checkDeepEq(expected, actual)) {
+        chrome.test.fail(error_msg +
+                         "\nActual: " + JSON.stringify(actual) +
+                         "\nExpected: " + JSON.stringify(expected));
+      }
+      return;
+    }
+    if (expected != actual) {
+      chrome.test.fail(error_msg +
+                       "\nActual: " + actual + "\nExpected: " + expected);
+    }
+    if (typeof(expected) != typeof(actual)) {
+      chrome.test.fail(error_msg +
+                       " (type mismatch)\nActual Type: " + typeof(actual) +
+                       "\nExpected Type:" + typeof(expected));
+    }
+  };
+
+  chrome.test.assertNoLastError = function() {
+    if (chrome.runtime.lastError != undefined) {
+      chrome.test.fail("lastError.message == " +
+                       chrome.runtime.lastError.message);
+    }
+  };
+
+  chrome.test.assertLastError = function(expectedError) {
+    chrome.test.assertEq(typeof(expectedError), 'string');
+    chrome.test.assertTrue(chrome.runtime.lastError != undefined,
+        "No lastError, but expected " + expectedError);
+    chrome.test.assertEq(expectedError, chrome.runtime.lastError.message);
+  }
+
+  function safeFunctionApply(func, args) {
+    try {
+      if (func)
+        func.apply(null, args);
+    } catch (e) {
+      var msg = "uncaught exception " + e;
+      chrome.test.fail(msg);
+    }
+  };
+
+  // Wrapper for generating test functions, that takes care of calling
+  // assertNoLastError() and (optionally) succeed() for you.
+  chrome.test.callback = function(func, expectedError) {
+    if (func) {
+      chrome.test.assertEq(typeof(func), 'function');
+    }
+    var callbackCompleted = chrome.test.callbackAdded();
+
+    return function() {
+      if (expectedError == null) {
+        chrome.test.assertNoLastError();
+      } else {
+        chrome.test.assertLastError(expectedError);
+      }
+
+      if (func) {
+        safeFunctionApply(func, arguments);
+      }
+
+      callbackCompleted();
+    };
+  };
+
+  chrome.test.listenOnce = function(event, func) {
+    var callbackCompleted = chrome.test.callbackAdded();
+    var listener = function() {
+      event.removeListener(listener);
+      safeFunctionApply(func, arguments);
+      callbackCompleted();
+    };
+    event.addListener(listener);
+  };
+
+  chrome.test.listenForever = function(event, func) {
+    var callbackCompleted = chrome.test.callbackAdded();
+
+    var listener = function() {
+      safeFunctionApply(func, arguments);
+    };
+
+    var done = function() {
+      event.removeListener(listener);
+      callbackCompleted();
+    };
+
+    event.addListener(listener);
+    return done;
+  };
+
+  chrome.test.callbackPass = function(func) {
+    return chrome.test.callback(func);
+  };
+
+  chrome.test.callbackFail = function(expectedError, func) {
+    return chrome.test.callback(func, expectedError);
+  };
+
+  chrome.test.runTests = function(tests) {
+    chrome.test.tests = tests;
+    testCount = chrome.test.tests.length;
+    chrome.test.runNextTest();
+  };
diff --git a/chrome/renderer/resources/extensions/app_custom_bindings.js b/chrome/renderer/resources/extensions/app_custom_bindings.js
index 0b9dd89..1a8bbcc 100644
--- a/chrome/renderer/resources/extensions/app_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/app_custom_bindings.js
@@ -2,11 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the app API.
+// Custom bindings for the app API.
 
 var appNatives = requireNative('app');
-var chrome = requireNative('chrome').GetChrome();
-var GetAvailability = requireNative('v8_context').GetAvailability;
 
 // This becomes chrome.app
 var app = {
@@ -45,7 +43,7 @@
 
 // appNotification stuff.
 //
-// TODO(kalman): move this stuff to its own custom binding.
+// TODO(kalman): move this stuff to its own custom bindings.
 // It will be bit tricky since I'll need to look into why there are
 // permissions defined for app notifications, yet this always sets it up?
 var callbacks = {};
@@ -69,11 +67,8 @@
   appNatives.GetInstallState(callbackId);
 };
 
-// These must match the names in InstallAppbinding() in
+// These must match the names in InstallAppBindings() in
 // chrome/renderer/extensions/dispatcher.cc.
-var availability = GetAvailability('app');
-if (availability.is_available) {
-  exports.chromeApp = app;
-  exports.chromeAppNotifications = appNotifications;
-  exports.chromeHiddenApp = chromeHiddenApp;
-}
+exports.chromeApp = app;
+exports.chromeAppNotifications = appNotifications;
+exports.chromeHiddenApp = chromeHiddenApp;
diff --git a/chrome/renderer/resources/extensions/app_runtime_custom_bindings.js b/chrome/renderer/resources/extensions/app_runtime_custom_bindings.js
index a50c964..0a70fc4 100644
--- a/chrome/renderer/resources/extensions/app_runtime_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/app_runtime_custom_bindings.js
@@ -2,12 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the chrome.app.runtime API.
-
-var binding = require('binding').Binding.create('app.runtime');
+// Custom bindings for the chrome.app.runtime API.
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var chrome = requireNative('chrome').GetChrome();
 var fileSystemHelpers = requireNative('file_system_natives');
 var GetIsolatedFileSystem = fileSystemHelpers.GetIsolatedFileSystem;
 var appNatives = requireNative('app_runtime');
@@ -52,5 +49,3 @@
     dispatch([]);
   }
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/app_window_custom_bindings.js b/chrome/renderer/resources/extensions/app_window_custom_bindings.js
index a145f67..1bb5bef 100644
--- a/chrome/renderer/resources/extensions/app_window_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/app_window_custom_bindings.js
@@ -2,21 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the app_window API.
+// Custom bindings for the app_window API.
 
-var Binding = require('binding').Binding;
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var chrome = requireNative('chrome').GetChrome();
 var sendRequest = require('sendRequest').sendRequest;
 var appWindowNatives = requireNative('app_window');
 var forEach = require('utils').forEach;
 var GetView = appWindowNatives.GetView;
 var OnContextReady = appWindowNatives.OnContextReady;
 
-var appWindow = Binding.create('app.window');
-appWindow.registerCustomHook(function(bindingsAPI) {
+chromeHidden.registerCustomHook('app.window', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
-
   apiFunctions.setCustomCallback('create',
                                  function(name, request, windowParams) {
     var view = null;
@@ -85,12 +81,10 @@
   // This is an internal function, but needs to be bound with setHandleRequest
   // because it is called from a different JS context.
   apiFunctions.setHandleRequest('initializeAppWindow', function(params) {
-    var currentWindowInternal =
-        Binding.create('app.currentWindowInternal').generate();
     var AppWindow = function() {};
-    forEach(currentWindowInternal, function(fn) {
+    forEach(chromeHidden.internalAPIs.app.currentWindowInternal, function(fn) {
       AppWindow.prototype[fn] =
-          currentWindowInternal[fn];
+          chromeHidden.internalAPIs.app.currentWindowInternal[fn];
     });
     AppWindow.prototype.moveTo = window.moveTo.bind(window);
     AppWindow.prototype.resizeTo = window.resizeTo.bind(window);
@@ -154,5 +148,3 @@
       (oldData.maximized && !update.maximized))
     currentWindow["onRestored"].dispatch();
 };
-
-exports.binding = appWindow.generate();
diff --git a/chrome/renderer/resources/extensions/binding.js b/chrome/renderer/resources/extensions/binding.js
deleted file mode 100644
index d6fb4df..0000000
--- a/chrome/renderer/resources/extensions/binding.js
+++ /dev/null
@@ -1,407 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-require('json_schema');
-require('event_bindings');
-var schemaRegistry = requireNative('schema_registry');
-var sendRequest = require('sendRequest').sendRequest;
-var utils = require('utils');
-var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var chrome = requireNative('chrome').GetChrome();
-var schemaUtils = require('schemaUtils');
-var process = requireNative('process');
-var manifestVersion = process.GetManifestVersion();
-var extensionId = process.GetExtensionId();
-var contextType = process.GetContextType();
-var GetAvailability = requireNative('v8_context').GetAvailability;
-var logging = requireNative('logging');
-
-// Stores the name and definition of each API function, with methods to
-// modify their behaviour (such as a custom way to handle requests to the
-// API, a custom callback, etc).
-function APIFunctions() {
-  this.apiFunctions_ = {};
-  this.unavailableApiFunctions_ = {};
-}
-
-APIFunctions.prototype.register = function(apiName, apiFunction) {
-  this.apiFunctions_[apiName] = apiFunction;
-};
-
-// Registers a function as existing but not available, meaning that calls to
-// the set* methods that reference this function should be ignored rather
-// than throwing Errors.
-APIFunctions.prototype.registerUnavailable = function(apiName) {
-  this.unavailableApiFunctions_[apiName] = apiName;
-};
-
-APIFunctions.prototype.setHook_ =
-    function(apiName, propertyName, customizedFunction) {
-  if (this.unavailableApiFunctions_.hasOwnProperty(apiName))
-    return;
-  if (!this.apiFunctions_.hasOwnProperty(apiName))
-    throw new Error('Tried to set hook for unknown API "' + apiName + '"');
-  this.apiFunctions_[apiName][propertyName] = customizedFunction;
-};
-
-APIFunctions.prototype.setHandleRequest =
-    function(apiName, customizedFunction) {
-  return this.setHook_(apiName, 'handleRequest', customizedFunction);
-};
-
-APIFunctions.prototype.setUpdateArgumentsPostValidate =
-    function(apiName, customizedFunction) {
-  return this.setHook_(
-    apiName, 'updateArgumentsPostValidate', customizedFunction);
-};
-
-APIFunctions.prototype.setUpdateArgumentsPreValidate =
-    function(apiName, customizedFunction) {
-  return this.setHook_(
-    apiName, 'updateArgumentsPreValidate', customizedFunction);
-};
-
-APIFunctions.prototype.setCustomCallback =
-    function(apiName, customizedFunction) {
-  return this.setHook_(apiName, 'customCallback', customizedFunction);
-};
-
-function CustomBindingsObject() {
-}
-
-CustomBindingsObject.prototype.setSchema = function(schema) {
-  // The functions in the schema are in list form, so we move them into a
-  // dictionary for easier access.
-  var self = this;
-  self.functionSchemas = {};
-  schema.functions.forEach(function(f) {
-    self.functionSchemas[f.name] = {
-      name: f.name,
-      definition: f
-    }
-  });
-};
-
-// Get the platform from navigator.appVersion.
-function getPlatform() {
-  var platforms = [
-    [/CrOS Touch/, "chromeos touch"],
-    [/CrOS/, "chromeos"],
-    [/Linux/, "linux"],
-    [/Mac/, "mac"],
-    [/Win/, "win"],
-  ];
-
-  for (var i = 0; i < platforms.length; i++) {
-    if (platforms[i][0].test(navigator.appVersion)) {
-      return platforms[i][1];
-    }
-  }
-  return "unknown";
-}
-
-function isPlatformSupported(schemaNode, platform) {
-  return !schemaNode.platforms ||
-      schemaNode.platforms.indexOf(platform) > -1;
-}
-
-function isManifestVersionSupported(schemaNode, manifestVersion) {
-  return !schemaNode.maximumManifestVersion ||
-      manifestVersion <= schemaNode.maximumManifestVersion;
-}
-
-function isSchemaNodeSupported(schemaNode, platform, manifestVersion) {
-  return isPlatformSupported(schemaNode, platform) &&
-      isManifestVersionSupported(schemaNode, manifestVersion);
-}
-
-var platform = getPlatform();
-
-function Binding(schema) {
-  this.schema_ = schema;
-  this.apiFunctions_ = new APIFunctions();
-  this.customEvent_ = null;
-  this.customTypes_ = {};
-  this.customHooks_ = [];
-};
-
-Binding.create = function(apiName) {
-  return new Binding(schemaRegistry.GetSchema(apiName));
-};
-
-Binding.prototype = {
-  // The API through which the ${api_name}_custom_bindings.js files customize
-  // their API bindings beyond what can be generated.
-  //
-  // There are 2 types of customizations available: those which are required in
-  // order to do the schema generation (registerCustomEvent and
-  // registerCustomType), and those which can only run after the bindings have
-  // been generated (registerCustomHook).
-  //
-
-  // Registers a custom type referenced via "$ref" fields in the API schema
-  // JSON.
-  registerCustomType: function(typeName, customTypeFactory) {
-    var customType = customTypeFactory();
-    customType.prototype = new CustomBindingsObject();
-    this.customTypes_[typeName] = customType;
-  },
-
-  // Registers a custom event type for the API identified by |namespace|.
-  // |event| is the event's constructor.
-  registerCustomEvent: function(event) {
-    this.customEvent_ = event;
-  },
-
-  // Registers a function |hook| to run after the schema for all APIs has been
-  // generated.  The hook is passed as its first argument an "API" object to
-  // interact with, and second the current extension ID. See where
-  // |customHooks| is used.
-  registerCustomHook: function(fn) {
-    this.customHooks_.push(fn);
-  },
-
-  // TODO(kalman/cduvall): Refactor this so |runHooks_| is not needed.
-  runHooks_: function(api) {
-    this.customHooks_.forEach(function(hook) {
-      if (!isSchemaNodeSupported(this.schema_, platform, manifestVersion))
-        return;
-
-      if (!hook)
-        return;
-
-      hook({
-        apiFunctions: this.apiFunctions_,
-        schema: this.schema_,
-        compiledApi: api
-      }, extensionId, contextType);
-    }, this);
-  },
-
-  // Generates the bindings from |this.schema_| and integrates any custom
-  // bindings that might be present.
-  generate: function() {
-    var schema = this.schema_;
-    var customTypes = this.customTypes_;
-
-    // TODO(kalman/cduvall): Make GetAvailability handle this, then delete the
-    // supporting code.
-    if (!isSchemaNodeSupported(schema, platform, manifestVersion))
-      return;
-
-    var availability = GetAvailability(schema.namespace);
-    if (!availability.is_available) {
-      console.error('chrome.' + schema.namespace + ' is not available: ' +
-                        availability.message);
-      return;
-    }
-
-    // See comment on internalAPIs at the top.
-    var mod = {};
-
-    var namespaces = schema.namespace.split('.');
-    for (var index = 0, name; name = namespaces[index]; index++) {
-      mod[name] = mod[name] || {};
-      mod = mod[name];
-    }
-
-    // Add types to global schemaValidator
-    if (schema.types) {
-      schema.types.forEach(function(t) {
-        if (!isSchemaNodeSupported(t, platform, manifestVersion))
-          return;
-
-        schemaUtils.schemaValidator.addTypes(t);
-        if (t.type == 'object' && this.customTypes_[t.id]) {
-          var parts = t.id.split(".");
-          this.customTypes_[t.id].prototype.setSchema(t);
-          mod[parts[parts.length - 1]] = this.customTypes_[t.id];
-        }
-      }, this);
-    }
-
-    // Returns whether access to the content of a schema should be denied,
-    // based on the presence of "unprivileged" and whether this is an
-    // extension process (versus e.g. a content script).
-    function isSchemaAccessAllowed(itemSchema) {
-      return (contextType == 'BLESSED_EXTENSION') ||
-             schema.unprivileged ||
-             itemSchema.unprivileged;
-    };
-
-    // Adds a getter that throws an access denied error to object |mod|
-    // for property |name|.
-    function addUnprivilegedAccessGetter(mod, name) {
-      mod.__defineGetter__(name, function() {
-        throw new Error(
-            '"' + name + '" can only be used in extension processes. See ' +
-            'the content scripts documentation for more details.');
-      });
-    }
-
-    // Setup Functions.
-    if (schema.functions) {
-      schema.functions.forEach(function(functionDef) {
-        if (functionDef.name in mod) {
-          throw new Error('Function ' + functionDef.name +
-                          ' already defined in ' + schema.namespace);
-        }
-
-        if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) {
-          this.apiFunctions_.registerUnavailable(functionDef.name);
-          return;
-        }
-        if (!isSchemaAccessAllowed(functionDef)) {
-          this.apiFunctions_.registerUnavailable(functionDef.name);
-          addUnprivilegedAccessGetter(mod, functionDef.name);
-          return;
-        }
-
-        var apiFunction = {};
-        apiFunction.definition = functionDef;
-        apiFunction.name = schema.namespace + '.' + functionDef.name;
-
-        // TODO(aa): It would be best to run this in a unit test, but in order
-        // to do that we would need to better factor this code so that it
-        // doesn't depend on so much v8::Extension machinery.
-        if (chromeHidden.validateAPI &&
-            schemaUtils.isFunctionSignatureAmbiguous(
-                apiFunction.definition)) {
-          throw new Error(
-              apiFunction.name + ' has ambiguous optional arguments. ' +
-              'To implement custom disambiguation logic, add ' +
-              '"allowAmbiguousOptionalArguments" to the function\'s schema.');
-        }
-
-        this.apiFunctions_.register(functionDef.name, apiFunction);
-
-        mod[functionDef.name] = (function() {
-          var args = Array.prototype.slice.call(arguments);
-          if (this.updateArgumentsPreValidate)
-            args = this.updateArgumentsPreValidate.apply(this, args);
-
-          args = schemaUtils.normalizeArgumentsAndValidate(args, this);
-          if (this.updateArgumentsPostValidate)
-            args = this.updateArgumentsPostValidate.apply(this, args);
-
-          var retval;
-          if (this.handleRequest) {
-            retval = this.handleRequest.apply(this, args);
-          } else {
-            var optArgs = {
-              customCallback: this.customCallback
-            };
-            retval = sendRequest(this.name, args,
-                                 this.definition.parameters,
-                                 optArgs);
-          }
-
-          // Validate return value if defined - only in debug.
-          if (chromeHidden.validateCallbacks &&
-              this.definition.returns) {
-            schemaUtils.validate([retval], [this.definition.returns]);
-          }
-          return retval;
-        }).bind(apiFunction);
-      }, this);
-    }
-
-    // Setup Events
-    if (schema.events) {
-      schema.events.forEach(function(eventDef) {
-        if (eventDef.name in mod) {
-          throw new Error('Event ' + eventDef.name +
-                          ' already defined in ' + schema.namespace);
-        }
-        if (!isSchemaNodeSupported(eventDef, platform, manifestVersion))
-          return;
-        if (!isSchemaAccessAllowed(eventDef)) {
-          addUnprivilegedAccessGetter(mod, eventDef.name);
-          return;
-        }
-
-        var eventName = schema.namespace + "." + eventDef.name;
-        var options = eventDef.options || {};
-
-        if (eventDef.filters && eventDef.filters.length > 0)
-          options.supportsFilters = true;
-
-        if (this.customEvent_) {
-          mod[eventDef.name] = new this.customEvent_(
-              eventName, eventDef.parameters, eventDef.extraParameters,
-              options);
-        } else if (eventDef.anonymous) {
-          mod[eventDef.name] = new chrome.Event();
-        } else {
-          mod[eventDef.name] = new chrome.Event(
-              eventName, eventDef.parameters, options);
-        }
-      }, this);
-    }
-
-    function addProperties(m, parentDef) {
-      var properties = parentDef.properties;
-      if (!properties)
-        return;
-
-      utils.forEach(properties, function(propertyName, propertyDef) {
-        if (propertyName in m)
-          return;  // TODO(kalman): be strict like functions/events somehow.
-        if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion))
-          return;
-        if (!isSchemaAccessAllowed(propertyDef)) {
-          addUnprivilegedAccessGetter(m, propertyName);
-          return;
-        }
-
-        var value = propertyDef.value;
-        if (value) {
-          // Values may just have raw types as defined in the JSON, such
-          // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here.
-          // TODO(kalman): enforce that things with a "value" property can't
-          // define their own types.
-          var type = propertyDef.type || typeof(value);
-          if (type === 'integer' || type === 'number') {
-            value = parseInt(value);
-          } else if (type === 'boolean') {
-            value = value === 'true';
-          } else if (propertyDef['$ref']) {
-            if (propertyDef['$ref'] in customTypes) {
-              var constructor = customTypes[propertyDef['$ref']];
-            } else {
-              var refParts = propertyDef['$ref'].split('.');
-              // This should never try to load a $ref in the current namespace.
-              var constructor = utils.loadRefDependency(
-                  propertyDef['$ref'])[refParts[refParts.length - 1]];
-            }
-            if (!constructor)
-              throw new Error('No custom binding for ' + propertyDef['$ref']);
-            var args = value;
-            // For an object propertyDef, |value| is an array of constructor
-            // arguments, but we want to pass the arguments directly (i.e.
-            // not as an array), so we have to fake calling |new| on the
-            // constructor.
-            value = { __proto__: constructor.prototype };
-            constructor.apply(value, args);
-            // Recursively add properties.
-            addProperties(value, propertyDef);
-          } else if (type === 'object') {
-            // Recursively add properties.
-            addProperties(value, propertyDef);
-          } else if (type !== 'string') {
-            throw new Error('NOT IMPLEMENTED (extension_api.json error): ' +
-                'Cannot parse values for type "' + type + '"');
-          }
-          m[propertyName] = value;
-        }
-      });
-    };
-
-    addProperties(mod, schema);
-    this.runHooks_(mod);
-    return mod;
-  }
-};
-
-exports.Binding = Binding;
diff --git a/chrome/renderer/resources/extensions/bluetooth_custom_bindings.js b/chrome/renderer/resources/extensions/bluetooth_custom_bindings.js
index 9531b3a2..e1f75f5 100644
--- a/chrome/renderer/resources/extensions/bluetooth_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/bluetooth_custom_bindings.js
@@ -2,19 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the Bluetooth API.
-
-var binding = require('binding').Binding.create('bluetooth');
+// Custom bindings for the Bluetooth API.
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var chrome = requireNative('chrome').GetChrome();
 var sendRequest = require('sendRequest').sendRequest;
 var lastError = require('lastError');
 
-// Use custom binding to create an undocumented event listener that will
+// Use custom bindings to create an undocumented event listener that will
 // receive events about device discovery and call the event listener that was
 // provided with the request to begin discovery.
-binding.registerCustomHook(function(api) {
+chromeHidden.registerCustomHook('bluetooth', function(api) {
   var apiFunctions = api.apiFunctions;
 
   chromeHidden.bluetooth = {};
@@ -157,5 +154,3 @@
         return args;
       });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/browser_action_custom_bindings.js b/chrome/renderer/resources/extensions/browser_action_custom_bindings.js
index d90ab182..9d1d92e 100644
--- a/chrome/renderer/resources/extensions/browser_action_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/browser_action_custom_bindings.js
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the browserAction API.
+// Custom bindings for the browserAction API.
 
-var binding = require('binding').Binding.create('browserAction');
-
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var setIcon = require('setIcon').setIcon;
 
-binding.registerCustomHook(function(bindingsAPI) {
+chromeHidden.registerCustomHook('browserAction', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setHandleRequest('setIcon', function(details, callback) {
@@ -16,5 +15,3 @@
         'browser action');
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/content_settings_custom_bindings.js b/chrome/renderer/resources/extensions/content_settings_custom_bindings.js
index 8347078..d92efc1 100644
--- a/chrome/renderer/resources/extensions/content_settings_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/content_settings_custom_bindings.js
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the contentSettings API.
+// Custom bindings for the contentSettings API.
 
-var binding = require('binding').Binding.create('contentSettings');
-
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var sendRequest = require('sendRequest').sendRequest;
 var validate = require('schemaUtils').validate;
 
-binding.registerCustomType('contentSettings.ContentSetting', function() {
+chromeHidden.registerCustomType('contentSettings.ContentSetting', function() {
   function extendSchema(schema) {
     var extendedSchema = schema.slice();
     extendedSchema.unshift({'type': 'string'});
@@ -52,5 +51,3 @@
 
   return ContentSetting;
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/context_menus_custom_bindings.js b/chrome/renderer/resources/extensions/context_menus_custom_bindings.js
index 4b0a936..e2629f7 100644
--- a/chrome/renderer/resources/extensions/context_menus_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/context_menus_custom_bindings.js
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the contextMenus API.
-
-var binding = require('binding').Binding.create('contextMenus');
+// Custom bindings for the contextMenus API.
 
 var contextMenus = requireNative('context_menus');
 var GetNextContextMenuId = contextMenus.GetNextContextMenuId;
@@ -12,7 +10,7 @@
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 
-binding.registerCustomHook(function(bindingsAPI) {
+chromeHidden.registerCustomHook('contextMenus', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   chromeHidden.contextMenus = {};
@@ -100,5 +98,3 @@
     chromeHidden.contextMenus.stringIdHandlers = {};
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js b/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js
index 066413e..3655c46 100644
--- a/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js
@@ -2,21 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the declarativeContent API.
+// Custom bindings for the declarativeContent API.
 
-var binding = require('binding').Binding.create('declarativeContent');
-
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var utils = require('utils');
 var validate = require('schemaUtils').validate;
 
-binding.registerCustomHook( function(api) {
-  var declarativeContent = api.compiledApi;
-
+chromeHidden.registerCustomHook('declarativeContent', function(api) {
   // Returns the schema definition of type |typeId| defined in |namespace|.
-  function getSchema(typeId) {
-    return utils.lookup(api.schema.types,
-                        'id',
-                        'declarativeContent.' + typeId);
+  function getSchema(namespace, typeId) {
+    var apiSchema = utils.lookup(api.apiDefinitions, 'namespace', namespace);
+    var resultSchema = utils.lookup(
+        apiSchema.types, 'id', namespace + '.' + typeId);
+    return resultSchema;
   }
 
   // Helper function for the constructor of concrete datatypes of the
@@ -31,17 +29,15 @@
       }
     }
     instance.instanceType = 'declarativeContent.' + typeId;
-    var schema = getSchema(typeId);
+    var schema = getSchema('declarativeContent', typeId);
     validate([instance], [schema]);
   }
 
   // Setup all data types for the declarative content API.
-  declarativeContent.PageStateMatcher = function(parameters) {
+  chrome.declarativeContent.PageStateMatcher = function(parameters) {
     setupInstance(this, parameters, 'PageStateMatcher');
   };
-  declarativeContent.ShowPageAction = function(parameters) {
+  chrome.declarativeContent.ShowPageAction = function(parameters) {
     setupInstance(this, parameters, 'ShowPageAction');
   };
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/declarative_webrequest_custom_bindings.js b/chrome/renderer/resources/extensions/declarative_webrequest_custom_bindings.js
index 9d0c3c5..f015bf7 100644
--- a/chrome/renderer/resources/extensions/declarative_webrequest_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/declarative_webrequest_custom_bindings.js
@@ -2,23 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the declarativeWebRequest API.
-
-var binding = require('binding').Binding.create('declarativeWebRequest');
+// Custom bindings for the declarativeWebRequest API.
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var chrome = requireNative('chrome').GetChrome();
 var utils = require('utils');
 var validate = require('schemaUtils').validate;
 
-binding.registerCustomHook(function(api) {
-  var declarativeWebRequest = api.compiledApi;
-
+chromeHidden.registerCustomHook('declarativeWebRequest', function(api) {
   // Returns the schema definition of type |typeId| defined in |namespace|.
-  function getSchema(typeId) {
-    return utils.lookup(api.schema.types,
-                        'id',
-                        'declarativeWebRequest.' + typeId);
+  function getSchema(namespace, typeId) {
+    var apiSchema = utils.lookup(api.apiDefinitions, 'namespace', namespace);
+    var resultSchema = utils.lookup(
+        apiSchema.types, 'id', namespace + '.' + typeId);
+    return resultSchema;
   }
 
   // Helper function for the constructor of concrete datatypes of the
@@ -33,66 +29,64 @@
       }
     }
     instance.instanceType = 'declarativeWebRequest.' + typeId;
-    var schema = getSchema(typeId);
+    var schema = getSchema('declarativeWebRequest', typeId);
     validate([instance], [schema]);
   }
 
   // Setup all data types for the declarative webRequest API.
-  declarativeWebRequest.RequestMatcher = function(parameters) {
+  chrome.declarativeWebRequest.RequestMatcher = function(parameters) {
     setupInstance(this, parameters, 'RequestMatcher');
   };
-  declarativeWebRequest.CancelRequest = function(parameters) {
+  chrome.declarativeWebRequest.CancelRequest = function(parameters) {
     setupInstance(this, parameters, 'CancelRequest');
   };
-  declarativeWebRequest.RedirectRequest = function(parameters) {
+  chrome.declarativeWebRequest.RedirectRequest = function(parameters) {
     setupInstance(this, parameters, 'RedirectRequest');
   };
-  declarativeWebRequest.SetRequestHeader = function(parameters) {
+  chrome.declarativeWebRequest.SetRequestHeader = function(parameters) {
     setupInstance(this, parameters, 'SetRequestHeader');
   };
-  declarativeWebRequest.RemoveRequestHeader = function(parameters) {
+  chrome.declarativeWebRequest.RemoveRequestHeader = function(parameters) {
     setupInstance(this, parameters, 'RemoveRequestHeader');
   };
-  declarativeWebRequest.AddResponseHeader = function(parameters) {
+  chrome.declarativeWebRequest.AddResponseHeader = function(parameters) {
     setupInstance(this, parameters, 'AddResponseHeader');
   };
-  declarativeWebRequest.RemoveResponseHeader = function(parameters) {
+  chrome.declarativeWebRequest.RemoveResponseHeader = function(parameters) {
     setupInstance(this, parameters, 'RemoveResponseHeader');
   };
-  declarativeWebRequest.RedirectToTransparentImage =
+  chrome.declarativeWebRequest.RedirectToTransparentImage =
       function(parameters) {
     setupInstance(this, parameters, 'RedirectToTransparentImage');
   };
-  declarativeWebRequest.RedirectToEmptyDocument = function(parameters) {
+  chrome.declarativeWebRequest.RedirectToEmptyDocument = function(parameters) {
     setupInstance(this, parameters, 'RedirectToEmptyDocument');
   };
-  declarativeWebRequest.RedirectByRegEx = function(parameters) {
+  chrome.declarativeWebRequest.RedirectByRegEx = function(parameters) {
     setupInstance(this, parameters, 'RedirectByRegEx');
   };
-  declarativeWebRequest.IgnoreRules = function(parameters) {
+  chrome.declarativeWebRequest.IgnoreRules = function(parameters) {
     setupInstance(this, parameters, 'IgnoreRules');
   };
-  declarativeWebRequest.AddRequestCookie = function(parameters) {
+  chrome.declarativeWebRequest.AddRequestCookie = function(parameters) {
     setupInstance(this, parameters, 'AddRequestCookie');
   };
-  declarativeWebRequest.AddResponseCookie = function(parameters) {
+  chrome.declarativeWebRequest.AddResponseCookie = function(parameters) {
     setupInstance(this, parameters, 'AddResponseCookie');
   };
-  declarativeWebRequest.EditRequestCookie = function(parameters) {
+  chrome.declarativeWebRequest.EditRequestCookie = function(parameters) {
     setupInstance(this, parameters, 'EditRequestCookie');
   };
-  declarativeWebRequest.EditResponseCookie = function(parameters) {
+  chrome.declarativeWebRequest.EditResponseCookie = function(parameters) {
     setupInstance(this, parameters, 'EditResponseCookie');
   };
-  declarativeWebRequest.RemoveRequestCookie = function(parameters) {
+  chrome.declarativeWebRequest.RemoveRequestCookie = function(parameters) {
     setupInstance(this, parameters, 'RemoveRequestCookie');
   };
-  declarativeWebRequest.RemoveResponseCookie = function(parameters) {
+  chrome.declarativeWebRequest.RemoveResponseCookie = function(parameters) {
     setupInstance(this, parameters, 'RemoveResponseCookie');
   };
-  declarativeWebRequest.SendMessageToExtension = function(parameters) {
+  chrome.declarativeWebRequest.SendMessageToExtension = function(parameters) {
     setupInstance(this, parameters, 'SendMessageToExtension');
   };
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/event.js b/chrome/renderer/resources/extensions/event.js
index 59e04d5..4e99256 100644
--- a/chrome/renderer/resources/extensions/event.js
+++ b/chrome/renderer/resources/extensions/event.js
@@ -3,8 +3,6 @@
 // found in the LICENSE file.
 
   var DCHECK = requireNative('logging').DCHECK;
-  // TODO(cduvall/kalman): json_schema shouldn't put things on chromeHidden.
-  require('json_schema');
   var eventBindingsNatives = requireNative('event_bindings');
   var AttachEvent = eventBindingsNatives.AttachEvent;
   var DetachEvent = eventBindingsNatives.DetachEvent;
@@ -16,8 +14,8 @@
   var validate = require('schemaUtils').validate;
 
   var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-  var chrome = requireNative('chrome').GetChrome();
-  var schemaRegistry = requireNative('schema_registry');
+  var GetExtensionAPIDefinition =
+      requireNative('apiDefinitions').GetExtensionAPIDefinition;
 
   // Schemas for the rule-style functions on the events API that
   // only need to be generated occasionally, so populate them lazily.
@@ -32,7 +30,7 @@
   function ensureRuleSchemasLoaded() {
     if (ruleFunctionSchemas.addRules)
       return;
-    var eventsSchema = schemaRegistry.GetSchema("events");
+    var eventsSchema = GetExtensionAPIDefinition("events")[0];
     var eventType = utils.lookup(eventsSchema.types, 'id', 'events.Event');
 
     ruleFunctionSchemas.addRules =
@@ -153,7 +151,7 @@
   //
   // If opt_eventOptions exists, it is a dictionary that contains the boolean
   // entries "supportsListeners" and "supportsRules".
-  var Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) {
+  chrome.Event = function(opt_eventName, opt_argSchemas, opt_eventOptions) {
     this.eventName_ = opt_eventName;
     this.listeners_ = [];
     this.eventOptions_ = chromeHidden.parseEventOptions(opt_eventOptions);
@@ -229,7 +227,7 @@
   };
 
   // Registers a callback to be called when this event is dispatched.
-  Event.prototype.addListener = function(cb, filters) {
+  chrome.Event.prototype.addListener = function(cb, filters) {
     if (!this.eventOptions_.supportsListeners)
       throw new Error("This event does not support listeners.");
     if (this.eventOptions_.maxListeners &&
@@ -246,7 +244,7 @@
     this.listeners_.push(listener);
   };
 
-  Event.prototype.attach_ = function(listener) {
+  chrome.Event.prototype.attach_ = function(listener) {
     this.attachmentStrategy_.onAddedListener(listener);
     if (this.listeners_.length == 0) {
       allAttachedEvents[allAttachedEvents.length] = this;
@@ -263,7 +261,7 @@
   };
 
   // Unregisters a callback.
-  Event.prototype.removeListener = function(cb) {
+  chrome.Event.prototype.removeListener = function(cb) {
     if (!this.eventOptions_.supportsListeners)
       throw new Error("This event does not support listeners.");
     var idx = this.findListener_(cb);
@@ -291,19 +289,19 @@
   };
 
   // Test if the given callback is registered for this event.
-  Event.prototype.hasListener = function(cb) {
+  chrome.Event.prototype.hasListener = function(cb) {
     if (!this.eventOptions_.supportsListeners)
       throw new Error("This event does not support listeners.");
     return this.findListener_(cb) > -1;
   };
 
   // Test if any callbacks are registered for this event.
-  Event.prototype.hasListeners = function() {
+  chrome.Event.prototype.hasListeners = function() {
     return this.getListenerCount() > 0;
   };
 
   // Return the number of listeners on this event.
-  Event.prototype.getListenerCount = function() {
+  chrome.Event.prototype.getListenerCount = function() {
     if (!this.eventOptions_.supportsListeners)
       throw new Error("This event does not support listeners.");
     return this.listeners_.length;
@@ -311,7 +309,7 @@
 
   // Returns the index of the given callback if registered, or -1 if not
   // found.
-  Event.prototype.findListener_ = function(cb) {
+  chrome.Event.prototype.findListener_ = function(cb) {
     for (var i = 0; i < this.listeners_.length; i++) {
       if (this.listeners_[i].callback == cb) {
         return i;
@@ -321,7 +319,7 @@
     return -1;
   };
 
-  Event.prototype.dispatch_ = function(args, listenerIDs) {
+  chrome.Event.prototype.dispatch_ = function(args, listenerIDs) {
     if (!this.eventOptions_.supportsListeners)
       throw new Error("This event does not support listeners.");
     var validationErrors = this.validateEventArgs_(args);
@@ -351,28 +349,28 @@
   }
 
   // Can be overridden to support custom dispatching.
-  Event.prototype.dispatchToListener = function(callback, args) {
+  chrome.Event.prototype.dispatchToListener = function(callback, args) {
     return callback.apply(null, args);
   }
 
   // Dispatches this event object to all listeners, passing all supplied
   // arguments to this function each listener.
-  Event.prototype.dispatch = function(varargs) {
+  chrome.Event.prototype.dispatch = function(varargs) {
     return this.dispatch_(Array.prototype.slice.call(arguments), undefined);
   };
 
   // Detaches this event object from its name.
-  Event.prototype.detach_ = function() {
+  chrome.Event.prototype.detach_ = function() {
     this.attachmentStrategy_.detach(false);
   };
 
-  Event.prototype.destroy_ = function() {
+  chrome.Event.prototype.destroy_ = function() {
     this.listeners_ = [];
     this.validateEventArgs_ = [];
     this.detach_(false);
   };
 
-  Event.prototype.addRules = function(rules, opt_cb) {
+  chrome.Event.prototype.addRules = function(rules, opt_cb) {
     if (!this.eventOptions_.supportsRules)
       throw new Error("This event does not support rules.");
 
@@ -422,7 +420,7 @@
                 ruleFunctionSchemas.addRules.parameters);
   }
 
-  Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) {
+  chrome.Event.prototype.removeRules = function(ruleIdentifiers, opt_cb) {
     if (!this.eventOptions_.supportsRules)
       throw new Error("This event does not support rules.");
     ensureRuleSchemasLoaded();
@@ -435,7 +433,7 @@
                 ruleFunctionSchemas.removeRules.parameters);
   }
 
-  Event.prototype.getRules = function(ruleIdentifiers, cb) {
+  chrome.Event.prototype.getRules = function(ruleIdentifiers, cb) {
     if (!this.eventOptions_.supportsRules)
       throw new Error("This event does not support rules.");
     ensureRuleSchemasLoaded();
@@ -452,8 +450,8 @@
   // Special load events: we don't use the DOM unload because that slows
   // down tab shutdown.  On the other hand, onUnload might not always fire,
   // since Chrome will terminate renderers on shutdown (SuddenTermination).
-  chromeHidden.onLoad = new Event();
-  chromeHidden.onUnload = new Event();
+  chromeHidden.onLoad = new chrome.Event();
+  chromeHidden.onUnload = new chrome.Event();
 
   chromeHidden.dispatchOnLoad =
       chromeHidden.onLoad.dispatch.bind(chromeHidden.onLoad);
@@ -473,4 +471,4 @@
     console.error(msg);
   };
 
-  chrome.Event = Event;
+  exports.Event = chrome.Event;
diff --git a/chrome/renderer/resources/extensions/experimental.media_galleries_custom_bindings.js b/chrome/renderer/resources/extensions/experimental.media_galleries_custom_bindings.js
index 8f82c4e..07b33d4 100644
--- a/chrome/renderer/resources/extensions/experimental.media_galleries_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/experimental.media_galleries_custom_bindings.js
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the Media Gallery API.
-
-var binding = require('binding').Binding.create('experimental.mediaGalleries');
+// Custom bindings for the Media Gallery API.
 
 var mediaGalleriesNatives = requireNative('mediaGalleries');
 
-binding.registerCustomHook(function(bindingsAPI, extensionId) {
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
+
+chromeHidden.registerCustomHook('experimental.mediaGalleries',
+                                function(bindingsAPI, extensionId) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   // extractEmbeddedThumbnails uses a renderer side handler so that it can
@@ -19,5 +20,3 @@
     return mediaGalleriesNatives.ExtractEmbeddedThumbnails(fileEntry);
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/experimental.offscreenTabs_custom_bindings.js b/chrome/renderer/resources/extensions/experimental.offscreenTabs_custom_bindings.js
index 7903a3d3..3635a2c6 100644
--- a/chrome/renderer/resources/extensions/experimental.offscreenTabs_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/experimental.offscreenTabs_custom_bindings.js
@@ -2,11 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the experimental offscreenTabs API.
+// Custom bindings for the experimental offscreenTabs API.
 
-var binding = require('binding').Binding.create('experimental.offscreenTabs');
+(function() {
 
-binding.registerCustomHook(
+native function GetChromeHidden();
+
+GetChromeHidden().registerCustomHook(
     'experimental.offscreenTabs', function(api) {
   var apiFunctions = api.apiFunctions;
 
@@ -58,4 +60,4 @@
       function() { return validate(arguments, mouseEventFilter); });
 });
 
-exports.binding = binding.generate();
+})();
diff --git a/chrome/renderer/resources/extensions/extension_custom_bindings.js b/chrome/renderer/resources/extensions/extension_custom_bindings.js
index 61e5e16b..60b4f12a 100644
--- a/chrome/renderer/resources/extensions/extension_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/extension_custom_bindings.js
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the extension API.
-
-var binding = require('binding').Binding.create('extension');
+// Custom bindings for the extension API.
 
 var extensionNatives = requireNative('extension');
 var GetExtensionViews = extensionNatives.GetExtensionViews;
@@ -12,14 +10,22 @@
 var OpenChannelToExtension = runtimeNatives.OpenChannelToExtension;
 var OpenChannelToNativeApp = runtimeNatives.OpenChannelToNativeApp;
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var chrome = requireNative('chrome').GetChrome();
 var sendMessageUpdateArguments =
     require('miscellaneous_bindings').sendMessageUpdateArguments;
 
 var inIncognitoContext = requireNative('process').InIncognitoContext();
 var sendRequestIsDisabled = requireNative('process').IsSendRequestDisabled();
 var contextType = requireNative('process').GetContextType();
+
+chrome.extension = chrome.extension || {};
+
 var manifestVersion = requireNative('process').GetManifestVersion();
+if (manifestVersion < 2) {
+  chrome.self = chrome.extension;
+  chrome.extension.inIncognitoTab = inIncognitoContext;
+}
+
+chrome.extension.inIncognitoContext = inIncognitoContext;
 
 // This should match chrome.windows.WINDOW_ID_NONE.
 //
@@ -28,14 +34,8 @@
 // which may not be the case.
 var WINDOW_ID_NONE = -1;
 
-binding.registerCustomHook(function(bindingsAPI, extensionId) {
-  var extension = bindingsAPI.compiledApi;
-  if (manifestVersion < 2) {
-    chrome.self = extension;
-    extension.inIncognitoTab = inIncognitoContext;
-  }
-  extension.inIncognitoContext = inIncognitoContext;
-
+chromeHidden.registerCustomHook('extension',
+                                function(bindingsAPI, extensionId) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setHandleRequest('getViews', function(properties) {
@@ -83,7 +83,7 @@
     // getters that throw exceptions. Assume that any getter is such a function.
     if (chrome.runtime.hasOwnProperty(alias) &&
         chrome.runtime.__lookupGetter__(alias) === undefined) {
-      extension[alias] = chrome.runtime[alias];
+      chrome.extension[alias] = chrome.runtime[alias];
     }
   });
 
@@ -100,15 +100,14 @@
   });
 
   if (sendRequestIsDisabled) {
-    extension.onRequest.addListener = function() {
+    chrome.extension.onRequest.addListener = function() {
       throw new Error(sendRequestIsDisabled);
     };
     if (contextType == 'BLESSED_EXTENSION') {
-      extension.onRequestExternal.addListener = function() {
+      chrome.extension.onRequestExternal.addListener = function() {
         throw new Error(sendRequestIsDisabled);
       };
     }
   }
-});
 
-exports.binding = binding.generate();
+});
diff --git a/chrome/renderer/resources/extensions/file_browser_handler_custom_bindings.js b/chrome/renderer/resources/extensions/file_browser_handler_custom_bindings.js
index 1fbe7b08c..17af969c 100644
--- a/chrome/renderer/resources/extensions/file_browser_handler_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_browser_handler_custom_bindings.js
@@ -2,16 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the fileBrowserHandler API.
-
-var binding = require('binding').Binding.create('fileBrowserHandler');
+// Custom bindings for the fileBrowserHandler API.
 
 var fileBrowserNatives = requireNative('file_browser_handler');
 var GetExternalFileEntry = fileBrowserNatives.GetExternalFileEntry;
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var fileBrowserHandlerInternal = require('binding').Binding.create(
-    'fileBrowserHandlerInternal').generate();
 
 chromeHidden.Event.registerArgumentMassager('fileBrowserHandler.onExecute',
     function(args, dispatch) {
@@ -32,7 +28,7 @@
   dispatch(args);
 });
 
-binding.registerCustomHook(function(bindingsAPI) {
+chromeHidden.registerCustomHook('fileBrowserHandler', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setHandleRequest('selectFile',
@@ -50,9 +46,7 @@
       externalCallback(result);
     }
 
-    return fileBrowserHandlerInternal.selectFile(
+    return chromeHidden.internalAPIs.fileBrowserHandlerInternal.selectFile(
         selectionParams, internalCallback.bind(null, callback));
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/file_browser_private_custom_bindings.js b/chrome/renderer/resources/extensions/file_browser_private_custom_bindings.js
index 10c616e9..0d9369d 100644
--- a/chrome/renderer/resources/extensions/file_browser_private_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_browser_private_custom_bindings.js
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the fileBrowserPrivate API.
-
-var binding = require('binding').Binding.create('fileBrowserPrivate');
+// Custom bindings for the fileBrowserPrivate API.
 
 var fileBrowserPrivateNatives = requireNative('file_browser_private');
 var GetLocalFileSystem = fileBrowserPrivateNatives.GetLocalFileSystem;
@@ -12,7 +10,9 @@
 var fileBrowserNatives = requireNative('file_browser_handler');
 var GetExternalFileEntry = fileBrowserNatives.GetExternalFileEntry;
 
-binding.registerCustomHook(function(bindingsAPI) {
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
+
+chromeHidden.registerCustomHook('fileBrowserPrivate', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setCustomCallback('requestLocalFileSystem',
@@ -61,5 +61,3 @@
     request.callback = null;
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/file_system_custom_bindings.js b/chrome/renderer/resources/extensions/file_system_custom_bindings.js
index 058a4234..29e20e7 100644
--- a/chrome/renderer/resources/extensions/file_system_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_system_custom_bindings.js
@@ -2,19 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the fileSystem API.
+// Custom bindings for the fileSystem API.
 
-var binding = require('binding').Binding.create('fileSystem');
-
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var fileSystemNatives = requireNative('file_system_natives');
 var GetIsolatedFileSystem = fileSystemNatives.GetIsolatedFileSystem;
 var lastError = require('lastError');
 var entryIdManager = require('entryIdManager');
 
-binding.registerCustomHook(function(bindingsAPI) {
+chromeHidden.registerCustomHook('fileSystem', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
-  var fileSystem = bindingsAPI.compiledApi;
-
   function bindFileEntryFunction(functionName) {
     apiFunctions.setUpdateArgumentsPostValidate(
         functionName, function(fileEntry, callback) {
@@ -68,23 +65,21 @@
   });
 
   // TODO(benwells): Remove these deprecated versions of the functions.
-  fileSystem.getWritableFileEntry = function() {
+  chrome.fileSystem.getWritableFileEntry = function() {
     console.log("chrome.fileSystem.getWritableFileEntry is deprecated");
     console.log("Please use chrome.fileSystem.getWritableEntry instead");
-    fileSystem.getWritableEntry.apply(this, arguments);
+    chrome.fileSystem.getWritableEntry.apply(this, arguments);
   };
 
-  fileSystem.isWritableFileEntry = function() {
+  chrome.fileSystem.isWritableFileEntry = function() {
     console.log("chrome.fileSystem.isWritableFileEntry is deprecated");
     console.log("Please use chrome.fileSystem.isWritableEntry instead");
-    fileSystem.isWritableEntry.apply(this, arguments);
+    chrome.fileSystem.isWritableEntry.apply(this, arguments);
   };
 
-  fileSystem.chooseFile = function() {
+  chrome.fileSystem.chooseFile = function() {
     console.log("chrome.fileSystem.chooseFile is deprecated");
     console.log("Please use chrome.fileSystem.chooseEntry instead");
-    fileSystem.chooseEntry.apply(this, arguments);
+    chrome.fileSystem.chooseEntry.apply(this, arguments);
   };
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/i18n_custom_bindings.js b/chrome/renderer/resources/extensions/i18n_custom_bindings.js
index 39f5a7d5..14120af 100644
--- a/chrome/renderer/resources/extensions/i18n_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/i18n_custom_bindings.js
@@ -2,16 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the i18n API.
-
-var binding = require('binding').Binding.create('i18n');
+// Custom bindings for the i18n API.
 
 var i18nNatives = requireNative('i18n');
 var GetL10nMessage = i18nNatives.GetL10nMessage;
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 
-binding.registerCustomHook(function(bindingsAPI, extensionId) {
+chromeHidden.registerCustomHook('i18n', function(bindingsAPI, extensionId) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setUpdateArgumentsPreValidate('getMessage', function() {
@@ -34,5 +32,3 @@
     return GetL10nMessage(messageName, substitutions, extensionId);
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/input.ime_custom_bindings.js b/chrome/renderer/resources/extensions/input.ime_custom_bindings.js
index 79ab4a3e..60b0105 100644
--- a/chrome/renderer/resources/extensions/input.ime_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/input.ime_custom_bindings.js
@@ -2,15 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the input ime API. Only injected into the
+// Custom bindings for the input ime API. Only injected into the
 // v8 contexts for extensions which have permission for the API.
 
-var binding = require('binding').Binding.create('input.ime');
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 
-binding.registerCustomHook(function(api) {
-  var input_ime = api.compiledApi;
-
-  input_ime.onKeyEvent.dispatchToListener = function(callback, args) {
+chromeHidden.registerCustomHook('input.ime', function() {
+  chrome.input.ime.onKeyEvent.dispatchToListener = function(callback, args) {
     var engineID = args[0];
     var keyData = args[1];
 
@@ -20,21 +18,19 @@
     } catch (e) {
       console.error('Error in event handler for onKeyEvent: ' + e.stack);
     }
-    if (!input_ime.onKeyEvent.async)
-      input_ime.keyEventHandled(keyData.requestId, result);
+    if (!chrome.input.ime.onKeyEvent.async)
+      chrome.input.ime.keyEventHandled(keyData.requestId, result);
   };
 
-  input_ime.onKeyEvent.addListener = function(cb, opt_extraInfo) {
-    input_ime.onKeyEvent.async = false;
+  chrome.input.ime.onKeyEvent.addListener = function(cb, opt_extraInfo) {
+    chrome.input.ime.onKeyEvent.async = false;
     if (opt_extraInfo instanceof Array) {
       for (var i = 0; i < opt_extraInfo.length; ++i) {
         if (opt_extraInfo[i] == "async") {
-          input_ime.onKeyEvent.async = true;
+          chrome.input.ime.onKeyEvent.async = true;
         }
       }
     }
     chrome.Event.prototype.addListener.call(this, cb, null);
   };
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/json_schema.js b/chrome/renderer/resources/extensions/json_schema.js
index 44ea2a06c..414356c8 100644
--- a/chrome/renderer/resources/extensions/json_schema.js
+++ b/chrome/renderer/resources/extensions/json_schema.js
@@ -38,9 +38,7 @@
 //   additional properties will be validated.
 //==============================================================================
 
-// TODO(cduvall): Make this file not depend on chromeHidden.
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var loadRefDependency = require('utils').loadRefDependency;
 
 function isInstanceOfClass(instance, className) {
   if (!instance)
@@ -242,7 +240,6 @@
   // If the schema has a $ref property, the instance must validate against
   // that schema too. It must be present in this.types to be referenced.
   if (schema["$ref"]) {
-    loadRefDependency(schema["$ref"]);
     if (!this.types[schema["$ref"]])
       this.addError(path, "unknownSchemaReference", [ schema["$ref"] ]);
     else
diff --git a/chrome/renderer/resources/extensions/last_error.js b/chrome/renderer/resources/extensions/last_error.js
index 5eb5c01..2b38a29 100644
--- a/chrome/renderer/resources/extensions/last_error.js
+++ b/chrome/renderer/resources/extensions/last_error.js
@@ -3,17 +3,16 @@
 // found in the LICENSE file.
 
 requireNative('runtime');
-var GetAvailability = requireNative('v8_context').GetAvailability;
 
 function set(message) {
   var errorObject = { 'message': message };
-  if (GetAvailability('extension').is_available)
+  if (chrome.extension)
     chrome.extension.lastError = errorObject;
   chrome.runtime.lastError = errorObject;
 };
 
 function clear() {
-  if (GetAvailability('extension').is_available)
+  if (chrome.extension)
     delete chrome.extension.lastError;
   delete chrome.runtime.lastError;
 };
diff --git a/chrome/renderer/resources/extensions/media_galleries_custom_bindings.js b/chrome/renderer/resources/extensions/media_galleries_custom_bindings.js
index 60ac567..f1f9edfe 100644
--- a/chrome/renderer/resources/extensions/media_galleries_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/media_galleries_custom_bindings.js
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the Media Gallery API.
-
-var binding = require('binding').Binding.create('mediaGalleries');
+// Custom bindings for the Media Gallery API.
 
 var mediaGalleriesNatives = requireNative('mediaGalleries');
 
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
+
 var mediaGalleriesMetadata = {};
 
-binding.registerCustomHook(function(bindingsAPI, extensionId) {
+chromeHidden.registerCustomHook('mediaGalleries',
+                                function(bindingsAPI, extensionId) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   // getMediaFileSystems uses a custom callback so that it can instantiate and
@@ -47,5 +48,3 @@
     return {};
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/miscellaneous_bindings.js b/chrome/renderer/resources/extensions/miscellaneous_bindings.js
index e1469f8..147528c 100644
--- a/chrome/renderer/resources/extensions/miscellaneous_bindings.js
+++ b/chrome/renderer/resources/extensions/miscellaneous_bindings.js
@@ -8,10 +8,10 @@
 // content scripts only.
 
   require('json_schema');
+  require('event_bindings');
   var json = require('json');
   var lastError = require('lastError');
   var miscNatives = requireNative('miscellaneous_bindings');
-  var chrome = requireNative('chrome').GetChrome();
   var CloseChannel = miscNatives.CloseChannel;
   var PortAddRef = miscNatives.PortAddRef;
   var PortRelease = miscNatives.PortRelease;
diff --git a/chrome/renderer/resources/extensions/notification_custom_bindings.js b/chrome/renderer/resources/extensions/notification_custom_bindings.js
index 0e2cd13..b88be098 100644
--- a/chrome/renderer/resources/extensions/notification_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/notification_custom_bindings.js
@@ -4,12 +4,10 @@
 
 // Custom bindings for the notification API.
 
-var binding = require('binding').Binding.create('experimental.notification');
-
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var sendRequest = require('sendRequest').sendRequest;
 var imageUtil = require('imageUtil');
 var lastError = require('lastError');
-var json = require('json');
 
 function url_getter(context, key) {
   var f = function() {
@@ -101,8 +99,8 @@
   return function(id, input_notification_details, callback) {
     // TODO(dewittj): Remove this hack. This is used as a way to deep
     // copy a complex JSON object.
-    var notification_details = json.parse(
-        json.stringify(input_notification_details));
+    var notification_details = JSON.parse(
+        JSON.stringify(input_notification_details));
     var that = this;
     replaceNotificationOptionURLs(notification_details, function(success) {
       if (success) {
@@ -126,6 +124,5 @@
   apiFunctions.setHandleRequest('update', handleCreate);
 };
 
-binding.registerCustomHook(experimentalNotificationCustomHook);
-
-exports.binding = binding.generate();
+chromeHidden.registerCustomHook('experimental.notification',
+                                experimentalNotificationCustomHook);
diff --git a/chrome/renderer/resources/extensions/omnibox_custom_bindings.js b/chrome/renderer/resources/extensions/omnibox_custom_bindings.js
index fa0ced3a..04ae1811 100644
--- a/chrome/renderer/resources/extensions/omnibox_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/omnibox_custom_bindings.js
@@ -2,11 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the omnibox API. Only injected into the v8 contexts
+// Custom bindings for the omnibox API. Only injected into the v8 contexts
 // for extensions which have permission for the omnibox API.
 
-var binding = require('binding').Binding.create('omnibox');
-
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var sendRequest = require('sendRequest').sendRequest;
 
@@ -81,7 +79,7 @@
   return result;
 }
 
-binding.registerCustomHook(function(bindingsAPI) {
+chromeHidden.registerCustomHook('omnibox', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setHandleRequest('setDefaultSuggestion', function(details) {
@@ -111,5 +109,3 @@
   };
   dispatch([text, suggestCallback]);
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/page_action_custom_bindings.js b/chrome/renderer/resources/extensions/page_action_custom_bindings.js
index 97a308eb..b527374 100644
--- a/chrome/renderer/resources/extensions/page_action_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/page_action_custom_bindings.js
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the pageAction API.
+// Custom bindings for the pageAction API.
 
-var binding = require('binding').Binding.create('pageAction');
-
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var setIcon = require('setIcon').setIcon;
 
-binding.registerCustomHook(function(bindingsAPI) {
+chromeHidden.registerCustomHook('pageAction', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setHandleRequest('setIcon', function(details, callback) {
@@ -16,5 +15,3 @@
         'page action');
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/page_actions_custom_bindings.js b/chrome/renderer/resources/extensions/page_actions_custom_bindings.js
index 0b72e07..d5ec8887 100644
--- a/chrome/renderer/resources/extensions/page_actions_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/page_actions_custom_bindings.js
@@ -2,21 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the pageActions API.
-
-var binding = require('binding').Binding.create('pageActions');
+// Custom bindings for the pageActions API.
 
 var pageActionsNatives = requireNative('page_actions');
 var GetCurrentPageActions = pageActionsNatives.GetCurrentPageActions;
 
-binding.registerCustomHook(function(bindingsAPI, extensionId) {
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
+
+chromeHidden.registerCustomHook('pageActions',
+                                function(bindingsAPI, extensionId) {
   var pageActions = GetCurrentPageActions(extensionId);
-  var pageActionsApi = bindingsAPI.compiledApi;
   var oldStyleEventName = 'pageActions';
   for (var i = 0; i < pageActions.length; ++i) {
     // Setup events for each extension_id/page_action_id string we find.
-    pageActionsApi[pageActions[i]] = new chrome.Event(oldStyleEventName);
+    chrome.pageActions[pageActions[i]] = new chrome.Event(oldStyleEventName);
   }
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/page_capture_custom_bindings.js b/chrome/renderer/resources/extensions/page_capture_custom_bindings.js
index f9147d7..15b82f9 100644
--- a/chrome/renderer/resources/extensions/page_capture_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/page_capture_custom_bindings.js
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the pageCapture API.
-
-var binding = require('binding').Binding.create('pageCapture');
+// Custom bindings for the pageCapture API.
 
 var pageCaptureNatives = requireNative('page_capture');
 var CreateBlob = pageCaptureNatives.CreateBlob;
 var SendResponseAck = pageCaptureNatives.SendResponseAck;
 
-binding.registerCustomHook(function(bindingsAPI) {
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
+
+chromeHidden.registerCustomHook('pageCapture', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setCustomCallback('saveAsMHTML',
@@ -27,5 +27,3 @@
     SendResponseAck(request.id);
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/permissions_custom_bindings.js b/chrome/renderer/resources/extensions/permissions_custom_bindings.js
index e728080..b32a57d 100644
--- a/chrome/renderer/resources/extensions/permissions_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/permissions_custom_bindings.js
@@ -2,23 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the Permissions API.
+// Custom bindings for the Permissions API.
 
-var binding = require('binding').Binding.create('permissions');
-
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var sendRequest = require('sendRequest').sendRequest;
 var lastError = require('lastError');
 
-// These custom binding are only necessary because it is not currently
+// These custom bindings are only necessary because it is not currently
 // possible to have a union of types as the type of the items in an array.
 // Once that is fixed, this entire file should go away.
 // See,
 // https://code.google.com/p/chromium/issues/detail?id=162044
 // https://code.google.com/p/chromium/issues/detail?id=162042
 // TODO(bryeung): delete this file.
-binding.registerCustomHook(function(api) {
+chromeHidden.registerCustomHook('permissions', function(api) {
   var apiFunctions = api.apiFunctions;
-  var permissions = api.compiledApi;
 
   function maybeConvertToObject(str) {
     var parts = str.split('|');
@@ -82,14 +80,12 @@
   // dispatchToListener call happens after argument validation, which works
   // around the problem that Permissions.permissions is supposed to be a list
   // of strings.
-  permissions.onAdded.dispatchToListener = function(callback, args) {
+  chrome.permissions.onAdded.dispatchToListener = function(callback, args) {
     for (var i = 0; i < args[0].permissions.length; i += 1) {
       args[0].permissions[i] = maybeConvertToObject(args[0].permissions[i]);
     }
     chrome.Event.prototype.dispatchToListener(callback, args);
   };
-  permissions.onRemoved.dispatchToListener =
-      permissions.onAdded.dispatchToListener;
+  chrome.permissions.onRemoved.dispatchToListener =
+      chrome.permissions.onAdded.dispatchToListener;
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/runtime_custom_bindings.js b/chrome/renderer/resources/extensions/runtime_custom_bindings.js
index 61be155e..91d402b1 100644
--- a/chrome/renderer/resources/extensions/runtime_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/runtime_custom_bindings.js
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the runtime API.
-
-var binding = require('binding').Binding.create('runtime');
+// Custom bindings for the runtime API.
 
 var runtimeNatives = requireNative('runtime');
 var extensionNatives = requireNative('extension');
@@ -15,15 +13,14 @@
 var sendMessageUpdateArguments =
     require('miscellaneous_bindings').sendMessageUpdateArguments;
 
-binding.registerCustomHook(function(binding, id, contextType) {
-  var apiFunctions = binding.apiFunctions;
-  var runtime = binding.compiledApi;
+chromeHidden.registerCustomHook('runtime', function(bindings, id, contextType) {
+  var apiFunctions = bindings.apiFunctions;
 
   //
   // Unprivileged APIs.
   //
 
-  runtime.id = id;
+  chrome.runtime.id = id;
 
   apiFunctions.setHandleRequest('getManifest', function() {
     return runtimeNatives.GetManifest();
@@ -43,14 +40,14 @@
 
   apiFunctions.setHandleRequest('sendMessage',
                                 function(targetId, message, responseCallback) {
-    var port = runtime.connect(targetId || runtime.id,
+    var port = chrome.runtime.connect(targetId || chrome.runtime.id,
         {name: chromeHidden.kMessageChannel});
     chromeHidden.Port.sendMessageImpl(port, message, responseCallback);
   });
 
   apiFunctions.setHandleRequest('sendNativeMessage',
                                 function(targetId, message, responseCallback) {
-    var port = runtime.connectNative(targetId);
+    var port = chrome.runtime.connectNative(targetId);
     chromeHidden.Port.sendMessageImpl(port, message, responseCallback);
   });
 
@@ -86,7 +83,7 @@
 
   apiFunctions.setHandleRequest('connect', function(targetId, connectInfo) {
     if (!targetId)
-      targetId = runtime.id;
+      targetId = chrome.runtime.id;
     var name = '';
     if (connectInfo && connectInfo.name)
       name = connectInfo.name;
@@ -94,7 +91,7 @@
     // Don't let orphaned content scripts communicate with their extension.
     // http://crbug.com/168263
     if (!chromeHidden.wasUnloaded) {
-      var portId = OpenChannelToExtension(runtime.id, targetId, name);
+      var portId = OpenChannelToExtension(chrome.runtime.id, targetId, name);
       if (portId >= 0)
         return chromeHidden.Port.createPort(portId, name);
     }
@@ -110,7 +107,7 @@
   apiFunctions.setHandleRequest('connectNative',
                                 function(nativeAppName) {
     if (!chromeHidden.wasUnloaded) {
-      var portId = OpenChannelToNativeApp(runtime.id, nativeAppName);
+      var portId = OpenChannelToNativeApp(chrome.runtime.id, nativeAppName);
       if (portId >= 0)
         return chromeHidden.Port.createPort(portId, '');
     }
@@ -127,5 +124,3 @@
   });
 
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/schema_generated_bindings.js b/chrome/renderer/resources/extensions/schema_generated_bindings.js
new file mode 100644
index 0000000..65d5cad
--- /dev/null
+++ b/chrome/renderer/resources/extensions/schema_generated_bindings.js
@@ -0,0 +1,437 @@
+// 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.
+
+// Generates the chrome.* API bindings from a list of schemas.
+
+  // TODO(battre): cleanup the usage of packages everywhere, as described here
+  // http://codereview.chromium.org/10392008/diff/38/chrome/renderer/resources/extensions/schema_generated_bindings.js
+
+  require('json_schema');
+  require('event_bindings');
+  var GetExtensionAPIDefinition =
+      requireNative('apiDefinitions').GetExtensionAPIDefinition;
+  var sendRequest = require('sendRequest').sendRequest;
+  var utils = require('utils');
+  var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
+  var schemaUtils = require('schemaUtils');
+
+  // The object to generate the bindings for "internal" APIs in, so that
+  // extensions can't directly call them (without access to chromeHidden),
+  // but are still needed for internal mechanisms of extensions (e.g. events).
+  //
+  // This is distinct to the "*Private" APIs which are controlled via
+  // having strict permissions and aren't generated *anywhere* unless needed.
+  var internalAPIs = {};
+  chromeHidden.internalAPIs = internalAPIs;
+
+  // Stores the name and definition of each API function, with methods to
+  // modify their behaviour (such as a custom way to handle requests to the
+  // API, a custom callback, etc).
+  function APIFunctions() {
+    this._apiFunctions = {};
+    this._unavailableApiFunctions = {};
+  }
+  APIFunctions.prototype.register = function(apiName, apiFunction) {
+    this._apiFunctions[apiName] = apiFunction;
+  };
+  // Registers a function as existing but not available, meaning that calls to
+  // the set* methods that reference this function should be ignored rather
+  // than throwing Errors.
+  APIFunctions.prototype.registerUnavailable = function(apiName) {
+    this._unavailableApiFunctions[apiName] = apiName;
+  };
+  APIFunctions.prototype._setHook =
+      function(apiName, propertyName, customizedFunction) {
+    if (this._unavailableApiFunctions.hasOwnProperty(apiName))
+      return;
+    if (!this._apiFunctions.hasOwnProperty(apiName))
+      throw new Error('Tried to set hook for unknown API "' + apiName + '"');
+    this._apiFunctions[apiName][propertyName] = customizedFunction;
+  };
+  APIFunctions.prototype.setHandleRequest =
+      function(apiName, customizedFunction) {
+    return this._setHook(apiName, 'handleRequest', customizedFunction);
+  };
+  APIFunctions.prototype.setUpdateArgumentsPostValidate =
+      function(apiName, customizedFunction) {
+    return this._setHook(
+      apiName, 'updateArgumentsPostValidate', customizedFunction);
+  };
+  APIFunctions.prototype.setUpdateArgumentsPreValidate =
+      function(apiName, customizedFunction) {
+    return this._setHook(
+      apiName, 'updateArgumentsPreValidate', customizedFunction);
+  };
+  APIFunctions.prototype.setCustomCallback =
+      function(apiName, customizedFunction) {
+    return this._setHook(apiName, 'customCallback', customizedFunction);
+  };
+
+  var apiFunctions = new APIFunctions();
+
+  // Wraps the calls to the set* methods of APIFunctions with the namespace of
+  // an API, and validates that all calls to set* methods aren't prefixed with
+  // a namespace.
+  //
+  // For example, if constructed with 'browserAction', a call to
+  // handleRequest('foo') will be transformed into
+  // handleRequest('browserAction.foo').
+  //
+  // Likewise, if a call to handleRequest is called with 'browserAction.foo',
+  // it will throw an error.
+  //
+  // These help with isolating custom bindings from each other.
+  function NamespacedAPIFunctions(namespace, delegate) {
+    var self = this;
+    function wrap(methodName) {
+      self[methodName] = function(apiName, customizedFunction) {
+        var prefix = namespace + '.';
+        if (apiName.indexOf(prefix) === 0) {
+          throw new Error(methodName + ' called with "' + apiName +
+                          '" which has a "' + prefix + '" prefix. ' +
+                          'This is unnecessary and must be left out.');
+        }
+        return delegate[methodName].call(delegate,
+                                         prefix + apiName, customizedFunction);
+      };
+    }
+
+    wrap('contains');
+    wrap('setHandleRequest');
+    wrap('setUpdateArgumentsPostValidate');
+    wrap('setUpdateArgumentsPreValidate');
+    wrap('setCustomCallback');
+  }
+
+  //
+  // The API through which the ${api_name}_custom_bindings.js files customize
+  // their API bindings beyond what can be generated.
+  //
+  // There are 2 types of customizations available: those which are required in
+  // order to do the schema generation (registerCustomEvent and
+  // registerCustomType), and those which can only run after the bindings have
+  // been generated (registerCustomHook).
+  //
+
+  // Registers a custom event type for the API identified by |namespace|.
+  // |event| is the event's constructor.
+  var customEvents = {};
+  chromeHidden.registerCustomEvent = function(namespace, event) {
+    if (typeof(namespace) !== 'string') {
+      throw new Error("registerCustomEvent requires the namespace of the " +
+                      "API as its first argument");
+    }
+    customEvents[namespace] = event;
+  };
+
+  // Registers a function |hook| to run after the schema for all APIs has been
+  // generated.  The hook is passed as its first argument an "API" object to
+  // interact with, and second the current extension ID. See where
+  // |customHooks| is used.
+  var customHooks = {};
+  chromeHidden.registerCustomHook = function(namespace, fn) {
+    if (typeof(namespace) !== 'string') {
+      throw new Error("registerCustomHook requires the namespace of the " +
+                      "API as its first argument");
+    }
+    customHooks[namespace] = fn;
+  };
+
+  function CustomBindingsObject() {
+  }
+  CustomBindingsObject.prototype.setSchema = function(schema) {
+    // The functions in the schema are in list form, so we move them into a
+    // dictionary for easier access.
+    var self = this;
+    self.functionSchemas = {};
+    schema.functions.forEach(function(f) {
+      self.functionSchemas[f.name] = {
+        name: f.name,
+        definition: f
+      }
+    });
+  };
+
+  // Registers a custom type referenced via "$ref" fields in the API schema
+  // JSON.
+  var customTypes = {};
+  chromeHidden.registerCustomType = function(typeName, customTypeFactory) {
+    var customType = customTypeFactory();
+    customType.prototype = new CustomBindingsObject();
+    customTypes[typeName] = customType;
+  };
+
+  // Get the platform from navigator.appVersion.
+  function getPlatform() {
+    var platforms = [
+      [/CrOS Touch/, "chromeos touch"],
+      [/CrOS/, "chromeos"],
+      [/Linux/, "linux"],
+      [/Mac/, "mac"],
+      [/Win/, "win"],
+    ];
+
+    for (var i = 0; i < platforms.length; i++) {
+      if (platforms[i][0].test(navigator.appVersion)) {
+        return platforms[i][1];
+      }
+    }
+    return "unknown";
+  }
+
+  function isPlatformSupported(schemaNode, platform) {
+    return !schemaNode.platforms ||
+        schemaNode.platforms.indexOf(platform) > -1;
+  }
+
+  function isManifestVersionSupported(schemaNode, manifestVersion) {
+    return !schemaNode.maximumManifestVersion ||
+        manifestVersion <= schemaNode.maximumManifestVersion;
+  }
+
+  function isSchemaNodeSupported(schemaNode, platform, manifestVersion) {
+    return isPlatformSupported(schemaNode, platform) &&
+        isManifestVersionSupported(schemaNode, manifestVersion);
+  }
+
+  chromeHidden.onLoad.addListener(function(extensionId,
+                                           contextType,
+                                           isIncognitoProcess,
+                                           manifestVersion) {
+    var apiDefinitions = GetExtensionAPIDefinition();
+
+    // Read api definitions and setup api functions in the chrome namespace.
+    var platform = getPlatform();
+
+    apiDefinitions.forEach(function(apiDef) {
+      // TODO(kalman): Remove this, or refactor schema_generated_bindings.js so
+      // that it isn't necessary. For now, chrome.app and chrome.webstore are
+      // entirely handwritten.
+      if (['app', 'webstore'].indexOf(apiDef.namespace) >= 0)
+        return;
+
+      if (!isSchemaNodeSupported(apiDef, platform, manifestVersion))
+        return;
+
+      // See comment on internalAPIs at the top.
+      var mod = apiDef.internal ? internalAPIs : chrome;
+
+      var namespaces = apiDef.namespace.split('.');
+      for (var index = 0, name; name = namespaces[index]; index++) {
+        mod[name] = mod[name] || {};
+        mod = mod[name];
+      }
+
+      // Add types to global schemaValidator
+      if (apiDef.types) {
+        apiDef.types.forEach(function(t) {
+          if (!isSchemaNodeSupported(t, platform, manifestVersion))
+            return;
+
+          schemaUtils.schemaValidator.addTypes(t);
+          if (t.type == 'object' && customTypes[t.id]) {
+            customTypes[t.id].prototype.setSchema(t);
+          }
+        });
+      }
+
+      // Returns whether access to the content of a schema should be denied,
+      // based on the presence of "unprivileged" and whether this is an
+      // extension process (versus e.g. a content script).
+      function isSchemaAccessAllowed(itemSchema) {
+        return (contextType == 'BLESSED_EXTENSION') ||
+               apiDef.unprivileged ||
+               itemSchema.unprivileged;
+      }
+
+      // Adds a getter that throws an access denied error to object |mod|
+      // for property |name|.
+      function addUnprivilegedAccessGetter(mod, name) {
+        mod.__defineGetter__(name, function() {
+          throw new Error(
+              '"' + name + '" can only be used in extension processes. See ' +
+              'the content scripts documentation for more details.');
+        });
+      }
+
+      // Setup Functions.
+      if (apiDef.functions) {
+        apiDef.functions.forEach(function(functionDef) {
+          if (functionDef.name in mod) {
+            throw new Error('Function ' + functionDef.name +
+                            ' already defined in ' + apiDef.namespace);
+          }
+
+          var apiFunctionName = apiDef.namespace + "." + functionDef.name;
+
+          if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) {
+            apiFunctions.registerUnavailable(apiFunctionName);
+            return;
+          }
+          if (!isSchemaAccessAllowed(functionDef)) {
+            apiFunctions.registerUnavailable(apiFunctionName);
+            addUnprivilegedAccessGetter(mod, functionDef.name);
+            return;
+          }
+
+          var apiFunction = {};
+          apiFunction.definition = functionDef;
+          apiFunction.name = apiFunctionName;
+
+          // TODO(aa): It would be best to run this in a unit test, but in order
+          // to do that we would need to better factor this code so that it
+          // doesn't depend on so much v8::Extension machinery.
+          if (chromeHidden.validateAPI &&
+              schemaUtils.isFunctionSignatureAmbiguous(
+                  apiFunction.definition)) {
+            throw new Error(
+                apiFunction.name + ' has ambiguous optional arguments. ' +
+                'To implement custom disambiguation logic, add ' +
+                '"allowAmbiguousOptionalArguments" to the function\'s schema.');
+          }
+
+          apiFunctions.register(apiFunction.name, apiFunction);
+
+          mod[functionDef.name] = (function() {
+            var args = Array.prototype.slice.call(arguments);
+            if (this.updateArgumentsPreValidate)
+              args = this.updateArgumentsPreValidate.apply(this, args);
+
+            args = schemaUtils.normalizeArgumentsAndValidate(args, this);
+            if (this.updateArgumentsPostValidate)
+              args = this.updateArgumentsPostValidate.apply(this, args);
+
+            var retval;
+            if (this.handleRequest) {
+              retval = this.handleRequest.apply(this, args);
+            } else {
+              var optArgs = {
+                customCallback: this.customCallback
+              };
+              retval = sendRequest(this.name, args,
+                                   this.definition.parameters,
+                                   optArgs);
+            }
+
+            // Validate return value if defined - only in debug.
+            if (chromeHidden.validateCallbacks &&
+                this.definition.returns) {
+              schemaUtils.validate([retval], [this.definition.returns]);
+            }
+            return retval;
+          }).bind(apiFunction);
+        });
+      }
+
+      // Setup Events
+      if (apiDef.events) {
+        apiDef.events.forEach(function(eventDef) {
+          if (eventDef.name in mod) {
+            throw new Error('Event ' + eventDef.name +
+                            ' already defined in ' + apiDef.namespace);
+          }
+          if (!isSchemaNodeSupported(eventDef, platform, manifestVersion))
+            return;
+          if (!isSchemaAccessAllowed(eventDef)) {
+            addUnprivilegedAccessGetter(mod, eventDef.name);
+            return;
+          }
+
+          var eventName = apiDef.namespace + "." + eventDef.name;
+          var customEvent = customEvents[apiDef.namespace];
+          var options = eventDef.options || {};
+
+          if (eventDef.filters && eventDef.filters.length > 0)
+            options.supportsFilters = true;
+
+          if (customEvent) {
+            mod[eventDef.name] = new customEvent(
+                eventName, eventDef.parameters, eventDef.extraParameters,
+                options);
+          } else if (eventDef.anonymous) {
+            mod[eventDef.name] = new chrome.Event();
+          } else {
+            mod[eventDef.name] = new chrome.Event(
+                eventName, eventDef.parameters, options);
+          }
+        });
+      }
+
+      function addProperties(m, parentDef) {
+        var properties = parentDef.properties;
+        if (!properties)
+          return;
+
+        utils.forEach(properties, function(propertyName, propertyDef) {
+          if (propertyName in m)
+            return;  // TODO(kalman): be strict like functions/events somehow.
+          if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion))
+            return;
+          if (!isSchemaAccessAllowed(propertyDef)) {
+            addUnprivilegedAccessGetter(m, propertyName);
+            return;
+          }
+
+          var value = propertyDef.value;
+          if (value) {
+            // Values may just have raw types as defined in the JSON, such
+            // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here.
+            // TODO(kalman): enforce that things with a "value" property can't
+            // define their own types.
+            var type = propertyDef.type || typeof(value);
+            if (type === 'integer' || type === 'number') {
+              value = parseInt(value);
+            } else if (type === 'boolean') {
+              value = value === "true";
+            } else if (propertyDef["$ref"]) {
+              var constructor = customTypes[propertyDef["$ref"]];
+              if (!constructor)
+                throw new Error("No custom binding for " + propertyDef["$ref"]);
+              var args = value;
+              // For an object propertyDef, |value| is an array of constructor
+              // arguments, but we want to pass the arguments directly (i.e.
+              // not as an array), so we have to fake calling |new| on the
+              // constructor.
+              value = { __proto__: constructor.prototype };
+              constructor.apply(value, args);
+              // Recursively add properties.
+              addProperties(value, propertyDef);
+            } else if (type === 'object') {
+              // Recursively add properties.
+              addProperties(value, propertyDef);
+            } else if (type !== 'string') {
+              throw new Error("NOT IMPLEMENTED (extension_api.json error): " +
+                  "Cannot parse values for type \"" + type + "\"");
+            }
+            m[propertyName] = value;
+          }
+        });
+      }
+
+      addProperties(mod, apiDef);
+    });
+
+    // Run the non-declarative custom hooks after all the schemas have been
+    // generated, in case hooks depend on other APIs being available.
+    apiDefinitions.forEach(function(apiDef) {
+      if (!isSchemaNodeSupported(apiDef, platform, manifestVersion))
+        return;
+
+      var hook = customHooks[apiDef.namespace];
+      if (!hook)
+        return;
+
+      // Pass through the public API of schema_generated_bindings, to be used
+      // by custom bindings JS files. Create a new one so that bindings can't
+      // interfere with each other.
+      hook({
+        apiFunctions: new NamespacedAPIFunctions(apiDef.namespace,
+                                                 apiFunctions),
+        apiDefinitions: apiDefinitions,
+      }, extensionId, contextType);
+    });
+
+    if (chrome.test)
+      chrome.test.getApiDefinitions = GetExtensionAPIDefinition;
+  });
diff --git a/chrome/renderer/resources/extensions/schema_utils.js b/chrome/renderer/resources/extensions/schema_utils.js
index 31531f90..8cd02128 100644
--- a/chrome/renderer/resources/extensions/schema_utils.js
+++ b/chrome/renderer/resources/extensions/schema_utils.js
@@ -5,7 +5,6 @@
 // Routines used to validate and normalize arguments.
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var chrome = requireNative('chrome').GetChrome();
 
 // TODO(benwells): unit test this file.
 // JSONSchemaValidator is not loaded in unit tests.
diff --git a/chrome/renderer/resources/extensions/send_request.js b/chrome/renderer/resources/extensions/send_request.js
index 1e5746f..d689a5b 100644
--- a/chrome/renderer/resources/extensions/send_request.js
+++ b/chrome/renderer/resources/extensions/send_request.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
-var DCHECK = requireNative('logging').DCHECK;
 var json = require('json');
 var lastError = require('lastError');
 var natives = requireNative('sendRequest');
@@ -15,7 +14,6 @@
                                        success, responseList, error) {
   try {
     var request = requests[requestId];
-    DCHECK(request != null);
     if (success) {
       lastError.clear();
     } else {
@@ -34,7 +32,7 @@
     if (request.callback) {
       // Validate callback in debug only -- and only when the
       // caller has provided a callback. Implementations of api
-      // calls may not return data if they observe the caller
+      // calls my not return data if they observe the caller
       // has not provided a callback.
       if (chromeHidden.validateCallbacks && !error) {
         try {
diff --git a/chrome/renderer/resources/extensions/storage_custom_bindings.js b/chrome/renderer/resources/extensions/storage_custom_bindings.js
index 869a5558..d230099 100644
--- a/chrome/renderer/resources/extensions/storage_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/storage_custom_bindings.js
@@ -2,15 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the storage API.
+// Custom bindings for the storage API.
 
-var binding = require('binding').Binding.create('storage');
-
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var normalizeArgumentsAndValidate =
     require('schemaUtils').normalizeArgumentsAndValidate
 var sendRequest = require('sendRequest').sendRequest;
 
-binding.registerCustomType('storage.StorageArea', function() {
+chromeHidden.registerCustomType('storage.StorageArea', function() {
   function extendSchema(schema) {
     var extendedSchema = schema.slice();
     extendedSchema.unshift({'type': 'string'});
@@ -22,7 +21,7 @@
     // storage.sync.get('foo') -> (binds to) ->
     // storage.get('sync', 'foo').
     //
-    // TODO(kalman): Put as a method on CustombindingObject and re-use (or
+    // TODO(kalman): Put as a method on CustomBindingsObject and re-use (or
     // even generate) for other APIs that need to do this. Same for other
     // callers of registerCustomType().
     var self = this;
@@ -44,5 +43,3 @@
 
   return StorageArea;
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/sync_file_system_custom_bindings.js b/chrome/renderer/resources/extensions/sync_file_system_custom_bindings.js
index bcb12193..03c6caa 100644
--- a/chrome/renderer/resources/extensions/sync_file_system_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/sync_file_system_custom_bindings.js
@@ -2,15 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the syncFileSystem API.
-
-var binding = require('binding').Binding.create('syncFileSystem');
+// Custom bindings for the syncFileSystem API.
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var fileSystemNatives = requireNative('file_system_natives');
 var syncFileSystemNatives = requireNative('sync_file_system');
 
-binding.registerCustomHook(function(bindingsAPI) {
+chromeHidden.registerCustomHook('syncFileSystem', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   // Functions which take in an [instanceOf=FileEntry].
@@ -69,5 +67,3 @@
   }
   dispatch([fileInfo]);
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/system_indicator_custom_bindings.js b/chrome/renderer/resources/extensions/system_indicator_custom_bindings.js
index 0551a833..0d58de1 100644
--- a/chrome/renderer/resources/extensions/system_indicator_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/system_indicator_custom_bindings.js
@@ -2,16 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the systemIndicator API.
-// TODO(dewittj) Refactor custom binding to reduce redundancy between the
+// Custom bindings for the systemIndicator API.
+// TODO(dewittj) Refactor custom bindings to reduce redundancy between the
 // extension action APIs.
 
-var binding = require('binding').Binding.create('systemIndicator');
-
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var setIcon = require('setIcon').setIcon;
 
-binding.registerCustomHook(function(bindingsAPI) {
+chromeHidden.registerCustomHook('systemIndicator', function(bindingsAPI) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setHandleRequest('setIcon', function(details, callback) {
@@ -20,4 +18,3 @@
   });
 });
 
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js b/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js
index a75d36d..f458a2f 100644
--- a/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/tab_capture_custom_bindings.js
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the Tab Capture API.
+// Custom bindings for the Tab Capture API.
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 
-var binding = require('binding').Binding.create('tabCapture');
-
-binding.registerCustomHook(function(bindingsAPI, extensionId) {
+chromeHidden.registerCustomHook('tabCapture',
+                                function(bindingsAPI, extensionId) {
   var apiFunctions = bindingsAPI.apiFunctions;
 
   apiFunctions.setCustomCallback('capture',
@@ -33,5 +33,3 @@
     request.callback = null;
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/tabs_custom_bindings.js b/chrome/renderer/resources/extensions/tabs_custom_bindings.js
index 0d7c405..90e5912 100644
--- a/chrome/renderer/resources/extensions/tabs_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/tabs_custom_bindings.js
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the tabs API.
-
-var binding = require('binding').Binding.create('tabs');
+// Custom bindings for the tabs API.
 
 var tabsNatives = requireNative('tabs');
 var OpenChannelToTab = tabsNatives.OpenChannelToTab;
@@ -12,9 +10,8 @@
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 
-binding.registerCustomHook(function(bindingsAPI, extensionId) {
+chromeHidden.registerCustomHook('tabs', function(bindingsAPI, extensionId) {
   var apiFunctions = bindingsAPI.apiFunctions;
-  var tabs = bindingsAPI.compiledApi;
 
   apiFunctions.setHandleRequest('connect', function(tabId, connectInfo) {
     var name = '';
@@ -29,15 +26,13 @@
                                 function(tabId, request, responseCallback) {
     if (sendRequestIsDisabled)
       throw new Error(sendRequestIsDisabled);
-    var port = tabs.connect(tabId, {name: chromeHidden.kRequestChannel});
+    var port = chrome.tabs.connect(tabId, {name: chromeHidden.kRequestChannel});
     chromeHidden.Port.sendMessageImpl(port, request, responseCallback);
   });
 
   apiFunctions.setHandleRequest('sendMessage',
                                 function(tabId, message, responseCallback) {
-    var port = tabs.connect(tabId, {name: chromeHidden.kMessageChannel});
+    var port = chrome.tabs.connect(tabId, {name: chromeHidden.kMessageChannel});
     chromeHidden.Port.sendMessageImpl(port, message, responseCallback);
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/test_custom_bindings.js b/chrome/renderer/resources/extensions/test_custom_bindings.js
deleted file mode 100644
index 300ba42..0000000
--- a/chrome/renderer/resources/extensions/test_custom_bindings.js
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// test_custom_bindings.js
-// mini-framework for ExtensionApiTest browser tests
-
-var binding = require('binding').Binding.create('test');
-
-var chrome = requireNative('chrome').GetChrome();
-var GetExtensionAPIDefinition =
-    requireNative('apiDefinitions').GetExtensionAPIDefinition;
-
-binding.registerCustomHook(function(api) {
-  var chromeTest = api.compiledApi;
-  var apiFunctions = api.apiFunctions;
-
-  chromeTest.tests = chromeTest.tests || [];
-
-  var currentTest = null;
-  var lastTest = null;
-  var testsFailed = 0;
-  var testCount = 1;
-  var failureException = 'chrome.test.failure';
-
-  // Helper function to get around the fact that function names in javascript
-  // are read-only, and you can't assign one to anonymous functions.
-  function testName(test) {
-    return test ? (test.name || test.generatedName) : "(no test)";
-  }
-
-  function testDone() {
-    // Use setTimeout here to allow previous test contexts to be
-    // eligible for garbage collection.
-    setTimeout(chromeTest.runNextTest, 0);
-  }
-
-  function allTestsDone() {
-    if (testsFailed == 0) {
-      chromeTest.notifyPass();
-    } else {
-      chromeTest.notifyFail('Failed ' + testsFailed + ' of ' +
-                             testCount + ' tests');
-    }
-
-    // Try to get the script to stop running immediately.
-    // This isn't an error, just an attempt at saying "done".
-    throw "completed";
-  }
-
-  var pendingCallbacks = 0;
-
-  apiFunctions.setHandleRequest('callbackAdded', function() {
-    pendingCallbacks++;
-
-    var called = false;
-    return function() {
-      chromeTest.assertFalse(called, 'callback has already been run');
-      called = true;
-
-      pendingCallbacks--;
-      if (pendingCallbacks == 0) {
-        chromeTest.succeed();
-      }
-    };
-  });
-
-  apiFunctions.setHandleRequest('runNextTest', function() {
-    // There may have been callbacks which were interrupted by failure
-    // exceptions.
-    pendingCallbacks = 0;
-
-    lastTest = currentTest;
-    currentTest = chromeTest.tests.shift();
-
-    if (!currentTest) {
-      allTestsDone();
-      return;
-    }
-
-    try {
-      chromeTest.log("( RUN      ) " + testName(currentTest));
-      currentTest.call();
-    } catch (e) {
-      if (e !== failureException)
-        chromeTest.fail('uncaught exception: ' + e);
-    }
-  });
-
-  apiFunctions.setHandleRequest('fail', function(message) {
-    chromeTest.log("(  FAILED  ) " + testName(currentTest));
-
-    var stack = {};
-    Error.captureStackTrace(stack, chromeTest.fail);
-
-    if (!message)
-      message = "FAIL (no message)";
-
-    message += "\n" + stack.stack;
-    console.log("[FAIL] " + testName(currentTest) + ": " + message);
-    testsFailed++;
-    testDone();
-
-    // Interrupt the rest of the test.
-    throw failureException;
-  });
-
-  apiFunctions.setHandleRequest('succeed', function() {
-    console.log("[SUCCESS] " + testName(currentTest));
-    chromeTest.log("(  SUCCESS )");
-    testDone();
-  });
-
-  apiFunctions.setHandleRequest('assertTrue', function(test, message) {
-    chromeTest.assertBool(test, true, message);
-  });
-
-  apiFunctions.setHandleRequest('assertFalse', function(test, message) {
-    chromeTest.assertBool(test, false, message);
-  });
-
-  apiFunctions.setHandleRequest('assertBool',
-                                function(test, expected, message) {
-    if (test !== expected) {
-      if (typeof(test) == "string") {
-        if (message)
-          message = test + "\n" + message;
-        else
-          message = test;
-      }
-      chromeTest.fail(message);
-    }
-  });
-
-  apiFunctions.setHandleRequest('checkDeepEq', function(expected, actual) {
-    if ((expected === null) != (actual === null))
-      return false;
-
-    if (expected === actual)
-      return true;
-
-    if (typeof(expected) !== typeof(actual))
-      return false;
-
-    for (var p in actual) {
-      if (actual.hasOwnProperty(p) && !expected.hasOwnProperty(p))
-        return false;
-    }
-    for (var p in expected) {
-      if (expected.hasOwnProperty(p) && !actual.hasOwnProperty(p))
-        return false;
-    }
-
-    for (var p in expected) {
-      var eq = true;
-      switch (typeof(expected[p])) {
-        case 'object':
-          eq = chromeTest.checkDeepEq(expected[p], actual[p]);
-          break;
-        case 'function':
-          eq = (typeof(actual[p]) != 'undefined' &&
-                expected[p].toString() == actual[p].toString());
-          break;
-        default:
-          eq = (expected[p] == actual[p] &&
-                typeof(expected[p]) == typeof(actual[p]));
-          break;
-      }
-      if (!eq)
-        return false;
-    }
-    return true;
-  });
-
-  apiFunctions.setHandleRequest('assertEq',
-                                function(expected, actual, message) {
-    var error_msg = "API Test Error in " + testName(currentTest);
-    if (message)
-      error_msg += ": " + message;
-    if (typeof(expected) == 'object') {
-      if (!chromeTest.checkDeepEq(expected, actual)) {
-        chromeTest.fail(error_msg +
-                         "\nActual: " + JSON.stringify(actual) +
-                         "\nExpected: " + JSON.stringify(expected));
-      }
-      return;
-    }
-    if (expected != actual) {
-      chromeTest.fail(error_msg +
-                       "\nActual: " + actual + "\nExpected: " + expected);
-    }
-    if (typeof(expected) != typeof(actual)) {
-      chromeTest.fail(error_msg +
-                       " (type mismatch)\nActual Type: " + typeof(actual) +
-                       "\nExpected Type:" + typeof(expected));
-    }
-  });
-
-  apiFunctions.setHandleRequest('assertNoLastError', function() {
-    if (chrome.runtime.lastError != undefined) {
-      chromeTest.fail("lastError.message == " +
-                       chrome.runtime.lastError.message);
-    }
-  });
-
-  apiFunctions.setHandleRequest('assertLastError', function(expectedError) {
-    chromeTest.assertEq(typeof(expectedError), 'string');
-    chromeTest.assertTrue(chrome.runtime.lastError != undefined,
-        "No lastError, but expected " + expectedError);
-    chromeTest.assertEq(expectedError, chrome.runtime.lastError.message);
-  });
-
-  function safeFunctionApply(func, args) {
-    try {
-      if (func)
-        func.apply(null, args);
-    } catch (e) {
-      var msg = "uncaught exception " + e;
-      chromeTest.fail(msg);
-    }
-  };
-
-  // Wrapper for generating test functions, that takes care of calling
-  // assertNoLastError() and (optionally) succeed() for you.
-  apiFunctions.setHandleRequest('callback', function(func, expectedError) {
-    if (func) {
-      chromeTest.assertEq(typeof(func), 'function');
-    }
-    var callbackCompleted = chromeTest.callbackAdded();
-
-    return function() {
-      if (expectedError == null) {
-        chromeTest.assertNoLastError();
-      } else {
-        chromeTest.assertLastError(expectedError);
-      }
-
-      if (func) {
-        safeFunctionApply(func, arguments);
-      }
-
-      callbackCompleted();
-    };
-  });
-
-  apiFunctions.setHandleRequest('listenOnce', function(event, func) {
-    var callbackCompleted = chromeTest.callbackAdded();
-    var listener = function() {
-      event.removeListener(listener);
-      safeFunctionApply(func, arguments);
-      callbackCompleted();
-    };
-    event.addListener(listener);
-  });
-
-  apiFunctions.setHandleRequest('listenForever', function(event, func) {
-    var callbackCompleted = chromeTest.callbackAdded();
-
-    var listener = function() {
-      safeFunctionApply(func, arguments);
-    };
-
-    var done = function() {
-      event.removeListener(listener);
-      callbackCompleted();
-    };
-
-    event.addListener(listener);
-    return done;
-  });
-
-  apiFunctions.setHandleRequest('callbackPass', function(func) {
-    return chromeTest.callback(func);
-  });
-
-  apiFunctions.setHandleRequest('callbackFail', function(expectedError, func) {
-    return chromeTest.callback(func, expectedError);
-  });
-
-  apiFunctions.setHandleRequest('runTests', function(tests) {
-    chromeTest.tests = tests;
-    testCount = chromeTest.tests.length;
-    chromeTest.runNextTest();
-  });
-
-  apiFunctions.setHandleRequest('getApiDefinitions', function(apiNames) {
-    return GetExtensionAPIDefinition();
-  });
-});
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/tts_custom_bindings.js b/chrome/renderer/resources/extensions/tts_custom_bindings.js
index 5c746665..634ee38 100644
--- a/chrome/renderer/resources/extensions/tts_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/tts_custom_bindings.js
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the tts API.
-
-var binding = require('binding').Binding.create('tts');
+// Custom bindings for the tts API.
 
 var ttsNatives = requireNative('tts');
 var GetNextTTSEventId = ttsNatives.GetNextTTSEventId;
@@ -13,9 +11,8 @@
 var sendRequest = require('sendRequest').sendRequest;
 var lazyBG = requireNative('lazy_background_page');
 
-binding.registerCustomHook(function(api) {
+chromeHidden.registerCustomHook('tts', function(api) {
   var apiFunctions = api.apiFunctions;
-  var tts = api.compiledApi;
 
   chromeHidden.tts = {
     handlers: {}
@@ -42,7 +39,7 @@
   // add a listener to chrome.tts.onEvent will fail.
   // See http://crbug.com/122474.
   try {
-    tts.onEvent.addListener(ttsEventListener);
+    chrome.tts.onEvent.addListener(ttsEventListener);
   } catch (e) {}
 
   apiFunctions.setHandleRequest('speak', function() {
@@ -59,5 +56,3 @@
     return id;
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/tts_engine_custom_bindings.js b/chrome/renderer/resources/extensions/tts_engine_custom_bindings.js
index 1d19cea..f16f1438 100644
--- a/chrome/renderer/resources/extensions/tts_engine_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/tts_engine_custom_bindings.js
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the ttsEngine API.
-
-var binding = require('binding').Binding.create('ttsEngine');
+// Custom bindings for the ttsEngine API.
 
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 
@@ -18,5 +16,3 @@
   };
   dispatch([text, options, sendTtsEvent]);
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/types_custom_bindings.js b/chrome/renderer/resources/extensions/types_custom_bindings.js
index 1c386f4..4af67b4d 100644
--- a/chrome/renderer/resources/extensions/types_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/types_custom_bindings.js
@@ -2,15 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the types API.
+// Custom bindings for the types API.
 
-var binding = require('binding').Binding.create('types');
-
-var chrome = requireNative('chrome').GetChrome();
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var sendRequest = require('sendRequest').sendRequest;
 var validate = require('schemaUtils').validate;
 
-binding.registerCustomType('types.ChromeSetting', function() {
+chromeHidden.registerCustomType('types.ChromeSetting', function() {
 
   function extendSchema(schema) {
     var extendedSchema = schema.slice();
@@ -47,5 +45,3 @@
 
   return ChromeSetting;
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/utils.js b/chrome/renderer/resources/extensions/utils.js
index ed35e15..beb9a51 100644
--- a/chrome/renderer/resources/extensions/utils.js
+++ b/chrome/renderer/resources/extensions/utils.js
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-var chrome = requireNative('chrome').GetChrome();
-
 function forEach(dict, f) {
   for (var key in dict) {
     if (dict.hasOwnProperty(key))
@@ -27,16 +25,5 @@
   }
 }
 
-// Specify |currentApi| if this should return an API for $refs in the current
-// namespace.
-function loadRefDependency(ref, currentApi) {
-  var parts = ref.split(".");
-  if (parts.length > 1)
-    return chrome[parts.slice(0, parts.length - 1).join(".")];
-  else
-    return currentApi;
-}
-
 exports.forEach = forEach;
-exports.loadRefDependency = loadRefDependency;
 exports.lookup = lookup;
diff --git a/chrome/renderer/resources/extensions/web_request_custom_bindings.js b/chrome/renderer/resources/extensions/web_request_custom_bindings.js
index 135da99d..9a458ded 100644
--- a/chrome/renderer/resources/extensions/web_request_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/web_request_custom_bindings.js
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the webRequest API.
-
-var binding = require('binding').Binding.create('webRequest');
+// Custom bindings for the webRequest API.
 
 var webRequestNatives = requireNative('web_request');
 var GetUniqueSubEventName = webRequestNatives.GetUniqueSubEventName;
@@ -12,7 +10,6 @@
 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var sendRequest = require('sendRequest').sendRequest;
 var validate = require('schemaUtils').validate;
-var webRequestInternal = require('webRequestInternal').binding;
 
 // WebRequestEvent object. This is used for special webRequest events with
 // extra parameters. Each invocation of addListener creates a new named
@@ -69,7 +66,7 @@
   // Note: this could fail to validate, in which case we would not add the
   // subEvent listener.
   validate(Array.prototype.slice.call(arguments, 1), this.extraArgSchemas_);
-  webRequestInternal.addEventListener(
+  chromeHidden.internalAPIs.webRequestInternal.addEventListener(
       cb, opt_filter, opt_extraInfo, this.eventName_, subEventName);
 
   var subEvent = new chrome.Event(subEventName, this.argSchemas_);
@@ -80,10 +77,10 @@
       var requestId = arguments[0].requestId;
       try {
         var result = cb.apply(null, arguments);
-        webRequestInternal.eventHandled(
+        chromeHidden.internalAPIs.webRequestInternal.eventHandled(
             eventName, subEventName, requestId, result);
       } catch (e) {
-        webRequestInternal.eventHandled(
+        chromeHidden.internalAPIs.webRequestInternal.eventHandled(
             eventName, subEventName, requestId);
         throw e;
       }
@@ -94,7 +91,7 @@
       var details = arguments[0];
       var requestId = details.requestId;
       var handledCallback = function(response) {
-        webRequestInternal.eventHandled(
+        chromeHidden.internalAPIs.webRequestInternal.eventHandled(
             eventName, subEventName, requestId, response);
       };
       cb.apply(null, [details, handledCallback]);
@@ -152,9 +149,9 @@
   this.eventForRules_.getRules(ruleIdentifiers, cb);
 }
 
-binding.registerCustomEvent(WebRequestEvent);
+chromeHidden.registerCustomEvent('webRequest', WebRequestEvent);
 
-binding.registerCustomHook(function(api) {
+chromeHidden.registerCustomHook('webRequest', function(api) {
   var apiFunctions = api.apiFunctions;
 
   apiFunctions.setHandleRequest('handlerBehaviorChanged', function() {
@@ -163,5 +160,3 @@
                 {forIOThread: true});
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/web_request_internal_custom_bindings.js b/chrome/renderer/resources/extensions/web_request_internal_custom_bindings.js
index 584be5f..a33599c4 100644
--- a/chrome/renderer/resources/extensions/web_request_internal_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/web_request_internal_custom_bindings.js
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the webRequestInternal API.
+// Custom bindings for the webRequestInternal API.
 
-var binding = require('binding').Binding.create('webRequestInternal');
-
+var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
 var sendRequest = require('sendRequest').sendRequest;
 
-binding.registerCustomHook(function(api) {
+chromeHidden.registerCustomHook('webRequestInternal', function(api) {
   var apiFunctions = api.apiFunctions;
 
   apiFunctions.setHandleRequest('addEventListener', function() {
@@ -23,5 +22,3 @@
                 {forIOThread: true});
   });
 });
-
-exports.binding = binding.generate();
diff --git a/chrome/renderer/resources/extensions/web_view.js b/chrome/renderer/resources/extensions/web_view.js
index 2fcf058..43c6dc4 100644
--- a/chrome/renderer/resources/extensions/web_view.js
+++ b/chrome/renderer/resources/extensions/web_view.js
@@ -7,9 +7,7 @@
 // The actual tag is implemented via the browser plugin. The internals of this
 // are hidden via Shadow DOM.
 
-var watchForTag = require('tagWatcher').watchForTag;
-
-var chrome = requireNative('chrome').GetChrome();
+var watchForTag = require("tagWatcher").watchForTag;
 
 var WEB_VIEW_ATTRIBUTES = ['name', 'src', 'partition', 'autosize', 'minheight',
     'minwidth', 'maxheight', 'maxwidth'];
diff --git a/chrome/renderer/resources/extensions/web_view_experimental.js b/chrome/renderer/resources/extensions/web_view_experimental.js
index fd7226e..c5fa217 100644
--- a/chrome/renderer/resources/extensions/web_view_experimental.js
+++ b/chrome/renderer/resources/extensions/web_view_experimental.js
@@ -11,7 +11,7 @@
 // permission API would only be available for channels CHANNEL_DEV and
 // CHANNEL_CANARY.
 
-var WebView = require('webView').WebView;
+var WebView = require('webview').WebView;
 
 /** @type {string} */
 var REQUEST_TYPE_MEDIA = 'media';
diff --git a/chrome/renderer/resources/extensions/webstore_custom_bindings.js b/chrome/renderer/resources/extensions/webstore_custom_bindings.js
index 8ef99358..19c8f0f 100644
--- a/chrome/renderer/resources/extensions/webstore_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/webstore_custom_bindings.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Custom binding for the webstore API.
+// Custom bindings for the webstore API.
 
 var webstoreNatives = requireNative('webstore');
 
@@ -49,14 +49,14 @@
   }
 };
 
-// Called by webstore_binding.cc.
+// Called by webstore_bindings.cc.
 var chromeHiddenWebstore = {
   onInstallResponse: function(installId, success, error) {
     installer.onInstallResponse(installId, success, error);
   }
 };
 
-// These must match the names in InstallWebstorebinding in
+// These must match the names in InstallWebstoreBindings in
 // chrome/renderer/extensions/dispatcher.cc.
 exports.chromeWebstore = chromeWebstore;
 exports.chromeHiddenWebstore = chromeHiddenWebstore;
diff --git a/chrome/renderer/resources/renderer_resources.grd b/chrome/renderer/resources/renderer_resources.grd
index 8a95160..593a799 100644
--- a/chrome/renderer/resources/renderer_resources.grd
+++ b/chrome/renderer/resources/renderer_resources.grd
@@ -26,12 +26,14 @@
       <include name="IDR_DISABLED_PLUGIN_HTML" file="disabled_plugin.html" flattenhtml="true" type="BINDATA" />
       <include name="IDR_ERROR_APP_HTML" file="error_app.html" flattenhtml="true" type="BINDATA" />
       <include name="IDR_EVENT_BINDINGS_JS" file="extensions\event.js" type="BINDATA" />
+      <include name="IDR_EXTENSION_APITEST_JS" file="extensions\apitest.js" type="BINDATA" />
       <include name="IDR_GREASEMONKEY_API_JS" file="extensions\greasemonkey_api.js" type="BINDATA" />
       <include name="IDR_SEARCHBOX_API" file="extensions\searchbox_api.js" type="BINDATA" />
       <include name="IDR_JSON_JS" file="extensions\json.js" type="BINDATA" />
       <include name="IDR_JSON_SCHEMA_JS" file="extensions\json_schema.js" type="BINDATA" />
       <include name="IDR_MISCELLANEOUS_BINDINGS_JS" file="extensions\miscellaneous_bindings.js" type="BINDATA" />
       <include name="IDR_NET_ERROR_HTML" file="neterror.html" flattenhtml="true" type="BINDATA" />
+      <include name="IDR_SCHEMA_GENERATED_BINDINGS_JS" file="extensions\schema_generated_bindings.js" type="BINDATA" />
 
       <!-- Libraries. -->
       <include name="IDR_TAG_WATCHER_JS" file="extensions\tag_watcher.js" type="BINDATA" />
@@ -71,13 +73,11 @@
         <include name="IDR_PAGE_ACTION_CUSTOM_BINDINGS_JS" file="extensions\page_action_custom_bindings.js" type="BINDATA" />
         <include name="IDR_PAGE_CAPTURE_CUSTOM_BINDINGS_JS" file="extensions\page_capture_custom_bindings.js" type="BINDATA" />
         <include name="IDR_RUNTIME_CUSTOM_BINDINGS_JS" file="extensions\runtime_custom_bindings.js" type="BINDATA" />
-        <include name="IDR_BINDING_JS" file="extensions\binding.js" type="BINDATA" />
         <include name="IDR_STORAGE_CUSTOM_BINDINGS_JS" file="extensions\storage_custom_bindings.js" type="BINDATA" />
         <include name="IDR_SYNC_FILE_SYSTEM_CUSTOM_BINDINGS_JS" file="extensions\sync_file_system_custom_bindings.js" type="BINDATA" />
         <include name="IDR_SYSTEM_INDICATOR_CUSTOM_BINDINGS_JS" file="extensions\system_indicator_custom_bindings.js" type="BINDATA" />
         <include name="IDR_TAB_CAPTURE_CUSTOM_BINDINGS_JS" file="extensions\tab_capture_custom_bindings.js" type="BINDATA" />
         <include name="IDR_TABS_CUSTOM_BINDINGS_JS" file="extensions\tabs_custom_bindings.js" type="BINDATA" />
-        <include name="IDR_TEST_CUSTOM_BINDINGS_JS" file="extensions\test_custom_bindings.js" type="BINDATA" />
         <include name="IDR_TTS_CUSTOM_BINDINGS_JS" file="extensions\tts_custom_bindings.js" type="BINDATA" />
         <include name="IDR_TTS_ENGINE_CUSTOM_BINDINGS_JS" file="extensions\tts_engine_custom_bindings.js" type="BINDATA" />
         <include name="IDR_TYPES_CUSTOM_BINDINGS_JS" file="extensions\types_custom_bindings.js" type="BINDATA" />